Skip to content

Commit

Permalink
resolve comments
Browse files Browse the repository at this point in the history
  • Loading branch information
volodymyr-basiuk committed Feb 8, 2024
1 parent a220d97 commit 1e31c17
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 60 deletions.
55 changes: 37 additions & 18 deletions src/circuits/verifiers/pub-signals-verifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { JSONObject } from '../../iden3comm';
import { parseQueriesMetadata } from '../../proof';
import { createSchemaHash } from '../../schema-processor';
import { IStateStorage, RootInfo, StateInfo } from '../../storage';
import { byteEncoder } from '../../utils';
import { bigIntCompare, byteEncoder } from '../../utils';
import { ProofQuery, ProofType } from '../../verifiable';
import { AtomicQueryMTPV2PubSignals } from '../atomic-query-mtp-v2';
import { AtomicQuerySigV2PubSignals } from '../atomic-query-sig-v2';
Expand All @@ -17,6 +17,10 @@ import { BaseConfig } from '../common';
import { LinkedMultiQueryPubSignals } from '../linked-multi-query';
import { checkQueryRequest, ClaimOutputs, VerifyOpts } from './query';

/**
* Verify Context - params for pub signal verification
* @type VerifyContext
*/
export type VerifyContext = {
pubSignals: string[];
query: ProofQuery;
Expand All @@ -32,22 +36,40 @@ const zeroInt = 0n;
const defaultProofVerifyOpts = 1 * 60 * 60 * 1000; // 1 hour
const defaultAuthVerifyOpts = 5 * 60 * 1000; // 5 minutes

/**
* PubSignalsVerifier provides verify method
* @public
* @class PubSignalsVerifier
*/
export class PubSignalsVerifier {
userId!: Id;
challenge!: bigint;

/**
* Creates an instance of PubSignalsVerifier.
* @param {DocumentLoader} _documentLoader document loader
* @param {IStateStorage} _stateStorage state storage
*/

constructor(
private readonly _documentLoader: DocumentLoader,
private readonly _stateStorage: IStateStorage
) {}

/**
* verify public signals
*
* @param {string} circuitId circuit id
* @param {VerifyContext} ctx verification parameters
* @returns `Promise<BaseConfig>`
*/
async verify(circuitId: string, ctx: VerifyContext): Promise<BaseConfig> {
const fnName = `${circuitId.split('-')[0]}Verify`;
const fn = (this as unknown as { [k: string]: (ctx: VerifyContext) => Promise<BaseConfig> })[
fnName
];
if (!fn) {
throw new Error(`pub signal verifier for ${circuitId} not found`);
throw new Error(`public signals verifier for ${circuitId} not found`);
}
return fn(ctx);
}
Expand Down Expand Up @@ -92,10 +114,13 @@ export class PubSignalsVerifier {
};
await checkQueryRequest(query, outs, this._documentLoader, verifiablePresentation, opts);
// verify state
await this.checkUserState(mtpv2PubSignals.issuerID, mtpv2PubSignals.issuerClaimIdenState);
await this.checkStateExistenceForId(
mtpv2PubSignals.issuerID,
mtpv2PubSignals.issuerClaimIdenState
);

if (mtpv2PubSignals.isRevocationChecked !== 0) {
const issuerNonRevStateResolved = await this.checkIssuerNonRevState(
const issuerNonRevStateResolved = await this.checkRevocationStateForId(
mtpv2PubSignals.issuerID,
mtpv2PubSignals.issuerClaimNonRevState
);
Expand Down Expand Up @@ -152,10 +177,10 @@ export class PubSignalsVerifier {
};
await checkQueryRequest(query, outs, this._documentLoader, verifiablePresentation, opts);
// verify state
await this.checkUserState(sigV2PubSignals.issuerID, sigV2PubSignals.issuerAuthState);
await this.checkStateExistenceForId(sigV2PubSignals.issuerID, sigV2PubSignals.issuerAuthState);

if (sigV2PubSignals.isRevocationChecked !== 0) {
const issuerNonRevStateResolved = await this.checkIssuerNonRevState(
const issuerNonRevStateResolved = await this.checkRevocationStateForId(
sigV2PubSignals.issuerID,
sigV2PubSignals.issuerClaimNonRevState
);
Expand Down Expand Up @@ -264,10 +289,10 @@ export class PubSignalsVerifier {
}

// verify state
await this.checkUserState(v3PubSignals.issuerID, v3PubSignals.issuerState);
await this.checkStateExistenceForId(v3PubSignals.issuerID, v3PubSignals.issuerState);

if (v3PubSignals.isRevocationChecked !== 0) {
const issuerNonRevStateResolved = await this.checkIssuerNonRevState(
const issuerNonRevStateResolved = await this.checkRevocationStateForId(
v3PubSignals.issuerID,
v3PubSignals.issuerClaimNonRevState
);
Expand Down Expand Up @@ -375,21 +400,15 @@ export class PubSignalsVerifier {

const circuitQueryHashes = multiQueryPubSignals.circuitQueryHash
.filter((i) => i !== 0n)
.sort(this.bigIntCompare);
queryHashes.sort(this.bigIntCompare);
.sort(bigIntCompare);
queryHashes.sort(bigIntCompare);
if (!queryHashes.every((queryHash, i) => queryHash === circuitQueryHashes[i])) {
throw new Error('query hashes do not match');
}

return multiQueryPubSignals as unknown as BaseConfig;
};

bigIntCompare = (a: bigint, b: bigint): number => {
if (a < b) return -1;
if (a > b) return 1;
return 0;
};

private verifyIdOwnership = (sender: string, challenge: bigint): void => {
const senderId = DID.idFromDID(DID.parse(sender));
if (senderId.string() !== this.userId.string()) {
Expand Down Expand Up @@ -485,7 +504,7 @@ export class PubSignalsVerifier {
};
}

private checkUserState = async (userId: Id, userState: Hash): Promise<void> => {
private checkStateExistenceForId = async (userId: Id, userState: Hash): Promise<void> => {
const { latest } = await this.resolve(userId, userState.bigInt());
if (!latest) {
throw userStateError;
Expand All @@ -502,7 +521,7 @@ export class PubSignalsVerifier {
return gistStateResolved;
};

private checkIssuerNonRevState = async (
private checkRevocationStateForId = async (
issuerId: Id,
issuerClaimNonRevState: Hash
): Promise<{
Expand Down
41 changes: 17 additions & 24 deletions src/circuits/verifiers/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Proof } from '@iden3/js-merkletree';
import { transformQueryValueToBigInts } from '../../proof';
import { createSchemaHash, Parser } from '../../schema-processor';
import { byteDecoder, byteEncoder } from '../../utils';
import { ProofQuery, VerifiableConstants } from '../../verifiable';
import { buildFieldPath, ProofQuery, VerifiableConstants } from '../../verifiable';
import { isValidOperation, Operators, QueryOperators } from '../comparer';

/**
Expand Down Expand Up @@ -58,9 +58,9 @@ export async function checkQueryRequest(
): Promise<void> {
// validate issuer
const userDID = DID.parseFromId(outputs.issuerId);
const issuerAllowed = query.allowedIssuers?.some(
(issuer) => issuer === '*' || issuer === userDID.string()
);
const issuerAllowed =
!query.allowedIssuers ||
query.allowedIssuers?.some((issuer) => issuer === '*' || issuer === userDID.string());
if (!issuerAllowed) {
throw new Error('issuer is not in allowed list');
}
Expand All @@ -74,13 +74,13 @@ export async function checkQueryRequest(
throw new Error(`can't load schema for request query`);
}

const schemaId: string = await Path.getTypeIDFromContext(
JSON.stringify(schema),
query.type ?? '',
{
documentLoader: schemaLoader
}
);
if (!query.type) {
throw new Error(`proof query type is undefined`);
}

const schemaId: string = await Path.getTypeIDFromContext(JSON.stringify(schema), query.type, {
documentLoader: schemaLoader
});
const schemaHash = createSchemaHash(byteEncoder.encode(schemaId));

if (schemaHash.bigInt() !== outputs.schemaHash.bigInt()) {
Expand All @@ -101,7 +101,7 @@ export async function checkQueryRequest(
// validate selective disclosure
if (cq.isSelectiveDisclosure) {
try {
await validateDisclosure(cq, outputs,verifiablePresentation, schemaLoader);
await validateDisclosure(cq, outputs, verifiablePresentation, schemaLoader);
} catch (e) {
throw new Error(`failed to validate selective disclosure: ${(e as Error).message}`);
}
Expand All @@ -126,15 +126,9 @@ export async function checkQueryRequest(
throw new Error(`proof doesn't contains target query key`);
}

const path = await Path.getContextPathKey(
JSON.stringify(schema),
query.type ?? '',
cq.fieldName,
{
documentLoader: schemaLoader
}
);
path.prepend([VerifiableConstants.CREDENTIAL_SUBJECT_PATH]);
const path = await buildFieldPath(JSON.stringify(schema), query.type, cq.fieldName, {
documentLoader: schemaLoader
});
const claimPathKey = await path.mtEntry();

if (outputs.claimPathKey !== claimPathKey) {
Expand All @@ -143,7 +137,7 @@ export async function checkQueryRequest(
} else {
const slotIndex = await Parser.getFieldSlotIndex(
cq.fieldName,
query.type ?? '',
query.type,
byteEncoder.encode(JSON.stringify(schema))
);

Expand Down Expand Up @@ -286,7 +280,7 @@ async function validateDisclosure(
documentLoader: ldLoader
});
} catch (e) {
throw new Error(`can't merkelize verifiablePresentation`);
throw new Error(`can't merklize verifiablePresentation`);
}

let merklizedPath: Path;
Expand Down Expand Up @@ -344,4 +338,3 @@ async function parsePredicate(
}
return [operator, values];
}

8 changes: 4 additions & 4 deletions src/iden3comm/handlers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,19 +373,19 @@ export class AuthHandler implements IAuthHandler {
const params = proofRequest.params ?? {};
params.verifierDid = DID.parse(request.from);

const pubSignals = await this._proofService.verifyZKPResponse(proofResp, {
const { linkID } = await this._proofService.verifyZKPResponse(proofResp, {
query: proofRequest.query as unknown as ProofQuery,
sender: response.from,
params,
opts
});
// write linkId to the proof response
const pubSig = pubSignals as unknown as { linkID?: number };
// const pubSig = pubSignals as unknown as { linkID?: number };

if (pubSig.linkID && groupId) {
if (linkID && groupId) {
groupIdToLinkIdMap.set(groupId, [
...(groupIdToLinkIdMap.get(groupId) ?? []),
{ linkID: pubSig.linkID, requestId: proofResp.id }
{ linkID: linkID, requestId: proofResp.id }
]);
}
}
Expand Down
24 changes: 18 additions & 6 deletions src/proof/proof-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { BytesHelper, DID, MerklizedRootPosition } from '@iden3/js-iden3-core';
import { Hash } from '@iden3/js-merkletree';
import {
AuthV2Inputs,
BaseConfig,
CircuitId,
Operators,
Query,
Expand Down Expand Up @@ -46,6 +45,15 @@ export interface QueryWithFieldName {
rawValue?: unknown;
isSelectiveDisclosure?: boolean;
}

/**
* Metadata that returns on verification
* @type VerificationResultMetadata
*/
export type VerificationResultMetadata = {
linkID?: number;
};

/**
* List of options to customize ProofService
*/
Expand Down Expand Up @@ -75,12 +83,12 @@ export interface IProofService {
*
* @param {ZeroKnowledgeProofResponse} response - zero knowledge proof response
* @param {ProofVerifyOpts} opts - proof verification options
* @returns `{Promise<BaseConfig>}`
* @returns `{Promise<VerificationResultMetadata>}`
*/
verifyZKPResponse(
proofResp: ZeroKnowledgeProofResponse,
opts: ProofVerifyOpts
): Promise<BaseConfig>;
): Promise<VerificationResultMetadata>;

/**
* Generate proof from given identity and credential for protocol proof request
Expand Down Expand Up @@ -188,10 +196,12 @@ export class ProofService implements IProofService {
async verifyZKPResponse(
proofResp: ZeroKnowledgeProofResponse,
opts: ProofVerifyOpts
): Promise<BaseConfig> {
): Promise<VerificationResultMetadata> {
const proofValid = await this._prover.verify(proofResp, proofResp.circuitId);
if (!proofValid) {
throw Error(`Proof with circuit id ${proofResp.circuitId} and request id ${proofResp.id} is not valid`);
throw Error(
`Proof with circuit id ${proofResp.circuitId} and request id ${proofResp.id} is not valid`
);
}

const verifyContext: VerifyContext = {
Expand All @@ -203,7 +213,9 @@ export class ProofService implements IProofService {
opts: opts.opts,
params: opts.params
};
return this._pubSignalsVerifier.verify(proofResp.circuitId, verifyContext);
const pubSignals = await this._pubSignalsVerifier.verify(proofResp.circuitId, verifyContext);

return { linkID: (pubSignals as unknown as { linkID?: number }).linkID };
}

/** {@inheritdoc IProofService.generateProof} */
Expand Down
Loading

0 comments on commit 1e31c17

Please sign in to comment.