implement AttestationVerifier.ts

This commit is contained in:
turnoffthiscomputer
2024-10-10 13:39:17 -07:00
parent cbfe45cb1a
commit de0e407dc7
8 changed files with 167 additions and 55 deletions

View File

@@ -0,0 +1,92 @@
import { groth16 } from 'snarkjs';
import { n_dsc, k_dsc, ECDSA_K_LENGTH_FACTOR, k_dsc_ecdsa } from '../../common/src/constants/constants';
import {
areArraysEqual,
getVkeyFromArtifacts,
verifyDSCValidity,
} from '../utils/utils';
import forge from 'node-forge';
import { splitToWords } from '../../common/src/utils/utils';
import { parseDSC } from '../../common/src/utils/certificates/handleCertificate';
import { OpenPassportAttestation, OpenPassportVerifierReport } from './index.web';
import { parsePublicSignalsProve } from '../../common/src/utils/openPassportAttestation';
export class AttestationVerifier {
protected parsedPublicSignals: any;
protected devMode: boolean;
protected report: OpenPassportVerifierReport;
constructor(devMode: boolean = false) {
this.devMode = devMode;
}
async verify(attestation: OpenPassportAttestation): Promise<boolean> {
const {
proof: {
value: { proof, publicSignals },
},
dsc: { value: dsc },
dscProof: {
value: { proof: dscProof, publicSignals: dscPublicSignals },
},
} = attestation;
const { signatureAlgorithm, hashFunction } = parseDSC(dsc); // inacurracy in the case of register circuit
const kScaled = signatureAlgorithm === 'ecdsa' ? ECDSA_K_LENGTH_FACTOR * k_dsc_ecdsa : k_dsc;
const parsedPublicSignals = parsePublicSignalsProve(publicSignals, kScaled);
await this.verifyProof(proof, publicSignals, dsc, 'prove');
switch (this.circuit) {
case 'prove':
if (this.circuitMode === 'prove_offchain') {
await this.verifyProveArguments();
await this.verifyDsc(dsc);
} else if (this.circuitMode === 'register') {
await this.verifyRegisterArguments();
await this.verifyDscProof(dscProof, dscPublicSignals, dsc);
}
break;
case 'disclose':
await this.verifyDiscloseArguments();
break;
}
return this.report.valid;
}
protected async verifyProof(proof: string[], publicSignals: string[], dsc: string, circuit: string): Promise<void> {
const vkey = this.getVerificationKey(dsc, circuit);
const isVerified = await groth16.verify(vkey, publicSignals, proof as any);
if (!isVerified) {
// throw new Error('Proof verification failed');
}
}
protected getVerificationKey(dsc: string, circuit: string) {
const { signatureAlgorithm, hashFunction } = parseDSC(dsc);
return getVkeyFromArtifacts(circuit, signatureAlgorithm, hashFunction);
}
protected async verifyDsc(dsc: string, pubKeyFromProof: string[]) {
const dscCertificate = forge.pki.certificateFromPem(dsc);
const isValidCertificate = verifyDSCValidity(dscCertificate, this.devMode);
if (!isValidCertificate) {
// throw new Error('Invalid certificate chain');
}
const dscModulus = BigInt((dscCertificate.publicKey as any).n);
const dscModulusWords = splitToWords(dscModulus, n_dsc, k_dsc);
const isModulusMatching = areArraysEqual(dscModulusWords, pubKeyFromProof);
if (!isModulusMatching) {
// throw new Error('Public key modulus does not match');
}
}
private getParsedPublicSignals(publicSignals: string[], kScaled: number) {
return parsePublicSignalsProve(publicSignals, kScaled);
}
}

View File

