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 | 9x 9x 9x 9x 9x 9x 9x 9x 9x 4x 9x 4x 4x 2x 2x 9x 9x 3x 9x 9x 2x 7x 7x 10x 9x 9x 6x 6x 1x 3x 9x 9x 9x 9x 9x 9x 7x 4x 3x | import { createDecipheriv } from 'crypto'
import { inflateRaw } from 'zlib'
import { promisify } from 'util'
import { EventsGetResponse } 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 | `${DecryptionAlgorithm}`
}
const SEALED_HEADER = Buffer.from([0x9e, 0x85, 0xdc, 0xed])
function isEventResponse(data: unknown): data is EventsGetResponse {
return Boolean(data && typeof data === 'object' && 'products' in data)
}
/**
* @private
* */
export function parseEventsResponse(unsealed: string): EventsGetResponse {
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
* @throws UnsealAggregateError
* @throws Error
*/
export async function unsealEventsResponse(
sealedData: Buffer,
decryptionKeys: DecryptionKey[]
): Promise<EventsGetResponse> {
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()
}
|