All files / src/encryption pbkdf2.ts

45.83% Statements 11/24
33.33% Branches 4/12
71.43% Functions 5/7
45.83% Lines 11/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 861x                                     6x                   6x     6x 6x 6x     6x                                                                             1x 6x 6x     6x        
import { getCryptoLib } from './cryptoUtils'
 
export type Pbkdf2Digests = 'sha512' | 'sha256'
 
export interface Pbkdf2 {
  derive(
    password: string, 
    salt: NodeJS.TypedArray,
    iterations: number, 
    keyLength: number, 
    digest: Pbkdf2Digests): Promise<Buffer>;
}
 
type NodePbkdf2Fn = typeof import('crypto').pbkdf2
 
class NodeCryptoPbkdf2 implements Pbkdf2 {
  pbkdf2: NodePbkdf2Fn
 
  constructor(pbkdf2: NodePbkdf2Fn) {
    this.pbkdf2 = pbkdf2
  }
 
  async derive(
    password: string, 
    salt: NodeJS.TypedArray,
    iterations: number, 
    keyLength: number, 
    digest: Pbkdf2Digests): 
    Promise<Buffer> {
    Iif (digest !== 'sha512' && digest !== 'sha256') {
      throw new Error(`Unsupported digest "${digest}" for Pbkdf2`)
    }
    return new Promise((resolve, reject) => {
      this.pbkdf2(password, salt, iterations, keyLength, digest, (error, result) => {
        Iif (error) {
          reject(error)
        }
        resolve(result)
      })
    })
  }
}
 
class WebCryptoPbkdf2 implements Pbkdf2 {
  subtleCrypto: SubtleCrypto
 
  constructor(subtleCrypto: SubtleCrypto) {
    this.subtleCrypto = subtleCrypto
  }
 
  async derive(
    password: string, 
    salt: NodeJS.TypedArray,
    iterations: number, 
    keyLength: number, 
    digest: Pbkdf2Digests): 
    Promise<Buffer> {
    let algo: string
    if (digest === 'sha256') {
      algo = 'SHA-256'
    } else if (digest === 'sha512') {
      algo = 'SHA-512'
    } else {
      throw new Error(`Unsupported Pbkdf2 digest algorithm "${digest}"`)
    }
    const passwordBytes = Buffer.from(password)
    const key = await this.subtleCrypto.importKey(
      'raw', passwordBytes, 'PBKDF2', false, ['deriveBits']
    )
    const result = await this.subtleCrypto.deriveBits({
      name: 'PBKDF2', salt, iterations, hash: { name: algo }
    }, key, keyLength * 8)
    return Buffer.from(result)
  }
}
 
export async function createPbkdf2(): Promise<Pbkdf2> {
  const cryptoLib = await getCryptoLib()
  Iif (cryptoLib.name === 'subtleCrypto') {
    return new WebCryptoPbkdf2(cryptoLib.lib)
  } else {
    return new NodeCryptoPbkdf2(cryptoLib.lib.pbkdf2)
  }
}