@@ -1,12 +1,12 @@
import { ArgumentsProveOffChain, ArgumentsRegisterOffChain, ArgumentsRegisterOnChain, Mode, OpenPassportAppPartial } from "../../common/src/utils/appType";
import { ArgumentsProveOffChain, ArgumentsRegister, Mode, OpenPassportAppPartial } from "../../common/src/utils/appType";
import { DEFAULT_RPC_URL, MODAL_SERVER_ADDRESS, WEBSOCKET_URL, countryNames } from "../../common/src/constants/constants";
import { OpenPassportApp } from "../../common/src/utils/appType";
import { UserIdType } from "../../common/src/utils/utils";
import * as pako from 'pako';
import msgpack from 'msgpack-lite';
import { OpenPassportAttestation } from "./index.web";
import { StringifyOptions } from "querystring";
export class OpenPassportVerifier {
import { AttestationVerifier } from './AttestationVerifier';
export class OpenPassportVerifier extends AttestationVerifier {
private mode: Mode;
private scope: string;
private minimumAge: { enabled: boolean; value: string } = { enabled: false, value: '18' };
@@ -16,9 +16,9 @@ export class OpenPassportVerifier {
private modalServerUrl: string = MODAL_SERVER_ADDRESS;
private rpcUrl: string = DEFAULT_RPC_URL;
private cscaMerkleTreeUrl: string = "";
private devMode: boolean = false;
constructor(mode: Mode, scope: string) {
constructor(mode: Mode, scope: string, devMode: boolean = false) {
super(devMode);
this.mode = mode;
this.scope = scope;
}
@@ -78,7 +78,7 @@ export class OpenPassportVerifier {
userIdType: userIdType,
};
let openPassportArguments: ArgumentsProveOffChain | ArgumentsRegisterOnChain;
let openPassportArguments: ArgumentsProveOffChain | ArgumentsRegister;
switch (this.mode) {
case "prove_offchain":
const argsProveOffChain: ArgumentsProveOffChain = {
@@ -92,10 +92,9 @@ export class OpenPassportVerifier {
openPassportArguments = argsProveOffChain;
break;
case "register":
const argsRegisterOnChain: ArgumentsRegisterOnChain = {
const argsRegisterOnChain: ArgumentsRegister = {
modalServerUrl: this.modalServerUrl,
cscaMerkleTreeUrl: this.cscaMerkleTreeUrl,
rpcUrl: this.rpcUrl,
};
openPassportArguments = argsRegisterOnChain;
break;
@@ -116,8 +115,5 @@ export class OpenPassportVerifier {
}
}
verify(attestation: OpenPassportAttestation): boolean {
return true;
}
}

View File

@@ -17,7 +17,7 @@ const handleWebSocketMessage =
setProofStep: (step: number) => void,
setProofVerified: (proofVerified: boolean) => void,
openPassportVerifier: OpenPassportVerifier,
onSuccess: (proof: OpenPassportAttestation, report: OpenPassportVerifierReport) => void
onSuccess: (proof: OpenPassportAttestation) => void
) =>
async (data) => {
console.log('received mobile status:', data.status);
@@ -44,18 +44,18 @@ const handleWebSocketMessage =
if (data.proof) {
console.log(data.proof);
try {
const local_proofVerified: OpenPassportVerifierReport = await openPassportVerifier.verify(
const local_proofVerified = await openPassportVerifier.verify(
data.proof
);
setProofVerified(local_proofVerified.valid);
setProofVerified(local_proofVerified);
setProofStep(QRcodeSteps.PROOF_VERIFIED);
setTimeout(() => {
newSocket.emit('proof_verified', {
sessionId,
proofVerified: local_proofVerified.toString(),
});
if (local_proofVerified.valid) {
onSuccess(data.proof, local_proofVerified);
if (local_proofVerified) {
onSuccess(data.proof);
}
}, 1500); // wait for animation to finish before sending the proof to mobile
} catch (error) {

View File

@@ -1,33 +1,23 @@
'use client';
import { OpenPassportQRcode } from '../../../../../src/QRcode/OpenPassportQRcode';
// import { OpenPassportQRcode } from '../../../../dist/bundle.web.js'
import { TextField } from '@mui/material';
import { useState } from 'react';
import { COMMITMENT_TREE_TRACKER_URL } from '../../../../../../common/src/constants/constants';
import { v4 as uuidv4 } from 'uuid';
export default function Register() {
const [appName, setAppName] = useState('🌐 OpenPassport');
import { OpenPassportVerifier } from '../../../../../src/OpenPassportVerifier';
export default function Prove() {
const userId = uuidv4();
return (
<div className="h-screen w-full bg-white flex flex-col items-center justify-center gap-12">
<div className="text-4xl text-black ">Register circuit</div>
<OpenPassportQRcode
appName={appName}
scope="test"
devMode={true}
circuit="prove"
circuitMode="register"
merkleTreeUrl={COMMITMENT_TREE_TRACKER_URL}
userId={userId}
/>
const scope = "scope"
<TextField
id="outlined-basic"
label="App Name"
variant="outlined"
value={appName}
onChange={(e) => setAppName(e.target.value)}
const openPassportVerifier = new OpenPassportVerifier('register', scope);
return (
<div className="h-screen w-full bg-white flex flex-col items-center justify-center gap-4">
<OpenPassportQRcode
appName="Mock App"
userId={userId}
userIdType={"uuid"}
openPassportVerifier={openPassportVerifier}
onSuccess={(attestation) => {
// send the code to the backend server
}}
/>
</div>
);