# All files / src/encryption pbkdf2Polyfill.ts

0% Statements 0/48
0% Branches 0/10
0% Functions 0/5
0% Lines 0/42

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

     /*
    An async implementation for browsers that support WebCrypto sha2 hash support
    but not pbkdf2.

    Extracted from crypto-browserify/pbkdf2 and modified to use WebCrypto
    for hashing operations.

    See original at: https://github.com/crypto-browserify/pbkdf2/tree/v3.0.17/lib
    */

    export async function pbkdf2NoHmacSupport(
      key: Buffer,
      salt: Buffer,
      iterations: number,
      keyLength: number,
      algorithm: 'sha256' | 'sha512'
    ) {
      const algo = algorithm === 'sha512' ? 'SHA-512' : 'SHA-256'
      const subtleCrypto = crypto.subtle
      const hash = (data: Uint8Array) => subtleCrypto
        .digest(algo, data)
        .then(hash => Buffer.from(hash))

      const saltLength = salt.length
      const blocksize = algorithm === 'sha512' ? 128 : 64
      const size = algorithm === 'sha512' ? 64 : 32

      if (key.length > blocksize) {
        key = await hash(key)
      } else if (key.length < blocksize) {
        const ZEROS = Buffer.alloc(128)
        key = Buffer.concat([key, ZEROS], blocksize)
      }

      const ipad2 = Buffer.alloc(blocksize + size)
      const opad = Buffer.alloc(blocksize + size)
      for (let i = 0; i < blocksize; i++) {
        ipad2[i] = key[i] ^ 0x36
        opad[i] = key[i] ^ 0x5C
      }

      const ipad1 = Buffer.alloc(blocksize + saltLength + 4)
      ipad2.copy(ipad1, 0, 0, blocksize)

      const DK = Buffer.alloc(keyLength)
      const block1 = Buffer.alloc(saltLength + 4)
      salt.copy(block1, 0, 0, saltLength)

      let destPos = 0
      const hLen = size
      const l = Math.ceil(keyLength / hLen)

      const run = async (data: Buffer, ipad: Buffer) => {
        data.copy(ipad, blocksize)
        const h = await hash(ipad)
        h.copy(opad, blocksize)
        return hash(opad)
      }

      for (let i = 1; i <= l; i++) {
        block1.writeUInt32BE(i, saltLength)

        // eslint-disable-next-line no-await-in-loop
        const T = await run(block1, ipad1)
        let U = T

        for (let j = 1; j < iterations; j++) {
          // eslint-disable-next-line no-await-in-loop
          U = await run(U, ipad2)
          for (let k = 0; k < hLen; k++) {
            T[k] ^= U[k]
          }
        }

        T.copy(DK, destPos)
        destPos += hLen
      }

      return DK
    }