@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
JavaScript
"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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