# All files / src/encryption aesCipher.ts

38.46% Statements 15/39
38.89% Branches 7/18
57.14% Functions 4/7
38.46% Lines 15/39

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
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    1291x

    40x
    40x

    21x

    21x
    21x
    21x

    19x

    19x
    19x
    17x

    1x
    40x
    40x

    40x

     import { getCryptoLib } from './cryptoUtils'

    type NodeCryptoCreateCipher = typeof import('crypto').createCipheriv;
    type NodeCryptoCreateDecipher = typeof import('crypto').createDecipheriv;

    export type CipherAlgorithm = 'aes-256-cbc' | 'aes-128-cbc';

    export interface Cipher {
      encrypt(
        algorithm: CipherAlgorithm,
        key: NodeJS.TypedArray,
        iv: NodeJS.TypedArray,
        data: NodeJS.TypedArray): Promise<Buffer>;

      decrypt(
        algorithm: CipherAlgorithm,
        key: NodeJS.TypedArray,
        iv: NodeJS.TypedArray,
        data: NodeJS.TypedArray): Promise<Buffer>;
    }

    class NodeCryptoCipher implements Cipher {
      createCipher: NodeCryptoCreateCipher

      createDecipher: NodeCryptoCreateDecipher

      constructor(createCipher: NodeCryptoCreateCipher, createDecipher: NodeCryptoCreateDecipher) {
        this.createCipher = createCipher
        this.createDecipher = createDecipher
      }

      async encrypt(
        algorithm: CipherAlgorithm,
        key: NodeJS.TypedArray,
        iv: NodeJS.TypedArray,
        data: NodeJS.TypedArray):
        Promise<Buffer> {
        Iif (algorithm !== 'aes-128-cbc' && algorithm !== 'aes-256-cbc') {
          throw new Error(`Unsupported cipher algorithm "${algorithm}"`)
        }
        const cipher = this.createCipher(algorithm, key, iv)
        const result = Buffer.concat([cipher.update(data), cipher.final()])
        return Promise.resolve(result)
      }

      async decrypt(
        algorithm: CipherAlgorithm,
        key: NodeJS.TypedArray,
        iv: NodeJS.TypedArray,
        data: NodeJS.TypedArray):
        Promise<Buffer> {
        Iif (algorithm !== 'aes-128-cbc' && algorithm !== 'aes-256-cbc') {
          throw new Error(`Unsupported cipher algorithm "${algorithm}"`)
        }
        const cipher = this.createDecipher(algorithm, key, iv)
        const result = Buffer.concat([cipher.update(data), cipher.final()])
        return Promise.resolve(result)
      }
    }

    class WebCryptoCipher implements Cipher {
      subtleCrypto: SubtleCrypto

      constructor(subtleCrypto: SubtleCrypto) {
        this.subtleCrypto = subtleCrypto
      }

      async encrypt(
        algorithm: CipherAlgorithm,
        key: NodeJS.TypedArray,
        iv: NodeJS.TypedArray,
        data: NodeJS.TypedArray):
        Promise<Buffer> {
        let algo: string
        let length: number
        if (algorithm === 'aes-128-cbc') {
          algo = 'AES-CBC'
          length = 128
        } else if (algorithm === 'aes-256-cbc') {
          algo = 'AES-CBC'
          length = 256
        } else {
          throw new Error(`Unsupported cipher algorithm "${algorithm}"`)
        }
        const cryptoKey = await this.subtleCrypto.importKey(
          'raw', key,
          { name: algo, length },
          false, ['encrypt']
        )
        const result = await this.subtleCrypto.encrypt({ name: algo, iv }, cryptoKey, data)
        return Buffer.from(result)
      }

      async decrypt(
        algorithm: CipherAlgorithm,
        key: NodeJS.TypedArray,
        iv: NodeJS.TypedArray,
        data: NodeJS.TypedArray):
        Promise<Buffer> {
        let algo: string
        let length: number
        if (algorithm === 'aes-128-cbc') {
          algo = 'AES-CBC'
          length = 128
        } else if (algorithm === 'aes-256-cbc') {
          algo = 'AES-CBC'
          length = 256
        } else {
          throw new Error(`Unsupported cipher algorithm "${algorithm}"`)
        }
        const cryptoKey = await this.subtleCrypto.importKey(
          'raw', key,
          { name: algo, length },
          false, ['decrypt']
        )
        const result = await this.subtleCrypto.decrypt({ name: algo, iv }, cryptoKey, data)
        return Buffer.from(result)
      }
    }

    export async function createCipher(): Promise<Cipher> {
      const cryptoLib = await getCryptoLib()
      Iif (cryptoLib.name === 'subtleCrypto') {
        return new WebCryptoCipher(cryptoLib.lib)
      } else {
        return new NodeCryptoCipher(cryptoLib.lib.createCipheriv, cryptoLib.lib.createDecipheriv)
      }
    }