UNPKG

@zkp2p/reclaim-witness-sdk

Version:

<div> <div> <img src="https://raw.githubusercontent.com/reclaimprotocol/.github/main/assets/banners/Attestor-Core.png" /> </div> </div>

420 lines 35.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createClaimOnAttestor = createClaimOnAttestor; const tls_1 = require("@reclaimprotocol/tls"); const make_rpc_tls_tunnel_1 = require("../client/tunnels/make-rpc-tls-tunnel"); const attestor_pool_1 = require("../client/utils/attestor-pool"); const config_1 = require("../config"); const api_1 = require("../proto/api"); const providers_1 = require("../providers"); const utils_1 = require("../utils"); const retries_1 = require("../utils/retries"); const signatures_1 = require("../utils/signatures"); const tls_2 = require("../utils/tls"); /** * Create a claim on the attestor */ function createClaimOnAttestor({ logger: _logger, maxRetries = 3, ...opts }) { const logger = _logger // if the client has already been initialised // and no logger is provided, use the client's logger // otherwise default to the global logger || ('logger' in opts.client ? opts.client.logger : utils_1.logger); return (0, retries_1.executeWithRetries)(attempt => (_createClaimOnAttestor({ ...opts, logger: attempt ? logger.child({ attempt }) : logger })), { maxRetries, logger, shouldRetry }); } function shouldRetry(err) { if (err instanceof TypeError) { return false; } return err instanceof utils_1.AttestorError && err.code !== 'ERROR_INVALID_CLAIM' && err.code !== 'ERROR_BAD_REQUEST' && err.code !== 'ERROR_AUTHENTICATION_FAILED'; } async function _createClaimOnAttestor({ name, params, secretParams, context, onStep, ownerPrivateKey, client: clientInit, logger = utils_1.logger, timestampS, updateProviderParams, updateParametersFromOprfData = true, ...zkOpts }) { const provider = providers_1.providers[name]; const hostPort = (0, utils_1.getProviderValue)(params, provider.hostPort, secretParams); const geoLocation = (0, utils_1.getProviderValue)(params, provider.geoLocation, secretParams); const providerTlsOpts = (0, utils_1.getProviderValue)(params, provider.additionalClientOptions); const tlsOpts = { ...(0, tls_2.getDefaultTlsOptions)(), ...providerTlsOpts, }; const { zkEngine = 'snarkjs' } = zkOpts; let redactionMode = (0, utils_1.getProviderValue)(params, provider.writeRedactionMode); const [host, port] = hostPort.split(':'); const resParser = (0, utils_1.makeHttpResponseParser)(); let client; let lastMsgRevealed = false; const revealMap = new Map(); onStep === null || onStep === void 0 ? void 0 : onStep({ name: 'connecting' }); let endedHttpRequest; const createTunnelReq = { host, port: port ? +port : config_1.DEFAULT_HTTPS_PORT, geoLocation, id: (0, utils_1.generateTunnelId)() }; const authRequest = 'authRequest' in clientInit ? (typeof clientInit.authRequest === 'function' ? await clientInit.authRequest() : clientInit.authRequest) : undefined; const tunnel = await (0, make_rpc_tls_tunnel_1.makeRpcTlsTunnel)({ tlsOpts, connect: (connectMsgs) => { let created = false; if ('metadata' in clientInit) { client = clientInit; } else { client = (0, attestor_pool_1.getAttestorClientFromPool)(clientInit.url, () => { created = true; return { authRequest: authRequest, initMessages: connectMsgs, logger }; }); } if (!created) { client .waitForInit() .then(() => client.sendMessage(...connectMsgs)) .catch(err => { logger.error({ err }, 'error in sending init msgs'); }); } return client; }, logger, request: createTunnelReq, onMessage(data) { logger.debug({ bytes: data.length }, 'recv data from server'); resParser.onChunk(data); if (resParser.res.complete) { logger === null || logger === void 0 ? void 0 : logger.debug('got complete HTTP response from server'); // wait a little bit to make sure the client has // finished writing the response setTimeout(() => { endedHttpRequest === null || endedHttpRequest === void 0 ? void 0 : endedHttpRequest(); }, 100); } }, onClose(err) { const level = err ? 'error' : 'debug'; logger === null || logger === void 0 ? void 0 : logger[level]({ err }, 'tls session ended'); endedHttpRequest === null || endedHttpRequest === void 0 ? void 0 : endedHttpRequest(err); try { resParser.streamEnded(); } catch (_a) { } }, }); const { version: tlsVersion, cipherSuite } = tunnel.tls.getMetadata(); if (tlsVersion === 'TLS1_2' && redactionMode !== 'zk') { redactionMode = 'zk'; logger.info('TLS1.2 detected, defaulting to zk redaction mode'); } const { redactions, data: requestStr } = provider.createRequest( // @ts-ignore secretParams, params, logger); const requestData = typeof requestStr === 'string' ? (0, tls_1.strToUint8Array)(requestStr) : requestStr; logger.debug({ redactions: redactions.length }, 'generated request'); const waitForAllData = new Promise((resolve, reject) => { endedHttpRequest = err => (err ? reject(err) : resolve()); }); onStep === null || onStep === void 0 ? void 0 : onStep({ name: 'sending-request-data' }); try { if (redactionMode === 'zk') { await writeRedactedZk(); } else { await writeRedactedWithKeyUpdate(); } logger.info('wrote request to server'); } catch (err) { // wait for complete stream end when the session is closed // mid-write, as this means the server could not process // our request due to some error. Hope the stream end // error will be more descriptive logger.error({ err }, 'session errored during write, waiting for stream end'); } onStep === null || onStep === void 0 ? void 0 : onStep({ name: 'waiting-for-response' }); await waitForAllData; await tunnel.close(); logger.info('got full response from server'); // update the response selections if (updateProviderParams) { const { params: updatedParms, secretParams: updatedSecretParms } = await updateProviderParams(tunnel.transcript, tlsVersion !== null && tlsVersion !== void 0 ? tlsVersion : 'TLS1_2'); params = { ...params, ...updatedParms }; secretParams = { ...secretParams, ...updatedSecretParms }; } const signatureAlg = signatures_1.SIGNATURES[client.metadata.signatureType]; let serverIV; let clientIV; const [serverBlock] = getLastBlocks('server', 1); if (serverBlock && serverBlock.message.type === 'ciphertext') { serverIV = serverBlock.message.fixedIv; } const [clientBlock] = getLastBlocks('client', 1); if (clientBlock && clientBlock.message.type === 'ciphertext') { clientIV = clientBlock.message.fixedIv; } const transcript = await generateTranscript(); // now that we have the full transcript, we need // to generate the ZK proofs & send them to the attestor // to verify & sign our claim const claimTunnelReq = api_1.ClaimTunnelRequest.create({ request: createTunnelReq, data: { provider: name, parameters: (0, utils_1.canonicalStringify)(params), context: (0, utils_1.canonicalStringify)(context), timestampS: timestampS !== null && timestampS !== void 0 ? timestampS : (0, utils_1.unixTimestampSeconds)(), owner: getAddress(), }, transcript: transcript, zkEngine: zkEngine === 'gnark' ? api_1.ZKProofEngine.ZK_ENGINE_GNARK : api_1.ZKProofEngine.ZK_ENGINE_SNARKJS, fixedServerIV: serverIV, fixedClientIV: clientIV, }); onStep === null || onStep === void 0 ? void 0 : onStep({ name: 'waiting-for-verification' }); const claimTunnelBytes = api_1.ClaimTunnelRequest .encode(claimTunnelReq).finish(); const requestSignature = await signatureAlg .sign(claimTunnelBytes, ownerPrivateKey); claimTunnelReq.signatures = { requestSignature }; const result = await client.rpc('claimTunnel', claimTunnelReq); logger.info({ success: !!result.claim }, 'recv claim response'); return result; async function writeRedactedWithKeyUpdate() { var _a; let currentIndex = 0; for (const section of redactions) { const block = requestData .slice(currentIndex, section.fromIndex); if (block.length) { await writeWithReveal(block, true); } const redacted = requestData .slice(section.fromIndex, section.toIndex); await writeWithReveal(redacted, false); currentIndex = section.toIndex; } // write if redactions were there const lastBlockStart = ((_a = redactions === null || redactions === void 0 ? void 0 : redactions[redactions.length - 1]) === null || _a === void 0 ? void 0 : _a.toIndex) || 0; const block = requestData.slice(lastBlockStart); if (block.length) { await writeWithReveal(block, true); } } async function writeRedactedZk() { let blocksWritten = tunnel.transcript.length; await tunnel.tls.write(requestData); blocksWritten = tunnel.transcript.length - blocksWritten; setRevealOfLastSentBlocks({ type: 'zk', redactedPlaintext: (0, utils_1.redactSlices)(requestData, redactions) }, blocksWritten); } /** * Write data to the tunnel, with the option to mark the packet * as revealable to the attestor or not */ async function writeWithReveal(data, reveal) { // if the reveal state has changed, update the traffic keys // to not accidentally reveal a packet not meant to be revealed // and vice versa if (reveal !== lastMsgRevealed) { await tunnel.tls.updateTrafficKeys(); } let blocksWritten = tunnel.transcript.length; await tunnel.write(data); blocksWritten = tunnel.transcript.length - blocksWritten; // now we mark the packet to be revealed to the attestor setRevealOfLastSentBlocks(reveal ? { type: 'complete' } : undefined, blocksWritten); lastMsgRevealed = reveal; } function setRevealOfLastSentBlocks(reveal, nBlocks = 1) { const lastBlocks = getLastBlocks('client', nBlocks); if (!lastBlocks.length) { return; } for (const block of lastBlocks) { setRevealOfMessage(block.message, reveal); } } function getLastBlocks(sender, nBlocks) { // set the correct index for the server blocks const lastBlocks = []; for (let i = tunnel.transcript.length - 1; i >= 0; i--) { const block = tunnel.transcript[i]; if (block.sender === sender) { lastBlocks.push(block); if (lastBlocks.length === nBlocks) { break; } } } return lastBlocks; } /** * Generate transcript with reveal data for the attestor to verify */ async function generateTranscript() { await addServerSideReveals(); const startMs = Date.now(); const revealedMessages = await (0, utils_1.preparePacketsForReveal)(tunnel.transcript, revealMap, { logger, cipherSuite: cipherSuite, onZkProgress(done, total) { const timeSinceStartMs = Date.now() - startMs; const timePerBlockMs = timeSinceStartMs / done; const timeLeftMs = timePerBlockMs * (total - done); onStep === null || onStep === void 0 ? void 0 : onStep({ name: 'generating-zk-proofs', proofsDone: done, proofsTotal: total, approxTimeLeftS: Math.round(timeLeftMs / 1000), }); }, ...zkOpts, }); return revealedMessages; } /** * Add reveals for server side blocks, using * the provider's redaction function if available. * Otherwise, opts to reveal all server side blocks. */ async function addServerSideReveals() { const allPackets = tunnel.transcript; let serverPacketsToReveal = 'all'; const packets = []; const serverBlocks = []; for (const b of allPackets) { if (b.message.type !== 'ciphertext' || !(0, utils_1.isApplicationData)(b.message, tlsVersion)) { continue; } const plaintext = tlsVersion === 'TLS1_3' ? b.message.plaintext.slice(0, -1) : b.message.plaintext; packets.push({ message: plaintext, sender: b.sender }); if (b.sender === 'server') { serverBlocks.push({ plaintext: plaintext, message: b.message }); } } if (provider.getResponseRedactions) { serverPacketsToReveal = await (0, utils_1.getBlocksToReveal)(serverBlocks, total => provider.getResponseRedactions({ response: total, params, logger, ctx: config_1.PROVIDER_CTX }), performOprf); } const revealedPackets = packets .filter(p => p.sender === 'client'); if (serverPacketsToReveal === 'all') { // reveal all server side blocks for (const { message, sender } of allPackets) { if (sender === 'server') { setRevealOfMessage(message, { type: 'complete' }); } } revealedPackets.push(...packets.filter(p => p.sender === 'server')); } else { for (const { block, redactedPlaintext, toprfs } of serverPacketsToReveal) { setRevealOfMessage(block.message, { type: 'zk', redactedPlaintext, toprfs }); revealedPackets.push({ sender: 'server', message: redactedPlaintext }); if (updateParametersFromOprfData && toprfs) { let strParams = (0, utils_1.canonicalStringify)(params); for (const toprf of toprfs) { strParams = strParams.replaceAll((0, utils_1.uint8ArrayToStr)(toprf.plaintext), (0, utils_1.binaryHashToStr)(toprf.nullifier, toprf.dataLocation.length)); } params = JSON.parse(strParams); } } } await provider.assertValidProviderReceipt({ receipt: revealedPackets, params: { ...params, // provide secret params for proper // request body validation secretParams, }, logger, ctx: config_1.PROVIDER_CTX }); // reveal all handshake blocks // so the attestor can verify there was no // hanky-panky for (const p of allPackets) { if (p.message.type !== 'ciphertext') { continue; } // break the moment we hit the first // application data packet if ((0, utils_1.isApplicationData)(p.message, tlsVersion)) { break; } setRevealOfMessage(p.message, { type: 'complete' }); } } async function performOprf(plaintext) { var _a; logger.info({ length: plaintext.length }, 'generating OPRF...'); const oprfOperator = ((_a = zkOpts.oprfOperators) === null || _a === void 0 ? void 0 : _a['chacha20']) || (0, utils_1.makeDefaultOPRFOperator)('chacha20', zkEngine, logger); const reqData = await oprfOperator.generateOPRFRequestData(plaintext, config_1.TOPRF_DOMAIN_SEPARATOR, logger); const res = await client.rpc('toprf', { maskedData: reqData.maskedData, engine: (0, utils_1.getEngineProto)(zkEngine) }); const nullifier = await oprfOperator.finaliseOPRF(client.initResponse.toprfPublicKey, reqData, [res]); const data = { nullifier, responses: [res], mask: reqData.mask, dataLocation: undefined, plaintext }; return data; } function setRevealOfMessage(message, reveal) { if (reveal) { revealMap.set(message, reveal); return; } revealMap.delete(message); } function getAddress() { const { getAddress, getPublicKey } = signatureAlg; const pubKey = getPublicKey(ownerPrivateKey); return getAddress(pubKey); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLWNsYWltLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9jcmVhdGUtY2xhaW0udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUE0Q0Esc0RBcUJDO0FBakVELDhDQUF3RTtBQUN4RSxnRkFBeUU7QUFDekUsa0VBQTBFO0FBQzFFLHVDQUFxRjtBQUNyRix1Q0FBaUU7QUFDakUsNkNBQXlDO0FBU3pDLHFDQWlCa0I7QUFDbEIsK0NBQXNEO0FBQ3RELHFEQUFpRDtBQUNqRCx1Q0FBb0Q7QUFPcEQ7O0dBRUc7QUFDSCxTQUFnQixxQkFBcUIsQ0FDcEMsRUFDQyxNQUFNLEVBQUUsT0FBTyxFQUFFLFVBQVUsR0FBRyxDQUFDLEVBQUUsR0FBRyxJQUFJLEVBQ1Y7SUFFL0IsTUFBTSxNQUFNLEdBQUcsT0FBTztRQUNyQiw2Q0FBNkM7UUFDN0MscURBQXFEO1FBQ3JELHlDQUF5QztXQUN0QyxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsY0FBTSxDQUFDLENBQUE7SUFDM0QsT0FBTyxJQUFBLDRCQUFrQixFQUN4QixPQUFPLENBQUMsRUFBRSxDQUFDLENBQ1Ysc0JBQXNCLENBQUk7UUFDekIsR0FBRyxJQUFJO1FBQ1AsTUFBTSxFQUFFLE9BQU87WUFDZCxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDO1lBQzNCLENBQUMsQ0FBQyxNQUFNO0tBQ1QsQ0FBQyxDQUNGLEVBQ0QsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxDQUNuQyxDQUFBO0FBQ0YsQ0FBQztBQUVELFNBQVMsV0FBVyxDQUFDLEdBQVU7SUFDOUIsSUFBRyxHQUFHLFlBQVksU0FBUyxFQUFFLENBQUM7UUFDN0IsT0FBTyxLQUFLLENBQUE7SUFDYixDQUFDO0lBRUQsT0FBTyxHQUFHLFlB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