import { EncryptionPayload, JsonRpcRequest, JsonRpcSuccess, JsonRpcFailure, } from '@binance/w3w-types' import { arrayToBuffer, hexToArray, arrayToHex, bufferToArray, concatArrays, utf8ToArray, arrayToUtf8, removeHexPrefix, convertArrayBufferToBuffer, convertBufferToArrayBuffer, } from '../encoding' import * as lib from './lib' export function generateKey(length?: number): ArrayBuffer { const _length = (length || 256) / 8 const bytes = lib.randomBytes(_length) const result = convertBufferToArrayBuffer(arrayToBuffer(bytes)) return result } function verifyHmac(payload: EncryptionPayload, key: Uint8Array): boolean { const cipherText = hexToArray(payload.data) const iv = hexToArray(payload.iv) const hmac = hexToArray(payload.hmac) const hmacHex: string = arrayToHex(hmac, false) const unsigned = concatArrays(cipherText, iv) const chmac = lib.hmacSha256Sign(key, unsigned) const chmacHex: string = arrayToHex(chmac, false) if (removeHexPrefix(hmacHex) === removeHexPrefix(chmacHex)) { return true } return false } export function encrypt( data: JsonRpcRequest, key: ArrayBuffer, providedIv?: ArrayBuffer ): EncryptionPayload { const _key = bufferToArray(convertArrayBufferToBuffer(key)) const ivArrayBuffer: ArrayBuffer = providedIv || generateKey(128) const iv = bufferToArray(convertArrayBufferToBuffer(ivArrayBuffer)) const ivHex: string = arrayToHex(iv, false) const contentString: string = JSON.stringify(data) const content = utf8ToArray(contentString) const cipherText = lib.aesCbcEncrypt(iv, _key, content) const cipherTextHex: string = arrayToHex(cipherText, false) const unsigned = concatArrays(cipherText, iv) const hmac = lib.hmacSha256Sign(_key, unsigned) const hmacHex: string = arrayToHex(hmac, false) return { data: cipherTextHex, hmac: hmacHex, iv: ivHex, } } export function decrypt( payload: EncryptionPayload, key: ArrayBuffer ): JsonRpcRequest | JsonRpcSuccess | JsonRpcFailure | null { const _key = bufferToArray(convertArrayBufferToBuffer(key)) if (!_key) { throw new Error('Missing key: required for decryption') } const verified: boolean = verifyHmac(payload, _key) if (!verified) { return null } const cipherText = hexToArray(payload.data) const iv = hexToArray(payload.iv) const buffer = lib.aesCbcDecrypt(iv, _key, cipherText) const utf8: string = arrayToUtf8(buffer) let data try { data = JSON.parse(utf8) } catch (error) { return null } return data }