reformat file according to prettier

This commit is contained in:
turnoffthiscomputer
2024-07-14 19:21:55 +02:00
parent 8920bc439d
commit 3669e7cdbc
14 changed files with 1238 additions and 1183 deletions

View File

@@ -1,14 +1,13 @@
# Proof of Passport Circuits
# Proof of Passport Circuits
## Requirements
| Requirement | Version | Installation Guide |
|-------------|---------|--------------------|
| nodejs | > v18 | [Install nodejs](https://nodejs.org/) |
| circom | Latest | [Install circom](https://docs.circom.io/) |
| Requirement | Version | Installation Guide |
| ----------- | ------- | --------------------------------------------------- |
| nodejs | > v18 | [Install nodejs](https://nodejs.org/) |
| circom | Latest | [Install circom](https://docs.circom.io/) |
| snarkjs | Latest | [Install snarkjs](https://github.com/iden3/snarkjs) |
## Overview of the circuits
Circom circuits are located in the `circuits/` folder.
@@ -16,6 +15,7 @@ The circuits are split into two parts: `register` and `disclose`.
This design is close to that of [semaphore](https://semaphore.pse.dev/).
The `register` circuit is used for the following:
1. Verify the signature of the passport
2. Verify that the public key which signed the passport is part of the registry merkle tree (a check of the merkle roots will be performed on-chain)
3. Generate commitment = H (secret + passportData + some other data)
@@ -27,6 +27,7 @@ The `register` will follow the `register_<hash>With<signature>.circom` naming co
One verifier for each register circuit will be deployed on-chain, all of them committing to the same merkle tree.
The `disclose` circuit is used for the following:
1. Verify that a user knows a secret e.g., he is able to reconstruct one leaf of the merkle tree (a check of the merkle roots will be performed on-chain)
2. Passport expiry is verified
3. A range check is performed over the age of the user
@@ -36,8 +37,8 @@ The `disclose` circuit is used for the following:
Any application that wants to use Proof of Passport can actually build its own `disclose` circuit.
### 🚧 Under development 🚧
Proof of Passport currently supports the following sig/hash algorithms:
Proof of Passport currently supports the following sig/hash algorithms:
- [x] sha256WithRSAEncryption
- [x] sha1WithRSAEncryption
@@ -48,8 +49,7 @@ Proof of Passport currently supports the following sig/hash algorithms:
- [ ] ecdsa-with-SHA512
- [ ] sha512WithRSAEncryption
> 💡 We currently have a bounty program if you implement a sig/hash setup.
> 💡 We currently have a bounty program if you implement a sig/hash setup.
## Installation
@@ -68,4 +68,5 @@ yarn install-circuits
```bash
yarn test
```
This will run tests with sample data generated on the fly.
This will run tests with sample data generated on the fly.

View File

@@ -1,204 +1,199 @@
import { assert, expect } from 'chai'
import path from "path";
const wasm_tester = require("circom_tester").wasm;
import { assert, expect } from 'chai';
import path from 'path';
const wasm_tester = require('circom_tester').wasm;
import { mockPassportData_sha256_rsa_65537 } from '../../../common/src/constants/mockPassportData';
import { formatMrz, packBytes } from '../../../common/src/utils/utils';
import { attributeToPosition, COMMITMENT_TREE_DEPTH } from "../../../common/src/constants/constants";
import { poseidon1, poseidon2, poseidon6 } from "poseidon-lite";
import { LeanIMT } from "@zk-kit/lean-imt";
import {
attributeToPosition,
COMMITMENT_TREE_DEPTH,
} from '../../../common/src/constants/constants';
import { poseidon1, poseidon2, poseidon6 } from 'poseidon-lite';
import { LeanIMT } from '@zk-kit/lean-imt';
import { getLeaf } from '../../../common/src/utils/pubkeyTree';
import { generateCircuitInputsDisclose } from '../../../common/src/utils/generateInputs';
import { unpackReveal } from '../../../common/src/utils/revealBitmap';
describe("Disclose", function () {
this.timeout(0);
let inputs: any;
let circuit: any;
let w: any;
let passportData = mockPassportData_sha256_rsa_65537;
let attestation_id: string;
let tree: any;
const attestation_name = "E-PASSPORT";
describe('Disclose', function () {
this.timeout(0);
let inputs: any;
let circuit: any;
let w: any;
let passportData = mockPassportData_sha256_rsa_65537;
let attestation_id: string;
let tree: any;
const attestation_name = 'E-PASSPORT';
before(async () => {
circuit = await wasm_tester(path.join(__dirname, "../../circuits/disclose/disclose.circom"),
{
include: [
"node_modules",
"./node_modules/@zk-kit/binary-merkle-root.circom/src",
"./node_modules/circomlib/circuits"
]
},
);
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
attestation_id = poseidon1([
BigInt(Buffer.from(attestation_name).readUIntBE(0, 6))
]).toString();
const majority = ["1", "8"];
const user_identifier = "0xE6E4b6a802F2e0aeE5676f6010e0AF5C9CDd0a50";
const bitmap = Array(90).fill("1")
const scope = poseidon1([
BigInt(Buffer.from("VOTEEEEE").readUIntBE(0, 6))
]).toString();
// compute the commitment and insert it in the tree
const pubkey_leaf = getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent,
}).toString();
const mrz_bytes = packBytes(formatMrz(passportData.mrz));
const commitment = poseidon6([
secret,
attestation_id,
pubkey_leaf,
mrz_bytes[0],
mrz_bytes[1],
mrz_bytes[2]
])
tree = new LeanIMT((a, b) => poseidon2([a, b]), []);
tree.insert(BigInt(commitment));
inputs = generateCircuitInputsDisclose(
secret,
attestation_id,
passportData,
tree,
majority,
bitmap,
scope,
user_identifier
);
before(async () => {
circuit = await wasm_tester(path.join(__dirname, '../../circuits/disclose/disclose.circom'), {
include: [
'node_modules',
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
],
});
it("should compile and load the circuit", async function () {
expect(circuit).to.not.be.undefined;
});
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
attestation_id = poseidon1([BigInt(Buffer.from(attestation_name).readUIntBE(0, 6))]).toString();
const majority = ['1', '8'];
const user_identifier = '0xE6E4b6a802F2e0aeE5676f6010e0AF5C9CDd0a50';
const bitmap = Array(90).fill('1');
const scope = poseidon1([BigInt(Buffer.from('VOTEEEEE').readUIntBE(0, 6))]).toString();
// compute the commitment and insert it in the tree
const pubkey_leaf = getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent,
}).toString();
const mrz_bytes = packBytes(formatMrz(passportData.mrz));
const commitment = poseidon6([
secret,
attestation_id,
pubkey_leaf,
mrz_bytes[0],
mrz_bytes[1],
mrz_bytes[2],
]);
tree = new LeanIMT((a, b) => poseidon2([a, b]), []);
tree.insert(BigInt(commitment));
inputs = generateCircuitInputsDisclose(
secret,
attestation_id,
passportData,
tree,
majority,
bitmap,
scope,
user_identifier
);
});
it('should compile and load the circuit', async function () {
expect(circuit).to.not.be.undefined;
});
it('should have nullifier == poseidon(secret, scope)', async function () {
w = await circuit.calculateWitness(inputs);
const nullifier_js = poseidon2([inputs.secret, inputs.scope]).toString();
const nullifier_circom = (await circuit.getOutput(w, ['nullifier'])).nullifier;
//console.log("nullifier_circom", nullifier_circom);
//console.log("nullifier_js", nullifier_js);
expect(nullifier_circom).to.equal(nullifier_js);
});
it('should fail to calculate witness with outdated passport', async function () {
try {
const invalidInputs = {
...inputs,
current_date: ['4', '4', '0', '5', '1', '0'], // 2044
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
it('should fail to calculate witness with different attestation_id', async function () {
try {
const invalidInputs = {
...inputs,
attestation_id: poseidon1([
BigInt(Buffer.from('ANON-AADHAAR').readUIntBE(0, 6)),
]).toString(),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
describe('MRZ selective disclosure', function () {
const attributeCombinations = [
['issuing_state', 'name'],
['passport_number', 'nationality', 'date_of_birth'],
['gender', 'expiry_date'],
];
attributeCombinations.forEach((combination) => {
it(`Disclosing ${combination.join(', ')}`, async function () {
const attributeToReveal = Object.keys(attributeToPosition).reduce((acc, attribute) => {
acc[attribute] = combination.includes(attribute);
return acc;
}, {});
const bitmap = Array(90).fill('0');
Object.entries(attributeToReveal).forEach(([attribute, reveal]) => {
if (reveal) {
const [start, end] = attributeToPosition[attribute];
bitmap.fill('1', start, end + 1);
}
});
inputs = {
...inputs,
bitmap: bitmap.map(String),
};
it("should have nullifier == poseidon(secret, scope)", async function () {
w = await circuit.calculateWitness(inputs);
const nullifier_js = poseidon2([inputs.secret, inputs.scope]).toString();
const nullifier_circom = (await circuit.getOutput(w, ["nullifier"])).nullifier;
//console.log("nullifier_circom", nullifier_circom);
//console.log("nullifier_js", nullifier_js);
expect(nullifier_circom).to.equal(nullifier_js);
});
it("should fail to calculate witness with outdated passport", async function () {
try {
const invalidInputs = {
...inputs,
current_date: ["4", "4", "0", "5", "1", "0"] // 2044
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
it("should fail to calculate witness with different attestation_id", async function () {
try {
const invalidInputs = {
...inputs,
attestation_id: poseidon1([
BigInt(Buffer.from("ANON-AADHAAR").readUIntBE(0, 6))
]).toString()
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
describe('MRZ selective disclosure', function () {
const attributeCombinations = [
['issuing_state', 'name'],
['passport_number', 'nationality', 'date_of_birth'],
['gender', 'expiry_date'],
];
attributeCombinations.forEach(combination => {
it(`Disclosing ${combination.join(", ")}`, async function () {
const attributeToReveal = Object.keys(attributeToPosition).reduce((acc, attribute) => {
acc[attribute] = combination.includes(attribute);
return acc;
}, {});
const bitmap = Array(90).fill('0');
Object.entries(attributeToReveal).forEach(([attribute, reveal]) => {
if (reveal) {
const [start, end] = attributeToPosition[attribute];
bitmap.fill('1', start, end + 1);
}
});
inputs = {
...inputs,
bitmap: bitmap.map(String),
}
w = await circuit.calculateWitness(inputs);
const revealedData_packed = await circuit.getOutput(w, ["revealedData_packed[3]"])
const reveal_unpacked = unpackReveal(revealedData_packed);
for (let i = 0; i < reveal_unpacked.length; i++) {
if (bitmap[i] == '1') {
const char = String.fromCharCode(Number(inputs.mrz[i + 5]));
assert(reveal_unpacked[i] == char, 'Should reveal the right character');
} else {
assert(reveal_unpacked[i] == '\x00', 'Should not reveal');
}
}
});
});
})
it("should allow disclosing majority", async function () {
const bitmap = Array(90).fill('0');
bitmap[88] = '1';
bitmap[89] = '1';
w = await circuit.calculateWitness({
...inputs,
bitmap: bitmap.map(String),
});
const revealedData_packed = await circuit.getOutput(w, ["revealedData_packed[3]"])
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[3]']);
const reveal_unpacked = unpackReveal(revealedData_packed);
//console.log("reveal_unpacked", reveal_unpacked)
expect(reveal_unpacked[88]).to.equal("1");
expect(reveal_unpacked[89]).to.equal("8");
for (let i = 0; i < reveal_unpacked.length; i++) {
if (bitmap[i] == '1') {
const char = String.fromCharCode(Number(inputs.mrz[i + 5]));
assert(reveal_unpacked[i] == char, 'Should reveal the right character');
} else {
assert(reveal_unpacked[i] == '\x00', 'Should not reveal');
}
}
});
});
});
it('should allow disclosing majority', async function () {
const bitmap = Array(90).fill('0');
bitmap[88] = '1';
bitmap[89] = '1';
w = await circuit.calculateWitness({
...inputs,
bitmap: bitmap.map(String),
});
it("shouldn't allow disclosing wrong majority", async function () {
const bitmap = Array(90).fill('0');
bitmap[88] = '1';
bitmap[89] = '1';
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[3]']);
w = await circuit.calculateWitness({
...inputs,
majority: ["5", "0"].map(char => BigInt(char.charCodeAt(0)).toString()),
bitmap: bitmap.map(String),
});
const reveal_unpacked = unpackReveal(revealedData_packed);
//console.log("reveal_unpacked", reveal_unpacked)
const revealedData_packed = await circuit.getOutput(w, ["revealedData_packed[3]"])
expect(reveal_unpacked[88]).to.equal('1');
expect(reveal_unpacked[89]).to.equal('8');
});
const reveal_unpacked = unpackReveal(revealedData_packed);
//console.log("reveal_unpacked", reveal_unpacked)
it("shouldn't allow disclosing wrong majority", async function () {
const bitmap = Array(90).fill('0');
bitmap[88] = '1';
bitmap[89] = '1';
expect(reveal_unpacked[88]).to.equal("\x00");
expect(reveal_unpacked[89]).to.equal("\x00");
w = await circuit.calculateWitness({
...inputs,
majority: ['5', '0'].map((char) => BigInt(char.charCodeAt(0)).toString()),
bitmap: bitmap.map(String),
});
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[3]']);
const reveal_unpacked = unpackReveal(revealedData_packed);
//console.log("reveal_unpacked", reveal_unpacked)
expect(reveal_unpacked[88]).to.equal('\x00');
expect(reveal_unpacked[89]).to.equal('\x00');
});
});

View File

@@ -1,57 +1,69 @@
import { assert, expect } from 'chai'
import fs from 'fs'
import { assert, expect } from 'chai';
import fs from 'fs';
const forge = require('node-forge');
import path from 'path';
const wasm_tester = require("circom_tester").wasm;
const wasm_tester = require('circom_tester').wasm;
import { getCSCAInputs } from '../../../common/src/utils/csca';
import { mock_dsc_sha1_rsa_2048, mock_csca_sha1_rsa_2048 } from '../../../common/src/constants/mockCertificates';
import {
mock_dsc_sha1_rsa_2048,
mock_csca_sha1_rsa_2048,
} from '../../../common/src/constants/mockCertificates';
describe('DSC chain certificate - SHA1 RSA', function () {
this.timeout(0); // Disable timeout
let circuit;
const n_dsc = 121;
const k_dsc = 17;
const n_csca = 121;
const k_csca = 17;
const max_cert_bytes = 960;
const dscCert = forge.pki.certificateFromPem(mock_dsc_sha1_rsa_2048);
const cscaCert = forge.pki.certificateFromPem(mock_csca_sha1_rsa_2048);
this.timeout(0); // Disable timeout
let circuit;
const n_dsc = 121;
const k_dsc = 17;
const n_csca = 121;
const k_csca = 17;
const max_cert_bytes = 960;
const dscCert = forge.pki.certificateFromPem(mock_dsc_sha1_rsa_2048);
const cscaCert = forge.pki.certificateFromPem(mock_csca_sha1_rsa_2048);
const inputs = getCSCAInputs(BigInt(0).toString(), dscCert, cscaCert, n_dsc, k_dsc, n_csca, k_csca, max_cert_bytes, true);
const inputs = getCSCAInputs(
BigInt(0).toString(),
dscCert,
cscaCert,
n_dsc,
k_dsc,
n_csca,
k_csca,
max_cert_bytes,
true
);
before(async () => {
const circuitPath = path.resolve(__dirname, '../../circuits/tests/dsc/dsc_sha1_rsa_2048.circom');
circuit = await wasm_tester(
circuitPath,
{
include: [
"node_modules",
"./node_modules/@zk-kit/binary-merkle-root.circom/src",
"./node_modules/circomlib/circuits"
]
}
);
before(async () => {
const circuitPath = path.resolve(
__dirname,
'../../circuits/tests/dsc/dsc_sha1_rsa_2048.circom'
);
circuit = await wasm_tester(circuitPath, {
include: [
'node_modules',
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
],
});
});
it('verify dsc has been signed by the csca', () => {
const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert);
const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes();
const md = forge.md.sha1.create();
md.update(tbsCertDer);
const tbsHash = md.digest().getBytes();
const signature = dscCert.signature;
const cscaCert = forge.pki.certificateFromPem(mock_csca_sha1_rsa_2048);
const publicKey = cscaCert.publicKey;
const verified = publicKey.verify(tbsHash, signature);
expect(verified).to.be.true;
})
it('verify dsc has been signed by the csca', () => {
const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert);
const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes();
const md = forge.md.sha1.create();
md.update(tbsCertDer);
const tbsHash = md.digest().getBytes();
const signature = dscCert.signature;
const cscaCert = forge.pki.certificateFromPem(mock_csca_sha1_rsa_2048);
const publicKey = cscaCert.publicKey;
const verified = publicKey.verify(tbsHash, signature);
expect(verified).to.be.true;
});
it('should compile and load the circuit', () => {
expect(circuit).to.not.be.undefined;
})
it('should compile and load the circuit', () => {
expect(circuit).to.not.be.undefined;
});
it('should compute the correct output', async () => {
const witness = await circuit.calculateWitness(inputs, true);
})
})
it('should compute the correct output', async () => {
const witness = await circuit.calculateWitness(inputs, true);
});
});

View File

@@ -1,56 +1,68 @@
import { assert, expect } from 'chai'
import fs from 'fs'
import { assert, expect } from 'chai';
import fs from 'fs';
const forge = require('node-forge');
import path from 'path';
const wasm_tester = require("circom_tester").wasm;
const wasm_tester = require('circom_tester').wasm;
import { getCSCAInputs } from '../../../common/src/utils/csca';
import { mock_dsc_sha256_rsa_2048, mock_csca_sha256_rsa_2048 } from '../../../common/src/constants/mockCertificates';
import {
mock_dsc_sha256_rsa_2048,
mock_csca_sha256_rsa_2048,
} from '../../../common/src/constants/mockCertificates';
describe('DSC chain certificate - SHA256 RSA', function () {
this.timeout(0); // Disable timeout
let circuit;
const n_dsc = 121;
const k_dsc = 17;
const n_csca = 121;
const k_csca = 17;
const max_cert_bytes = 960;
const dscCert = forge.pki.certificateFromPem(mock_dsc_sha256_rsa_2048);
const cscaCert = forge.pki.certificateFromPem(mock_csca_sha256_rsa_2048);
this.timeout(0); // Disable timeout
let circuit;
const n_dsc = 121;
const k_dsc = 17;
const n_csca = 121;
const k_csca = 17;
const max_cert_bytes = 960;
const dscCert = forge.pki.certificateFromPem(mock_dsc_sha256_rsa_2048);
const cscaCert = forge.pki.certificateFromPem(mock_csca_sha256_rsa_2048);
const inputs = getCSCAInputs(BigInt(0).toString(), dscCert, cscaCert, n_dsc, k_dsc, n_csca, k_csca, max_cert_bytes, true);
const inputs = getCSCAInputs(
BigInt(0).toString(),
dscCert,
cscaCert,
n_dsc,
k_dsc,
n_csca,
k_csca,
max_cert_bytes,
true
);
before(async () => {
const circuitPath = path.resolve(__dirname, '../../circuits/tests/dsc/dsc_sha256_rsa_2048.circom');
circuit = await wasm_tester(
circuitPath,
{
include: [
"node_modules",
"./node_modules/@zk-kit/binary-merkle-root.circom/src",
"./node_modules/circomlib/circuits"
]
}
);
before(async () => {
const circuitPath = path.resolve(
__dirname,
'../../circuits/tests/dsc/dsc_sha256_rsa_2048.circom'
);
circuit = await wasm_tester(circuitPath, {
include: [
'node_modules',
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
],
});
});
it('verify dsc has been signed by the csca', () => {
const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert);
const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes();
const md = forge.md.sha256.create();
md.update(tbsCertDer);
const tbsHash = md.digest().getBytes();
const signature = dscCert.signature;
const publicKey = cscaCert.publicKey;
const verified = publicKey.verify(tbsHash, signature);
expect(verified).to.be.true;
})
it('verify dsc has been signed by the csca', () => {
const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert);
const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes();
const md = forge.md.sha256.create();
md.update(tbsCertDer);
const tbsHash = md.digest().getBytes();
const signature = dscCert.signature;
const publicKey = cscaCert.publicKey;
const verified = publicKey.verify(tbsHash, signature);
expect(verified).to.be.true;
});
it('should compile and load the circuit', () => {
expect(circuit).to.not.be.undefined;
})
it('should compile and load the circuit', () => {
expect(circuit).to.not.be.undefined;
});
it('should compute the correct output', async () => {
const witness = await circuit.calculateWitness(inputs, true);
})
})
it('should compute the correct output', async () => {
const witness = await circuit.calculateWitness(inputs, true);
});
});

View File

@@ -1,87 +1,99 @@
import { assert, expect } from 'chai'
import fs from 'fs'
import { assert, expect } from 'chai';
import fs from 'fs';
const forge = require('node-forge');
import path from 'path';
const wasm_tester = require("circom_tester").wasm;
const wasm_tester = require('circom_tester').wasm;
import { getCSCAInputs } from '../../../common/src/utils/csca';
import crypto from 'crypto';
import { mock_dsc_sha256_rsapss_2048, mock_csca_sha256_rsapss_2048 } from '../../../common/src/constants/mockCertificates';
import {
mock_dsc_sha256_rsapss_2048,
mock_csca_sha256_rsapss_2048,
} from '../../../common/src/constants/mockCertificates';
describe('DSC chain certificate - SHA256 RSA-PSS', function () {
this.timeout(0); // Disable timeout
let circuit;
const n_dsc = 64;
const k_dsc = 32;
const n_csca = 64;
const k_csca = 32;
const max_cert_bytes = 960;
const dscCert = forge.pki.certificateFromPem(mock_dsc_sha256_rsapss_2048);
const cscaCert = forge.pki.certificateFromPem(mock_csca_sha256_rsapss_2048);
this.timeout(0); // Disable timeout
let circuit;
const n_dsc = 64;
const k_dsc = 32;
const n_csca = 64;
const k_csca = 32;
const max_cert_bytes = 960;
const dscCert = forge.pki.certificateFromPem(mock_dsc_sha256_rsapss_2048);
const cscaCert = forge.pki.certificateFromPem(mock_csca_sha256_rsapss_2048);
const inputs = getCSCAInputs(BigInt(0).toString(), dscCert, cscaCert, n_dsc, k_dsc, n_csca, k_csca, max_cert_bytes, true);
const inputs = getCSCAInputs(
BigInt(0).toString(),
dscCert,
cscaCert,
n_dsc,
k_dsc,
n_csca,
k_csca,
max_cert_bytes,
true
);
before(async () => {
const circuitPath = path.resolve(__dirname, '../../circuits/tests/dsc/dsc_sha256_rsapss_2048.circom');
circuit = await wasm_tester(
circuitPath,
{
include: [
"node_modules",
"./node_modules/@zk-kit/binary-merkle-root.circom/src",
"./node_modules/circomlib/circuits"
]
}
);
before(async () => {
const circuitPath = path.resolve(
__dirname,
'../../circuits/tests/dsc/dsc_sha256_rsapss_2048.circom'
);
circuit = await wasm_tester(circuitPath, {
include: [
'node_modules',
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
],
});
// TODO: Verify the certificate chain in ts too.
// it('verify dsc has been signed by the csca using RSA-PSS', () => {
// // Extract TBS (To Be Signed) certificate
// const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert);
// const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes();
});
// TODO: Verify the certificate chain in ts too.
// it('verify dsc has been signed by the csca using RSA-PSS', () => {
// // Extract TBS (To Be Signed) certificate
// const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert);
// const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes();
// // Create SHA-256 hash of the TBS certificate
// const tbsHash = crypto.createHash('sha256').update(Buffer.from(tbsCertDer, 'binary')).digest();
// // Create SHA-256 hash of the TBS certificate
// const tbsHash = crypto.createHash('sha256').update(Buffer.from(tbsCertDer, 'binary')).digest();
// // Extract signature from DSC certificate
// const signature = Buffer.from(dscCert.signature, 'binary');
// // Extract signature from DSC certificate
// const signature = Buffer.from(dscCert.signature, 'binary');
// // Get public key from CSCA certificate
// const publicKeyPem = forge.pki.publicKeyToPem(cscaCert.publicKey);
// const publicKey = crypto.createPublicKey(publicKeyPem);
// // Get public key from CSCA certificate
// const publicKeyPem = forge.pki.publicKeyToPem(cscaCert.publicKey);
// const publicKey = crypto.createPublicKey(publicKeyPem);
// // Verify signature
// const pssOptions = {
// saltLength: 32,
// mgf1Hash: 'sha256'
// };
// // Verify signature
// const pssOptions = {
// saltLength: 32,
// mgf1Hash: 'sha256'
// };
// try {
// const verifier = crypto.createVerify('RSA-SHA256');
// verifier.update(tbsHash);
// const isValid = verifier.verify({
// key: publicKey,
// padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
// saltLength: pssOptions.saltLength
// }, signature);
// try {
// const verifier = crypto.createVerify('RSA-SHA256');
// verifier.update(tbsHash);
// const isValid = verifier.verify({
// key: publicKey,
// padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
// saltLength: pssOptions.saltLength
// }, signature);
// console.log('TBS Hash:', tbsHash.toString('hex'));
// console.log('Signature:', signature.toString('hex'));
// console.log('Public Key:', publicKeyPem);
// console.log('Verification result:', isValid);
// console.log('TBS Hash:', tbsHash.toString('hex'));
// console.log('Signature:', signature.toString('hex'));
// console.log('Public Key:', publicKeyPem);
// console.log('Verification result:', isValid);
// expect(isValid).to.be.true;
// } catch (error) {
// console.error('Verification error:', error);
// throw error;
// }
// })
// expect(isValid).to.be.true;
// } catch (error) {
// console.error('Verification error:', error);
// throw error;
// }
// })
it('should compile and load the circuit', () => {
expect(circuit).to.not.be.undefined;
})
it('should compile and load the circuit', () => {
expect(circuit).to.not.be.undefined;
});
it('should compute the correct output', async () => {
const witness = await circuit.calculateWitness(inputs, true);
})
})
it('should compute the correct output', async () => {
const witness = await circuit.calculateWitness(inputs, true);
});
});

View File

@@ -1,124 +1,124 @@
import { describe } from 'mocha'
import { assert, expect } from 'chai'
import path from "path";
const wasm_tester = require("circom_tester").wasm;
import { poseidon1, poseidon6 } from "poseidon-lite";
import { mockPassportData_sha1_rsa_65537 } from "../../../common/src/constants/mockPassportData";
import { describe } from 'mocha';
import { assert, expect } from 'chai';
import path from 'path';
const wasm_tester = require('circom_tester').wasm;
import { poseidon1, poseidon6 } from 'poseidon-lite';
import { mockPassportData_sha1_rsa_65537 } from '../../../common/src/constants/mockPassportData';
import { generateCircuitInputsRegister } from '../../../common/src/utils/generateInputs';
import { getLeaf } from '../../../common/src/utils/pubkeyTree';
import { packBytes } from '../../../common/src/utils/utils';
describe("Register - SHA1 RSA", function () {
this.timeout(0);
let inputs: any;
let circuit: any;
let passportData = mockPassportData_sha1_rsa_65537;
let attestation_id: string;
const attestation_name = "E-PASSPORT";
const n_dsc = 121;
const k_dsc = 17;
describe('Register - SHA1 RSA', function () {
this.timeout(0);
let inputs: any;
let circuit: any;
let passportData = mockPassportData_sha1_rsa_65537;
let attestation_id: string;
const attestation_name = 'E-PASSPORT';
const n_dsc = 121;
const k_dsc = 17;
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, "../../circuits/register/register_sha1WithRSAEncryption_65537.circom"),
{
include: [
"node_modules",
"./node_modules/@zk-kit/binary-merkle-root.circom/src",
"./node_modules/circomlib/circuits",
"./node_modules/dmpierre/sha1-circom/circuits",
]
},
);
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, '../../circuits/register/register_sha1WithRSAEncryption_65537.circom'),
{
include: [
'node_modules',
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
'./node_modules/dmpierre/sha1-circom/circuits',
],
}
);
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
const dscSecret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
const dscSecret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
attestation_id = poseidon1([
BigInt(Buffer.from(attestation_name).readUIntBE(0, 6))
]).toString();
attestation_id = poseidon1([BigInt(Buffer.from(attestation_name).readUIntBE(0, 6))]).toString();
inputs = generateCircuitInputsRegister(
secret,
dscSecret,
attestation_id,
passportData,
n_dsc,
k_dsc
);
});
inputs = generateCircuitInputsRegister(
secret,
dscSecret,
attestation_id,
passportData,
n_dsc,
k_dsc
);
});
it("should compile and load the circuit", async function () {
expect(circuit).to.not.be.undefined;
});
it('should compile and load the circuit', async function () {
expect(circuit).to.not.be.undefined;
});
it("should calculate the witness with correct inputs", async function () {
const w = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(w);
it('should calculate the witness with correct inputs', async function () {
const w = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(w);
const nullifier = (await circuit.getOutput(w, ["nullifier"])).nullifier;
console.log("\x1b[34m%s\x1b[0m", "nullifier", nullifier);
const commitment_circom = (await circuit.getOutput(w, ["commitment"])).commitment;
console.log("\x1b[34m%s\x1b[0m", "commitment", commitment_circom);
const blinded_dsc_commitment = (await circuit.getOutput(w, ["blinded_dsc_commitment"])).blinded_dsc_commitment;
console.log("\x1b[34m%s\x1b[0m", "blinded_dsc_commitment", blinded_dsc_commitment);
const nullifier = (await circuit.getOutput(w, ['nullifier'])).nullifier;
console.log('\x1b[34m%s\x1b[0m', 'nullifier', nullifier);
const commitment_circom = (await circuit.getOutput(w, ['commitment'])).commitment;
console.log('\x1b[34m%s\x1b[0m', 'commitment', commitment_circom);
const blinded_dsc_commitment = (await circuit.getOutput(w, ['blinded_dsc_commitment']))
.blinded_dsc_commitment;
console.log('\x1b[34m%s\x1b[0m', 'blinded_dsc_commitment', blinded_dsc_commitment);
const mrz_bytes = packBytes(inputs.mrz);
const commitment_bytes = poseidon6([
inputs.secret[0],
attestation_id,
getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent
}),
mrz_bytes[0],
mrz_bytes[1],
mrz_bytes[2]
]);
const commitment_js = commitment_bytes.toString();
//console.log('commitment_js', commitment_js)
//console.log('commitment_circom', commitment_circom)
expect(commitment_circom).to.be.equal(commitment_js);
});
const mrz_bytes = packBytes(inputs.mrz);
const commitment_bytes = poseidon6([
inputs.secret[0],
attestation_id,
getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent,
}),
mrz_bytes[0],
mrz_bytes[1],
mrz_bytes[2],
]);
const commitment_js = commitment_bytes.toString();
//console.log('commitment_js', commitment_js)
//console.log('commitment_circom', commitment_circom)
expect(commitment_circom).to.be.equal(commitment_js);
});
it("should fail to calculate witness with invalid mrz", async function () {
try {
const invalidInputs = {
...inputs,
mrz: Array(93).fill(0).map(byte => BigInt(byte).toString())
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
it('should fail to calculate witness with invalid mrz', async function () {
try {
const invalidInputs = {
...inputs,
mrz: Array(93)
.fill(0)
.map((byte) => BigInt(byte).toString()),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
it("should fail to calculate witness with invalid econtent", async function () {
try {
const invalidInputs = {
...inputs,
econtent: inputs.econtent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
it('should fail to calculate witness with invalid econtent', async function () {
try {
const invalidInputs = {
...inputs,
econtent: inputs.econtent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
it("should fail to calculate witness with invalid signature", async function () {
try {
const invalidInputs = {
...inputs,
signature: inputs.signature.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
});
it('should fail to calculate witness with invalid signature', async function () {
try {
const invalidInputs = {
...inputs,
signature: inputs.signature.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
});

View File

@@ -1,121 +1,121 @@
import { describe } from 'mocha'
import { assert, expect } from 'chai'
import path from "path";
const wasm_tester = require("circom_tester").wasm;
import { poseidon1, poseidon6 } from "poseidon-lite";
import { mockPassportData_sha256_rsa_65537 } from "../../../common/src/constants/mockPassportData";
import { describe } from 'mocha';
import { assert, expect } from 'chai';
import path from 'path';
const wasm_tester = require('circom_tester').wasm;
import { poseidon1, poseidon6 } from 'poseidon-lite';
import { mockPassportData_sha256_rsa_65537 } from '../../../common/src/constants/mockPassportData';
import { generateCircuitInputsRegister } from '../../../common/src/utils/generateInputs';
import { getLeaf } from '../../../common/src/utils/pubkeyTree';
import { packBytes } from '../../../common/src/utils/utils';
describe("Register - SHA256 RSA", function () {
this.timeout(0);
let inputs: any;
let circuit: any;
let passportData = mockPassportData_sha256_rsa_65537;
let attestation_id: string;
const attestation_name = "E-PASSPORT";
const n_dsc = 121;
const k_dsc = 17;
describe('Register - SHA256 RSA', function () {
this.timeout(0);
let inputs: any;
let circuit: any;
let passportData = mockPassportData_sha256_rsa_65537;
let attestation_id: string;
const attestation_name = 'E-PASSPORT';
const n_dsc = 121;
const k_dsc = 17;
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, "../../circuits/register/register_sha256WithRSAEncryption_65537.circom"),
{
include: [
"node_modules",
"./node_modules/@zk-kit/binary-merkle-root.circom/src",
"./node_modules/circomlib/circuits"
]
},
);
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, '../../circuits/register/register_sha256WithRSAEncryption_65537.circom'),
{
include: [
'node_modules',
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
],
}
);
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
const dscSecret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
attestation_id = poseidon1([
BigInt(Buffer.from(attestation_name).readUIntBE(0, 6))
]).toString();
inputs = generateCircuitInputsRegister(
secret,
dscSecret,
attestation_id,
passportData,
n_dsc,
k_dsc
);
});
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
const dscSecret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
attestation_id = poseidon1([BigInt(Buffer.from(attestation_name).readUIntBE(0, 6))]).toString();
inputs = generateCircuitInputsRegister(
secret,
dscSecret,
attestation_id,
passportData,
n_dsc,
k_dsc
);
});
it("should compile and load the circuit", async function () {
expect(circuit).to.not.be.undefined;
});
it('should compile and load the circuit', async function () {
expect(circuit).to.not.be.undefined;
});
it("should calculate the witness with correct inputs", async function () {
const w = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(w);
it('should calculate the witness with correct inputs', async function () {
const w = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(w);
const nullifier = (await circuit.getOutput(w, ["nullifier"])).nullifier;
console.log("\x1b[34m%s\x1b[0m", "nullifier", nullifier);
const commitment_circom = (await circuit.getOutput(w, ["commitment"])).commitment;
console.log("\x1b[34m%s\x1b[0m", "commitment", commitment_circom);
const blinded_dsc_commitment = (await circuit.getOutput(w, ["blinded_dsc_commitment"])).blinded_dsc_commitment;
console.log("\x1b[34m%s\x1b[0m", "blinded_dsc_commitment", blinded_dsc_commitment);
const nullifier = (await circuit.getOutput(w, ['nullifier'])).nullifier;
console.log('\x1b[34m%s\x1b[0m', 'nullifier', nullifier);
const commitment_circom = (await circuit.getOutput(w, ['commitment'])).commitment;
console.log('\x1b[34m%s\x1b[0m', 'commitment', commitment_circom);
const blinded_dsc_commitment = (await circuit.getOutput(w, ['blinded_dsc_commitment']))
.blinded_dsc_commitment;
console.log('\x1b[34m%s\x1b[0m', 'blinded_dsc_commitment', blinded_dsc_commitment);
const mrz_bytes = packBytes(inputs.mrz);
const commitment_bytes = poseidon6([
inputs.secret[0],
attestation_id,
getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent
}),
mrz_bytes[0],
mrz_bytes[1],
mrz_bytes[2]
]);
const commitment_js = commitment_bytes.toString();
//console.log('commitment_js', commitment_js)
//console.log('commitment_circom', commitment_circom)
expect(commitment_circom).to.be.equal(commitment_js);
});
const mrz_bytes = packBytes(inputs.mrz);
const commitment_bytes = poseidon6([
inputs.secret[0],
attestation_id,
getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent,
}),
mrz_bytes[0],
mrz_bytes[1],
mrz_bytes[2],
]);
const commitment_js = commitment_bytes.toString();
//console.log('commitment_js', commitment_js)
//console.log('commitment_circom', commitment_circom)
expect(commitment_circom).to.be.equal(commitment_js);
});
it("should fail to calculate witness with invalid mrz", async function () {
try {
const invalidInputs = {
...inputs,
mrz: Array(93).fill(0).map(byte => BigInt(byte).toString())
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
it('should fail to calculate witness with invalid mrz', async function () {
try {
const invalidInputs = {
...inputs,
mrz: Array(93)
.fill(0)
.map((byte) => BigInt(byte).toString()),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
it("should fail to calculate witness with invalid econtent", async function () {
try {
const invalidInputs = {
...inputs,
econtent: inputs.econtent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
it('should fail to calculate witness with invalid econtent', async function () {
try {
const invalidInputs = {
...inputs,
econtent: inputs.econtent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
it("should fail to calculate witness with invalid signature", async function () {
try {
const invalidInputs = {
...inputs,
signature: inputs.signature.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
});
it('should fail to calculate witness with invalid signature', async function () {
try {
const invalidInputs = {
...inputs,
signature: inputs.signature.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
});

View File

@@ -1,126 +1,125 @@
import { describe } from 'mocha'
import { assert, expect } from 'chai'
import path from "path";
const wasm_tester = require("circom_tester").wasm;
import { poseidon1, poseidon6 } from "poseidon-lite";
import { mockPassportData_sha256_rsapss_65537 } from "../../../common/src/constants/mockPassportData";
import { describe } from 'mocha';
import { assert, expect } from 'chai';
import path from 'path';
const wasm_tester = require('circom_tester').wasm;
import { poseidon1, poseidon6 } from 'poseidon-lite';
import { mockPassportData_sha256_rsapss_65537 } from '../../../common/src/constants/mockPassportData';
import { generateCircuitInputsRegister } from '../../../common/src/utils/generateInputs';
import { getLeaf } from '../../../common/src/utils/pubkeyTree';
import { packBytes } from '../../../common/src/utils/utils';
describe("Register - SHA256 RSASSAPSS", function () {
this.timeout(0);
let inputs: any;
let circuit: any;
let passportData = mockPassportData_sha256_rsapss_65537;
const attestation_name = "E-PASSPORT";
let attestation_id: string;
const n_dsc = 64;
const k_dsc = 32;
describe('Register - SHA256 RSASSAPSS', function () {
this.timeout(0);
let inputs: any;
let circuit: any;
let passportData = mockPassportData_sha256_rsapss_65537;
const attestation_name = 'E-PASSPORT';
let attestation_id: string;
const n_dsc = 64;
const k_dsc = 32;
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, "../../circuits/register/register_sha256WithRSASSAPSS_65537.circom"),
{
include: [
"node_modules",
"node_modules/@zk-email/circuits/helpers/sha.circom",
"./node_modules/@zk-kit/binary-merkle-root.circom/src",
"./node_modules/circomlib/circuits"
]
},
);
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, '../../circuits/register/register_sha256WithRSASSAPSS_65537.circom'),
{
include: [
'node_modules',
'node_modules/@zk-email/circuits/helpers/sha.circom',
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
],
}
);
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
const dscSecret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
attestation_id = poseidon1([
BigInt(Buffer.from(attestation_name).readUIntBE(0, 6))
]).toString();
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
const dscSecret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
attestation_id = poseidon1([BigInt(Buffer.from(attestation_name).readUIntBE(0, 6))]).toString();
inputs = generateCircuitInputsRegister(
secret,
dscSecret,
attestation_id,
passportData,
n_dsc,
k_dsc
);
inputs = generateCircuitInputsRegister(
secret,
dscSecret,
attestation_id,
passportData,
n_dsc,
k_dsc
);
});
});
it('should compile and load the circuit', async function () {
expect(circuit).to.not.be.undefined;
});
it("should compile and load the circuit", async function () {
expect(circuit).to.not.be.undefined;
});
it('should calculate the witness with correct inputs', async function () {
console.time('calculateWitness');
const w = await circuit.calculateWitness(inputs);
console.timeEnd('calculateWitness');
await circuit.checkConstraints(w);
it("should calculate the witness with correct inputs", async function () {
console.time('calculateWitness')
const w = await circuit.calculateWitness(inputs);
console.timeEnd('calculateWitness')
await circuit.checkConstraints(w);
const nullifier = (await circuit.getOutput(w, ['nullifier'])).nullifier;
console.log('\x1b[34m%s\x1b[0m', 'nullifier', nullifier);
const commitment_circom = (await circuit.getOutput(w, ['commitment'])).commitment;
console.log('\x1b[34m%s\x1b[0m', 'commitment', commitment_circom);
const blinded_dsc_commitment = (await circuit.getOutput(w, ['blinded_dsc_commitment']))
.blinded_dsc_commitment;
console.log('\x1b[34m%s\x1b[0m', 'blinded_dsc_commitment', blinded_dsc_commitment);
const nullifier = (await circuit.getOutput(w, ["nullifier"])).nullifier;
console.log("\x1b[34m%s\x1b[0m", "nullifier", nullifier);
const commitment_circom = (await circuit.getOutput(w, ["commitment"])).commitment;
console.log("\x1b[34m%s\x1b[0m", "commitment", commitment_circom);
const blinded_dsc_commitment = (await circuit.getOutput(w, ["blinded_dsc_commitment"])).blinded_dsc_commitment;
console.log("\x1b[34m%s\x1b[0m", "blinded_dsc_commitment", blinded_dsc_commitment);
const mrz_bytes = packBytes(inputs.mrz);
const commitment_bytes = poseidon6([
inputs.secret[0],
attestation_id,
getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent,
}),
mrz_bytes[0],
mrz_bytes[1],
mrz_bytes[2],
]);
const commitment_js = commitment_bytes.toString();
//console.log('commitment_js', commitment_js)
//console.log('commitment_circom', commitment_circom)
expect(commitment_circom).to.be.equal(commitment_js);
});
const mrz_bytes = packBytes(inputs.mrz);
const commitment_bytes = poseidon6([
inputs.secret[0],
attestation_id,
getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent
}),
mrz_bytes[0],
mrz_bytes[1],
mrz_bytes[2]
]);
const commitment_js = commitment_bytes.toString();
//console.log('commitment_js', commitment_js)
//console.log('commitment_circom', commitment_circom)
expect(commitment_circom).to.be.equal(commitment_js);
});
it('should fail to calculate witness with invalid mrz', async function () {
try {
const invalidInputs = {
...inputs,
mrz: Array(93)
.fill(0)
.map((byte) => BigInt(byte).toString()),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
it("should fail to calculate witness with invalid mrz", async function () {
try {
const invalidInputs = {
...inputs,
mrz: Array(93).fill(0).map(byte => BigInt(byte).toString())
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
it('should fail to calculate witness with invalid econtent', async function () {
try {
const invalidInputs = {
...inputs,
econtent: inputs.econtent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
it("should fail to calculate witness with invalid econtent", async function () {
try {
const invalidInputs = {
...inputs,
econtent: inputs.econtent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
it("should fail to calculate witness with invalid signature", async function () {
try {
const invalidInputs = {
...inputs,
signature: inputs.signature.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
}
await circuit.calculateWitness(invalidInputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
});
it('should fail to calculate witness with invalid signature', async function () {
try {
const invalidInputs = {
...inputs,
signature: inputs.signature.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
};
await circuit.calculateWitness(invalidInputs);
expect.fail('Expected an error but none was thrown.');
} catch (error) {
expect(error.message).to.include('Assert Failed');
}
});
});

View File

@@ -1,154 +1,149 @@
import path from 'path';
import { createHash, randomBytes } from 'node:crypto';
const wasm_tester = require("circom_tester").wasm;
const wasm_tester = require('circom_tester').wasm;
describe('Mgf1_sha256 Circuit Test', function () {
this.timeout(0); // Disable timeout
const hashLen = 32; // SHA256 length
this.timeout(0); // Disable timeout
const hashLen = 32; // SHA256 length
const compileCircuit = async (circuitPath: string) => {
return await wasm_tester(
path.join(circuitPath),
{
include: [
"node_modules",
]
}
);
}
function buffer2bitArray(b) {
const res = [];
for (let i=0; i<b.length; i++) {
for (let j=0; j<8; j++) {
res.push((b[i] >> (7-j) &1));
}
}
return res;
}
function num2Bits(n, input) {
let out = [];
for (let i = 0; i < n; i++) {
out[i] = (input >> i) & 1;
}
return out;
}
const bitArray2buffer = (a) => {
const len = Math.floor((a.length -1 )/8)+1;
const b = Buffer.alloc(len);
for (let i=0; i<a.length; i++) {
const p = Math.floor(i/8);
b[p] = b[p] | (Number(a[i]) << ( 7 - (i%8) ));
}
return b;
}
const MGF1 = (mgfSeed: Buffer, maskLen: number) => {
const hLen = hashLen;
if (maskLen > 0xffffffff * hLen) {
throw new Error('mask too long');
}
var T = [];
for (var i = 0; i <= Math.ceil(maskLen / hLen) - 1; i++) {
var C = Buffer.alloc(4);
C.writeUInt32BE(i);
const hash3 = createHash('sha256');
hash3.update(Buffer.concat([mgfSeed, C]));
T.push(hash3.digest());
}
return Buffer.concat(T).slice(0, maskLen);
}
it('Should compile', async function () {
await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom');
})
it('Should generate correct MGF1 output - 4 Byte Seed', async function () {
const seed = 12345678;
const maskLen = 32;
const seedLen = 4; // 4 bytes - set in the circuit
const bitArray = num2Bits(seedLen * 8, seed);
const mgfSeed = bitArray2buffer(bitArray);
const expected = MGF1(mgfSeed, maskLen);
const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: seed,
expected_mask_output,
};
const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
const compileCircuit = async (circuitPath: string) => {
return await wasm_tester(path.join(circuitPath), {
include: ['node_modules'],
});
};
it('Should generate correct MGF1 output - 32 Byte Seed', async function () {
const randBytes = randomBytes(32);
function buffer2bitArray(b) {
const res = [];
for (let i = 0; i < b.length; i++) {
for (let j = 0; j < 8; j++) {
res.push((b[i] >> (7 - j)) & 1);
}
}
return res;
}
const maskLen = 32;
const seedLen = 32; // set in circuit
const expected = MGF1(randBytes, maskLen);
function num2Bits(n, input) {
let out = [];
for (let i = 0; i < n; i++) {
out[i] = (input >> i) & 1;
}
return out;
}
const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_32Bytes_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: buffer2bitArray(randBytes),
expected_mask_output,
const bitArray2buffer = (a) => {
const len = Math.floor((a.length - 1) / 8) + 1;
const b = Buffer.alloc(len);
};
for (let i = 0; i < a.length; i++) {
const p = Math.floor(i / 8);
b[p] = b[p] | (Number(a[i]) << (7 - (i % 8)));
}
return b;
};
const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});
const MGF1 = (mgfSeed: Buffer, maskLen: number) => {
const hLen = hashLen;
if (maskLen > 0xffffffff * hLen) {
throw new Error('mask too long');
}
it('Should generate correct MGF1 output - seedLen value > than actual seed length', async function () {
const seed = 1234;
const maskLen = 32;
const seedLen = 4; //set in circuit
var T = [];
for (var i = 0; i <= Math.ceil(maskLen / hLen) - 1; i++) {
var C = Buffer.alloc(4);
C.writeUInt32BE(i);
const hash3 = createHash('sha256');
hash3.update(Buffer.concat([mgfSeed, C]));
T.push(hash3.digest());
}
return Buffer.concat(T).slice(0, maskLen);
};
const bitArray = num2Bits(seedLen * 8, seed);
const mgfSeed = bitArray2buffer(bitArray);
it('Should compile', async function () {
await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom');
});
const expected = MGF1(mgfSeed, maskLen);
it('Should generate correct MGF1 output - 4 Byte Seed', async function () {
const seed = 12345678;
const maskLen = 32;
const seedLen = 4; // 4 bytes - set in the circuit
const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: seed,
expected_mask_output,
const bitArray = num2Bits(seedLen * 8, seed);
const mgfSeed = bitArray2buffer(bitArray);
};
const expected = MGF1(mgfSeed, maskLen);
const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});
const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: seed,
expected_mask_output,
};
it('Should generate correct MGF1 output - maskLen == 1', async function () {
const seed = 12345678;
const maskLen = 1;
const seedLen = 4;
const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});
const bitArray = num2Bits(seedLen * 8, seed);
const mgfSeed = bitArray2buffer(bitArray);
it('Should generate correct MGF1 output - 32 Byte Seed', async function () {
const randBytes = randomBytes(32);
const expected = MGF1(mgfSeed, maskLen);
const maskLen = 32;
const seedLen = 32; // set in circuit
const expected = MGF1(randBytes, maskLen);
const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_1ByteMask_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: seed,
expected_mask_output,
const circuit = await compileCircuit(
'circuits/tests/mgf1Sha256/Mgf1Sha256_32Bytes_tester.circom'
);
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: buffer2bitArray(randBytes),
expected_mask_output,
};
};
const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});
const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});
});
it('Should generate correct MGF1 output - seedLen value > than actual seed length', async function () {
const seed = 1234;
const maskLen = 32;
const seedLen = 4; //set in circuit
const bitArray = num2Bits(seedLen * 8, seed);
const mgfSeed = bitArray2buffer(bitArray);
const expected = MGF1(mgfSeed, maskLen);
const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: seed,
expected_mask_output,
};
const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});
it('Should generate correct MGF1 output - maskLen == 1', async function () {
const seed = 12345678;
const maskLen = 1;
const seedLen = 4;
const bitArray = num2Bits(seedLen * 8, seed);
const mgfSeed = bitArray2buffer(bitArray);
const expected = MGF1(mgfSeed, maskLen);
const circuit = await compileCircuit(
'circuits/tests/mgf1Sha256/Mgf1Sha256_1ByteMask_tester.circom'
);
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: seed,
expected_mask_output,
};
const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});
});

View File

@@ -1,179 +1,182 @@
import chai, { expect, assert } from 'chai';
import path from 'path';
const wasm_tester = require("circom_tester").wasm;
const wasm_tester = require('circom_tester').wasm;
describe('DateIsLessChecker Circuit Test', function () {
this.timeout(0); // Disable timeout
this.timeout(0); // Disable timeout
let circuit;
let circuit;
/**
* Test parameters
*
* n: number of dates to test
* majority: age of majority
* yearStart: start year for random current dates
* yearEnd: end year for random current dates
*
* According to circuit logic, user has to be majority years and 1 day old to be major
*
*/
/**
* Test parameters
*
* n: number of dates to test
* majority: age of majority
* yearStart: start year for random current dates
* yearEnd: end year for random current dates
*
* According to circuit logic, user has to be majority years and 1 day old to be major
*
*/
const n = 10;
const majority = 18;
const n = 10;
const majority = 18;
const yearStart = 2023;
const yearEnd = 2200;
const maxDiff = 99; // Maximum age difference
const minDiff = majority; // Minimum age for majority
const yearStart = 2023;
const yearEnd = 2200;
const maxDiff = 99; // Maximum age difference
const minDiff = majority; // Minimum age for majority
// Helper function to generate a random date within a given range
function generateRandomDate(yearStart, yearEnd) {
const year = Math.floor(Math.random() * (yearEnd - yearStart + 1)) + yearStart;
const month = Math.floor(Math.random() * 12) + 1;
const day = Math.floor(Math.random() * 28) + 1; // Simplification for month lengths
return { year, month, day };
}
// Helper function to generate a random date within a given range
function generateRandomDate(yearStart, yearEnd) {
const year = Math.floor(Math.random() * (yearEnd - yearStart + 1)) + yearStart;
const month = Math.floor(Math.random() * 12) + 1;
const day = Math.floor(Math.random() * 28) + 1; // Simplification for month lengths
return { year, month, day };
}
// Generate arrays for current dates
const currentDates = Array(n).fill(0).map(() => generateRandomDate(yearStart, yearEnd));
// Generate arrays for current dates
const currentDates = Array(n)
.fill(0)
.map(() => generateRandomDate(yearStart, yearEnd));
// Generate majority birthDates ensuring the age difference is at least minDiff
const majorityBirthDates = currentDates.map(currentDate => {
// Subtract a random number of years within the allowed age difference, plus a random number of days and months for additional variance
const yearDiff = Math.floor(Math.random() * (maxDiff - minDiff)) + minDiff;
const monthDiff = Math.floor(Math.random() * 12);
const dayDiff = Math.floor(Math.random() * 28) + 1;
return {
year: currentDate.year - yearDiff,
month: Math.max(1, currentDate.month - monthDiff), // Ensure month is within valid range
day: Math.max(1, currentDate.day - dayDiff) // Ensure day is within valid range
// Generate majority birthDates ensuring the age difference is at least minDiff
const majorityBirthDates = currentDates.map((currentDate) => {
// Subtract a random number of years within the allowed age difference, plus a random number of days and months for additional variance
const yearDiff = Math.floor(Math.random() * (maxDiff - minDiff)) + minDiff;
const monthDiff = Math.floor(Math.random() * 12);
const dayDiff = Math.floor(Math.random() * 28) + 1;
return {
year: currentDate.year - yearDiff,
month: Math.max(1, currentDate.month - monthDiff), // Ensure month is within valid range
day: Math.max(1, currentDate.day - dayDiff), // Ensure day is within valid range
};
});
// Generate minority birthDates ensuring the age difference is less than minDiff
const minorityBirthDates = currentDates.map((currentDate) => {
const yearDiff = Math.floor(Math.random() * minDiff);
const monthDiff = Math.floor(Math.random() * 12);
const dayDiff = Math.floor(Math.random() * 28);
return {
year: currentDate.year - yearDiff,
month: Math.max(1, currentDate.month - monthDiff), // Ensure month is within valid range
day: Math.max(1, currentDate.day - dayDiff), // Ensure day is within valid range
};
});
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, '../../circuits/tests/utils/isOlderThan_tester.circom'),
{
include: ['node_modules'],
}
);
});
it('compile and load the circuit', async function () {
expect(circuit).to.not.be.undefined;
});
describe('Majority Tests', function () {
majorityBirthDates.forEach((date, index) => {
it(`majority check for birthdate ${genDateStr(majorityBirthDates[index])} and current date ${genDateStr(currentDates[index])} and age: ${getAgeFromDates(majorityBirthDates[index], currentDates[index])}`, async function () {
const inputs = {
majority: [49, 56],
currDate: [
Math.floor(currentDates[index].year / 10) % 10,
currentDates[index].year % 10,
Math.floor(currentDates[index].month / 10),
currentDates[index].month % 10,
Math.floor(currentDates[index].day / 10),
currentDates[index].day % 10,
],
birthDateASCII: [
Math.floor(date.year / 10) % 10,
date.year % 10,
Math.floor(date.month / 10),
date.month % 10,
Math.floor(date.day / 10),
date.day % 10,
].map((n) => n + 48), // Convert to ASCII for the circuit input
};
/*
console.log("current date: " + JSON.stringify(currentDates[index]));
console.log("majority birth date: " + JSON.stringify(majorityBirthDates[index]));
console.log("yearDiff: " + (currentDates[index].year - majorityBirthDates[index].year) + " monthDiff: " + (currentDates[index].month - majorityBirthDates[index].month) + " dayDiff: " + (currentDates[index].day - majorityBirthDates[index].day));
*/
const witness = await circuit.calculateWitness(inputs, true);
const output = await circuit.getOutput(witness, ['out']);
assert.strictEqual(output.out, '1', 'Person should be of majority age');
});
});
});
// Generate minority birthDates ensuring the age difference is less than minDiff
const minorityBirthDates = currentDates.map(currentDate => {
const yearDiff = Math.floor(Math.random() * minDiff);
const monthDiff = Math.floor(Math.random() * 12);
const dayDiff = Math.floor(Math.random() * 28);
return {
year: currentDate.year - yearDiff,
month: Math.max(1, currentDate.month - monthDiff), // Ensure month is within valid range
day: Math.max(1, currentDate.day - dayDiff) // Ensure day is within valid range
describe('Minority Tests', function () {
minorityBirthDates.forEach((date, index) => {
it(`minority check for birthdate ${genDateStr(minorityBirthDates[index])} and current date ${genDateStr(currentDates[index])} and age: ${getAgeFromDates(minorityBirthDates[index], currentDates[index])}`, async function () {
const inputs = {
majority: [49, 58],
currDate: [
Math.floor(currentDates[index].year / 10) % 10,
currentDates[index].year % 10,
Math.floor(currentDates[index].month / 10),
currentDates[index].month % 10,
Math.floor(currentDates[index].day / 10),
currentDates[index].day % 10,
],
birthDateASCII: [
Math.floor(date.year / 10) % 10,
date.year % 10,
Math.floor(date.month / 10),
date.month % 10,
Math.floor(date.day / 10),
date.day % 10,
].map((n) => n + 48), // Convert to ASCII for the circuit input
};
/*
console.log("current date: " + JSON.stringify(currentDates[index]));
console.log("minority birth date: " + JSON.stringify(minorityBirthDates[index]));
console.log("yearDiff: " + (currentDates[index].year - minorityBirthDates[index].year) + " monthDiff: " + (currentDates[index].month - minorityBirthDates[index].month) + " dayDiff: " + (currentDates[index].day - minorityBirthDates[index].day));
*/
const witness = await circuit.calculateWitness(inputs, true);
const output = await circuit.getOutput(witness, ['out']);
assert.strictEqual(output.out, '0', 'Person should not be of majority age');
});
});
});
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, '../../circuits/tests/utils/isOlderThan_tester.circom'),
{
include: [
"node_modules",
]
}
);
});
function genDateStr(currentDate: { year: number; month: number; day: number }): string {
// Ensure month and day are two digits by padding with '0' if necessary
const formattedMonth = currentDate.month.toString().padStart(2, '0');
const formattedDay = currentDate.day.toString().padStart(2, '0');
return `${currentDate.year}${formattedMonth}${formattedDay}`;
}
it("compile and load the circuit", async function () {
expect(circuit).to.not.be.undefined;
});
function getAgeFromDates(
birthDate: { year: number; month: number; day: number },
currentDate: { year: number; month: number; day: number }
): string {
let years = currentDate.year - birthDate.year;
let months = currentDate.month - birthDate.month;
let days = currentDate.day - birthDate.day;
describe("Majority Tests", function () {
majorityBirthDates.forEach((date, index) => {
it(`majority check for birthdate ${genDateStr(majorityBirthDates[index])} and current date ${genDateStr(currentDates[index])} and age: ${getAgeFromDates(majorityBirthDates[index], currentDates[index])}`, async function () {
const inputs = {
majority: [49, 56],
currDate: [
Math.floor(currentDates[index].year / 10) % 10,
currentDates[index].year % 10,
Math.floor(currentDates[index].month / 10),
currentDates[index].month % 10,
Math.floor(currentDates[index].day / 10),
currentDates[index].day % 10
],
birthDateASCII: [
Math.floor(date.year / 10) % 10,
date.year % 10,
Math.floor(date.month / 10),
date.month % 10,
Math.floor(date.day / 10),
date.day % 10
].map(n => n + 48) // Convert to ASCII for the circuit input
};
/*
console.log("current date: " + JSON.stringify(currentDates[index]));
console.log("majority birth date: " + JSON.stringify(majorityBirthDates[index]));
console.log("yearDiff: " + (currentDates[index].year - majorityBirthDates[index].year) + " monthDiff: " + (currentDates[index].month - majorityBirthDates[index].month) + " dayDiff: " + (currentDates[index].day - majorityBirthDates[index].day));
*/
const witness = await circuit.calculateWitness(inputs, true);
const output = await circuit.getOutput(witness, ["out"]);
assert.strictEqual(output.out, '1', "Person should be of majority age");
});
});
});
describe("Minority Tests", function () {
minorityBirthDates.forEach((date, index) => {
it(`minority check for birthdate ${genDateStr(minorityBirthDates[index])} and current date ${genDateStr(currentDates[index])} and age: ${getAgeFromDates(minorityBirthDates[index], currentDates[index])}`, async function () {
const inputs = {
majority: [49, 58],
currDate: [
Math.floor(currentDates[index].year / 10) % 10,
currentDates[index].year % 10,
Math.floor(currentDates[index].month / 10),
currentDates[index].month % 10,
Math.floor(currentDates[index].day / 10),
currentDates[index].day % 10
],
birthDateASCII: [
Math.floor(date.year / 10) % 10,
date.year % 10,
Math.floor(date.month / 10),
date.month % 10,
Math.floor(date.day / 10),
date.day % 10
].map(n => n + 48) // Convert to ASCII for the circuit input
};
/*
console.log("current date: " + JSON.stringify(currentDates[index]));
console.log("minority birth date: " + JSON.stringify(minorityBirthDates[index]));
console.log("yearDiff: " + (currentDates[index].year - minorityBirthDates[index].year) + " monthDiff: " + (currentDates[index].month - minorityBirthDates[index].month) + " dayDiff: " + (currentDates[index].day - minorityBirthDates[index].day));
*/
const witness = await circuit.calculateWitness(inputs, true);
const output = await circuit.getOutput(witness, ["out"]);
assert.strictEqual(output.out, '0', "Person should not be of majority age");
});
});
});
function genDateStr(currentDate: { year: number; month: number; day: number }): string {
// Ensure month and day are two digits by padding with '0' if necessary
const formattedMonth = currentDate.month.toString().padStart(2, '0');
const formattedDay = currentDate.day.toString().padStart(2, '0');
return `${currentDate.year}${formattedMonth}${formattedDay}`;
if (days < 0) {
months -= 1;
const lastDayOfPreviousMonth = new Date(currentDate.year, currentDate.month - 1, 0).getDate();
days += lastDayOfPreviousMonth;
}
function getAgeFromDates(birthDate: { year: number; month: number; day: number }, currentDate: { year: number; month: number; day: number }): string {
let years = currentDate.year - birthDate.year;
let months = currentDate.month - birthDate.month;
let days = currentDate.day - birthDate.day;
if (days < 0) {
months -= 1;
const lastDayOfPreviousMonth = new Date(currentDate.year, currentDate.month - 1, 0).getDate();
days += lastDayOfPreviousMonth;
}
if (months < 0) {
years -= 1;
months += 12;
}
// Add 's' at the end of year, month, and day if they are greater than 1
const yearStr = years > 1 ? 'years' : 'year';
const monthStr = months > 1 ? 'months' : 'month';
const dayStr = days > 1 ? 'days' : 'day';
return `${years} ${yearStr}, ${months} ${monthStr}, ${days} ${dayStr}`;
if (months < 0) {
years -= 1;
months += 12;
}
// Add 's' at the end of year, month, and day if they are greater than 1
const yearStr = years > 1 ? 'years' : 'year';
const monthStr = months > 1 ? 'months' : 'month';
const dayStr = days > 1 ? 'days' : 'day';
return `${years} ${yearStr}, ${months} ${monthStr}, ${days} ${dayStr}`;
}
});

View File

@@ -1,176 +1,179 @@
import chai, { expect, assert } from 'chai';
import path from 'path';
const wasm_tester = require("circom_tester").wasm;
const wasm_tester = require('circom_tester').wasm;
describe('DateIsLessChecker Circuit Test', function () {
this.timeout(0); // Disable timeout
this.timeout(0); // Disable timeout
let circuit;
let circuit;
/**
* Test parameters
*
* n: number of dates to test
* majority: age of majority
* yearStart: start year for random current dates
* yearEnd: end year for random current dates
*
* According to circuit logic, user has to be majority years and 1 day old to be major
*
*/
/**
* Test parameters
*
* n: number of dates to test
* majority: age of majority
* yearStart: start year for random current dates
* yearEnd: end year for random current dates
*
* According to circuit logic, user has to be majority years and 1 day old to be major
*
*/
const n = 10;
const n = 10;
const yearStart = 2023;
const yearEnd = 2049;
const maxDiff = 20; // Maximum date difference
const minDiff = -10; // Minimum date for majority
const yearStart = 2023;
const yearEnd = 2049;
const maxDiff = 20; // Maximum date difference
const minDiff = -10; // Minimum date for majority
// Helper function to generate a random date within a given range
function generateRandomDate(yearStart, yearEnd) {
const year = Math.floor(Math.random() * (yearEnd - yearStart + 1)) + yearStart;
const month = Math.floor(Math.random() * 12) + 1;
const day = Math.floor(Math.random() * 28) + 1; // Simplification for month lengths
return { year, month, day };
}
// Helper function to generate a random date within a given range
function generateRandomDate(yearStart, yearEnd) {
const year = Math.floor(Math.random() * (yearEnd - yearStart + 1)) + yearStart;
const month = Math.floor(Math.random() * 12) + 1;
const day = Math.floor(Math.random() * 28) + 1; // Simplification for month lengths
return { year, month, day };
}
// Generate arrays for current dates
const currentDates = Array(n).fill(0).map(() => generateRandomDate(yearStart, yearEnd));
// Generate arrays for current dates
const currentDates = Array(n)
.fill(0)
.map(() => generateRandomDate(yearStart, yearEnd));
// Generate majority birthDates ensuring the age difference is at least minDiff
const unvalidExpiryDates = currentDates.map(currentDate => {
// Subtract a random number of years within the allowed age difference, plus a random number of days and months for additional variance
const yearDiff = Math.floor(Math.random() * (maxDiff - 0)) + 0;
const monthDiff = Math.floor(Math.random() * 12);
const dayDiff = Math.floor(Math.random() * 28) + 1;
return {
year: currentDate.year - yearDiff,
month: Math.max(1, currentDate.month - monthDiff), // Ensure month is within valid range
day: Math.max(1, currentDate.day - dayDiff) // Ensure day is within valid range
// Generate majority birthDates ensuring the age difference is at least minDiff
const unvalidExpiryDates = currentDates.map((currentDate) => {
// Subtract a random number of years within the allowed age difference, plus a random number of days and months for additional variance
const yearDiff = Math.floor(Math.random() * (maxDiff - 0)) + 0;
const monthDiff = Math.floor(Math.random() * 12);
const dayDiff = Math.floor(Math.random() * 28) + 1;
return {
year: currentDate.year - yearDiff,
month: Math.max(1, currentDate.month - monthDiff), // Ensure month is within valid range
day: Math.max(1, currentDate.day - dayDiff), // Ensure day is within valid range
};
});
// Generate minority birthDates ensuring the age difference is less than minDiff
const validExpiryDates = currentDates.map((currentDate) => {
const yearDiff = Math.floor(Math.random() * -maxDiff);
const monthDiff = Math.floor(Math.random() * 12);
const dayDiff = Math.floor(Math.random() * 28);
return {
year: currentDate.year - yearDiff,
month: Math.max(1, currentDate.month - monthDiff), // Ensure month is within valid range
day: Math.max(1, currentDate.day - dayDiff), // Ensure day is within valid range
};
});
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, '../../circuits/tests/utils/isValid_tester.circom'),
{
include: ['node_modules'],
}
);
});
it('compile and load the circuit', async function () {
expect(circuit).to.not.be.undefined;
});
describe('Unvlaidity Tests', function () {
unvalidExpiryDates.forEach((date, index) => {
it(`unvalidity check for expiry date ${genDateStr(unvalidExpiryDates[index])} and current date ${genDateStr(currentDates[index])}, expired since: ${getAgeFromDates(unvalidExpiryDates[index], currentDates[index])}`, async function () {
const inputs = {
currDate: [
Math.floor(currentDates[index].year / 10) % 10,
currentDates[index].year % 10,
Math.floor(currentDates[index].month / 10),
currentDates[index].month % 10,
Math.floor(currentDates[index].day / 10),
currentDates[index].day % 10,
],
validityDateASCII: [
Math.floor(date.year / 10) % 10,
date.year % 10,
Math.floor(date.month / 10),
date.month % 10,
Math.floor(date.day / 10),
date.day % 10,
].map((n) => n + 48), // Convert to ASCII for the circuit input
};
});
// Generate minority birthDates ensuring the age difference is less than minDiff
const validExpiryDates = currentDates.map(currentDate => {
const yearDiff = Math.floor(Math.random() * (- maxDiff));
const monthDiff = Math.floor(Math.random() * 12);
const dayDiff = Math.floor(Math.random() * 28);
return {
year: currentDate.year - yearDiff,
month: Math.max(1, currentDate.month - monthDiff), // Ensure month is within valid range
day: Math.max(1, currentDate.day - dayDiff) // Ensure day is within valid range
};
});
before(async () => {
circuit = await wasm_tester(
path.join(__dirname, '../../circuits/tests/utils/isValid_tester.circom'),
{
include: [
"node_modules",
]
}
);
});
it("compile and load the circuit", async function () {
expect(circuit).to.not.be.undefined;
});
describe("Unvlaidity Tests", function () {
unvalidExpiryDates.forEach((date, index) => {
it(`unvalidity check for expiry date ${genDateStr(unvalidExpiryDates[index])} and current date ${genDateStr(currentDates[index])}, expired since: ${getAgeFromDates(unvalidExpiryDates[index], currentDates[index])}`, async function () {
const inputs = {
currDate: [
Math.floor(currentDates[index].year / 10) % 10,
currentDates[index].year % 10,
Math.floor(currentDates[index].month / 10),
currentDates[index].month % 10,
Math.floor(currentDates[index].day / 10),
currentDates[index].day % 10
],
validityDateASCII: [
Math.floor(date.year / 10) % 10,
date.year % 10,
Math.floor(date.month / 10),
date.month % 10,
Math.floor(date.day / 10),
date.day % 10
].map(n => n + 48) // Convert to ASCII for the circuit input
};
/*
/*
console.log("current date: " + JSON.stringify(currentDates[index]));
console.log("majority birth date: " + JSON.stringify(majorityBirthDates[index]));
console.log("yearDiff: " + (currentDates[index].year - majorityBirthDates[index].year) + " monthDiff: " + (currentDates[index].month - majorityBirthDates[index].month) + " dayDiff: " + (currentDates[index].day - majorityBirthDates[index].day));
*/
const witness = await circuit.calculateWitness(inputs, true);
const output = await circuit.getOutput(witness, ["out"]);
assert.strictEqual(output.out, '0', "Passport should not be valid");
});
});
const witness = await circuit.calculateWitness(inputs, true);
const output = await circuit.getOutput(witness, ['out']);
assert.strictEqual(output.out, '0', 'Passport should not be valid');
});
});
});
describe("Validity Tests", function () {
validExpiryDates.forEach((date, index) => {
it(`validity check for expiry date ${genDateStr(validExpiryDates[index])} and current date ${genDateStr(currentDates[index])} valid until: ${getAgeFromDates(currentDates[index], validExpiryDates[index])}`, async function () {
const inputs = {
currDate: [
Math.floor(currentDates[index].year / 10) % 10,
currentDates[index].year % 10,
Math.floor(currentDates[index].month / 10),
currentDates[index].month % 10,
Math.floor(currentDates[index].day / 10),
currentDates[index].day % 10
],
validityDateASCII: [
Math.floor(date.year / 10) % 10,
date.year % 10,
Math.floor(date.month / 10),
date.month % 10,
Math.floor(date.day / 10),
date.day % 10
].map(n => n + 48) // Convert to ASCII for the circuit input
};
/*
describe('Validity Tests', function () {
validExpiryDates.forEach((date, index) => {
it(`validity check for expiry date ${genDateStr(validExpiryDates[index])} and current date ${genDateStr(currentDates[index])} valid until: ${getAgeFromDates(currentDates[index], validExpiryDates[index])}`, async function () {
const inputs = {
currDate: [
Math.floor(currentDates[index].year / 10) % 10,
currentDates[index].year % 10,
Math.floor(currentDates[index].month / 10),
currentDates[index].month % 10,
Math.floor(currentDates[index].day / 10),
currentDates[index].day % 10,
],
validityDateASCII: [
Math.floor(date.year / 10) % 10,
date.year % 10,
Math.floor(date.month / 10),
date.month % 10,
Math.floor(date.day / 10),
date.day % 10,
].map((n) => n + 48), // Convert to ASCII for the circuit input
};
/*
console.log("current date: " + JSON.stringify(currentDates[index]));
console.log("minority birth date: " + JSON.stringify(minorityBirthDates[index]));
console.log("yearDiff: " + (currentDates[index].year - minorityBirthDates[index].year) + " monthDiff: " + (currentDates[index].month - minorityBirthDates[index].month) + " dayDiff: " + (currentDates[index].day - minorityBirthDates[index].day));
*/
const witness = await circuit.calculateWitness(inputs, true);
const output = await circuit.getOutput(witness, ["out"]);
assert.strictEqual(output.out, '1', "Passport should be valid");
});
});
const witness = await circuit.calculateWitness(inputs, true);
const output = await circuit.getOutput(witness, ['out']);
assert.strictEqual(output.out, '1', 'Passport should be valid');
});
});
});
function genDateStr(currentDate: { year: number; month: number; day: number }): string {
// Ensure month and day are two digits by padding with '0' if necessary
const formattedMonth = currentDate.month.toString().padStart(2, '0');
const formattedDay = currentDate.day.toString().padStart(2, '0');
return `${currentDate.year}${formattedMonth}${formattedDay}`;
function genDateStr(currentDate: { year: number; month: number; day: number }): string {
// Ensure month and day are two digits by padding with '0' if necessary
const formattedMonth = currentDate.month.toString().padStart(2, '0');
const formattedDay = currentDate.day.toString().padStart(2, '0');
return `${currentDate.year}${formattedMonth}${formattedDay}`;
}
function getAgeFromDates(
birthDate: { year: number; month: number; day: number },
currentDate: { year: number; month: number; day: number }
): string {
let years = currentDate.year - birthDate.year;
let months = currentDate.month - birthDate.month;
let days = currentDate.day - birthDate.day;
if (days < 0) {
months -= 1;
const lastDayOfPreviousMonth = new Date(currentDate.year, currentDate.month - 1, 0).getDate();
days += lastDayOfPreviousMonth;
}
function getAgeFromDates(birthDate: { year: number; month: number; day: number }, currentDate: { year: number; month: number; day: number }): string {
let years = currentDate.year - birthDate.year;
let months = currentDate.month - birthDate.month;
let days = currentDate.day - birthDate.day;
if (days < 0) {
months -= 1;
const lastDayOfPreviousMonth = new Date(currentDate.year, currentDate.month - 1, 0).getDate();
days += lastDayOfPreviousMonth;
}
if (months < 0) {
years -= 1;
months += 12;
}
// Add 's' at the end of year, month, and day if they are greater than 1
const yearStr = years > 1 ? 'years' : 'year';
const monthStr = months > 1 ? 'months' : 'month';
const dayStr = days > 1 ? 'days' : 'day';
return `${years} ${yearStr}, ${months} ${monthStr}, ${days} ${dayStr}`;
if (months < 0) {
years -= 1;
months += 12;
}
// Add 's' at the end of year, month, and day if they are greater than 1
const yearStr = years > 1 ? 'years' : 'year';
const monthStr = months > 1 ? 'months' : 'month';
const dayStr = days > 1 ? 'days' : 'day';
return `${years} ${yearStr}, ${months} ${monthStr}, ${days} ${dayStr}`;
}
});

View File

@@ -2,86 +2,104 @@ import { expect } from 'chai';
import { X509Certificate } from 'crypto';
import path from 'path';
import { getCSCAInputs, getTBSHash } from '../../../common/src/utils/csca';
const wasm_tester = require("circom_tester").wasm;
const wasm_tester = require('circom_tester').wasm;
import forge from 'node-forge';
import { mock_dsc_sha256_rsa_2048, mock_csca_sha256_rsa_2048, mock_dsc_sha1_rsa_2048, mock_csca_sha1_rsa_2048 } from '../../../common/src/constants/mockCertificates';
import {
mock_dsc_sha256_rsa_2048,
mock_csca_sha256_rsa_2048,
mock_dsc_sha1_rsa_2048,
mock_csca_sha1_rsa_2048,
} from '../../../common/src/constants/mockCertificates';
function loadCertificates(dscCertContent: string, cscaCertContent: string) {
const dscCert = new X509Certificate(dscCertContent);
const cscaCert = new X509Certificate(cscaCertContent);
const dscCert_forge = forge.pki.certificateFromPem(dscCertContent);
const cscaCert_forge = forge.pki.certificateFromPem(cscaCertContent);
const dscCert = new X509Certificate(dscCertContent);
const cscaCert = new X509Certificate(cscaCertContent);
const dscCert_forge = forge.pki.certificateFromPem(dscCertContent);
const cscaCert_forge = forge.pki.certificateFromPem(cscaCertContent);
return { dscCert, cscaCert, dscCert_forge, cscaCert_forge };
return { dscCert, cscaCert, dscCert_forge, cscaCert_forge };
}
describe('RSA Verifier', function () {
this.timeout(0);
let circuit;
this.timeout(0);
let circuit;
this.beforeAll(async () => {
const circuitPath = path.resolve(__dirname, '../../circuits/tests/utils/rsa_verifier.circom');
circuit = await wasm_tester(
circuitPath,
{
include: [
"node_modules",
"./node_modules/@zk-kit/binary-merkle-root.circom/src",
"./node_modules/circomlib/circuits"
]
}
);
this.beforeAll(async () => {
const circuitPath = path.resolve(__dirname, '../../circuits/tests/utils/rsa_verifier.circom');
circuit = await wasm_tester(circuitPath, {
include: [
'node_modules',
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
],
});
describe('Circuit', () => {
it('should compile and load the circuit', () => {
expect(circuit).not.to.be.undefined;
});
});
describe('Circuit', () => {
it('should compile and load the circuit', () => {
expect(circuit).not.to.be.undefined;
});
});
describe('SHA-256 certificates', async () => {
const { dscCert, cscaCert, dscCert_forge, cscaCert_forge } = loadCertificates(
mock_dsc_sha256_rsa_2048,
mock_csca_sha256_rsa_2048
);
it('should verify DSC has been signed by the CSCA', () => {
const isVerified = dscCert.verify(cscaCert.publicKey);
console.log(`SHA-256 DSC certificate verification result: ${isVerified}`);
expect(isVerified).to.be.true;
});
describe('SHA-256 certificates', async () => {
const { dscCert, cscaCert, dscCert_forge, cscaCert_forge } = loadCertificates(mock_dsc_sha256_rsa_2048, mock_csca_sha256_rsa_2048);
it('should extract and log certificate information', async () => {
const csca_inputs = getCSCAInputs(
'0',
dscCert_forge,
cscaCert_forge,
64,
32,
64,
32,
2048,
true
);
const tbsCertificateHashFormatted = getTBSHash(dscCert_forge, 'sha256');
it('should verify DSC has been signed by the CSCA', () => {
const isVerified = dscCert.verify(cscaCert.publicKey);
console.log(`SHA-256 DSC certificate verification result: ${isVerified}`);
expect(isVerified).to.be.true;
});
it('should extract and log certificate information', async () => {
const csca_inputs = getCSCAInputs("0", dscCert_forge, cscaCert_forge, 64, 32, 64, 32, 2048, true);
const tbsCertificateHashFormatted = getTBSHash(dscCert_forge, 'sha256');
const inputs = {
"message": tbsCertificateHashFormatted,
"signature": csca_inputs.dsc_signature,
"modulus": csca_inputs.csca_modulus
}
const witness = await circuit.calculateWitness(inputs, true);
});
const inputs = {
message: tbsCertificateHashFormatted,
signature: csca_inputs.dsc_signature,
modulus: csca_inputs.csca_modulus,
};
const witness = await circuit.calculateWitness(inputs, true);
});
});
describe('SHA-1 certificates', () => {
const { dscCert, cscaCert, dscCert_forge, cscaCert_forge } = loadCertificates(mock_dsc_sha1_rsa_2048, mock_csca_sha1_rsa_2048);
describe('SHA-1 certificates', () => {
const { dscCert, cscaCert, dscCert_forge, cscaCert_forge } = loadCertificates(
mock_dsc_sha1_rsa_2048,
mock_csca_sha1_rsa_2048
);
it('should verify DSC has been signed by the CSCA', () => {
const isVerified = dscCert.verify(cscaCert.publicKey);
console.log(`SHA-1 DSC certificate verification result: ${isVerified}`);
expect(isVerified).to.be.true;
});
/// TODO: Use SHA1RSA verifier circuit (won't work either case because of padding)
// it('should extract and log certificate information', async () => {
// const csca_inputs = getCSCAInputs("0", dscCert_forge, cscaCert_forge, 64, 32, 64, 32, 2048, true);
// const tbsCertificateHashFormatted = getTBSHash(dscCert_forge, 'sha1');
// const inputs = {
// "message": tbsCertificateHashFormatted,
// "signature": csca_inputs.dsc_signature,
// "modulus": csca_inputs.csca_modulus
// }
// console.log("final inputs: ", inputs);
// const witness = await circuit.calculateWitness(inputs, true);
// console.log(witness);
// });
it('should verify DSC has been signed by the CSCA', () => {
const isVerified = dscCert.verify(cscaCert.publicKey);
console.log(`SHA-1 DSC certificate verification result: ${isVerified}`);
expect(isVerified).to.be.true;
});
});
/// TODO: Use SHA1RSA verifier circuit (won't work either case because of padding)
// it('should extract and log certificate information', async () => {
// const csca_inputs = getCSCAInputs("0", dscCert_forge, cscaCert_forge, 64, 32, 64, 32, 2048, true);
// const tbsCertificateHashFormatted = getTBSHash(dscCert_forge, 'sha1');
// const inputs = {
// "message": tbsCertificateHashFormatted,
// "signature": csca_inputs.dsc_signature,
// "modulus": csca_inputs.csca_modulus
// }
// console.log("final inputs: ", inputs);
// const witness = await circuit.calculateWitness(inputs, true);
// console.log(witness);
// });
});
});

View File

@@ -4,6 +4,6 @@
"esModuleInterop": true,
"target": "ES2020",
"moduleResolution": "node",
"module": "CommonJS",
"module": "CommonJS"
}
}
}

View File

@@ -1739,6 +1739,11 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
prettier@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
psl@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"