@reclaimprotocol/attestor-core
Version:
<div> <div> <img src="https://raw.githubusercontent.com/reclaimprotocol/.github/main/assets/banners/Attestor-Core.png" /> </div> </div>
424 lines • 36.4 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) {
var _a;
if (err instanceof TypeError) {
return false;
}
// possibly a network error, or the server
// closed the connection before we received the full data
if ((_a = err.message) === null || _a === void 0 ? void 0 : _a.includes('stream ended before')) {
return true;
}
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)()
};
logger = logger.child({ tunnelId: createTunnelReq.id });
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('session closed, processing response');
// 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLWNsYWltLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9jcmVhdGUtY2xhaW0udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUE0Q0Esc0RBcUJDO0FBakVELDhDQUF3RTtBQUN4RSxnRkFBeUU7QUFDekUsa0VBQTBFO0FBQzFFLHVDQUFxRjtBQUNyRix1Q0FBaUU7QUFDakUsNkNBQXlDO0FBU3pDLHFDQWlCa0I7QUFDbEIsK0NBQXNEO0FBQ3RELHFEQUFpRDtBQUNqRCx1Q0FBb0Q7QUFPcEQ7O0dBRUc7QUFDSCxTQUFnQixxQkFBcUIsQ0FDcEMsRUFDQyxNQUFNLEVBQUUsT0FBTyxFQUFFLFVBQVUsR0FBRyxDQUFDLEVBQUUsR0FBRyxJQUFJLEVBQ1Y7SUFFL0IsTUFBTSxNQUFNLEdBQUcsT0FBTztRQUNyQiw2Q0FBNkM7UUFDN0MscURBQXFEO1FBQ3JELHlDQUF5QztXQUN0QyxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsY0FBTSxDQUFDLENBQUE7SUFDM0QsT0FBTyxJQUFBLDRCQUFrQixFQUN4QixPQUFPLENBQUMsRUFBRSxDQUFDLENBQ1Ysc0JBQXNCLENBQUk7UUFDekIsR0FBRyxJQUFJO1FBQ1AsTUFBTSxFQUFFLE9BQU87WUFDZCxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDO1lBQzNCLENBQUMsQ0FBQyxNQUFNO0tBQ1QsQ0FBQyxDQUNGLEVBQ0QsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxDQUNuQyxDQUFBO0FBQ0YsQ0FBQztBQUVELFNBQVMsV0FBVyxDQUFDLEdBQVU7O0lBQzlCLElBQUcsR0FBRyxZQUFZLFNBQVMsRUFBRSxDQUFDO1FBQzdCLE9BQU8sS0FBSyxDQUFBO0lBQ2IsQ0FBQztJQUVELDBDQUEwQztJQUMx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