import { Buffer } from '@stacks/common' ; 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 , key , iv , data ) : Promise < Buffer > ; decrypt ( algorithm , key , iv , data ) : Promise < Buffer > ; } export class NodeCryptoAesCipher implements AesCipher { createCipher : NodeCryptoCreateCipher ; createDecipher : NodeCryptoCreateDecipher ; constructor ( createCipher , createDecipher ) { this . createCipher = createCipher ; this . createDecipher = createDecipher ; } async encrypt ( algorithm , key , iv , data ) { 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 , key , iv , data ) { 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 ) { this . subtleCrypto = subtleCrypto ; } async encrypt ( algorithm , key , iv , data ) { let algo ; let length ; 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 , key , iv , data ) { let algo ; let length ; 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 ( ) { const cryptoLib = await getCryptoLib ( ) ; if ( cryptoLib . name === 'subtleCrypto' ) { return new WebCryptoAesCipher ( cryptoLib . lib ) ; } else { return new NodeCryptoAesCipher ( cryptoLib . lib . createCipheriv , cryptoLib . lib . createDecipheriv ) ; } }