Skip to content

Commit

Permalink
support verification for noop and exists for v3 circuit (#205)
Browse files Browse the repository at this point in the history
* support verification for noop and exists for v3 circuit
vmidyllic authored Mar 26, 2024
1 parent 102cfeb commit d2866ed
Showing 9 changed files with 404 additions and 106 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@0xpolygonid/js-sdk",
"version": "1.9.2",
"version": "1.9.3",
"description": "SDK to work with Polygon ID",
"main": "dist/node/cjs/index.js",
"module": "dist/node/esm/index.js",
5 changes: 5 additions & 0 deletions src/circuits/comparer.ts
Original file line number Diff line number Diff line change
@@ -54,6 +54,11 @@ export const QueryOperators = {
$nullify: Operators.NULLIFY
};

export const getOperatorNameByValue = (operator: number): string => {
const ops = Object.entries(QueryOperators).find(([, queryOp]) => queryOp === operator);
return ops ? ops[0] : 'unknown';
};

const allOperations = Object.values(QueryOperators);

export const availableTypesOperators: Map<string, Operators[]> = new Map([
3 changes: 1 addition & 2 deletions src/iden3comm/handlers/common.ts
Original file line number Diff line number Diff line change
@@ -56,10 +56,9 @@ const getGroupedQueries = (
return acc;
}, new Map<number, { query: JSONObject; linkNonce: number }>());


/**
* Processes zero knowledge proof requests.
*
*
* @param senderIdentifier - The identifier of the sender.
* @param requests - An array of zero knowledge proof requests.
* @param from - The identifier of the sender.
3 changes: 1 addition & 2 deletions src/proof/common.ts
Original file line number Diff line number Diff line change
@@ -5,8 +5,7 @@ import {
GISTProof,
isValidOperation,
Operators,
QueryOperators,
XSDNS
QueryOperators
} from '../circuits';
import { StateProof } from '../storage/entities/state';
import {
31 changes: 30 additions & 1 deletion src/proof/provers/inputs-generator.ts
Original file line number Diff line number Diff line change
@@ -21,7 +21,8 @@ import {
Query,
QueryOperators,
TreeState,
ValueProof
ValueProof,
getOperatorNameByValue
} from '../../circuits';
import {
PreparedAuthBJJCredential,
@@ -237,6 +238,8 @@ export class InputGenerator {
circuitInputs.profileNonce = BigInt(params.authProfileNonce);
circuitInputs.skipClaimRevocationCheck = params.skipRevocation;

this.checkOperatorSupport(proofReq.circuitId, query.operator);

return circuitInputs.inputsMarshal();
};

@@ -305,6 +308,8 @@ export class InputGenerator {
circuitInputs.profileNonce = BigInt(params.authProfileNonce);
circuitInputs.skipClaimRevocationCheck = params.skipRevocation;

this.checkOperatorSupport(proofReq.circuitId, query.operator);

return circuitInputs.inputsMarshal();
};

@@ -336,6 +341,9 @@ export class InputGenerator {
query.operator = this.transformV2QueryOperator(query.operator);
circuitInputs.query = query;
circuitInputs.currentTimeStamp = getUnixTimestamp(new Date());

this.checkOperatorSupport(proofReq.circuitId, query.operator);

return circuitInputs.inputsMarshal();
};

@@ -405,6 +413,8 @@ export class InputGenerator {
circuitInputs.signature = signature;
circuitInputs.challenge = params.challenge;

this.checkOperatorSupport(proofReq.circuitId, query.operator);

return circuitInputs.inputsMarshal();
};

@@ -464,6 +474,9 @@ export class InputGenerator {
circuitInputs.nullifierSessionID = proofReq.params?.nullifierSessionId
? BigInt(proofReq.params?.nullifierSessionId?.toString())
: BigInt(0);

this.checkOperatorSupport(proofReq.circuitId, query.operator);

return circuitInputs.inputsMarshal();
};

@@ -560,12 +573,16 @@ export class InputGenerator {
circuitInputs.treeState = authClaimData.treeState;
circuitInputs.signature = signature;
}

this.checkOperatorSupport(proofReq.circuitId, query.operator);

return circuitInputs.inputsMarshal();
};

private linkedMultiQuery10PrepareInputs = async ({
preparedCredential,
params,
proofReq,
circuitQueries
}: InputContext): Promise<Uint8Array> => {
const circuitClaimData = await this.newCircuitClaimData(preparedCredential);
@@ -577,10 +594,22 @@ export class InputGenerator {
circuitInputs.claim = circuitClaimData.claim;
circuitInputs.query = circuitQueries;

circuitQueries.forEach((query) => {
this.checkOperatorSupport(proofReq.circuitId, query.operator);
});

return circuitInputs.inputsMarshal();
};

private transformV2QueryOperator(operator: number): number {
return operator === Operators.SD || operator === Operators.NOOP ? Operators.EQ : operator;
}
private checkOperatorSupport(circuitId: string, operator: number) {
const supportedOperators = circuitValidator[circuitId as CircuitId].supportedOperations;
if (!supportedOperators.includes(operator)) {
throw new Error(
`operator ${getOperatorNameByValue(operator)} is not supported by ${circuitId}`
);
}
}
}
161 changes: 148 additions & 13 deletions src/proof/verifiers/pub-signals-verifier.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DID, getDateFromUnixTimestamp, Id } from '@iden3/js-iden3-core';
import { DocumentLoader, Path } from '@iden3/js-jsonld-merklization';
import { DocumentLoader, getDocumentLoader, Path } from '@iden3/js-jsonld-merklization';
import { Hash } from '@iden3/js-merkletree';
import { JSONObject } from '../../iden3comm';
import { IStateStorage, RootInfo, StateInfo } from '../../storage';
@@ -19,11 +19,19 @@ import {
checkQueryRequest,
ClaimOutputs,
VerifyOpts,
fieldValueFromVerifiablePresentation
fieldValueFromVerifiablePresentation,
validateDisclosureV2Circuit,
validateEmptyCredentialSubjectV2Circuit,
validateOperators,
verifyFieldValueInclusionV2,
validateDisclosureNativeSDSupport,
validateEmptyCredentialSubjectNoopNativeSupport,
verifyFieldValueInclusionNativeExistsSupport
} from './query';
import { parseQueriesMetadata, QueryMetadata } from '../common';
import { Operators } from '../../circuits';
import { calculateQueryHashV3 } from './query-hash';
import { JsonLd } from 'jsonld/jsonld-spec';

/**
* Verify Context - params for pub signal verification
@@ -120,14 +128,15 @@ export class PubSignalsVerifier {
valueArraySize: mtpv2PubSignals.getValueArrSize(),
isRevocationChecked: mtpv2PubSignals.isRevocationChecked
};
await checkQueryRequest(

await this.checkQueryV2Circuits(
CircuitId.AtomicQueryMTPV2,
query,
outs,
CircuitId.AtomicQueryMTPV2,
this._documentLoader,
verifiablePresentation,
opts
opts,
verifiablePresentation
);

// verify state
await this.checkStateExistenceForId(
mtpv2PubSignals.issuerID,
@@ -177,14 +186,15 @@ export class PubSignalsVerifier {
valueArraySize: sigV2PubSignals.getValueArrSize(),
isRevocationChecked: sigV2PubSignals.isRevocationChecked
};
await checkQueryRequest(

await this.checkQueryV2Circuits(
CircuitId.AtomicQuerySigV2,
query,
outs,
CircuitId.AtomicQuerySigV2,
this._documentLoader,
verifiablePresentation,
opts
opts,
verifiablePresentation
);

// verify state
await this.checkStateExistenceForId(sigV2PubSignals.issuerID, sigV2PubSignals.issuerAuthState);

@@ -227,17 +237,75 @@ export class PubSignalsVerifier {
merklized: v3PubSignals.merklized,
claimPathKey: v3PubSignals.claimPathKey,
valueArraySize: v3PubSignals.getValueArrSize(),
operatorOutput: v3PubSignals.operatorOutput,
isRevocationChecked: v3PubSignals.isRevocationChecked
};

if (!query.type) {
throw new Error(`proof query type is undefined`);
}

const loader = this._documentLoader ?? getDocumentLoader();

// validate schema
let context: JsonLd;
try {
context = (await loader(query.context ?? '')).document;
} catch (e) {
throw new Error(`can't load schema for request query`);
}

const queriesMetadata = await parseQueriesMetadata(
query.type,
JSON.stringify(context),
query.credentialSubject as JSONObject,
{
documentLoader: loader
}
);

await checkQueryRequest(
query,
queriesMetadata,
context,
outs,
CircuitId.AtomicQueryV3,
this._documentLoader,
verifiablePresentation,
opts
);

const queryMetadata = queriesMetadata[0]; // only one query is supported

// validate selective disclosure
if (queryMetadata.operator === Operators.SD) {
try {
await validateDisclosureNativeSDSupport(
queryMetadata,
outs,
verifiablePresentation,
loader
);
} catch (e) {
throw new Error(`failed to validate selective disclosure: ${(e as Error).message}`);
}
} else if (!queryMetadata.fieldName && queryMetadata.operator == Operators.NOOP) {
try {
await validateEmptyCredentialSubjectNoopNativeSupport(outs);
} catch (e: unknown) {
throw new Error(`failed to validate operators: ${(e as Error).message}`);
}
} else {
try {
await validateOperators(queryMetadata, outs);
} catch (e) {
throw new Error(`failed to validate operators: ${(e as Error).message}`);
}
}

// verify field inclusion / non-inclusion

verifyFieldValueInclusionNativeExistsSupport(outs, queryMetadata);

const { proofType, verifierID, nullifier, nullifierSessionID, linkID } = v3PubSignals;

switch (query.proofType) {
@@ -447,6 +515,73 @@ export class PubSignalsVerifier {
}
};

private async checkQueryV2Circuits(
circuitId: CircuitId.AtomicQueryMTPV2 | CircuitId.AtomicQuerySigV2,
query: ProofQuery,
outs: ClaimOutputs,
opts: VerifyOpts | undefined,
verifiablePresentation: JSON | undefined
) {
if (!query.type) {
throw new Error(`proof query type is undefined`);
}

const loader = this._documentLoader ?? getDocumentLoader();

// validate schema
let context: JsonLd;
try {
context = (await loader(query.context ?? '')).document;
} catch (e) {
throw new Error(`can't load schema for request query`);
}

const queriesMetadata = await parseQueriesMetadata(
query.type,
JSON.stringify(context),
query.credentialSubject as JSONObject,
{
documentLoader: loader
}
);

await checkQueryRequest(
query,
queriesMetadata,
context,
outs,
circuitId,
this._documentLoader,
opts
);

const queryMetadata = queriesMetadata[0]; // only one query is supported

// validate selective disclosure
if (queryMetadata.operator === Operators.SD) {
try {
await validateDisclosureV2Circuit(queryMetadata, outs, verifiablePresentation, loader);
} catch (e) {
throw new Error(`failed to validate selective disclosure: ${(e as Error).message}`);
}
} else if (!queryMetadata.fieldName && queryMetadata.operator == Operators.NOOP) {
try {
await validateEmptyCredentialSubjectV2Circuit(queryMetadata, outs);
} catch (e: unknown) {
throw new Error(`failed to validate operators: ${(e as Error).message}`);
}
} else {
try {
await validateOperators(queryMetadata, outs);
} catch (e) {
throw new Error(`failed to validate operators: ${(e as Error).message}`);
}
}

// verify field inclusion
verifyFieldValueInclusionV2(outs, queryMetadata);
}

private async resolve(
id: Id,
state: bigint
Loading

0 comments on commit d2866ed

Please sign in to comment.