# 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)
      }
    }