UNPKG

blockstack

Version:

The Blockstack Javascript library for authentication, identity, and storage.

129 lines (114 loc) 3.58 kB
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 AesCipher { encrypt( algorithm: CipherAlgorithm, key: Buffer, iv: Buffer, data: Buffer): Promise<Buffer>; decrypt( algorithm: CipherAlgorithm, key: Buffer, iv: Buffer, data: Buffer): Promise<Buffer>; } export class NodeCryptoAesCipher implements AesCipher { createCipher: NodeCryptoCreateCipher createDecipher: NodeCryptoCreateDecipher constructor(createCipher: NodeCryptoCreateCipher, createDecipher: NodeCryptoCreateDecipher) { this.createCipher = createCipher this.createDecipher = createDecipher } async encrypt( algorithm: CipherAlgorithm, key: Buffer, iv: Buffer, data: Buffer): Promise<Buffer> { if (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: Buffer, iv: Buffer, data: Buffer): Promise<Buffer> { if (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) } } export class WebCryptoAesCipher implements AesCipher { subtleCrypto: SubtleCrypto constructor(subtleCrypto: SubtleCrypto) { this.subtleCrypto = subtleCrypto } async encrypt( algorithm: CipherAlgorithm, key: Buffer, iv: Buffer, data: Buffer): 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: Buffer, iv: Buffer, data: Buffer): 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<AesCipher> { const cryptoLib = await getCryptoLib() if (cryptoLib.name === 'subtleCrypto') { return new WebCryptoAesCipher(cryptoLib.lib) } else { return new NodeCryptoAesCipher(cryptoLib.lib.createCipheriv, cryptoLib.lib.createDecipheriv) } }