mirror of
https://github.com/zkemail/zk-email-verify.git
synced 2026-01-10 05:58:08 -05:00
circuit: refactor rsa circuit
This commit is contained in:
@@ -27,7 +27,7 @@ This template implements the SHA (Secure Hash Algorithm) family of cryptographic
|
||||
|
||||
This template provides functionality for performing arithmetic operations on big integers, such as addition and subtraction modulo 2^n.
|
||||
|
||||
### bigint_func.circom
|
||||
### bigint-func.circom
|
||||
|
||||
This template offers utility functions for handling big integers within arithmetic circuits, performing various mathematical operations on large numbers represented across multiple registers.
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/poseidon.circom";
|
||||
include "@zk-email/zk-regex-circom/circuits/common/body_hash_regex.circom";
|
||||
include "./lib/base64.circom";
|
||||
include "./lib/rsa.circom";
|
||||
include "./helpers/sha.circom";
|
||||
include "./helpers/rsa.circom";
|
||||
include "./helpers/extract.circom";
|
||||
include "./helpers/utils.circom";
|
||||
|
||||
@@ -66,12 +66,12 @@ template EmailVerifier(maxHeaderLength, maxBodyLength, n, k, ignoreBodyHashCheck
|
||||
}
|
||||
|
||||
// Verify RSA signature - 149,251 constraints
|
||||
component rsaVerifier = RSAVerify65537(n, k);
|
||||
component rsaVerifier = RSAVerifier65537(n, k);
|
||||
for (var i = 0; i < rsaMessageSize; i++) {
|
||||
rsaVerifier.base_message[i] <== rsaMessage[i].out;
|
||||
rsaVerifier.message[i] <== rsaMessage[i].out;
|
||||
}
|
||||
for (var i = rsaMessageSize; i < k; i++) {
|
||||
rsaVerifier.base_message[i] <== 0;
|
||||
rsaVerifier.message[i] <== 0;
|
||||
}
|
||||
rsaVerifier.modulus <== pubkey;
|
||||
rsaVerifier.signature <== signature;
|
||||
|
||||
@@ -3,8 +3,8 @@ pragma circom 2.1.5;
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/gates.circom";
|
||||
include "./bigint-func.circom";
|
||||
|
||||
include "bigint_func.circom";
|
||||
|
||||
// addition mod 2**n with carry bit
|
||||
template ModSum(n) {
|
||||
@@ -4,7 +4,7 @@ include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "circomlib/circuits/sign.circom";
|
||||
include "./bigint.circom";
|
||||
include "./bigint_func.circom";
|
||||
include "./bigint-func.circom";
|
||||
|
||||
// These functions operate over values in Z/Zp for some integer p (typically,
|
||||
// but not necessarily prime). Values are stored as standard bignums with k
|
||||
@@ -2,13 +2,60 @@ pragma circom 2.1.5;
|
||||
|
||||
include "./fp.circom";
|
||||
|
||||
// Computes base^65537 mod modulus
|
||||
// Does not necessarily reduce fully mod modulus (the answer could be
|
||||
// too big by a multiple of modulus)
|
||||
|
||||
/// @title RSAVerifier65537
|
||||
/// @notice Verifies an RSA signature with exponent 65537.
|
||||
/// @param n Number of bits per chunk the modulus is split into. Recommended to be 121.
|
||||
/// @param k Number of chunks the modulus is split into. Recommended to be 17.
|
||||
/// @input message The message that was signed.
|
||||
/// @input signature The signature to verify.
|
||||
/// @input modulus The modulus of the RSA key (pubkey).
|
||||
template RSAVerifier65537(n, k) {
|
||||
signal input message[k];
|
||||
signal input signature[k];
|
||||
signal input modulus[k];
|
||||
|
||||
component padder = RSAPad(n, k);
|
||||
for (var i = 0; i < k; i++) {
|
||||
padder.modulus[i] <== modulus[i];
|
||||
padder.message[i] <== message[i];
|
||||
}
|
||||
|
||||
// Check that the signature is in proper form and reduced mod modulus.
|
||||
component signatureRangeCheck[k];
|
||||
component bigLessThan = BigLessThan(n, k);
|
||||
for (var i = 0; i < k; i++) {
|
||||
signatureRangeCheck[i] = Num2Bits(n);
|
||||
signatureRangeCheck[i].in <== signature[i];
|
||||
bigLessThan.a[i] <== signature[i];
|
||||
bigLessThan.b[i] <== modulus[i];
|
||||
}
|
||||
bigLessThan.out === 1;
|
||||
|
||||
component bigPow = FpPow65537Mod(n, k);
|
||||
for (var i = 0; i < k; i++) {
|
||||
bigPow.base[i] <== signature[i];
|
||||
bigPow.modulus[i] <== modulus[i];
|
||||
}
|
||||
|
||||
// By construction of the padding, the padded message is necessarily
|
||||
// smaller than the modulus. Thus, we don't have to check that bigPow is fully reduced.
|
||||
for (var i = 0; i < k; i++) {
|
||||
bigPow.out[i] === padder.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title FpPow65537Mod
|
||||
/// @notice Computes base^65537 mod modulus
|
||||
/// @dev Does not necessarily reduce fully mod modulus (the answer could be too big by a multiple of modulus)
|
||||
/// @param n Number of bits per chunk the modulus is split into.
|
||||
/// @param k Number of chunks the modulus is split into.
|
||||
/// @input base The base to exponentiate.
|
||||
template FpPow65537Mod(n, k) {
|
||||
signal input base[k];
|
||||
// Exponent is hardcoded at 65537
|
||||
signal input modulus[k];
|
||||
|
||||
signal output out[k];
|
||||
|
||||
component doublers[16];
|
||||
@@ -42,10 +89,17 @@ template FpPow65537Mod(n, k) {
|
||||
}
|
||||
}
|
||||
|
||||
/// @title RSAPad
|
||||
/// @notice Pads a message for RSA signing.
|
||||
/// @param n Number of bits per chunk the modulus is split into.
|
||||
/// @param k Number of chunks the modulus is split into.
|
||||
/// @input modulus The modulus of the RSA key (pubkey).
|
||||
/// @input message The message to pad.
|
||||
/// @output out The padded message.
|
||||
template RSAPad(n, k) {
|
||||
signal input modulus[k];
|
||||
signal input base_message[k];
|
||||
signal output padded_message[k];
|
||||
signal input message[k];
|
||||
signal output out[k];
|
||||
|
||||
var base_len = 408;
|
||||
var msg_len = 256;
|
||||
@@ -53,14 +107,14 @@ template RSAPad(n, k) {
|
||||
signal padded_message_bits[n*k];
|
||||
|
||||
component modulus_n2b[k];
|
||||
component base_message_n2b[k];
|
||||
component message_n2b[k];
|
||||
signal modulus_bits[n*k];
|
||||
signal base_message_bits[n*k];
|
||||
signal message_bits[n*k];
|
||||
for (var i = 0; i < k; i++) {
|
||||
base_message_n2b[i] = Num2Bits(n);
|
||||
base_message_n2b[i].in <== base_message[i];
|
||||
message_n2b[i] = Num2Bits(n);
|
||||
message_n2b[i].in <== message[i];
|
||||
for (var j = 0; j < n; j++) {
|
||||
base_message_bits[i*n+j] <== base_message_n2b[i].out[j];
|
||||
message_bits[i*n+j] <== message_n2b[i].out[j];
|
||||
}
|
||||
modulus_n2b[i] = Num2Bits(n);
|
||||
modulus_n2b[i].in <== modulus[i];
|
||||
@@ -70,11 +124,11 @@ template RSAPad(n, k) {
|
||||
}
|
||||
|
||||
for (var i = msg_len; i < n*k; i++) {
|
||||
base_message_bits[i] === 0;
|
||||
message_bits[i] === 0;
|
||||
}
|
||||
|
||||
for (var i = 0; i < msg_len; i++) {
|
||||
padded_message_bits[i] <== base_message_bits[i];
|
||||
padded_message_bits[i] <== message_bits[i];
|
||||
}
|
||||
|
||||
for (var i = base_len; i < base_len + 8; i++) {
|
||||
@@ -117,40 +171,6 @@ template RSAPad(n, k) {
|
||||
for (var j = 0; j < n; j++) {
|
||||
padded_message_b2n[i].in[j] <== padded_message_bits[i*n+j];
|
||||
}
|
||||
padded_message[i] <== padded_message_b2n[i].out;
|
||||
}
|
||||
}
|
||||
|
||||
template RSAVerify65537(n, k) {
|
||||
signal input signature[k];
|
||||
signal input modulus[k];
|
||||
signal input base_message[k];
|
||||
|
||||
component padder = RSAPad(n, k);
|
||||
for (var i = 0; i < k; i++) {
|
||||
padder.modulus[i] <== modulus[i];
|
||||
padder.base_message[i] <== base_message[i];
|
||||
}
|
||||
|
||||
// Check that the signature is in proper form and reduced mod modulus.
|
||||
component signatureRangeCheck[k];
|
||||
component bigLessThan = BigLessThan(n, k);
|
||||
for (var i = 0; i < k; i++) {
|
||||
signatureRangeCheck[i] = Num2Bits(n);
|
||||
signatureRangeCheck[i].in <== signature[i];
|
||||
bigLessThan.a[i] <== signature[i];
|
||||
bigLessThan.b[i] <== modulus[i];
|
||||
}
|
||||
bigLessThan.out === 1;
|
||||
|
||||
component bigPow = FpPow65537Mod(n, k);
|
||||
for (var i = 0; i < k; i++) {
|
||||
bigPow.base[i] <== signature[i];
|
||||
bigPow.modulus[i] <== modulus[i];
|
||||
}
|
||||
// By construction of the padding, the padded message is necessarily
|
||||
// smaller than the modulus. Thus, we don't have to check that bigPow is fully reduced.
|
||||
for (var i = 0; i < k; i++) {
|
||||
bigPow.out[i] === padder.padded_message[i];
|
||||
out[i] <== padded_message_b2n[i].out;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ exports.p = Scalar.fromString(
|
||||
"21888242871839275222246405745257275088548364400416034343698204186575808495617"
|
||||
);
|
||||
|
||||
describe.only("Base64 Lookup", () => {
|
||||
describe("Base64 Lookup", () => {
|
||||
jest.setTimeout(10 * 60 * 1000); // 10 minutes
|
||||
|
||||
let circuit: any;
|
||||
|
||||
@@ -27,7 +27,7 @@ describe("RSA", () => {
|
||||
// output: path.join(__dirname, "./compiled-test-circuits"),
|
||||
}
|
||||
);
|
||||
const rawEmail = fs.readFileSync(path.join(__dirname, "./test.eml"));
|
||||
const rawEmail = fs.readFileSync(path.join(__dirname, "./test-emails/test.eml"));
|
||||
dkimResult = await verifyDKIMSignature(rawEmail);
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ describe("RSA", () => {
|
||||
signature: emailVerifierInputs.signature,
|
||||
modulus: emailVerifierInputs.pubkey,
|
||||
// TODO: generate this from the input
|
||||
base_message: ["1156466847851242602709362303526378170", "191372789510123109308037416804949834", "7204", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"],
|
||||
message: ["1156466847851242602709362303526378170", "191372789510123109308037416804949834", "7204", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"],
|
||||
});
|
||||
await circuit.checkConstraints(witness);
|
||||
await circuit.assertOut(witness, {})
|
||||
@@ -70,7 +70,7 @@ describe("RSA", () => {
|
||||
signature: signature,
|
||||
modulus: pubkey,
|
||||
// TODO: generate this from the input
|
||||
base_message: ["1156466847851242602709362303526378170", "191372789510123109308037416804949834", "7204", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"],
|
||||
message: ["1156466847851242602709362303526378170", "191372789510123109308037416804949834", "7204", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"],
|
||||
});
|
||||
await circuit.checkConstraints(witness);
|
||||
await circuit.assertOut(witness, {});
|
||||
@@ -93,7 +93,7 @@ describe("RSA", () => {
|
||||
const witness = await circuit.calculateWitness({
|
||||
signature: emailVerifierInputs.signature,
|
||||
modulus: emailVerifierInputs.pubkey,
|
||||
base_message: ["1156466847851242602709362303526378171", "191372789510123109308037416804949834", "7204", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"],
|
||||
message: ["1156466847851242602709362303526378171", "191372789510123109308037416804949834", "7204", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"],
|
||||
});
|
||||
await circuit.checkConstraints(witness);
|
||||
await circuit.assertOut(witness, {})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pragma circom 2.1.5;
|
||||
|
||||
include "../helpers/rsa.circom";
|
||||
include "../../lib/rsa.circom";
|
||||
|
||||
component main { public [modulus] } = RSAVerify65537(121, 17);
|
||||
component main { public [modulus] } = RSAVerifier65537(121, 17);
|
||||
Reference in New Issue
Block a user