circuit: refactor rsa circuit

This commit is contained in:
Saleel
2024-03-28 23:11:12 +05:30
parent a38542abb1
commit 8e2a119863
9 changed files with 82 additions and 62 deletions

View File

@@ -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.

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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, {})

View File

@@ -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);