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 | 8x 8x 8x 8x 8x 8x 8x 8x 8x 3x 8x 3x 3x 2x 1x 8x 8x 2x 8x 8x 2x 6x 6x 8x 7x 7x 5x 5x 1x 3x 7x 7x 7x 7x 7x 7x 5x 3x 2x | import { createDecipheriv } from 'crypto' import { inflateRaw } from 'zlib' import { promisify } from 'util' import { EventResponse } from './types' import { UnsealAggregateError, UnsealError } from './errors/unsealError' import { Buffer } from 'buffer' const asyncInflateRaw = promisify(inflateRaw) export enum DecryptionAlgorithm { Aes256Gcm = 'aes-256-gcm', } export interface DecryptionKey { key: Buffer algorithm: DecryptionAlgorithm } const SEALED_HEADER = Buffer.from([0x9e, 0x85, 0xdc, 0xed]) function isEventResponse(data: unknown): data is EventResponse { return Boolean(data && typeof data === 'object' && 'products' in data) } /** * @private * */ export function parseEventsResponse(unsealed: string): EventResponse { const json = JSON.parse(unsealed) if (!isEventResponse(json)) { throw new Error('Sealed data is not valid events response') } return json } /** * Decrypts the sealed response with the provided keys. * The SDK will try to decrypt the result with each key until it succeeds. * To learn more about sealed results visit: https://dev.fingerprint.com/docs/sealed-client-results */ export async function unsealEventsResponse( sealedData: Buffer, decryptionKeys: DecryptionKey[] ): Promise<EventResponse> { const unsealed = await unseal(sealedData, decryptionKeys) return parseEventsResponse(unsealed) } /** * @private * */ export async function unseal(sealedData: Buffer, decryptionKeys: DecryptionKey[]) { if (sealedData.subarray(0, SEALED_HEADER.length).toString('hex') !== SEALED_HEADER.toString('hex')) { throw new Error('Invalid sealed data header') } const errors = new UnsealAggregateError([]) for (const decryptionKey of decryptionKeys) { switch (decryptionKey.algorithm) { case DecryptionAlgorithm.Aes256Gcm: try { return await unsealAes256Gcm(sealedData, decryptionKey.key) } catch (e) { errors.addError(new UnsealError(decryptionKey, e as Error)) continue } default: throw new Error(`Unsupported decryption algorithm: ${decryptionKey.algorithm}`) } } throw errors } async function unsealAes256Gcm(sealedData: Buffer, decryptionKey: Buffer) { const nonceLength = 12 const nonce = sealedData.subarray(SEALED_HEADER.length, SEALED_HEADER.length + nonceLength) const authTagLength = 16 const authTag = sealedData.subarray(-authTagLength) const ciphertext = sealedData.subarray(SEALED_HEADER.length + nonceLength, -authTagLength) const decipher = createDecipheriv('aes-256-gcm', decryptionKey, nonce).setAuthTag(authTag) const compressed = Buffer.concat([decipher.update(ciphertext), decipher.final()]) const payload = await asyncInflateRaw(compressed) return payload.toString() } |