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: add support for public exponent 3 #8

Merged
merged 2 commits into from
Sep 12, 2024
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
2 changes: 1 addition & 1 deletion lib/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ authors = [""]
compiler_version = ">=0.32.0"

[dependencies]
bignum = {tag = "v0.3.0", git = "https://github.com/noir-lang/noir-bignum"}
bignum = {tag = "v0.3.2", git = "https://github.com/noir-lang/noir-bignum"}
73 changes: 51 additions & 22 deletions lib/src/rsa.nr
Original file line number Diff line number Diff line change
Expand Up @@ -53,30 +53,36 @@ impl<BN, BNInstance, let NumBytes: u32> RSA<BN, BNInstance, NumBytes> where BN:
* when converting a BigNum into a byte array, the number of bytes is required and currently cannot be inferred.
* Once numeric generics can be derived by applying operations to other numeric generics the need for this will go away.
*
* @note We assume the public key exponent `e` is 65537
* @note The exponent `e` can be either 65537 or 3 (i.e. the most common values in use for RSA)
* Rough cost: 2,048 bit RSA: 26,888 gates per verification
* 1,024 bit RSA: 11,983 gates per verification
* A circuit that verifies 1 signature (and does nothing else) will cost ~32k due to initialization costs of lookup tables
**/
pub fn verify_sha256_pkcs1v15(_: Self, instance: BNInstance, msg_hash: [u8; 32], sig: BN) -> bool {
// e = 65537 = 1 0000 0000 0000 0001
let mut exponentiated = instance.mul(sig, sig);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, exponentiated);
exponentiated = instance.mul(exponentiated, sig);
pub fn verify_sha256_pkcs1v15(_: Self, instance: BNInstance, msg_hash: [u8; 32], sig: BN, exponent: u32) -> bool {
assert((exponent == 3) | (exponent == 65537), "Exponent must be 65537 or 3");
let mut exponentiated = instance.mul(sig, sig); // sig^2

if exponent == 65537 {
// e = 65537 = 1 0000 0000 0000 0001
exponentiated = instance.mul(exponentiated, exponentiated); // sig^2 * sig^2 = sig^4
exponentiated = instance.mul(exponentiated, exponentiated); // sig^8
exponentiated = instance.mul(exponentiated, exponentiated); // sig^16
exponentiated = instance.mul(exponentiated, exponentiated); // sig^32
exponentiated = instance.mul(exponentiated, exponentiated); // sig^64
exponentiated = instance.mul(exponentiated, exponentiated); // sig^128
exponentiated = instance.mul(exponentiated, exponentiated); // sig^256
exponentiated = instance.mul(exponentiated, exponentiated); // sig^512
exponentiated = instance.mul(exponentiated, exponentiated); // sig^1024
exponentiated = instance.mul(exponentiated, exponentiated); // sig^2048
exponentiated = instance.mul(exponentiated, exponentiated); // sig^4096
exponentiated = instance.mul(exponentiated, exponentiated); // sig^8192
exponentiated = instance.mul(exponentiated, exponentiated); // sig^16384
exponentiated = instance.mul(exponentiated, exponentiated); // sig^32768
exponentiated = instance.mul(exponentiated, exponentiated); // sig^65536
}
// otherwise, e = 3 = 11

exponentiated = instance.mul(exponentiated, sig); // either sig^2 * sig = sig^3 or sig^65536 * sig = sig^65537

let mut padded_sha256_hash_bytes: [u8; NumBytes] = exponentiated.to_le_bytes();
compare_signature_sha256(padded_sha256_hash_bytes, msg_hash)
Expand All @@ -102,7 +108,7 @@ fn test_verify_sha256_pkcs1v15_1024() {
);

let rsa: RSA1024 = RSA {};
assert(rsa.verify_sha256_pkcs1v15(BNInstance, sha256_hash, signature));
assert(rsa.verify_sha256_pkcs1v15(BNInstance, sha256_hash, signature, 65537));
}

#[test]
Expand All @@ -122,5 +128,28 @@ fn test_verify_sha256_pkcs1v15_2048() {
]
);
let rsa: RSA2048 = RSA {};
assert(rsa.verify_sha256_pkcs1v15(BNInstance, sha256_hash, signature));
assert(rsa.verify_sha256_pkcs1v15(BNInstance, sha256_hash, signature, 65537));
}

