Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: sha1 with rsa encryption #121

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/src/stores/userStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useNavigationStore from './navigationStore';
import { Steps } from '../utils/utils';
import { downloadZkey } from '../utils/zkeyDownload';
import { generateCircuitInputsRegister } from '../../../common/src/utils/generateInputs';
import { PASSPORT_ATTESTATION_ID, RPC_URL } from '../../../common/src/constants/constants';
import { PASSPORT_ATTESTATION_ID, RPC_URL, SignatureAlgorithm } from '../../../common/src/constants/constants';
import { generateProof } from '../utils/prover';
import { formatSigAlg } from '../../../common/src/utils/utils';
import { sendRegisterTransaction } from '../utils/transactions';
Expand Down Expand Up @@ -109,7 +109,6 @@ const useUserStore = create<UserState>((set, get) => ({
secret,
PASSPORT_ATTESTATION_ID,
passportData,
{ developmentMode: true }
);

amplitude.track(`Sig alg supported: ${passportData.signatureAlgorithm}`);
Expand All @@ -125,6 +124,7 @@ const useUserStore = create<UserState>((set, get) => ({
const start = Date.now();

const sigAlgFormatted = formatSigAlg(passportData.signatureAlgorithm, passportData.pubKey.exponent);
const sigAlgIndex = SignatureAlgorithm[sigAlgFormatted as keyof typeof SignatureAlgorithm]

const proof = await generateProof(
`register_${sigAlgFormatted}`,
Expand All @@ -139,7 +139,7 @@ const useUserStore = create<UserState>((set, get) => ({

const provider = new ethers.JsonRpcProvider(RPC_URL);

const serverResponse = await sendRegisterTransaction(proof)
const serverResponse = await sendRegisterTransaction(proof, sigAlgIndex)
const txHash = serverResponse?.data.hash;

const receipt = await provider.waitForTransaction(txHash);
Expand Down
5 changes: 3 additions & 2 deletions app/src/utils/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import groth16ExportSolidityCallData from '../../utils/snarkjs';
import contractAddresses from "../../deployments/deployed_addresses.json";
import registerArtefacts from "../../deployments/artifacts/Deploy_Registry#ProofOfPassportRegister.json";
import sbtArtefacts from "../../deployments/artifacts/Deploy_Registry#SBT.json";
import { CHAIN_NAME, RELAYER_URL, RPC_URL } from '../../../common/src/constants/constants';
import { CHAIN_NAME, RELAYER_URL, RPC_URL, SignatureAlgorithm } from '../../../common/src/constants/constants';
import { Proof } from "../../../common/src/utils/types";
import { formatCallData_disclose, formatCallData_register } from "../../../common/src/utils/formatCallData";

export const sendRegisterTransaction = async (
proof: Proof,
sigAlgIndex: SignatureAlgorithm
) => {
const provider = new ethers.JsonRpcProvider(RPC_URL);

Expand All @@ -35,7 +36,7 @@ export const sendRegisterTransaction = async (
);

const transactionRequest = await registerContract
.validateProof.populateTransaction(formattedCallData_register, 1);
.validateProof.populateTransaction(formattedCallData_register, sigAlgIndex);
console.log('transactionRequest', transactionRequest);

const response = await axios.post(RELAYER_URL, {
Expand Down
14 changes: 4 additions & 10 deletions circuits/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ Proof of Passport currently supports the following sig/hash algorithms:


- [x] sha256WithRSAEncryption
- [ ] sha1WithRSAEncryption (under development)
- [ ] rsassaPss
- [x] sha1WithRSAEncryption
- [ ] rsassaPss (under development)
- [ ] ecdsa-with-SHA384
- [ ] ecdsa-with-SHA1
- [ ] ecdsa-with-SHA256
Expand All @@ -57,21 +57,15 @@ Proof of Passport currently supports the following sig/hash algorithms:
yarn install-circuits
```



## Build circuits (dev only)

```bash
./scripts/build_circuit.sh
./scripts/build_circuits.sh
```

## Run tests

```bash
yarn test
```
This will run tests with sample data generated on the fly.

The

To run tests with your own passport data, extract your `passportData.json` using the app (available soon), place it in `inputs/`, then run `yarn test`
This will run tests with sample data generated on the fly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
pragma circom 2.1.5;

include "./utils/rsaPkcs1.circom";
include "@zk-email/circuits/helpers/extract.circom";
include "./utils/Sha1BytesStatic.circom";
include "./utils/Sha1Bytes.circom";
include "dmpierre/sha1-circom/circuits/sha1.circom";

template PassportVerifier_sha1WithRSAEncryption_65537(n, k, max_datahashes_bytes) {
signal input mrz[93]; // formatted mrz (5 + 88) chars
signal input dataHashes[max_datahashes_bytes];
signal input datahashes_padded_length;
signal input eContentBytes[92];

// pubkey that signed the passport
signal input pubkey[k];

// signature of the passport
signal input signature[k];

// compute sha1 of formatted mrz
signal mrzSha[160] <== Sha1BytesStatic(93)(mrz);

// mrzSha_bytes: list of 32 Bits2Num
component mrzSha_bytes[20];

// cast the 160 bits from mrzSha into a list of 32 bytes
for (var i = 0; i < 20; i++) {
mrzSha_bytes[i] = Bits2Num(8);

for (var j = 0; j < 8; j++) {
mrzSha_bytes[i].in[7 - j] <== mrzSha[i * 8 + j];
}
}

// assert mrz_hash equals the one extracted from dataHashes input (bytes 32 to 52)
for(var i = 0; i < 20; i++) {
dataHashes[31 + i] === mrzSha_bytes[i].out;
}

// hash dataHashes dynamically
signal dataHashesSha[160] <== Sha1Bytes(max_datahashes_bytes)(dataHashes, datahashes_padded_length);

// get output of dataHashes 160 into bytes to check against eContent
component dataHashesSha_bytes[20];
for (var i = 0; i < 20; i++) {
dataHashesSha_bytes[i] = Bits2Num(8);
for (var j = 0; j < 8; j++) {
dataHashesSha_bytes[i].in[7 - j] <== dataHashesSha[i * 8 + j];
}
}

// assert dataHashesSha is in eContentBytes in range bytes 72 to 92
for(var i = 0; i < 20; i++) {
// log(dataHashesSha_bytes[i].out);

eContentBytes[72 + i] === dataHashesSha_bytes[i].out;
}

// hash eContentBytes
signal eContentSha[160] <== Sha1BytesStatic(92)(eContentBytes);

// get output of eContentBytes sha1 into k chunks of n bits each
var msg_len = (160 + n) \ n;

//eContentHash: list of length 160/n +1 of components of n bits
component eContentHash[msg_len];
for (var i = 0; i < msg_len; i++) {
//instantiate each component of the list of Bits2Num of size n
eContentHash[i] = Bits2Num(n);
}

for (var i = 0; i < 160; i++) {
eContentHash[i \ n].in[i % n] <== eContentSha[159 - i];
}

for (var i = 160; i < n * msg_len; i++) {
eContentHash[i \ n].in[i % n] <== 0;
}

// verify eContentHash signature
// component rsa = RSAVerify65537(64, 32);
component rsa = RSAVerify65537(n, k);


for (var i = 0; i < msg_len; i++) {
rsa.base_message[i] <== eContentHash[i].out;
}

for (var i = msg_len; i < k; i++) {
rsa.base_message[i] <== 0;
}

rsa.modulus <== pubkey;
rsa.signature <== signature;
}
59 changes: 59 additions & 0 deletions circuits/circuits/register_sha1WithRSAEncryption_65537.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
pragma circom 2.1.5;

include "circomlib/circuits/poseidon.circom";
include "@zk-email/circuits/helpers/extract.circom";
include "./passport_verifier_sha1WithRSAEncryption_65537.circom";
include "./utils/chunk_data.circom";
include "./utils/compute_pubkey_leaf.circom";
include "binary-merkle-root.circom";

template Register_sha1WithRSAEncryption_65537(n, k, max_datahashes_bytes, nLevels, signatureAlgorithm) {
signal input secret;

signal input mrz[93];
signal input econtent[max_datahashes_bytes];
signal input datahashes_padded_length;
signal input signed_attributes[92];
signal input signature[k];

signal input pubkey[k];
signal input merkle_root;
signal input path[nLevels];
signal input siblings[nLevels];

signal input attestation_id;

// Verify inclusion of the pubkey in the pubkey tree
signal leaf <== ComputePubkeyLeaf(n, k, signatureAlgorithm)(pubkey);
signal computed_merkle_root <== BinaryMerkleRoot(nLevels)(leaf, nLevels, path, siblings);
merkle_root === computed_merkle_root;

// Verify passport validity
component PV = PassportVerifier_sha1WithRSAEncryption_65537(n, k, max_datahashes_bytes);
PV.mrz <== mrz;
PV.dataHashes <== econtent;
PV.datahashes_padded_length <== datahashes_padded_length;
PV.eContentBytes <== signed_attributes;
PV.pubkey <== pubkey;
PV.signature <== signature;

// Generate the commitment
component poseidon_hasher = Poseidon(6);
poseidon_hasher.inputs[0] <== secret;
poseidon_hasher.inputs[1] <== attestation_id;
poseidon_hasher.inputs[2] <== leaf;

signal mrz_packed[3] <== PackBytes(93, 3, 31)(mrz);
for (var i = 0; i < 3; i++) {
poseidon_hasher.inputs[i + 3] <== mrz_packed[i];
}
signal output commitment <== poseidon_hasher.out;

// Generate the nullifier
var chunk_size = 11; // Since ceil(32 / 3) in integer division is 11
signal chunked_signature[chunk_size] <== ChunkData(n, k, chunk_size)(signature);
signal output nullifier <== Poseidon(chunk_size)(chunked_signature);
}

// We hardcode 3 here for sha1WithRSAEncryption_65537
component main { public [ merkle_root, attestation_id ] } = Register_sha1WithRSAEncryption_65537(64, 32, 320, 16, 3);
115 changes: 115 additions & 0 deletions circuits/circuits/utils/Sha1Bytes.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
pragma circom 2.1.5;

include "@zk-email/circuits/helpers/utils.circom";
include "dmpierre/sha1-circom/circuits/sha1compression.circom";

//Adapted from @zk-email/circuits/helpers/sha.circom
template Sha1Bytes(max_num_bytes) {
signal input in_padded[max_num_bytes];
signal input in_len_padded_bytes;
signal output out[160];

var num_bits = max_num_bytes * 8;
component sha = Sha1General(num_bits);

component bytes[max_num_bytes];
for (var i = 0; i < max_num_bytes; i++) {
bytes[i] = Num2Bits(8);
bytes[i].in <== in_padded[i];
for (var j = 0; j < 8; j++) {
sha.paddedIn[i*8+j] <== bytes[i].out[7-j];
}
}

sha.in_len_padded_bits <== in_len_padded_bytes * 8;

for (var i = 0; i < 160; i++) {
out[i] <== sha.out[i];
}

}

//Adapted from @zk-email/circuits/helpers/sha256general.circom
//Sha1 template from https://github.com/dmpierre/sha1-circom/blob/fe18319cf72b9f3b83d0cea8f49a1f04482c125b/circuits/sha1.circom
template Sha1General(maxBitsPadded) {
assert(maxBitsPadded % 512 == 0);
var maxBitsPaddedBits = log2_ceil(maxBitsPadded);
assert(2 ** maxBitsPaddedBits > maxBitsPadded);

signal input paddedIn[maxBitsPadded];
signal output out[160];
signal input in_len_padded_bits;
signal inBlockIndex;

var i;
var k;
var j;
var maxBlocks;
maxBlocks = (maxBitsPadded\512);
var maxBlocksBits = log2_ceil(maxBlocks);
assert(2 ** maxBlocksBits > maxBlocks);

inBlockIndex <-- (in_len_padded_bits >> 9);
in_len_padded_bits === inBlockIndex * 512;

component bitLengthVerifier = LessEqThan(maxBitsPaddedBits);
bitLengthVerifier.in[0] <== in_len_padded_bits;
bitLengthVerifier.in[1] <== maxBitsPadded;
bitLengthVerifier.out === 1;

component ha0 = H(0);
component hb0 = H(1);
component hc0 = H(2);
component hd0 = H(3);
component he0 = H(4);

component sha1compression[maxBlocks];

for (i=0; i<maxBlocks; i++) {

sha1compression[i] = Sha1compression();

if (i==0) {
for (k=0; k<32; k++) {
sha1compression[i].hin[0*32+k] <== ha0.out[k];
sha1compression[i].hin[1*32+k] <== hb0.out[k];
sha1compression[i].hin[2*32+k] <== hc0.out[k];
sha1compression[i].hin[3*32+k] <== hd0.out[k];
sha1compression[i].hin[4*32+k] <== he0.out[k];
}
} else {
for (k=0; k<32; k++) {
sha1compression[i].hin[32*0+k] <== sha1compression[i-1].out[32*0+31-k];
sha1compression[i].hin[32*1+k] <== sha1compression[i-1].out[32*1+31-k];
sha1compression[i].hin[32*2+k] <== sha1compression[i-1].out[32*2+31-k];
sha1compression[i].hin[32*3+k] <== sha1compression[i-1].out[32*3+31-k];
sha1compression[i].hin[32*4+k] <== sha1compression[i-1].out[32*4+31-k];
}
}

for (k=0; k<512; k++) {
sha1compression[i].inp[k] <== paddedIn[i*512+k];
}

}

component arraySelectors[160];

var outs[maxBlocks][160];
for (i=0; i<maxBlocks; i++) {
for (j=0; j<5; j++) {
for (k=0; k<32; k++) {
outs[i][32*j + k] = sha1compression[i].out[32*j + (31-k)];
}
}
}

for (i =0; i < 160; i++) {
arraySelectors[i] = QuinSelector(maxBlocks, maxBlocksBits);
for (j=0; j<maxBlocks; j++) {
arraySelectors[i].in[j] <== outs[j][i];
}
arraySelectors[i].index <== inBlockIndex - 1;
out[i] <== arraySelectors[i].out;
}
}
Loading