Skip to content

Commit

Permalink
Merge pull request #121 from seshanthS/feat/sha1WithRSAEncryption
Browse files Browse the repository at this point in the history
Feat: sha1 with rsa encryption
  • Loading branch information
0xturboblitz authored Jun 19, 2024
2 parents a2dfc8e + aa3fc65 commit acc5ed5
Show file tree
Hide file tree
Showing 28 changed files with 1,105 additions and 335 deletions.
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 @@ -112,7 +112,6 @@ const useUserStore = create<UserState>((set, get) => ({
secret,
PASSPORT_ATTESTATION_ID,
passportData,
{ developmentMode: true }
);

amplitude.track(`Sig alg supported: ${passportData.signatureAlgorithm}`);
Expand All @@ -128,6 +127,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 @@ -142,7 +142,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

0 comments on commit acc5ed5

Please sign in to comment.