#[test]
fn test_verify_sha256_pkcs1v15_2048_exponent_3() {
let sha256_hash: [u8; 32] = dep::std::hash::sha256("Hello World! This is Noir-RSA".as_bytes());

let BNInstance: BNInst2048 = BigNumInstance::new(
[
0xc6a1c5e80ce354c6b00ccf20cf3a1d, 0x178d135f925a03eceb25f79bab56ee, 0x13ab3d6d8a5c5586752b5a3bc74ec3, 0x3d13b47b152367e3e2fc014d03d19f, 0xe89a7278a2945b4a672011691db30f, 0x5b4c1b061378143629dbb29dea1e4, 0x26a48b6f4e8df1472fd4fc12b17c18, 0xc7c92ead0ce810520cf3a8267254c1, 0x806b8cdba93909e9d9a71ee1bcdac2, 0x703ef80f8eb703b84c201366dff1c7, 0x7361034bb2c4c081aad8b1bcca83de, 0xb23c7e1109e65e6d08fa72cc862008, 0x750bc927874455782cd2d6fd5a51f6, 0xf0b83665fbf8cb5cf31cee9f89848e, 0x20d447b08953c7ce3330197938a8ae, 0x11a08bb5a2241c6a2a69f930d8b28b, 0xef5bca8dd582570a44705cb123d09e, 0xb7
],
[
0xbc93ee57c1c8adc53f0a995a6221ca, 0x2a9b43587534b20dd85a5233329f10, 0xc587fd488f64eed02adc1f462f7448, 0xf1484d37676bb0e800996757382522, 0xc2126c48221aa61c9f52c6b918bab3, 0x8660c861dd52ed958beaf6c6c2cff0, 0x5edd9dc4f02a000f350948c70bdf94, 0x6f3b9603149272e9b232a379a017bb, 0x950fd85cffbdf4476b1cb66c1f63d6, 0xee459417b1a56b6f7ef3b89e385ac, 0x48daeef6d1a055f3746ab71058e137, 0x3cbc0ba96d541feee92dd27f9d0306, 0x6a2a42384cc388fa113ee80317e0a0, 0x43b4f89c508a42d309f295c0d9f3a5, 0x8d8c28b05f71b962b40ea906ff407f, 0x390a7989eb9cecc5827cb00e1ca693, 0x4cbf158eabf7e96ef7f2586d0ce613, 0x164
]
);

let signature: BN2048 = BigNum::from_array(
[
0x19772b9af8a031170a7844ce4f3d7c, 0x4808e817258f57805a7326f70bcd74, 0xca8f3f98e374d52100115bfa645a7d, 0x49547189edff3b683fee267e717b7f, 0x96f263b47e96925f3b5898a7389ceb, 0x4cc50a893da91d0e085fc6656b30bc, 0x67e84ff92d88c0ad2c17ad2701309e, 0x095326818578173289665fcd9ad788, 0x775c6e85b745065db9411b9d579763, 0xad0f20c8a5265dfca4080ca877a2b8, 0xbfd199372f1680b3bc583a08bd8ba9, 0x663476ca3e5ede3e5976887db2c4e5, 0x531192309d0d49fed47c0216c27f9e, 0x37d26d31c86b951ca1c17b517063b7, 0x3cdb362ed5dfd06568eb9a9bbb6a91, 0x14520b9c23f583314729a9d858bca9, 0x5e0505067ada1026721d45997bf2c4, 0x3e
]
);

let rsa: RSA2048 = RSA {};
assert(rsa.verify_sha256_pkcs1v15(BNInstance, sha256_hash, signature, 3));
}
15 changes: 12 additions & 3 deletions signature_gen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn format_limbs_as_toml_value(limbs: &Vec<BigUint>) -> Vec<Value> {
.collect()
}

fn generate_2048_bit_signature_parameters(msg: &str, as_toml: bool) {
fn generate_2048_bit_signature_parameters(msg: &str, as_toml: bool, exponent: u32) {
let mut hasher = Sha256::new();
hasher.update(msg.as_bytes());
let hashed_message = hasher.finalize();
Expand All @@ -45,7 +45,7 @@ fn generate_2048_bit_signature_parameters(msg: &str, as_toml: bool) {
let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
let bits: usize = 2048;
let priv_key: RsaPrivateKey =
RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
RsaPrivateKey::new_with_exp(&mut rng, bits, &BigUint::from(exponent)).expect("failed to generate a key");
let pub_key: RsaPublicKey = priv_key.clone().into();

let signing_key = rsa::pkcs1v15::SigningKey::<Sha256>::new(priv_key);
Expand Down Expand Up @@ -108,12 +108,21 @@ fn main() {
.long("toml")
.help("Print output in TOML format"),
)
.arg(
Arg::with_name("exponent")
.short("e")
.long("exponent")
.takes_value(true)
.help("Exponent to use for the key")
.default_value("65537"),
)
.get_matches();

let msg = matches.value_of("msg").unwrap();
let as_toml = matches.is_present("toml");
let e: u32 = matches.value_of("exponent").unwrap().parse().unwrap();

generate_2048_bit_signature_parameters(msg, as_toml);
generate_2048_bit_signature_parameters(msg, as_toml, e);
}

fn test_signature_generation_impl() {
Expand Down
Loading