From 0a522bd7f9d9772b943d222c83e25a13a0e230aa Mon Sep 17 00:00:00 2001 From: umadayal Date: Wed, 13 Nov 2024 12:49:37 -0800 Subject: [PATCH 01/27] call precompile --- Cargo.lock | 72 +++++++++++++++++++++++---- Cargo.toml | 4 ++ src/algorithms/rsa.rs | 112 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 177 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c20e26f..5d82b6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,15 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -70,6 +79,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bytemuck" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -116,6 +145,16 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -367,9 +406,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -402,9 +441,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -468,7 +507,10 @@ name = "rsa" version = "0.9.6" dependencies = [ "base64ct", + "bytemuck", + "cfg-if", "const-oid", + "crypto-bigint", "digest", "hex-literal", "num-bigint-dig", @@ -487,6 +529,7 @@ dependencies = [ "sha2", "sha3", "signature", + "sp1-lib", "spki", "subtle", "zeroize", @@ -539,18 +582,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.192" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -614,6 +657,15 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "sp1-lib" +version = "3.0.0" +source = "git+https://github.com/succinctlabs/sp1.git?branch=dev#0c08cafd4adeb0d246b21411d8422611b3861337" +dependencies = [ + "bincode", + "serde", +] + [[package]] name = "spin" version = "0.5.2" @@ -638,9 +690,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.39" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index e4b2265..b58a4a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,10 @@ pkcs8 = { version = "0.10.2", default-features = false, features = ["alloc"] } signature = { version = ">2.0, <2.3", default-features = false , features = ["alloc", "digest", "rand_core"] } spki = { version = "0.7.3", default-features = false, features = ["alloc"] } zeroize = { version = "1.5", features = ["alloc"] } +crypto-bigint = "0.5.5" +sp1-lib = { git = "https://github.com/succinctlabs/sp1.git", branch = "dev" } +cfg-if = "1.0.0" +bytemuck = { version = "1.16.1", features = ["derive"] } # optional dependencies sha1 = { version = "0.10.5", optional = true, default-features = false, features = ["oid"] } diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 3510152..4dd59e0 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -7,6 +7,10 @@ use num_integer::{sqrt, Integer}; use num_traits::{FromPrimitive, One, Pow, Signed, Zero}; use rand_core::CryptoRngCore; use zeroize::{Zeroize, Zeroizing}; +use bytemuck::cast_ref; +use sp1_lib::io::hint_slice; +use crypto_bigint::{Integer as CryptoInteger, NonZero, Encoding, U2048, U256, U4096}; +use core::convert::TryInto; use crate::errors::{Error, Result}; use crate::traits::{PrivateKeyParts, PublicKeyParts}; @@ -19,7 +23,113 @@ use crate::traits::{PrivateKeyParts, PublicKeyParts}; /// or signature scheme. See the [module-level documentation][crate::hazmat] for more information. #[inline] pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { - Ok(m.modpow(key.e(), key.n())) + // Ok(m.modpow(key.e(), key.n())) + let m_u2048 = from_biguint_to_u2048(m); + let e_u2048 = from_biguint_to_u2048(key.e()); + let n_u2048 = from_biguint_to_u2048(key.n()); + Ok(custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048)) +} + +/// Performs modular exponentiation of `base` to the power of `exp` modulo `modulus`. +/// This function takes in U2048 operands and returns the result as a BigUint. +fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { + if *modulus == U2048::ONE { + return BigUint::zero(); + } + + let mut result = U2048::ONE; + let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero + let mut base = base.rem(&modulus_nonzero); + + + let mut exp = *exp; + while exp > U2048::ZERO { + if exp.is_odd().into() { + result = mul_mod_u2048(&result, &base, &modulus_nonzero); + } + exp = exp.shr(1); + base = mul_mod_u2048(&base, &base, &modulus_nonzero); + } + + let result_biguint = BigUint::from_bytes_le(&result.to_le_bytes()); + result_biguint + +} + + +/// Performs modular multiplication of `a` and `b` with `modulus`. +/// It calculates the quotient and remainder in unconstrained. +fn mul_mod_u2048(a: &U2048, b: &U2048, modulus: &U2048) -> U2048 { + let prod = mul_u2048(*a, *b); + sp1_lib::unconstrained! { + let modulus_u4096 = U4096::from(modulus); + let modulus_u4096_nonzero = NonZero::new(modulus_u4096).unwrap(); // Convert modulus to NonZero + let (quotient, result) = prod.div_rem(&modulus_u4096_nonzero); + let result_bytes = result.to_le_bytes(); + let quotient_bytes = quotient.to_le_bytes(); + + hint_slice(&result_bytes); + hint_slice("ient_bytes[..256]); + } + + let result_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); + let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); + + let q_array = U2048::from_le_slice("ient_bytes); + let result_u4096 = U4096::from_le_slice(&result_bytes); + let result_u2048 = U2048::from_le_slice(&result_bytes[..256]); + + assert!(prod.wrapping_sub(&mul_u2048(q_array, *modulus)).wrapping_sub(&result_u4096) == U4096::ZERO); + result_u2048 +} + + + +/// Performs multiplication of `a` and `b`, which are both U2048, +/// and returns a U4096. +fn mul_u2048(a_array: U2048, b_array: U2048) -> U4096 { + let mut sum = U4096::ZERO; + let a_words = a_array.to_words(); + + for i in 0..8 { + let chunk = a_words[i*8..(i+1)*8].try_into().unwrap(); + let a_chunk: U256 = U256::from_words(chunk); + let mut prod = mul_array(a_chunk, b_array); + let mut shifted_words = [0u32; 128]; + shifted_words[i*8..].copy_from_slice(&prod.to_words()[..(128 - 8*i)]); + let shifted_prod = U4096::from_words(shifted_words); + sum = sum.wrapping_add(&shifted_prod); + } + + sum +} + +/// Performs multiplication of `a` a U256 and `b` which is a U2048. +fn mul_array(a: U256, b_array: U2048) -> U4096 { + let mut result_words = [0u32; 128]; + let result_ptr = result_words.as_mut_ptr(); + unsafe { + sp1_lib::syscall_u256x2048_mul( + cast_ref(&a.to_words()), + cast_ref(&b_array.to_words()), + result_ptr as *mut [u32; 64], + result_ptr.add(64) as *mut [u32; 8], + ); + } + + U4096::from_words(result_words) +} + +/// Converts a BigUint to a U2048. +fn from_biguint_to_u2048(value: &BigUint) -> U2048 { + let mut padded_bytes = [0u8; 256]; + let a_bytes = value.to_bytes_le(); + for (i, &byte) in a_bytes.iter().enumerate() { + if i >= 256 { break; } + padded_bytes[i] = byte; + } + + U2048::from_le_slice(&padded_bytes) } /// ⚠️ Performs raw RSA decryption with no padding or error checking. From 481395d7ebb2f9910cd468e738ae92c7e40d405b Mon Sep 17 00:00:00 2001 From: umadayal Date: Wed, 13 Nov 2024 18:48:21 -0800 Subject: [PATCH 02/27] cfg if zkvm --- Cargo.toml | 6 +- src/algorithms/rsa.rs | 183 ++++++++++++++++++++++-------------------- 2 files changed, 102 insertions(+), 87 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b58a4a7..c427ce7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,10 +26,14 @@ signature = { version = ">2.0, <2.3", default-features = false , features = ["al spki = { version = "0.7.3", default-features = false, features = ["alloc"] } zeroize = { version = "1.5", features = ["alloc"] } crypto-bigint = "0.5.5" -sp1-lib = { git = "https://github.com/succinctlabs/sp1.git", branch = "dev" } cfg-if = "1.0.0" bytemuck = { version = "1.16.1", features = ["derive"] } +[target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] +sp1-lib = { git = "https://github.com/succinctlabs/sp1.git", branch = "dev" } + + + # optional dependencies sha1 = { version = "0.10.5", optional = true, default-features = false, features = ["oid"] } sha2 = { version = "0.10.6", optional = true, default-features = false, features = ["oid"] } diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 4dd59e0..0fec99e 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -8,6 +8,7 @@ use num_traits::{FromPrimitive, One, Pow, Signed, Zero}; use rand_core::CryptoRngCore; use zeroize::{Zeroize, Zeroizing}; use bytemuck::cast_ref; +#[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] use sp1_lib::io::hint_slice; use crypto_bigint::{Integer as CryptoInteger, NonZero, Encoding, U2048, U256, U4096}; use core::convert::TryInto; @@ -24,114 +25,124 @@ use crate::traits::{PrivateKeyParts, PublicKeyParts}; #[inline] pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { // Ok(m.modpow(key.e(), key.n())) - let m_u2048 = from_biguint_to_u2048(m); - let e_u2048 = from_biguint_to_u2048(key.e()); - let n_u2048 = from_biguint_to_u2048(key.n()); - Ok(custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048)) + cfg_if::cfg_if! { + if #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] { + let m_u2048 = from_biguint_to_u2048(m); + let e_u2048 = from_biguint_to_u2048(key.e()); + let n_u2048 = from_biguint_to_u2048(key.n()); + Ok(custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048)) + } + } + Ok(m.modpow(key.e(), key.n())) } -/// Performs modular exponentiation of `base` to the power of `exp` modulo `modulus`. -/// This function takes in U2048 operands and returns the result as a BigUint. -fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { - if *modulus == U2048::ONE { - return BigUint::zero(); - } +cfg_if::cfg_if! { + if #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] { + /// Performs modular exponentiation of `base` to the power of `exp` modulo `modulus`. + /// This function takes in U2048 operands and returns the result as a BigUint. + fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { + if *modulus == U2048::ONE { + return BigUint::zero(); + } - let mut result = U2048::ONE; - let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero - let mut base = base.rem(&modulus_nonzero); + let mut result = U2048::ONE; + let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero + let mut base = base.rem(&modulus_nonzero); - let mut exp = *exp; - while exp > U2048::ZERO { - if exp.is_odd().into() { - result = mul_mod_u2048(&result, &base, &modulus_nonzero); - } - exp = exp.shr(1); - base = mul_mod_u2048(&base, &base, &modulus_nonzero); - } + let mut exp = *exp; + while exp > U2048::ZERO { + if exp.is_odd().into() { + result = mul_mod_u2048(&result, &base, &modulus_nonzero); + } + exp = exp.shr(1); + base = mul_mod_u2048(&base, &base, &modulus_nonzero); + } - let result_biguint = BigUint::from_bytes_le(&result.to_le_bytes()); - result_biguint - -} + let result_biguint = BigUint::from_bytes_le(&result.to_le_bytes()); + result_biguint + + } -/// Performs modular multiplication of `a` and `b` with `modulus`. -/// It calculates the quotient and remainder in unconstrained. -fn mul_mod_u2048(a: &U2048, b: &U2048, modulus: &U2048) -> U2048 { - let prod = mul_u2048(*a, *b); - sp1_lib::unconstrained! { - let modulus_u4096 = U4096::from(modulus); - let modulus_u4096_nonzero = NonZero::new(modulus_u4096).unwrap(); // Convert modulus to NonZero - let (quotient, result) = prod.div_rem(&modulus_u4096_nonzero); - let result_bytes = result.to_le_bytes(); - let quotient_bytes = quotient.to_le_bytes(); - - hint_slice(&result_bytes); - hint_slice("ient_bytes[..256]); - } + /// Performs modular multiplication of `a` and `b` with `modulus`. + /// It calculates the quotient and remainder in unconstrained. + fn mul_mod_u2048(a: &U2048, b: &U2048, modulus: &U2048) -> U2048 { + let prod = mul_u2048(*a, *b); + sp1_lib::unconstrained! { + let modulus_u4096 = U4096::from(modulus); + let modulus_u4096_nonzero = NonZero::new(modulus_u4096).unwrap(); // Convert modulus to NonZero + let (quotient, result) = prod.div_rem(&modulus_u4096_nonzero); + let result_bytes = result.to_le_bytes(); + let quotient_bytes = quotient.to_le_bytes(); + + hint_slice(&result_bytes); + hint_slice("ient_bytes[..256]); + } - let result_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); - let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); + let result_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); + let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); - let q_array = U2048::from_le_slice("ient_bytes); - let result_u4096 = U4096::from_le_slice(&result_bytes); - let result_u2048 = U2048::from_le_slice(&result_bytes[..256]); + let q_array = U2048::from_le_slice("ient_bytes); + let result_u4096 = U4096::from_le_slice(&result_bytes); + let result_u2048 = U2048::from_le_slice(&result_bytes[..256]); - assert!(prod.wrapping_sub(&mul_u2048(q_array, *modulus)).wrapping_sub(&result_u4096) == U4096::ZERO); - result_u2048 -} + assert!(prod.wrapping_sub(&mul_u2048(q_array, *modulus)).wrapping_sub(&result_u4096) == U4096::ZERO); + result_u2048 + } -/// Performs multiplication of `a` and `b`, which are both U2048, -/// and returns a U4096. -fn mul_u2048(a_array: U2048, b_array: U2048) -> U4096 { - let mut sum = U4096::ZERO; - let a_words = a_array.to_words(); + /// Performs multiplication of `a` and `b`, which are both U2048, + /// and returns a U4096. + fn mul_u2048(a_array: U2048, b_array: U2048) -> U4096 { + let mut sum = U4096::ZERO; + let a_words = a_array.to_words(); - for i in 0..8 { - let chunk = a_words[i*8..(i+1)*8].try_into().unwrap(); - let a_chunk: U256 = U256::from_words(chunk); - let mut prod = mul_array(a_chunk, b_array); - let mut shifted_words = [0u32; 128]; - shifted_words[i*8..].copy_from_slice(&prod.to_words()[..(128 - 8*i)]); - let shifted_prod = U4096::from_words(shifted_words); - sum = sum.wrapping_add(&shifted_prod); - } + for i in 0..8 { + let chunk = a_words[i*8..(i+1)*8].try_into().unwrap(); + let a_chunk: U256 = U256::from_words(chunk); + let mut prod = mul_array(a_chunk, b_array); + let mut shifted_words = [0u32; 128]; + shifted_words[i*8..].copy_from_slice(&prod.to_words()[..(128 - 8*i)]); + let shifted_prod = U4096::from_words(shifted_words); + sum = sum.wrapping_add(&shifted_prod); + } - sum -} + sum + } -/// Performs multiplication of `a` a U256 and `b` which is a U2048. -fn mul_array(a: U256, b_array: U2048) -> U4096 { - let mut result_words = [0u32; 128]; - let result_ptr = result_words.as_mut_ptr(); - unsafe { - sp1_lib::syscall_u256x2048_mul( - cast_ref(&a.to_words()), - cast_ref(&b_array.to_words()), - result_ptr as *mut [u32; 64], - result_ptr.add(64) as *mut [u32; 8], - ); - } + /// Performs multiplication of `a` a U256 and `b` which is a U2048. + fn mul_array(a: U256, b_array: U2048) -> U4096 { + let mut result_words = [0u32; 128]; + let result_ptr = result_words.as_mut_ptr(); + unsafe { + sp1_lib::syscall_u256x2048_mul( + cast_ref(&a.to_words()), + cast_ref(&b_array.to_words()), + result_ptr as *mut [u32; 64], + result_ptr.add(64) as *mut [u32; 8], + ); + } - U4096::from_words(result_words) -} + U4096::from_words(result_words) + } -/// Converts a BigUint to a U2048. -fn from_biguint_to_u2048(value: &BigUint) -> U2048 { - let mut padded_bytes = [0u8; 256]; - let a_bytes = value.to_bytes_le(); - for (i, &byte) in a_bytes.iter().enumerate() { - if i >= 256 { break; } - padded_bytes[i] = byte; + /// Converts a BigUint to a U2048. + fn from_biguint_to_u2048(value: &BigUint) -> U2048 { + let mut padded_bytes = [0u8; 256]; + let a_bytes = value.to_bytes_le(); + for (i, &byte) in a_bytes.iter().enumerate() { + if i >= 256 { break; } + padded_bytes[i] = byte; + } + + U2048::from_le_slice(&padded_bytes) + } } - - U2048::from_le_slice(&padded_bytes) } + /// ⚠️ Performs raw RSA decryption with no padding or error checking. /// /// Returns a plaintext `BigUint`. Performs RSA blinding if an `Rng` is passed. From 7ad3ca72fb9055e1d9f07bc1c99fcec6806ffea6 Mon Sep 17 00:00:00 2001 From: umadayal Date: Wed, 13 Nov 2024 18:57:03 -0800 Subject: [PATCH 03/27] fix --- src/algorithms/rsa.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 0fec99e..bb4d242 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -30,7 +30,7 @@ pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { let m_u2048 = from_biguint_to_u2048(m); let e_u2048 = from_biguint_to_u2048(key.e()); let n_u2048 = from_biguint_to_u2048(key.n()); - Ok(custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048)) + return Ok(custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048)); } } Ok(m.modpow(key.e(), key.n())) From 2d1aadaaf2e1bb119be4d41ee9765873946fd71e Mon Sep 17 00:00:00 2001 From: nhtyy Date: Thu, 12 Dec 2024 16:02:06 -0800 Subject: [PATCH 04/27] fix: use hook instead of unconstrained --- Cargo.lock | 4 +- Cargo.toml | 4 +- src/algorithms/rsa.rs | 185 +++++++++++++++++++++--------------------- 3 files changed, 95 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d82b6d..941d174 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -659,8 +659,8 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "sp1-lib" -version = "3.0.0" -source = "git+https://github.com/succinctlabs/sp1.git?branch=dev#0c08cafd4adeb0d246b21411d8422611b3861337" +version = "4.0.0-rc.2" +source = "git+https://github.com/succinctlabs/sp1.git?branch=n/rsa-hook#a8789a6c62a11bb435c965db774b90075f7e0735" dependencies = [ "bincode", "serde", diff --git a/Cargo.toml b/Cargo.toml index c427ce7..c16dc0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,9 +30,7 @@ cfg-if = "1.0.0" bytemuck = { version = "1.16.1", features = ["derive"] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = { git = "https://github.com/succinctlabs/sp1.git", branch = "dev" } - - +sp1-lib = { git = "https://github.com/succinctlabs/sp1.git", branch = "n/rsa-hook" } # optional dependencies sha1 = { version = "0.10.5", optional = true, default-features = false, features = ["oid"] } diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index bb4d242..45cf8f3 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -7,10 +7,14 @@ use num_integer::{sqrt, Integer}; use num_traits::{FromPrimitive, One, Pow, Signed, Zero}; use rand_core::CryptoRngCore; use zeroize::{Zeroize, Zeroizing}; + +#[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] use bytemuck::cast_ref; #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] use sp1_lib::io::hint_slice; +#[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] use crypto_bigint::{Integer as CryptoInteger, NonZero, Encoding, U2048, U256, U4096}; +#[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] use core::convert::TryInto; use crate::errors::{Error, Result}; @@ -24,121 +28,116 @@ use crate::traits::{PrivateKeyParts, PublicKeyParts}; /// or signature scheme. See the [module-level documentation][crate::hazmat] for more information. #[inline] pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { - // Ok(m.modpow(key.e(), key.n())) - cfg_if::cfg_if! { - if #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] { - let m_u2048 = from_biguint_to_u2048(m); - let e_u2048 = from_biguint_to_u2048(key.e()); - let n_u2048 = from_biguint_to_u2048(key.n()); - return Ok(custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048)); - } - } - Ok(m.modpow(key.e(), key.n())) -} + #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] + { + use zkvm::*; -cfg_if::cfg_if! { - if #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] { - /// Performs modular exponentiation of `base` to the power of `exp` modulo `modulus`. - /// This function takes in U2048 operands and returns the result as a BigUint. - fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { - if *modulus == U2048::ONE { - return BigUint::zero(); - } + let m_u2048 = from_biguint_to_u2048(m); + let e_u2048 = from_biguint_to_u2048(key.e()); + let n_u2048 = from_biguint_to_u2048(key.n()); - let mut result = U2048::ONE; - let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero - let mut base = base.rem(&modulus_nonzero); + return Ok(custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048)); + } + Ok(m.modpow(key.e(), key.n())) +} - let mut exp = *exp; - while exp > U2048::ZERO { - if exp.is_odd().into() { - result = mul_mod_u2048(&result, &base, &modulus_nonzero); - } - exp = exp.shr(1); - base = mul_mod_u2048(&base, &base, &modulus_nonzero); - } +#[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] +mod zkvm { + use super::*; - let result_biguint = BigUint::from_bytes_le(&result.to_le_bytes()); - result_biguint - + /// Performs modular exponentiation of `base` to the power of `exp` modulo `modulus`. + /// This function takes in U2048 operands and returns the result as a BigUint. + pub(crate) fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { + if *modulus == U2048::ONE { + return BigUint::zero(); } + let mut result = U2048::ONE; + let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero + let mut base = base.rem(&modulus_nonzero); - /// Performs modular multiplication of `a` and `b` with `modulus`. - /// It calculates the quotient and remainder in unconstrained. - fn mul_mod_u2048(a: &U2048, b: &U2048, modulus: &U2048) -> U2048 { - let prod = mul_u2048(*a, *b); - sp1_lib::unconstrained! { - let modulus_u4096 = U4096::from(modulus); - let modulus_u4096_nonzero = NonZero::new(modulus_u4096).unwrap(); // Convert modulus to NonZero - let (quotient, result) = prod.div_rem(&modulus_u4096_nonzero); - let result_bytes = result.to_le_bytes(); - let quotient_bytes = quotient.to_le_bytes(); - - hint_slice(&result_bytes); - hint_slice("ient_bytes[..256]); + let mut exp = *exp; + while exp > U2048::ZERO { + if exp.is_odd().into() { + result = mul_mod_u2048(&result, &base, &modulus_nonzero); } + exp = exp.shr(1); + base = mul_mod_u2048(&base, &base, &modulus_nonzero); + } - let result_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); - let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); - - let q_array = U2048::from_le_slice("ient_bytes); - let result_u4096 = U4096::from_le_slice(&result_bytes); - let result_u2048 = U2048::from_le_slice(&result_bytes[..256]); + let result_biguint = BigUint::from_bytes_le(&result.to_le_bytes()); + result_biguint + + } - assert!(prod.wrapping_sub(&mul_u2048(q_array, *modulus)).wrapping_sub(&result_u4096) == U4096::ZERO); - result_u2048 - } + /// Performs modular multiplication of `a` and `b` with `modulus`. + /// It calculates the quotient and remainder in unconstrained. + fn mul_mod_u2048(a: &U2048, b: &U2048, modulus: &U2048) -> U2048 { + let prod = mul_u2048(*a, *b); + + // Call the hook to perform the modmul opertaion in the executor. + sp1_lib::io::write(sp1_lib::io::FD_RSA_MUL_MOD, &prod.to_le_bytes().into_iter().chain(modulus.to_le_bytes().into_iter()).collect::>()); + let result_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); + let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); - /// Performs multiplication of `a` and `b`, which are both U2048, - /// and returns a U4096. - fn mul_u2048(a_array: U2048, b_array: U2048) -> U4096 { - let mut sum = U4096::ZERO; - let a_words = a_array.to_words(); + let q_array = U2048::from_le_slice("ient_bytes); + let result_u4096 = U4096::from_le_slice(&result_bytes); + let result_u2048 = U2048::from_le_slice(&result_bytes[..256]); + + // Constrain that a * b % modulus = result + assert!(prod.wrapping_sub(&mul_u2048(q_array, *modulus)).wrapping_sub(&result_u4096) == U4096::ZERO); - for i in 0..8 { - let chunk = a_words[i*8..(i+1)*8].try_into().unwrap(); - let a_chunk: U256 = U256::from_words(chunk); - let mut prod = mul_array(a_chunk, b_array); - let mut shifted_words = [0u32; 128]; - shifted_words[i*8..].copy_from_slice(&prod.to_words()[..(128 - 8*i)]); - let shifted_prod = U4096::from_words(shifted_words); - sum = sum.wrapping_add(&shifted_prod); - } + result_u2048 + } - sum + /// Performs multiplication of `a` and `b`, which are both U2048, + /// and returns a U4096. + fn mul_u2048(a_array: U2048, b_array: U2048) -> U4096 { + let mut sum = U4096::ZERO; + let a_words = a_array.to_words(); + + for i in 0..8 { + let chunk = a_words[i*8..(i+1)*8].try_into().unwrap(); + let a_chunk: U256 = U256::from_words(chunk); + let mut prod = mul_array(a_chunk, b_array); + let mut shifted_words = [0u32; 128]; + shifted_words[i*8..].copy_from_slice(&prod.to_words()[..(128 - 8*i)]); + let shifted_prod = U4096::from_words(shifted_words); + sum = sum.wrapping_add(&shifted_prod); } - /// Performs multiplication of `a` a U256 and `b` which is a U2048. - fn mul_array(a: U256, b_array: U2048) -> U4096 { - let mut result_words = [0u32; 128]; - let result_ptr = result_words.as_mut_ptr(); - unsafe { - sp1_lib::syscall_u256x2048_mul( - cast_ref(&a.to_words()), - cast_ref(&b_array.to_words()), - result_ptr as *mut [u32; 64], - result_ptr.add(64) as *mut [u32; 8], - ); - } + sum + } - U4096::from_words(result_words) + /// Performs multiplication of `a` a U256 and `b` which is a U2048. + fn mul_array(a: U256, b_array: U2048) -> U4096 { + let mut result_words = [0u32; 128]; + let result_ptr = result_words.as_mut_ptr(); + unsafe { + sp1_lib::syscall_u256x2048_mul( + cast_ref(&a.to_words()), + cast_ref(&b_array.to_words()), + result_ptr as *mut [u32; 64], + result_ptr.add(64) as *mut [u32; 8], + ); } - /// Converts a BigUint to a U2048. - fn from_biguint_to_u2048(value: &BigUint) -> U2048 { - let mut padded_bytes = [0u8; 256]; - let a_bytes = value.to_bytes_le(); - for (i, &byte) in a_bytes.iter().enumerate() { - if i >= 256 { break; } - padded_bytes[i] = byte; - } - - U2048::from_le_slice(&padded_bytes) + U4096::from_words(result_words) + } + + /// Converts a BigUint to a U2048. + pub(super) fn from_biguint_to_u2048(value: &BigUint) -> U2048 { + let mut padded_bytes = [0u8; 256]; + let a_bytes = value.to_bytes_le(); + for (i, &byte) in a_bytes.iter().enumerate() { + if i >= 256 { break; } + padded_bytes[i] = byte; } + + U2048::from_le_slice(&padded_bytes) } } From 9e31447c203eefb4ac50d7c1ce421fb418a8638c Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 17 Dec 2024 18:15:36 -0800 Subject: [PATCH 05/27] fix: range check --- src/algorithms/rsa.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 45cf8f3..7813de7 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -42,13 +42,19 @@ pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { Ok(m.modpow(key.e(), key.n())) } +/// # ☢️️ WARNING: HAZARDOUS API ☢️ +/// +/// All inputs are ASSUMED to be on the range of [0, 2^2048).] +/// +/// Attempting to use this function with values outside of this range will result in truncation, +/// and may have unintended side effects! #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] mod zkvm { use super::*; /// Performs modular exponentiation of `base` to the power of `exp` modulo `modulus`. /// This function takes in U2048 operands and returns the result as a BigUint. - pub(crate) fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { + fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { if *modulus == U2048::ONE { return BigUint::zero(); } @@ -68,29 +74,28 @@ mod zkvm { let result_biguint = BigUint::from_bytes_le(&result.to_le_bytes()); result_biguint - } /// Performs modular multiplication of `a` and `b` with `modulus`. /// It calculates the quotient and remainder in unconstrained. + /// + /// Note: This function assumes that 0 <= a, b < modulus. fn mul_mod_u2048(a: &U2048, b: &U2048, modulus: &U2048) -> U2048 { let prod = mul_u2048(*a, *b); // Call the hook to perform the modmul opertaion in the executor. sp1_lib::io::write(sp1_lib::io::FD_RSA_MUL_MOD, &prod.to_le_bytes().into_iter().chain(modulus.to_le_bytes().into_iter()).collect::>()); - let result_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); + let result_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); let q_array = U2048::from_le_slice("ient_bytes); - let result_u4096 = U4096::from_le_slice(&result_bytes); - let result_u2048 = U2048::from_le_slice(&result_bytes[..256]); - - // Constrain that a * b % modulus = result - assert!(prod.wrapping_sub(&mul_u2048(q_array, *modulus)).wrapping_sub(&result_u4096) == U4096::ZERO); + let result = U2048::from_le_slice(&result_bytes); - result_u2048 + assert!(result >= U2048::ZERO && result < *modulus); + assert!(prod == mul_u2048(q_array, *modulus).wrapping_add(&U4096::from(&result))); + result } /// Performs multiplication of `a` and `b`, which are both U2048, From be83694938da0cdcbaf08c9816346f37f9848f6b Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 31 Dec 2024 15:05:39 -0800 Subject: [PATCH 06/27] patch: bump to v4rc --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c16dc0c..46966ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,14 +29,14 @@ crypto-bigint = "0.5.5" cfg-if = "1.0.0" bytemuck = { version = "1.16.1", features = ["derive"] } -[target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = { git = "https://github.com/succinctlabs/sp1.git", branch = "n/rsa-hook" } - # optional dependencies sha1 = { version = "0.10.5", optional = true, default-features = false, features = ["oid"] } sha2 = { version = "0.10.6", optional = true, default-features = false, features = ["oid"] } serde = { version = "1.0.184", optional = true, default-features = false, features = ["derive"] } +[target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] +sp1-lib = "4.0.0-rc.3" + [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } hex-literal = "0.4.1" From 5dc15bb011b64d7b0062850bf61a675a12a41064 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 31 Dec 2024 15:08:14 -0800 Subject: [PATCH 07/27] fix: fmt --- src/algorithms/rsa.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 7813de7..d5642cd 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -11,11 +11,11 @@ use zeroize::{Zeroize, Zeroizing}; #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] use bytemuck::cast_ref; #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] -use sp1_lib::io::hint_slice; +use core::convert::TryInto; #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] -use crypto_bigint::{Integer as CryptoInteger, NonZero, Encoding, U2048, U256, U4096}; +use crypto_bigint::{Encoding, Integer as CryptoInteger, NonZero, U2048, U256, U4096}; #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] -use core::convert::TryInto; +use sp1_lib::io::hint_slice; use crate::errors::{Error, Result}; use crate::traits::{PrivateKeyParts, PublicKeyParts}; @@ -54,7 +54,7 @@ mod zkvm { /// Performs modular exponentiation of `base` to the power of `exp` modulo `modulus`. /// This function takes in U2048 operands and returns the result as a BigUint. - fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { + pub(super) fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { if *modulus == U2048::ONE { return BigUint::zero(); } @@ -76,16 +76,22 @@ mod zkvm { result_biguint } - /// Performs modular multiplication of `a` and `b` with `modulus`. /// It calculates the quotient and remainder in unconstrained. /// /// Note: This function assumes that 0 <= a, b < modulus. fn mul_mod_u2048(a: &U2048, b: &U2048, modulus: &U2048) -> U2048 { let prod = mul_u2048(*a, *b); - + // Call the hook to perform the modmul opertaion in the executor. - sp1_lib::io::write(sp1_lib::io::FD_RSA_MUL_MOD, &prod.to_le_bytes().into_iter().chain(modulus.to_le_bytes().into_iter()).collect::>()); + sp1_lib::io::write( + sp1_lib::io::FD_RSA_MUL_MOD, + &prod + .to_le_bytes() + .into_iter() + .chain(modulus.to_le_bytes().into_iter()) + .collect::>(), + ); let result_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); @@ -95,7 +101,7 @@ mod zkvm { assert!(result >= U2048::ZERO && result < *modulus); assert!(prod == mul_u2048(q_array, *modulus).wrapping_add(&U4096::from(&result))); - result + result } /// Performs multiplication of `a` and `b`, which are both U2048, @@ -105,11 +111,11 @@ mod zkvm { let a_words = a_array.to_words(); for i in 0..8 { - let chunk = a_words[i*8..(i+1)*8].try_into().unwrap(); + let chunk = a_words[i * 8..(i + 1) * 8].try_into().unwrap(); let a_chunk: U256 = U256::from_words(chunk); let mut prod = mul_array(a_chunk, b_array); let mut shifted_words = [0u32; 128]; - shifted_words[i*8..].copy_from_slice(&prod.to_words()[..(128 - 8*i)]); + shifted_words[i * 8..].copy_from_slice(&prod.to_words()[..(128 - 8 * i)]); let shifted_prod = U4096::from_words(shifted_words); sum = sum.wrapping_add(&shifted_prod); } @@ -130,7 +136,7 @@ mod zkvm { ); } - U4096::from_words(result_words) + U4096::from_words(result_words) } /// Converts a BigUint to a U2048. @@ -138,15 +144,16 @@ mod zkvm { let mut padded_bytes = [0u8; 256]; let a_bytes = value.to_bytes_le(); for (i, &byte) in a_bytes.iter().enumerate() { - if i >= 256 { break; } + if i >= 256 { + break; + } padded_bytes[i] = byte; } - + U2048::from_le_slice(&padded_bytes) } } - /// ⚠️ Performs raw RSA decryption with no padding or error checking. /// /// Returns a plaintext `BigUint`. Performs RSA blinding if an `Rng` is passed. From da9913ce6a3f5f001dc6e32c9aac07336bf8b7f7 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 14 Jan 2025 13:34:24 -0800 Subject: [PATCH 08/27] chore: bump 4.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 46966ec..964c4d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ sha2 = { version = "0.10.6", optional = true, default-features = false, features serde = { version = "1.0.184", optional = true, default-features = false, features = ["derive"] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = "4.0.0-rc.3" +sp1-lib = "4.0.0" [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } From 27cc8e7b1dfb43a2bc7ecc3726c13f049a4ec096 Mon Sep 17 00:00:00 2001 From: tamirhemo Date: Fri, 24 Jan 2025 21:23:33 +0000 Subject: [PATCH 09/27] optimize patch --- Cargo.lock | 233 ++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 38 +++++-- src/algorithms/rsa.rs | 59 +++++++---- 3 files changed, 292 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 941d174..5a229f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "errno" version = "0.3.7" @@ -210,6 +216,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + [[package]] name = "generic-array" version = "0.14.7" @@ -231,6 +243,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hex-literal" version = "0.4.1" @@ -256,6 +274,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "keccak" version = "0.1.4" @@ -267,9 +294,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ "spin", ] @@ -292,6 +319,16 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -312,11 +349,10 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] @@ -333,14 +369,132 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "p3-baby-bear" +version = "0.2.0-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080896e9d09e9761982febafe3b3da5cbf320e32f0c89b6e2e01e875129f4c2d" +dependencies = [ + "num-bigint", + "p3-field", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "rand", + "serde", +] + +[[package]] +name = "p3-dft" +version = "0.2.0-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "292e97d02d4c38d8b306c2b8c0428bf15f4d32a11a40bcf80018f675bf33267e" +dependencies = [ + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.2.0-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91d8e5f9ede1171adafdb0b6a0df1827fbd4eb6a6217bfa36374e5d86248757" +dependencies = [ + "itertools", + "num-bigint", + "num-traits", + "p3-util", + "rand", + "serde", +] + +[[package]] +name = "p3-matrix" +version = "0.2.0-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98bf2c7680b8e906a5e147fe4ceb05a11cc9fa35678aa724333bcb35c72483c1" +dependencies = [ + "itertools", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand", + "serde", + "tracing", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3925562a4c03183eafc92fd07b19f65ac6cb4b48d68c3920ce58d9bee6efe362" + +[[package]] +name = "p3-mds" +version = "0.2.0-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706cea48976f54702dc68dffa512684c1304d1a3606cadea423cfe0b1ee25134" +dependencies = [ + "itertools", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-symmetric", + "p3-util", + "rand", +] + +[[package]] +name = "p3-poseidon2" +version = "0.2.0-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ce5f5ec7f1ba3a233a671621029def7bd416e7c51218c9d1167d21602cf312" +dependencies = [ + "gcd", + "p3-field", + "p3-mds", + "p3-symmetric", + "rand", + "serde", +] + +[[package]] +name = "p3-symmetric" +version = "0.2.0-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f29dc5bb6c99d3de75869d5c086874b64890280eeb7d3e068955f939e219253" +dependencies = [ + "itertools", + "p3-field", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88dd5ca3eb6ff33cb20084778c32a6d68064a1913b4632437408c5a1098408b3" +dependencies = [ + "serde", +] + [[package]] name = "pbkdf2" version = "0.12.2" @@ -360,6 +514,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "pkcs1" version = "0.7.5" @@ -659,18 +819,38 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "sp1-lib" -version = "4.0.0-rc.2" -source = "git+https://github.com/succinctlabs/sp1.git?branch=n/rsa-hook#a8789a6c62a11bb435c965db774b90075f7e0735" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac3d3deeed25e9cad80e4275faf5954aa63f213ed3422f0e098dd2d0c1b0c0e" dependencies = [ "bincode", "serde", + "sp1-primitives", +] + +[[package]] +name = "sp1-primitives" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b25a09b455dfae9c688da05718b205e8bd4afab36cd912d54639f5d4035815" +dependencies = [ + "bincode", + "hex", + "lazy_static", + "num-bigint", + "p3-baby-bear", + "p3-field", + "p3-poseidon2", + "p3-symmetric", + "serde", + "sha2", ] [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" @@ -712,6 +892,37 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index 964c4d9..46ab60c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,16 +13,32 @@ readme = "README.md" rust-version = "1.65" [dependencies] -num-bigint = { version = "0.8.2", features = ["i128", "prime", "zeroize"], default-features = false, package = "num-bigint-dig" } -num-traits = { version= "0.2.9", default-features = false, features = ["libm"] } +num-bigint = { version = "0.8.2", features = [ + "i128", + "prime", + "zeroize", +], default-features = false, package = "num-bigint-dig" } +num-traits = { version = "0.2.9", default-features = false, features = [ + "libm", +] } num-integer = { version = "0.1.39", default-features = false } rand_core = { version = "0.6.4", default-features = false } const-oid = { version = "0.9", default-features = false } subtle = { version = "2.1.1", default-features = false } -digest = { version = "0.10.5", default-features = false, features = ["alloc", "oid"] } -pkcs1 = { version = "0.7.5", default-features = false, features = ["alloc", "pkcs8"] } +digest = { version = "0.10.5", default-features = false, features = [ + "alloc", + "oid", +] } +pkcs1 = { version = "0.7.5", default-features = false, features = [ + "alloc", + "pkcs8", +] } pkcs8 = { version = "0.10.2", default-features = false, features = ["alloc"] } -signature = { version = ">2.0, <2.3", default-features = false , features = ["alloc", "digest", "rand_core"] } +signature = { version = ">2.0, <2.3", default-features = false, features = [ + "alloc", + "digest", + "rand_core", +] } spki = { version = "0.7.3", default-features = false, features = ["alloc"] } zeroize = { version = "1.5", features = ["alloc"] } crypto-bigint = "0.5.5" @@ -30,9 +46,15 @@ cfg-if = "1.0.0" bytemuck = { version = "1.16.1", features = ["derive"] } # optional dependencies -sha1 = { version = "0.10.5", optional = true, default-features = false, features = ["oid"] } -sha2 = { version = "0.10.6", optional = true, default-features = false, features = ["oid"] } -serde = { version = "1.0.184", optional = true, default-features = false, features = ["derive"] } +sha1 = { version = "0.10.5", optional = true, default-features = false, features = [ + "oid", +] } +sha2 = { version = "0.10.6", optional = true, default-features = false, features = [ + "oid", +] } +serde = { version = "1.0.184", optional = true, default-features = false, features = [ + "derive", +] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] sp1-lib = "4.0.0" diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index d5642cd..9a31a8a 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -36,9 +36,10 @@ pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { let e_u2048 = from_biguint_to_u2048(key.e()); let n_u2048 = from_biguint_to_u2048(key.n()); - return Ok(custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048)); - } + let result = custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048); + return Ok(result); + } Ok(m.modpow(key.e(), key.n())) } @@ -59,18 +60,35 @@ mod zkvm { return BigUint::zero(); } - let mut result = U2048::ONE; - let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero - let mut base = base.rem(&modulus_nonzero); + // The most common exponent is 65537, so we optimize for that case, otherwise we use the + // generic square and multiply algorithm. + let result = if (exp == &U2048::from_u64(65537u64)) { + let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero + let mut base = base.rem(&modulus_nonzero); + let mut result = base; - let mut exp = *exp; - while exp > U2048::ZERO { - if exp.is_odd().into() { - result = mul_mod_u2048(&result, &base, &modulus_nonzero); + // Square 16 times + for i in 0..16 { + result = mul_mod_u2048(&result, &result, &modulus_nonzero); } - exp = exp.shr(1); - base = mul_mod_u2048(&base, &base, &modulus_nonzero); - } + // Multiply by the base + mul_mod_u2048(&result, &base, &modulus_nonzero) + } else { + let mut result = U2048::ONE; + let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero + let mut base = base.rem(&modulus_nonzero); + + let mut exp = *exp; + while exp > U2048::ZERO { + if exp.is_odd().into() { + result = mul_mod_u2048(&result, &base, &modulus_nonzero); + } + exp = exp.shr(1); + base = mul_mod_u2048(&base, &base, &modulus_nonzero); + } + + result + }; let result_biguint = BigUint::from_bytes_le(&result.to_le_bytes()); result_biguint @@ -108,14 +126,18 @@ mod zkvm { /// and returns a U4096. fn mul_u2048(a_array: U2048, b_array: U2048) -> U4096 { let mut sum = U4096::ZERO; - let a_words = a_array.to_words(); - for i in 0..8 { - let chunk = a_words[i * 8..(i + 1) * 8].try_into().unwrap(); - let a_chunk: U256 = U256::from_words(chunk); - let mut prod = mul_array(a_chunk, b_array); + for (i, chunk) in a_array.as_words().chunks(8).enumerate() { let mut shifted_words = [0u32; 128]; - shifted_words[i * 8..].copy_from_slice(&prod.to_words()[..(128 - 8 * i)]); + let prod_result_ptr = shifted_words[i * 8..].as_mut_ptr(); + unsafe { + sp1_lib::syscall_u256x2048_mul( + chunk.as_ptr() as *const [u32; 8], + b_array.as_words().as_ptr() as *const [u32; 64], + prod_result_ptr as *mut [u32; 64], + prod_result_ptr.add(64) as *mut [u32; 8], + ); + } let shifted_prod = U4096::from_words(shifted_words); sum = sum.wrapping_add(&shifted_prod); } @@ -149,7 +171,6 @@ mod zkvm { } padded_bytes[i] = byte; } - U2048::from_le_slice(&padded_bytes) } } From 3e940a7177084750c0b26913aad8c75fe046f2d6 Mon Sep 17 00:00:00 2001 From: leruaa Date: Tue, 1 Apr 2025 14:25:18 -0700 Subject: [PATCH 10/27] fix: use precompile only if the key is 2048 --- src/algorithms/rsa.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 9a31a8a..24957c7 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -30,15 +30,17 @@ use crate::traits::{PrivateKeyParts, PublicKeyParts}; pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] { - use zkvm::*; + if key.size() == 2048 { + use zkvm::*; - let m_u2048 = from_biguint_to_u2048(m); - let e_u2048 = from_biguint_to_u2048(key.e()); - let n_u2048 = from_biguint_to_u2048(key.n()); + let m_u2048 = from_biguint_to_u2048(m); + let e_u2048 = from_biguint_to_u2048(key.e()); + let n_u2048 = from_biguint_to_u2048(key.n()); - let result = custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048); + let result = custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048); - return Ok(result); + return Ok(result); + } } Ok(m.modpow(key.e(), key.n())) } From d73bfc58b6e4b951ed8e08580072866b57fc7e3d Mon Sep 17 00:00:00 2001 From: leruaa Date: Mon, 14 Apr 2025 04:17:52 -0700 Subject: [PATCH 11/27] fix: incorrect size --- src/algorithms/rsa.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 24957c7..90d8bc8 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -30,7 +30,7 @@ use crate::traits::{PrivateKeyParts, PublicKeyParts}; pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] { - if key.size() == 2048 { + if key.size() == 256 { use zkvm::*; let m_u2048 = from_biguint_to_u2048(m); From ecd3092f39acbf69752bbb009a52340c0fdf470c Mon Sep 17 00:00:00 2001 From: nhtyy Date: Thu, 29 May 2025 16:25:46 -0700 Subject: [PATCH 12/27] chore: bump 5.0.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 46ab60c..e0f17d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur ] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = "4.0.0" +sp1-lib = "5.0.0" [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } From 9234ecb39664a5a5f88fac37345e7be2f390469d Mon Sep 17 00:00:00 2001 From: rkm0959 Date: Tue, 17 Jun 2025 05:06:08 +0000 Subject: [PATCH 13/27] setup --- Cargo.lock | 191 ++++++++++++++++++++++++++++++++++++++++++----------- Cargo.toml | 2 +- 2 files changed, 155 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a229f1..dc142f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,12 +13,30 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64ct" version = "1.6.0" @@ -61,6 +79,19 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -114,6 +145,15 @@ dependencies = [ "cipher", ] +[[package]] +name = "cc" +version = "1.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -136,6 +176,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "cpufeatures" version = "0.2.11" @@ -151,8 +197,10 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ + "generic-array", "rand_core", "subtle", + "zeroize", ] [[package]] @@ -194,6 +242,25 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "errno" version = "0.3.7" @@ -210,6 +277,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "fnv" version = "1.0.7" @@ -230,6 +307,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -243,6 +321,17 @@ dependencies = [ "wasi", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "hex" version = "0.4.3" @@ -255,6 +344,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -283,6 +381,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "keccak" version = "0.1.4" @@ -385,9 +492,8 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "p3-baby-bear" -version = "0.2.0-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080896e9d09e9761982febafe3b3da5cbf320e32f0c89b6e2e01e875129f4c2d" +version = "0.1.0" +source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" dependencies = [ "num-bigint", "p3-field", @@ -400,9 +506,8 @@ dependencies = [ [[package]] name = "p3-dft" -version = "0.2.0-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "292e97d02d4c38d8b306c2b8c0428bf15f4d32a11a40bcf80018f675bf33267e" +version = "0.1.0" +source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" dependencies = [ "p3-field", "p3-matrix", @@ -413,11 +518,10 @@ dependencies = [ [[package]] name = "p3-field" -version = "0.2.0-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91d8e5f9ede1171adafdb0b6a0df1827fbd4eb6a6217bfa36374e5d86248757" +version = "0.1.0" +source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" dependencies = [ - "itertools", + "itertools 0.12.1", "num-bigint", "num-traits", "p3-util", @@ -427,11 +531,10 @@ dependencies = [ [[package]] name = "p3-matrix" -version = "0.2.0-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98bf2c7680b8e906a5e147fe4ceb05a11cc9fa35678aa724333bcb35c72483c1" +version = "0.1.0" +source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" dependencies = [ - "itertools", + "itertools 0.12.1", "p3-field", "p3-maybe-rayon", "p3-util", @@ -442,17 +545,15 @@ dependencies = [ [[package]] name = "p3-maybe-rayon" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3925562a4c03183eafc92fd07b19f65ac6cb4b48d68c3920ce58d9bee6efe362" +version = "0.1.0" +source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" [[package]] name = "p3-mds" -version = "0.2.0-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "706cea48976f54702dc68dffa512684c1304d1a3606cadea423cfe0b1ee25134" +version = "0.1.0" +source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" dependencies = [ - "itertools", + "itertools 0.12.1", "p3-dft", "p3-field", "p3-matrix", @@ -463,9 +564,8 @@ dependencies = [ [[package]] name = "p3-poseidon2" -version = "0.2.0-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ce5f5ec7f1ba3a233a671621029def7bd416e7c51218c9d1167d21602cf312" +version = "0.1.0" +source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" dependencies = [ "gcd", "p3-field", @@ -477,20 +577,18 @@ dependencies = [ [[package]] name = "p3-symmetric" -version = "0.2.0-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f29dc5bb6c99d3de75869d5c086874b64890280eeb7d3e068955f939e219253" +version = "0.1.0" +source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" dependencies = [ - "itertools", + "itertools 0.12.1", "p3-field", "serde", ] [[package]] name = "p3-util" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88dd5ca3eb6ff33cb20084778c32a6d68064a1913b4632437408c5a1098408b3" +version = "0.1.0" +source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" dependencies = [ "serde", ] @@ -740,6 +838,19 @@ dependencies = [ "sha2", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + [[package]] name = "serde" version = "1.0.215" @@ -801,6 +912,12 @@ dependencies = [ "keccak", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signature" version = "2.2.0" @@ -819,23 +936,23 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "sp1-lib" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac3d3deeed25e9cad80e4275faf5954aa63f213ed3422f0e098dd2d0c1b0c0e" +version = "5.0.0" dependencies = [ "bincode", + "elliptic-curve", "serde", "sp1-primitives", ] [[package]] name = "sp1-primitives" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b25a09b455dfae9c688da05718b205e8bd4afab36cd912d54639f5d4035815" +version = "5.0.0" dependencies = [ "bincode", + "blake3", + "cfg-if", "hex", + "itertools 0.13.0", "lazy_static", "num-bigint", "p3-baby-bear", diff --git a/Cargo.toml b/Cargo.toml index e0f17d0..38bd93a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur ] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = "5.0.0" +sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } From 449a96cc25d332bb2498a3c6c4039a2c91847a96 Mon Sep 17 00:00:00 2001 From: rkm0959 Date: Thu, 19 Jun 2025 02:34:29 +0000 Subject: [PATCH 14/27] checkpoint --- Cargo.lock | 2 + Cargo.toml | 3 +- src/algorithms/rsa.rs | 357 ++++++++++++++++++++++++++++++------------ 3 files changed, 259 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc142f4..14c2f97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,6 +937,7 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "sp1-lib" version = "5.0.0" +source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/better-rsa#1f5056d7a9cc19e1bcef850f9fbdbd22241382ea" dependencies = [ "bincode", "elliptic-curve", @@ -947,6 +948,7 @@ dependencies = [ [[package]] name = "sp1-primitives" version = "5.0.0" +source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/better-rsa#1f5056d7a9cc19e1bcef850f9fbdbd22241382ea" dependencies = [ "bincode", "blake3", diff --git a/Cargo.toml b/Cargo.toml index 38bd93a..5cf8be2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,8 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur ] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } +sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "min/better-rsa", optional = true } +# sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 90d8bc8..e7e9fbe 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -13,7 +13,7 @@ use bytemuck::cast_ref; #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] use core::convert::TryInto; #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] -use crypto_bigint::{Encoding, Integer as CryptoInteger, NonZero, U2048, U256, U4096}; +use crypto_bigint::{Encoding, Integer as CryptoInteger, NonZero, U2048, U256, U3072, U4096, U6144, U8192}; #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] use sp1_lib::io::hint_slice; @@ -30,16 +30,18 @@ use crate::traits::{PrivateKeyParts, PublicKeyParts}; pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { #[cfg(all(target_os = "zkvm", target_vendor = "succinct"))] { - if key.size() == 256 { - use zkvm::*; - - let m_u2048 = from_biguint_to_u2048(m); - let e_u2048 = from_biguint_to_u2048(key.e()); - let n_u2048 = from_biguint_to_u2048(key.n()); - - let result = custom_modpow_u2048(&m_u2048, &e_u2048, &n_u2048); - - return Ok(result); + match key.size() { + 256 => { + use zkvm::*; + let m_chunks = zkvm::from_biguint_to_chunks::<8>(m); + let e_chunks = zkvm::from_biguint_to_chunks::<8>(key.e()); + let n_chunks = zkvm::from_biguint_to_chunks::<8>(key.n()); + let result = zkvm::custom_modpow_2048(&m_chunks, &e_chunks, &n_chunks); + return Ok(result); + }, + _ => { + // Fall through to standard modpow for unsupported sizes + } } } Ok(m.modpow(key.e(), key.n())) @@ -55,126 +57,277 @@ pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { mod zkvm { use super::*; - /// Performs modular exponentiation of `base` to the power of `exp` modulo `modulus`. - /// This function takes in U2048 operands and returns the result as a BigUint. - pub(super) fn custom_modpow_u2048(base: &U2048, exp: &U2048, modulus: &U2048) -> BigUint { - if *modulus == U2048::ONE { - return BigUint::zero(); + /// Modular multiplication for 2048-bit keys + fn mul_mod_2048(a_chunks: &[[u32; 8]; 8], b_chunks: &[[u32; 8]; 8], modulus_chunks: &[[u32; 8]; 8]) -> [[u32; 8]; 8] { + let prod_chunks = mul_generic_chunks::<8, 16>(a_chunks, b_chunks); + + // Convert to bytes for SP1 I/O using direct transmute + let prod_bytes: [u8; 512] = unsafe { + std::mem::transmute::<[[u32; 8]; 16], [u8; 512]>(prod_chunks) + }; + let modulus_bytes: [u8; 256] = unsafe { + std::mem::transmute::<[[u32; 8]; 8], [u8; 256]>(*modulus_chunks) + }; + + // Call the hook to perform the modmul operation in the executor + sp1_lib::io::write( + sp1_lib::io::FD_RSA_MUL_MOD, + &prod_bytes.into_iter().chain(modulus_bytes.into_iter()).collect::>(), + ); + + let result_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); + let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); + + // Convert back to chunks + let result_chunks: [[u32; 8]; 8] = unsafe { + std::mem::transmute::<[u8; 256], [[u32; 8]; 8]>(result_bytes) + }; + let quotient_chunks: [[u32; 8]; 8] = unsafe { + std::mem::transmute::<[u8; 256], [[u32; 8]; 8]>(quotient_bytes) + }; + + // Verify: prod == quotient * modulus + result + let quotient_mul_chunks = mul_generic_chunks::<8, 16>("ient_chunks, modulus_chunks); + + let mut verification_prod = quotient_mul_chunks; + + add_generic_chunks(&mut verification_prod, &result_chunks); + + // Check prod == verification_prod + for i in 0..16 { + for j in 0..8 { + assert_eq!(prod_chunks[i][j], verification_prod[i][j]); + } } - // The most common exponent is 65537, so we optimize for that case, otherwise we use the - // generic square and multiply algorithm. - let result = if (exp == &U2048::from_u64(65537u64)) { - let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero - let mut base = base.rem(&modulus_nonzero); - let mut result = base; + // Check result < modulus + assert_less_than::<8>(&result_chunks, modulus_chunks); + + result_chunks + } + /// Modular exponentiation for 2048-bit keys + pub(super) fn custom_modpow_2048(base_chunks: &[[u32; 8]; 8], exp_chunks: &[[u32; 8]; 8], modulus_chunks: &[[u32; 8]; 8]) -> BigUint { + // Convert chunks to U2048 for easier manipulation + let exp_bytes = chunks_to_bytes::<8>(exp_chunks); + let exp_u2048 = U2048::from_le_slice(&exp_bytes); + + assert!(!chunks_is_zero::<8>(modulus_chunks)); + + let result_chunks = if exp_u2048 == U2048::from_u64(65537u64) { + // Optimized path for e = 65537 + // First reduce base mod modulus using mul_mod_2048(base, 1, modulus) + let one_chunks = chunks_one(); + let mut result_chunks = mul_mod_2048(base_chunks, &one_chunks, modulus_chunks); + let base_reduced = result_chunks; + // Square 16 times - for i in 0..16 { - result = mul_mod_u2048(&result, &result, &modulus_nonzero); + for _ in 0..16 { + result_chunks = mul_mod_2048(&result_chunks, &result_chunks, modulus_chunks); } - // Multiply by the base - mul_mod_u2048(&result, &base, &modulus_nonzero) + + mul_mod_2048(&result_chunks, &base_reduced, modulus_chunks) } else { - let mut result = U2048::ONE; - let modulus_nonzero = NonZero::new(*modulus).unwrap(); // Convert modulus to NonZero - let mut base = base.rem(&modulus_nonzero); - - let mut exp = *exp; + // Square-and-multiply + let one_chunks = chunks_one(); + let mut result_chunks = one_chunks; + let mut base_chunks = mul_mod_2048(base_chunks, &one_chunks, modulus_chunks); + let mut exp = exp_u2048; + while exp > U2048::ZERO { if exp.is_odd().into() { - result = mul_mod_u2048(&result, &base, &modulus_nonzero); + result_chunks = mul_mod_2048(&result_chunks, &base_chunks, modulus_chunks); } exp = exp.shr(1); - base = mul_mod_u2048(&base, &base, &modulus_nonzero); + base_chunks = mul_mod_2048(&base_chunks, &base_chunks, modulus_chunks); } - - result + + result_chunks }; - - let result_biguint = BigUint::from_bytes_le(&result.to_le_bytes()); - result_biguint + + let result_u2048 = chunks_to_u2048(&result_chunks); + + BigUint::from_bytes_le(&result_u2048.to_le_bytes()) } + + /// Generic multiplication using schoolbook algorithm with 256-bit chunks + /// Returns a vector of 256-bit chunks representing the full product + fn mul_generic_chunks(a_chunks: &[[u32; 8]; N], b_chunks: &[[u32; 8]; N]) -> [[u32; 8]; N2] { + let mut out = [[0u32; 8]; N2]; + + let mut lo = [0u32; 8]; + let mut hi = [0u32; 8]; + let mut tmp_hi = [0u32; 8]; + let zero_carry = [0u32; 8]; + + for i in 0..N { + for j in 0..N { + let k = i + j; + + unsafe { + sp1_lib::syscall_uint256_mul_with_carry( + a_chunks[i].as_ptr() as *const [u32; 8], + b_chunks[j].as_ptr() as *const [u32; 8], + zero_carry.as_ptr() as *const [u32; 8], + lo.as_mut_ptr() as *mut [u32; 8], + hi.as_mut_ptr() as *mut [u32; 8], + ); + } + + unsafe { + sp1_lib::syscall_uint256_add_with_carry( + out[k].as_ptr() as *const [u32; 8], + lo.as_ptr() as *const [u32; 8], + zero_carry.as_ptr() as *const [u32; 8], + out[k].as_mut_ptr() as *mut [u32; 8], + tmp_hi.as_mut_ptr() as *mut [u32; 8], + ); + } - /// Performs modular multiplication of `a` and `b` with `modulus`. - /// It calculates the quotient and remainder in unconstrained. - /// - /// Note: This function assumes that 0 <= a, b < modulus. - fn mul_mod_u2048(a: &U2048, b: &U2048, modulus: &U2048) -> U2048 { - let prod = mul_u2048(*a, *b); - - // Call the hook to perform the modmul opertaion in the executor. - sp1_lib::io::write( - sp1_lib::io::FD_RSA_MUL_MOD, - &prod - .to_le_bytes() - .into_iter() - .chain(modulus.to_le_bytes().into_iter()) - .collect::>(), - ); - - let result_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); - let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); - - let q_array = U2048::from_le_slice("ient_bytes); - let result = U2048::from_le_slice(&result_bytes); - - assert!(result >= U2048::ZERO && result < *modulus); - assert!(prod == mul_u2048(q_array, *modulus).wrapping_add(&U4096::from(&result))); - result + unsafe { + sp1_lib::syscall_uint256_add_with_carry( + out[k + 1].as_ptr() as *const [u32; 8], + hi.as_ptr() as *const [u32; 8], + tmp_hi.as_ptr() as *const [u32; 8], + out[k + 1].as_mut_ptr() as *mut [u32; 8], + tmp_hi.as_mut_ptr() as *mut [u32; 8], + ); + } + + let mut idx = k + 2; + while tmp_hi[0] != 0 && idx < N2 { + unsafe { + sp1_lib::syscall_uint256_add_with_carry( + out[idx].as_ptr() as *const [u32; 8], + zero_carry.as_ptr() as *const [u32; 8], + tmp_hi.as_ptr() as *const [u32; 8], + out[idx].as_mut_ptr() as *mut [u32; 8], + tmp_hi.as_mut_ptr() as *mut [u32; 8], + ); + } + idx += 1; + } + } + } + + out } - /// Performs multiplication of `a` and `b`, which are both U2048, - /// and returns a U4096. - fn mul_u2048(a_array: U2048, b_array: U2048) -> U4096 { - let mut sum = U4096::ZERO; - - for (i, chunk) in a_array.as_words().chunks(8).enumerate() { - let mut shifted_words = [0u32; 128]; - let prod_result_ptr = shifted_words[i * 8..].as_mut_ptr(); + /// Generic addition of two chunk arrays with different sizes + /// Adds smaller array to the lower part of larger array, handling carries + fn add_generic_chunks(larger: &mut [[u32; 8]], smaller: &[[u32; 8]]) { + let mut carry = [0u32; 8]; + let zero_carry = [0u32; 8]; + + for i in 0..smaller.len() { unsafe { - sp1_lib::syscall_u256x2048_mul( - chunk.as_ptr() as *const [u32; 8], - b_array.as_words().as_ptr() as *const [u32; 64], - prod_result_ptr as *mut [u32; 64], - prod_result_ptr.add(64) as *mut [u32; 8], + sp1_lib::syscall_uint256_add_with_carry( + larger[i].as_ptr() as *const [u32; 8], + smaller[i].as_ptr() as *const [u32; 8], + carry.as_ptr() as *const [u32; 8], + larger[i].as_mut_ptr() as *mut [u32; 8], + carry.as_mut_ptr() as *mut [u32; 8], ); } - let shifted_prod = U4096::from_words(shifted_words); - sum = sum.wrapping_add(&shifted_prod); } - - sum + + let mut idx = smaller.len(); + while idx < larger.len() && carry[0] != 0 { + unsafe { + sp1_lib::syscall_uint256_add_with_carry( + larger[idx].as_ptr() as *const [u32; 8], + zero_carry.as_ptr() as *const [u32; 8], + carry.as_ptr() as *const [u32; 8], + larger[idx].as_mut_ptr() as *mut [u32; 8], + carry.as_mut_ptr() as *mut [u32; 8], + ); + } + idx += 1; + } } - /// Performs multiplication of `a` a U256 and `b` which is a U2048. - fn mul_array(a: U256, b_array: U2048) -> U4096 { - let mut result_words = [0u32; 128]; - let result_ptr = result_words.as_mut_ptr(); - unsafe { - sp1_lib::syscall_u256x2048_mul( - cast_ref(&a.to_words()), - cast_ref(&b_array.to_words()), - result_ptr as *mut [u32; 64], - result_ptr.add(64) as *mut [u32; 8], - ); + /// Assert that the result is less than the modulus + fn assert_less_than(result_chunk: &[[u32; 8]; N], modulus_chunk: &[[u32; 8]; N]) { + for i in (0..N).rev() { + for j in (0..8).rev() { + if result_chunk[i][j] < modulus_chunk[i][j] { + return; + } + assert!(result_chunk[i][j] == modulus_chunk[i][j]); + } } - - U4096::from_words(result_words) + assert!(false); } - - /// Converts a BigUint to a U2048. - pub(super) fn from_biguint_to_u2048(value: &BigUint) -> U2048 { - let mut padded_bytes = [0u8; 256]; - let a_bytes = value.to_bytes_le(); - for (i, &byte) in a_bytes.iter().enumerate() { - if i >= 256 { + + /// Generic helper to convert bytes to chunks + fn bytes_to_chunks(bytes: &[u8]) -> [[u32; 8]; N] { + let mut chunks = [[0u32; 8]; N]; + for (i, chunk) in chunks.iter_mut().enumerate() { + for (j, word) in chunk.iter_mut().enumerate() { + let byte_idx = (i * 8 + j) * 4; + if byte_idx + 4 <= bytes.len() { + *word = u32::from_le_bytes([ + bytes[byte_idx], + bytes[byte_idx + 1], + bytes[byte_idx + 2], + bytes[byte_idx + 3], + ]); + } + } + } + chunks + } + + /// Generic helper to convert chunks to bytes + fn chunks_to_bytes(chunks: &[[u32; 8]; N]) -> Vec { + chunks.iter() + .flat_map(|chunk| chunk.iter().flat_map(|&word| word.to_le_bytes())) + .collect() + } + + /// Convert chunks to U2048 + fn chunks_to_u2048(chunks: &[[u32; 8]; 8]) -> U2048 { + let bytes = chunks_to_bytes::<8>(chunks); + U2048::from_le_slice(&bytes) + } + + /// Convert U2048 to chunks + fn u2048_to_chunks(value: &U2048) -> [[u32; 8]; 8] { + bytes_to_chunks::<8>(&value.to_le_bytes()) + } + + /// Check if chunk array is zero + fn chunks_is_zero(chunks: &[[u32; 8]; N]) -> bool { + for i in 0..N { + for j in 0..8 { + if chunks[i][j] != 0 { + return false; + } + } + } + true + } + + /// Get chunk array representing one + fn chunks_one() -> [[u32; 8]; N] { + let mut chunks = [[0u32; 8]; N]; + chunks[0][0] = 1; + chunks + } + + /// Convert BigUint to chunks for arbitrary key sizes + pub(super) fn from_biguint_to_chunks(value: &BigUint) -> [[u32; 8]; N] { + let mut padded_bytes = vec![0u8; N * 32]; // N chunks * 32 bytes per chunk + let value_bytes = value.to_bytes_le(); + for (i, &byte) in value_bytes.iter().enumerate() { + if i >= padded_bytes.len() { break; } padded_bytes[i] = byte; } - U2048::from_le_slice(&padded_bytes) + bytes_to_chunks(&padded_bytes) } + } /// ⚠️ Performs raw RSA decryption with no padding or error checking. From 9b0e419a2c0c2deb503b6a36d95be52fad600c23 Mon Sep 17 00:00:00 2001 From: rkm0959 Date: Thu, 19 Jun 2025 02:51:38 +0000 Subject: [PATCH 15/27] remove optional --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5cf8be2..7ac8115 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur ] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "min/better-rsa", optional = true } +sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "min/better-rsa" } # sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } [dev-dependencies] From da59df60bf52cad8491f18025e06935a1ca34a9c Mon Sep 17 00:00:00 2001 From: rkm0959 Date: Thu, 19 Jun 2025 03:15:53 +0000 Subject: [PATCH 16/27] update sp1-wip --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14c2f97..a7bd69c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,7 +937,7 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "sp1-lib" version = "5.0.0" -source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/better-rsa#1f5056d7a9cc19e1bcef850f9fbdbd22241382ea" +source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/better-rsa#7524e864a93de5609ebabd12021f76eb26995b6a" dependencies = [ "bincode", "elliptic-curve", @@ -948,7 +948,7 @@ dependencies = [ [[package]] name = "sp1-primitives" version = "5.0.0" -source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/better-rsa#1f5056d7a9cc19e1bcef850f9fbdbd22241382ea" +source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/better-rsa#7524e864a93de5609ebabd12021f76eb26995b6a" dependencies = [ "bincode", "blake3", From 4a2d3e70339584a649aee1e912cf914bf0426161 Mon Sep 17 00:00:00 2001 From: rkm0959 Date: Thu, 19 Jun 2025 18:07:09 +0000 Subject: [PATCH 17/27] checkpoint with multiple key sizes --- src/algorithms/rsa.rs | 242 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 233 insertions(+), 9 deletions(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index e7e9fbe..ab2b0a9 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -39,6 +39,22 @@ pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { let result = zkvm::custom_modpow_2048(&m_chunks, &e_chunks, &n_chunks); return Ok(result); }, + 384 => { + use zkvm::*; + let m_chunks = zkvm::from_biguint_to_chunks::<12>(m); + let e_chunks = zkvm::from_biguint_to_chunks::<12>(key.e()); + let n_chunks = zkvm::from_biguint_to_chunks::<12>(key.n()); + let result = zkvm::custom_modpow_3072(&m_chunks, &e_chunks, &n_chunks); + return Ok(result); + }, + 512 => { + use zkvm::*; + let m_chunks = zkvm::from_biguint_to_chunks::<16>(m); + let e_chunks = zkvm::from_biguint_to_chunks::<16>(key.e()); + let n_chunks = zkvm::from_biguint_to_chunks::<16>(key.n()); + let result = zkvm::custom_modpow_4096(&m_chunks, &e_chunks, &n_chunks); + return Ok(result); + }, _ => { // Fall through to standard modpow for unsupported sizes } @@ -106,6 +122,105 @@ mod zkvm { result_chunks } + /// Modular multiplication for 3072-bit keys + fn mul_mod_3072(a_chunks: &[[u32; 8]; 12], b_chunks: &[[u32; 8]; 12], modulus_chunks: &[[u32; 8]; 12]) -> [[u32; 8]; 12] { + let prod_chunks = mul_generic_chunks::<12, 24>(a_chunks, b_chunks); + + // Convert to bytes for SP1 I/O using direct transmute + let prod_bytes: [u8; 768] = unsafe { + std::mem::transmute::<[[u32; 8]; 24], [u8; 768]>(prod_chunks) + }; + let modulus_bytes: [u8; 384] = unsafe { + std::mem::transmute::<[[u32; 8]; 12], [u8; 384]>(*modulus_chunks) + }; + + // Call the hook to perform the modmul operation in the executor + sp1_lib::io::write( + sp1_lib::io::FD_RSA_MUL_MOD, + &prod_bytes.into_iter().chain(modulus_bytes.into_iter()).collect::>(), + ); + + let result_bytes: [u8; 384] = sp1_lib::io::read_vec().try_into().unwrap(); + let quotient_bytes: [u8; 384] = sp1_lib::io::read_vec().try_into().unwrap(); + + // Convert back to chunks + let result_chunks: [[u32; 8]; 12] = unsafe { + std::mem::transmute::<[u8; 384], [[u32; 8]; 12]>(result_bytes) + }; + let quotient_chunks: [[u32; 8]; 12] = unsafe { + std::mem::transmute::<[u8; 384], [[u32; 8]; 12]>(quotient_bytes) + }; + + // Verify: prod == quotient * modulus + result + let quotient_mul_chunks = mul_generic_chunks::<12, 24>("ient_chunks, modulus_chunks); + + let mut verification_prod = quotient_mul_chunks; + + add_generic_chunks(&mut verification_prod, &result_chunks); + + // Check prod == verification_prod + for i in 0..24 { + for j in 0..8 { + assert_eq!(prod_chunks[i][j], verification_prod[i][j]); + } + } + + // Check result < modulus + assert_less_than::<12>(&result_chunks, modulus_chunks); + + result_chunks + } + + /// Modular multiplication for 4096-bit keys + fn mul_mod_4096(a_chunks: &[[u32; 8]; 16], b_chunks: &[[u32; 8]; 16], modulus_chunks: &[[u32; 8]; 16]) -> [[u32; 8]; 16] { + let prod_chunks = mul_generic_chunks::<16, 32>(a_chunks, b_chunks); + + // Convert to bytes for SP1 I/O using direct transmute + let prod_bytes: [u8; 1024] = unsafe { + std::mem::transmute::<[[u32; 8]; 32], [u8; 1024]>(prod_chunks) + }; + let modulus_bytes: [u8; 512] = unsafe { + std::mem::transmute::<[[u32; 8]; 16], [u8; 512]>(*modulus_chunks) + }; + + // Call the hook to perform the modmul operation in the executor + sp1_lib::io::write( + sp1_lib::io::FD_RSA_MUL_MOD, + &prod_bytes.into_iter().chain(modulus_bytes.into_iter()).collect::>(), + ); + + let result_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); + let quotient_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); + + // Convert back to chunks + let result_chunks: [[u32; 8]; 16] = unsafe { + std::mem::transmute::<[u8; 512], [[u32; 8]; 16]>(result_bytes) + }; + let quotient_chunks: [[u32; 8]; 16] = unsafe { + std::mem::transmute::<[u8; 512], [[u32; 8]; 16]>(quotient_bytes) + }; + + // Verify: prod == quotient * modulus + result + let quotient_mul_chunks = mul_generic_chunks::<16, 32>("ient_chunks, modulus_chunks); + + let mut verification_prod = quotient_mul_chunks; + + add_generic_chunks(&mut verification_prod, &result_chunks); + + // Check prod == verification_prod + for i in 0..32 { + for j in 0..8 { + assert_eq!(prod_chunks[i][j], verification_prod[i][j]); + } + } + + // Check result < modulus + assert_less_than::<16>(&result_chunks, modulus_chunks); + + result_chunks + } + + /// Modular exponentiation for 2048-bit keys pub(super) fn custom_modpow_2048(base_chunks: &[[u32; 8]; 8], exp_chunks: &[[u32; 8]; 8], modulus_chunks: &[[u32; 8]; 8]) -> BigUint { // Convert chunks to U2048 for easier manipulation @@ -149,6 +264,94 @@ mod zkvm { BigUint::from_bytes_le(&result_u2048.to_le_bytes()) } + + /// Modular exponentiation for 3072-bit keys + pub(super) fn custom_modpow_3072(base_chunks: &[[u32; 8]; 12], exp_chunks: &[[u32; 8]; 12], modulus_chunks: &[[u32; 8]; 12]) -> BigUint { + // Convert chunks to U3072 for easier manipulation + let exp_bytes = chunks_to_bytes::<12>(exp_chunks); + let exp_u3072 = U3072::from_le_slice(&exp_bytes); + + assert!(!chunks_is_zero::<12>(modulus_chunks)); + + let result_chunks = if exp_u3072 == U3072::from_u64(65537u64) { + // Optimized path for e = 65537 + // First reduce base mod modulus using mul_mod_3072(base, 1, modulus) + let one_chunks = chunks_one(); + let mut result_chunks = mul_mod_3072(base_chunks, &one_chunks, modulus_chunks); + let base_reduced = result_chunks; + + // Square 16 times + for _ in 0..16 { + result_chunks = mul_mod_3072(&result_chunks, &result_chunks, modulus_chunks); + } + + mul_mod_3072(&result_chunks, &base_reduced, modulus_chunks) + } else { + // Square-and-multiply + let one_chunks = chunks_one(); + let mut result_chunks = one_chunks; + let mut base_chunks = mul_mod_3072(base_chunks, &one_chunks, modulus_chunks); + let mut exp = exp_u3072; + + while exp > U3072::ZERO { + if exp.is_odd().into() { + result_chunks = mul_mod_3072(&result_chunks, &base_chunks, modulus_chunks); + } + exp = exp.shr(1); + base_chunks = mul_mod_3072(&base_chunks, &base_chunks, modulus_chunks); + } + + result_chunks + }; + + let result_u3072 = chunks_to_u3072(&result_chunks); + + BigUint::from_bytes_le(&result_u3072.to_le_bytes()) + } + + /// Modular exponentiation for 4096-bit keys + pub(super) fn custom_modpow_4096(base_chunks: &[[u32; 8]; 16], exp_chunks: &[[u32; 8]; 16], modulus_chunks: &[[u32; 8]; 16]) -> BigUint { + // Convert chunks to U4096 for easier manipulation + let exp_bytes = chunks_to_bytes::<16>(exp_chunks); + let exp_u4096 = U4096::from_le_slice(&exp_bytes); + + assert!(!chunks_is_zero::<16>(modulus_chunks)); + + let result_chunks = if exp_u4096 == U4096::from_u64(65537u64) { + // Optimized path for e = 65537 + // First reduce base mod modulus using mul_mod_4096(base, 1, modulus) + let one_chunks = chunks_one(); + let mut result_chunks = mul_mod_4096(base_chunks, &one_chunks, modulus_chunks); + let base_reduced = result_chunks; + + // Square 16 times + for _ in 0..16 { + result_chunks = mul_mod_4096(&result_chunks, &result_chunks, modulus_chunks); + } + + mul_mod_4096(&result_chunks, &base_reduced, modulus_chunks) + } else { + // Square-and-multiply + let one_chunks = chunks_one(); + let mut result_chunks = one_chunks; + let mut base_chunks = mul_mod_4096(base_chunks, &one_chunks, modulus_chunks); + let mut exp = exp_u4096; + + while exp > U4096::ZERO { + if exp.is_odd().into() { + result_chunks = mul_mod_4096(&result_chunks, &base_chunks, modulus_chunks); + } + exp = exp.shr(1); + base_chunks = mul_mod_4096(&base_chunks, &base_chunks, modulus_chunks); + } + + result_chunks + }; + + let result_u4096 = chunks_to_u4096(&result_chunks); + + BigUint::from_bytes_le(&result_u4096.to_le_bytes()) + } /// Generic multiplication using schoolbook algorithm with 256-bit chunks /// Returns a vector of 256-bit chunks representing the full product @@ -262,17 +465,16 @@ mod zkvm { /// Generic helper to convert bytes to chunks fn bytes_to_chunks(bytes: &[u8]) -> [[u32; 8]; N] { let mut chunks = [[0u32; 8]; N]; + assert!(bytes.len() == 32 * N, "incorrect length"); for (i, chunk) in chunks.iter_mut().enumerate() { for (j, word) in chunk.iter_mut().enumerate() { let byte_idx = (i * 8 + j) * 4; - if byte_idx + 4 <= bytes.len() { - *word = u32::from_le_bytes([ - bytes[byte_idx], - bytes[byte_idx + 1], - bytes[byte_idx + 2], - bytes[byte_idx + 3], - ]); - } + *word = u32::from_le_bytes([ + bytes[byte_idx], + bytes[byte_idx + 1], + bytes[byte_idx + 2], + bytes[byte_idx + 3], + ]); } } chunks @@ -295,6 +497,28 @@ mod zkvm { fn u2048_to_chunks(value: &U2048) -> [[u32; 8]; 8] { bytes_to_chunks::<8>(&value.to_le_bytes()) } + + /// Convert chunks to U3072 + fn chunks_to_u3072(chunks: &[[u32; 8]; 12]) -> U3072 { + let bytes = chunks_to_bytes::<12>(chunks); + U3072::from_le_slice(&bytes) + } + + /// Convert U3072 to chunks + fn u3072_to_chunks(value: &U3072) -> [[u32; 8]; 12] { + bytes_to_chunks::<12>(&value.to_le_bytes()) + } + + /// Convert chunks to U4096 + fn chunks_to_u4096(chunks: &[[u32; 8]; 16]) -> U4096 { + let bytes = chunks_to_bytes::<16>(chunks); + U4096::from_le_slice(&bytes) + } + + /// Convert U4096 to chunks + fn u4096_to_chunks(value: &U4096) -> [[u32; 8]; 16] { + bytes_to_chunks::<16>(&value.to_le_bytes()) + } /// Check if chunk array is zero fn chunks_is_zero(chunks: &[[u32; 8]; N]) -> bool { @@ -321,7 +545,7 @@ mod zkvm { let value_bytes = value.to_bytes_le(); for (i, &byte) in value_bytes.iter().enumerate() { if i >= padded_bytes.len() { - break; + panic!("value larger than allowed size"); } padded_bytes[i] = byte; } From 81a177883d52a387aa793acb286f3d6e20913863 Mon Sep 17 00:00:00 2001 From: rkm0959 Date: Thu, 19 Jun 2025 18:41:45 +0000 Subject: [PATCH 18/27] macro for multiple key sizes --- src/algorithms/rsa.rs | 410 +++++++++++------------------------------- 1 file changed, 105 insertions(+), 305 deletions(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index ab2b0a9..98878cb 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -65,7 +65,7 @@ pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { /// # ☢️️ WARNING: HAZARDOUS API ☢️ /// -/// All inputs are ASSUMED to be on the range of [0, 2^2048).] +/// All inputs are ASSUMED to be on the range of [0, 2^4096).] /// /// Attempting to use this function with values outside of this range will result in truncation, /// and may have unintended side effects! @@ -73,285 +73,118 @@ pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { mod zkvm { use super::*; - /// Modular multiplication for 2048-bit keys - fn mul_mod_2048(a_chunks: &[[u32; 8]; 8], b_chunks: &[[u32; 8]; 8], modulus_chunks: &[[u32; 8]; 8]) -> [[u32; 8]; 8] { - let prod_chunks = mul_generic_chunks::<8, 16>(a_chunks, b_chunks); - - // Convert to bytes for SP1 I/O using direct transmute - let prod_bytes: [u8; 512] = unsafe { - std::mem::transmute::<[[u32; 8]; 16], [u8; 512]>(prod_chunks) - }; - let modulus_bytes: [u8; 256] = unsafe { - std::mem::transmute::<[[u32; 8]; 8], [u8; 256]>(*modulus_chunks) - }; - - // Call the hook to perform the modmul operation in the executor - sp1_lib::io::write( - sp1_lib::io::FD_RSA_MUL_MOD, - &prod_bytes.into_iter().chain(modulus_bytes.into_iter()).collect::>(), - ); - - let result_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); - let quotient_bytes: [u8; 256] = sp1_lib::io::read_vec().try_into().unwrap(); - - // Convert back to chunks - let result_chunks: [[u32; 8]; 8] = unsafe { - std::mem::transmute::<[u8; 256], [[u32; 8]; 8]>(result_bytes) - }; - let quotient_chunks: [[u32; 8]; 8] = unsafe { - std::mem::transmute::<[u8; 256], [[u32; 8]; 8]>(quotient_bytes) - }; - - // Verify: prod == quotient * modulus + result - let quotient_mul_chunks = mul_generic_chunks::<8, 16>("ient_chunks, modulus_chunks); - - let mut verification_prod = quotient_mul_chunks; - - add_generic_chunks(&mut verification_prod, &result_chunks); - - // Check prod == verification_prod - for i in 0..16 { - for j in 0..8 { - assert_eq!(prod_chunks[i][j], verification_prod[i][j]); - } - } - - // Check result < modulus - assert_less_than::<8>(&result_chunks, modulus_chunks); - - result_chunks - } - - /// Modular multiplication for 3072-bit keys - fn mul_mod_3072(a_chunks: &[[u32; 8]; 12], b_chunks: &[[u32; 8]; 12], modulus_chunks: &[[u32; 8]; 12]) -> [[u32; 8]; 12] { - let prod_chunks = mul_generic_chunks::<12, 24>(a_chunks, b_chunks); - - // Convert to bytes for SP1 I/O using direct transmute - let prod_bytes: [u8; 768] = unsafe { - std::mem::transmute::<[[u32; 8]; 24], [u8; 768]>(prod_chunks) - }; - let modulus_bytes: [u8; 384] = unsafe { - std::mem::transmute::<[[u32; 8]; 12], [u8; 384]>(*modulus_chunks) - }; - - // Call the hook to perform the modmul operation in the executor - sp1_lib::io::write( - sp1_lib::io::FD_RSA_MUL_MOD, - &prod_bytes.into_iter().chain(modulus_bytes.into_iter()).collect::>(), - ); - - let result_bytes: [u8; 384] = sp1_lib::io::read_vec().try_into().unwrap(); - let quotient_bytes: [u8; 384] = sp1_lib::io::read_vec().try_into().unwrap(); - - // Convert back to chunks - let result_chunks: [[u32; 8]; 12] = unsafe { - std::mem::transmute::<[u8; 384], [[u32; 8]; 12]>(result_bytes) - }; - let quotient_chunks: [[u32; 8]; 12] = unsafe { - std::mem::transmute::<[u8; 384], [[u32; 8]; 12]>(quotient_bytes) - }; - - // Verify: prod == quotient * modulus + result - let quotient_mul_chunks = mul_generic_chunks::<12, 24>("ient_chunks, modulus_chunks); - - let mut verification_prod = quotient_mul_chunks; - - add_generic_chunks(&mut verification_prod, &result_chunks); - - // Check prod == verification_prod - for i in 0..24 { - for j in 0..8 { - assert_eq!(prod_chunks[i][j], verification_prod[i][j]); - } - } - - // Check result < modulus - assert_less_than::<12>(&result_chunks, modulus_chunks); - - result_chunks - } - - /// Modular multiplication for 4096-bit keys - fn mul_mod_4096(a_chunks: &[[u32; 8]; 16], b_chunks: &[[u32; 8]; 16], modulus_chunks: &[[u32; 8]; 16]) -> [[u32; 8]; 16] { - let prod_chunks = mul_generic_chunks::<16, 32>(a_chunks, b_chunks); - - // Convert to bytes for SP1 I/O using direct transmute - let prod_bytes: [u8; 1024] = unsafe { - std::mem::transmute::<[[u32; 8]; 32], [u8; 1024]>(prod_chunks) - }; - let modulus_bytes: [u8; 512] = unsafe { - std::mem::transmute::<[[u32; 8]; 16], [u8; 512]>(*modulus_chunks) - }; - - // Call the hook to perform the modmul operation in the executor - sp1_lib::io::write( - sp1_lib::io::FD_RSA_MUL_MOD, - &prod_bytes.into_iter().chain(modulus_bytes.into_iter()).collect::>(), - ); - - let result_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); - let quotient_bytes: [u8; 512] = sp1_lib::io::read_vec().try_into().unwrap(); - - // Convert back to chunks - let result_chunks: [[u32; 8]; 16] = unsafe { - std::mem::transmute::<[u8; 512], [[u32; 8]; 16]>(result_bytes) - }; - let quotient_chunks: [[u32; 8]; 16] = unsafe { - std::mem::transmute::<[u8; 512], [[u32; 8]; 16]>(quotient_bytes) - }; - - // Verify: prod == quotient * modulus + result - let quotient_mul_chunks = mul_generic_chunks::<16, 32>("ient_chunks, modulus_chunks); - - let mut verification_prod = quotient_mul_chunks; - - add_generic_chunks(&mut verification_prod, &result_chunks); - - // Check prod == verification_prod - for i in 0..32 { - for j in 0..8 { - assert_eq!(prod_chunks[i][j], verification_prod[i][j]); - } - } - - // Check result < modulus - assert_less_than::<16>(&result_chunks, modulus_chunks); - - result_chunks - } + // Macro to generate mul_mod functions for different bit sizes + macro_rules! impl_mul_mod { + ($name:ident, $chunks:expr, $bytes:expr, $fd_type:expr) => { + fn $name( + a_chunks: &[[u32; 8]; $chunks], + b_chunks: &[[u32; 8]; $chunks], + modulus_chunks: &[[u32; 8]; $chunks] + ) -> [[u32; 8]; $chunks] { + let prod_chunks = mul_generic_chunks::<$chunks, {$chunks * 2}>(a_chunks, b_chunks); + + // Convert to bytes for SP1 I/O using direct transmute + let prod_bytes: [u8; $bytes * 2] = unsafe { + std::mem::transmute::<[[u32; 8]; $chunks * 2], [u8; $bytes * 2]>(prod_chunks) + }; + let modulus_bytes: [u8; $bytes] = unsafe { + std::mem::transmute::<[[u32; 8]; $chunks], [u8; $bytes]>(*modulus_chunks) + }; + + // Call the hook to perform the modmul operation in the executor + sp1_lib::io::write( + $fd_type, + &prod_bytes.into_iter().chain(modulus_bytes.into_iter()).collect::>(), + ); + let result_bytes: [u8; $bytes] = sp1_lib::io::read_vec().try_into().unwrap(); + let quotient_bytes: [u8; $bytes] = sp1_lib::io::read_vec().try_into().unwrap(); - /// Modular exponentiation for 2048-bit keys - pub(super) fn custom_modpow_2048(base_chunks: &[[u32; 8]; 8], exp_chunks: &[[u32; 8]; 8], modulus_chunks: &[[u32; 8]; 8]) -> BigUint { - // Convert chunks to U2048 for easier manipulation - let exp_bytes = chunks_to_bytes::<8>(exp_chunks); - let exp_u2048 = U2048::from_le_slice(&exp_bytes); - - assert!(!chunks_is_zero::<8>(modulus_chunks)); - - let result_chunks = if exp_u2048 == U2048::from_u64(65537u64) { - // Optimized path for e = 65537 - // First reduce base mod modulus using mul_mod_2048(base, 1, modulus) - let one_chunks = chunks_one(); - let mut result_chunks = mul_mod_2048(base_chunks, &one_chunks, modulus_chunks); - let base_reduced = result_chunks; - - // Square 16 times - for _ in 0..16 { - result_chunks = mul_mod_2048(&result_chunks, &result_chunks, modulus_chunks); - } - - mul_mod_2048(&result_chunks, &base_reduced, modulus_chunks) - } else { - // Square-and-multiply - let one_chunks = chunks_one(); - let mut result_chunks = one_chunks; - let mut base_chunks = mul_mod_2048(base_chunks, &one_chunks, modulus_chunks); - let mut exp = exp_u2048; - - while exp > U2048::ZERO { - if exp.is_odd().into() { - result_chunks = mul_mod_2048(&result_chunks, &base_chunks, modulus_chunks); + // Convert back to chunks + let result_chunks: [[u32; 8]; $chunks] = unsafe { + std::mem::transmute::<[u8; $bytes], [[u32; 8]; $chunks]>(result_bytes) + }; + let quotient_chunks: [[u32; 8]; $chunks] = unsafe { + std::mem::transmute::<[u8; $bytes], [[u32; 8]; $chunks]>(quotient_bytes) + }; + + // Verify: prod == quotient * modulus + result and 0 <= result < modulus. + let quotient_mul_chunks = mul_generic_chunks::<$chunks, {$chunks * 2}>("ient_chunks, modulus_chunks); + + let mut verification_prod = quotient_mul_chunks; + add_generic_chunks(&mut verification_prod, &result_chunks); + + for i in 0..($chunks * 2) { + for j in 0..8 { + assert_eq!(prod_chunks[i][j], verification_prod[i][j], "equality check failed"); + } } - exp = exp.shr(1); - base_chunks = mul_mod_2048(&base_chunks, &base_chunks, modulus_chunks); - } - - result_chunks - }; - - let result_u2048 = chunks_to_u2048(&result_chunks); - - BigUint::from_bytes_le(&result_u2048.to_le_bytes()) - } - /// Modular exponentiation for 3072-bit keys - pub(super) fn custom_modpow_3072(base_chunks: &[[u32; 8]; 12], exp_chunks: &[[u32; 8]; 12], modulus_chunks: &[[u32; 8]; 12]) -> BigUint { - // Convert chunks to U3072 for easier manipulation - let exp_bytes = chunks_to_bytes::<12>(exp_chunks); - let exp_u3072 = U3072::from_le_slice(&exp_bytes); - - assert!(!chunks_is_zero::<12>(modulus_chunks)); - - let result_chunks = if exp_u3072 == U3072::from_u64(65537u64) { - // Optimized path for e = 65537 - // First reduce base mod modulus using mul_mod_3072(base, 1, modulus) - let one_chunks = chunks_one(); - let mut result_chunks = mul_mod_3072(base_chunks, &one_chunks, modulus_chunks); - let base_reduced = result_chunks; - - // Square 16 times - for _ in 0..16 { - result_chunks = mul_mod_3072(&result_chunks, &result_chunks, modulus_chunks); - } - - mul_mod_3072(&result_chunks, &base_reduced, modulus_chunks) - } else { - // Square-and-multiply - let one_chunks = chunks_one(); - let mut result_chunks = one_chunks; - let mut base_chunks = mul_mod_3072(base_chunks, &one_chunks, modulus_chunks); - let mut exp = exp_u3072; - - while exp > U3072::ZERO { - if exp.is_odd().into() { - result_chunks = mul_mod_3072(&result_chunks, &base_chunks, modulus_chunks); - } - exp = exp.shr(1); - base_chunks = mul_mod_3072(&base_chunks, &base_chunks, modulus_chunks); + assert_less_than::<$chunks>(&result_chunks, modulus_chunks); + + result_chunks } - - result_chunks }; - - let result_u3072 = chunks_to_u3072(&result_chunks); - - BigUint::from_bytes_le(&result_u3072.to_le_bytes()) } - /// Modular exponentiation for 4096-bit keys - pub(super) fn custom_modpow_4096(base_chunks: &[[u32; 8]; 16], exp_chunks: &[[u32; 8]; 16], modulus_chunks: &[[u32; 8]; 16]) -> BigUint { - // Convert chunks to U4096 for easier manipulation - let exp_bytes = chunks_to_bytes::<16>(exp_chunks); - let exp_u4096 = U4096::from_le_slice(&exp_bytes); - - assert!(!chunks_is_zero::<16>(modulus_chunks)); - - let result_chunks = if exp_u4096 == U4096::from_u64(65537u64) { - // Optimized path for e = 65537 - // First reduce base mod modulus using mul_mod_4096(base, 1, modulus) - let one_chunks = chunks_one(); - let mut result_chunks = mul_mod_4096(base_chunks, &one_chunks, modulus_chunks); - let base_reduced = result_chunks; - - // Square 16 times - for _ in 0..16 { - result_chunks = mul_mod_4096(&result_chunks, &result_chunks, modulus_chunks); - } - - mul_mod_4096(&result_chunks, &base_reduced, modulus_chunks) - } else { - // Square-and-multiply - let one_chunks = chunks_one(); - let mut result_chunks = one_chunks; - let mut base_chunks = mul_mod_4096(base_chunks, &one_chunks, modulus_chunks); - let mut exp = exp_u4096; - - while exp > U4096::ZERO { - if exp.is_odd().into() { - result_chunks = mul_mod_4096(&result_chunks, &base_chunks, modulus_chunks); - } - exp = exp.shr(1); - base_chunks = mul_mod_4096(&base_chunks, &base_chunks, modulus_chunks); + // Generate the three mul_mod functions + impl_mul_mod!(mul_mod_2048, 8, 256, sp1_lib::io::FD_RSA_MUL_MOD); + impl_mul_mod!(mul_mod_3072, 12, 384, sp1_lib::io::FD_RSA_MUL_MOD); + impl_mul_mod!(mul_mod_4096, 16, 512, sp1_lib::io::FD_RSA_MUL_MOD); + + // Macro to generate modpow functions + macro_rules! impl_modpow { + ($name:ident, $chunks:expr, $bigint_type:ty, $mul_mod_fn:ident) => { + pub(super) fn $name( + base_chunks: &[[u32; 8]; $chunks], + exp_chunks: &[[u32; 8]; $chunks], + modulus_chunks: &[[u32; 8]; $chunks] + ) -> BigUint { + // Convert chunks to crypto_bigint type for easier manipulation + let exp_bytes = chunks_to_bytes::<$chunks>(exp_chunks); + let exp_bigint = <$bigint_type>::from_le_slice(&exp_bytes); + + assert!(!chunks_is_zero::<$chunks>(modulus_chunks), "modulo cannot be zero"); + + let result_chunks = if exp_bigint == <$bigint_type>::from_u64(65537u64) { + // Optimized path for e = 65537 + let one_chunks = chunks_one::<$chunks>(); + let mut result_chunks = $mul_mod_fn(base_chunks, &one_chunks, modulus_chunks); + let base_reduced = result_chunks; + + // Square 16 times + for _ in 0..16 { + result_chunks = $mul_mod_fn(&result_chunks, &result_chunks, modulus_chunks); + } + + $mul_mod_fn(&result_chunks, &base_reduced, modulus_chunks) + } else { + // Square-and-multiply + let one_chunks = chunks_one::<$chunks>(); + let mut result_chunks = one_chunks; + let mut base_chunks = $mul_mod_fn(base_chunks, &one_chunks, modulus_chunks); + let mut exp = exp_bigint; + + while exp > <$bigint_type>::ZERO { + if exp.is_odd().into() { + result_chunks = $mul_mod_fn(&result_chunks, &base_chunks, modulus_chunks); + } + exp = exp.shr(1); + base_chunks = $mul_mod_fn(&base_chunks, &base_chunks, modulus_chunks); + } + + result_chunks + }; + + BigUint::from_bytes_le(&chunks_to_bytes::<$chunks>(&result_chunks)) } - - result_chunks }; - - let result_u4096 = chunks_to_u4096(&result_chunks); - - BigUint::from_bytes_le(&result_u4096.to_le_bytes()) } + + // Generate the three modpow functions + impl_modpow!(custom_modpow_2048, 8, U2048, mul_mod_2048); + impl_modpow!(custom_modpow_3072, 12, U3072, mul_mod_3072); + impl_modpow!(custom_modpow_4096, 16, U4096, mul_mod_4096); /// Generic multiplication using schoolbook algorithm with 256-bit chunks /// Returns a vector of 256-bit chunks representing the full product @@ -456,10 +289,10 @@ mod zkvm { if result_chunk[i][j] < modulus_chunk[i][j] { return; } - assert!(result_chunk[i][j] == modulus_chunk[i][j]); + assert!(result_chunk[i][j] == modulus_chunk[i][j], "result < modulus check failed"); } } - assert!(false); + assert!(false, "result < modulus check failed"); } /// Generic helper to convert bytes to chunks @@ -487,39 +320,6 @@ mod zkvm { .collect() } - /// Convert chunks to U2048 - fn chunks_to_u2048(chunks: &[[u32; 8]; 8]) -> U2048 { - let bytes = chunks_to_bytes::<8>(chunks); - U2048::from_le_slice(&bytes) - } - - /// Convert U2048 to chunks - fn u2048_to_chunks(value: &U2048) -> [[u32; 8]; 8] { - bytes_to_chunks::<8>(&value.to_le_bytes()) - } - - /// Convert chunks to U3072 - fn chunks_to_u3072(chunks: &[[u32; 8]; 12]) -> U3072 { - let bytes = chunks_to_bytes::<12>(chunks); - U3072::from_le_slice(&bytes) - } - - /// Convert U3072 to chunks - fn u3072_to_chunks(value: &U3072) -> [[u32; 8]; 12] { - bytes_to_chunks::<12>(&value.to_le_bytes()) - } - - /// Convert chunks to U4096 - fn chunks_to_u4096(chunks: &[[u32; 8]; 16]) -> U4096 { - let bytes = chunks_to_bytes::<16>(chunks); - U4096::from_le_slice(&bytes) - } - - /// Convert U4096 to chunks - fn u4096_to_chunks(value: &U4096) -> [[u32; 8]; 16] { - bytes_to_chunks::<16>(&value.to_le_bytes()) - } - /// Check if chunk array is zero fn chunks_is_zero(chunks: &[[u32; 8]; N]) -> bool { for i in 0..N { @@ -541,7 +341,7 @@ mod zkvm { /// Convert BigUint to chunks for arbitrary key sizes pub(super) fn from_biguint_to_chunks(value: &BigUint) -> [[u32; 8]; N] { - let mut padded_bytes = vec![0u8; N * 32]; // N chunks * 32 bytes per chunk + let mut padded_bytes = vec![0u8; N * 32]; let value_bytes = value.to_bytes_le(); for (i, &byte) in value_bytes.iter().enumerate() { if i >= padded_bytes.len() { From fe25388aedf08e3ee747faec419c28aca54b5075 Mon Sep 17 00:00:00 2001 From: rkm0959 Date: Fri, 27 Jun 2025 04:31:55 +0000 Subject: [PATCH 19/27] update sp1-wip --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7bd69c..8575cb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,7 +937,7 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "sp1-lib" version = "5.0.0" -source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/better-rsa#7524e864a93de5609ebabd12021f76eb26995b6a" +source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=multilinear_v6#f57c32f25dcf439996cd6cd80aad7865b32ddc44" dependencies = [ "bincode", "elliptic-curve", @@ -948,7 +948,7 @@ dependencies = [ [[package]] name = "sp1-primitives" version = "5.0.0" -source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/better-rsa#7524e864a93de5609ebabd12021f76eb26995b6a" +source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=multilinear_v6#f57c32f25dcf439996cd6cd80aad7865b32ddc44" dependencies = [ "bincode", "blake3", diff --git a/Cargo.toml b/Cargo.toml index 7ac8115..b7421fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur ] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "min/better-rsa" } +sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "multilinear_v6" } # sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } [dev-dependencies] From 298de4702c66f140047f8ea9cc0e4dc094a8df3d Mon Sep 17 00:00:00 2001 From: rkm0959 Date: Fri, 27 Jun 2025 05:12:05 +0000 Subject: [PATCH 20/27] ok --- Cargo.lock | 2 - Cargo.toml | 4 +- src/algorithms/rsa.rs | 155 +++++++++++++++++++++++------------------- 3 files changed, 87 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8575cb3..dc142f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,7 +937,6 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "sp1-lib" version = "5.0.0" -source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=multilinear_v6#f57c32f25dcf439996cd6cd80aad7865b32ddc44" dependencies = [ "bincode", "elliptic-curve", @@ -948,7 +947,6 @@ dependencies = [ [[package]] name = "sp1-primitives" version = "5.0.0" -source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=multilinear_v6#f57c32f25dcf439996cd6cd80aad7865b32ddc44" dependencies = [ "bincode", "blake3", diff --git a/Cargo.toml b/Cargo.toml index b7421fc..cea6a2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,8 +57,8 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur ] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "multilinear_v6" } -# sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } +# sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "multilinear_v6" } +sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 98878cb..6ac5e2c 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -20,6 +20,19 @@ use sp1_lib::io::hint_slice; use crate::errors::{Error, Result}; use crate::traits::{PrivateKeyParts, PublicKeyParts}; +// Architecture-specific type definitions +#[cfg(target_pointer_width = "32")] +type ChunkWord = u32; +#[cfg(target_pointer_width = "64")] +type ChunkWord = u64; + +#[cfg(target_pointer_width = "32")] +const CHUNK_SIZE: usize = 8; +#[cfg(target_pointer_width = "64")] +const CHUNK_SIZE: usize = 4; + +type Chunk = [ChunkWord; CHUNK_SIZE]; + /// ⚠️ Raw RSA encryption of m with the public key. No padding is performed. /// /// # ☢️️ WARNING: HAZARDOUS API ☢️ @@ -77,18 +90,18 @@ mod zkvm { macro_rules! impl_mul_mod { ($name:ident, $chunks:expr, $bytes:expr, $fd_type:expr) => { fn $name( - a_chunks: &[[u32; 8]; $chunks], - b_chunks: &[[u32; 8]; $chunks], - modulus_chunks: &[[u32; 8]; $chunks] - ) -> [[u32; 8]; $chunks] { + a_chunks: &[Chunk; $chunks], + b_chunks: &[Chunk; $chunks], + modulus_chunks: &[Chunk; $chunks] + ) -> [Chunk; $chunks] { let prod_chunks = mul_generic_chunks::<$chunks, {$chunks * 2}>(a_chunks, b_chunks); // Convert to bytes for SP1 I/O using direct transmute let prod_bytes: [u8; $bytes * 2] = unsafe { - std::mem::transmute::<[[u32; 8]; $chunks * 2], [u8; $bytes * 2]>(prod_chunks) + std::mem::transmute::<[Chunk; $chunks * 2], [u8; $bytes * 2]>(prod_chunks) }; let modulus_bytes: [u8; $bytes] = unsafe { - std::mem::transmute::<[[u32; 8]; $chunks], [u8; $bytes]>(*modulus_chunks) + std::mem::transmute::<[Chunk; $chunks], [u8; $bytes]>(*modulus_chunks) }; // Call the hook to perform the modmul operation in the executor @@ -101,11 +114,11 @@ mod zkvm { let quotient_bytes: [u8; $bytes] = sp1_lib::io::read_vec().try_into().unwrap(); // Convert back to chunks - let result_chunks: [[u32; 8]; $chunks] = unsafe { - std::mem::transmute::<[u8; $bytes], [[u32; 8]; $chunks]>(result_bytes) + let result_chunks: [Chunk; $chunks] = unsafe { + std::mem::transmute::<[u8; $bytes], [Chunk; $chunks]>(result_bytes) }; - let quotient_chunks: [[u32; 8]; $chunks] = unsafe { - std::mem::transmute::<[u8; $bytes], [[u32; 8]; $chunks]>(quotient_bytes) + let quotient_chunks: [Chunk; $chunks] = unsafe { + std::mem::transmute::<[u8; $bytes], [Chunk; $chunks]>(quotient_bytes) }; // Verify: prod == quotient * modulus + result and 0 <= result < modulus. @@ -115,7 +128,7 @@ mod zkvm { add_generic_chunks(&mut verification_prod, &result_chunks); for i in 0..($chunks * 2) { - for j in 0..8 { + for j in 0..CHUNK_SIZE { assert_eq!(prod_chunks[i][j], verification_prod[i][j], "equality check failed"); } } @@ -136,9 +149,9 @@ mod zkvm { macro_rules! impl_modpow { ($name:ident, $chunks:expr, $bigint_type:ty, $mul_mod_fn:ident) => { pub(super) fn $name( - base_chunks: &[[u32; 8]; $chunks], - exp_chunks: &[[u32; 8]; $chunks], - modulus_chunks: &[[u32; 8]; $chunks] + base_chunks: &[Chunk; $chunks], + exp_chunks: &[Chunk; $chunks], + modulus_chunks: &[Chunk; $chunks] ) -> BigUint { // Convert chunks to crypto_bigint type for easier manipulation let exp_bytes = chunks_to_bytes::<$chunks>(exp_chunks); @@ -188,13 +201,13 @@ mod zkvm { /// Generic multiplication using schoolbook algorithm with 256-bit chunks /// Returns a vector of 256-bit chunks representing the full product - fn mul_generic_chunks(a_chunks: &[[u32; 8]; N], b_chunks: &[[u32; 8]; N]) -> [[u32; 8]; N2] { - let mut out = [[0u32; 8]; N2]; + fn mul_generic_chunks(a_chunks: &[Chunk; N], b_chunks: &[Chunk; N]) -> [Chunk; N2] { + let mut out = [chunk_zero(); N2]; - let mut lo = [0u32; 8]; - let mut hi = [0u32; 8]; - let mut tmp_hi = [0u32; 8]; - let zero_carry = [0u32; 8]; + let mut lo = chunk_zero(); + let mut hi = chunk_zero(); + let mut tmp_hi = chunk_zero(); + let zero_carry = chunk_zero(); for i in 0..N { for j in 0..N { @@ -202,31 +215,31 @@ mod zkvm { unsafe { sp1_lib::syscall_uint256_mul_with_carry( - a_chunks[i].as_ptr() as *const [u32; 8], - b_chunks[j].as_ptr() as *const [u32; 8], - zero_carry.as_ptr() as *const [u32; 8], - lo.as_mut_ptr() as *mut [u32; 8], - hi.as_mut_ptr() as *mut [u32; 8], + a_chunks[i].as_ptr() as *const Chunk, + b_chunks[j].as_ptr() as *const Chunk, + zero_carry.as_ptr() as *const Chunk, + lo.as_mut_ptr() as *mut Chunk, + hi.as_mut_ptr() as *mut Chunk, ); } unsafe { sp1_lib::syscall_uint256_add_with_carry( - out[k].as_ptr() as *const [u32; 8], - lo.as_ptr() as *const [u32; 8], - zero_carry.as_ptr() as *const [u32; 8], - out[k].as_mut_ptr() as *mut [u32; 8], - tmp_hi.as_mut_ptr() as *mut [u32; 8], + out[k].as_ptr() as *const Chunk, + lo.as_ptr() as *const Chunk, + zero_carry.as_ptr() as *const Chunk, + out[k].as_mut_ptr() as *mut Chunk, + tmp_hi.as_mut_ptr() as *mut Chunk, ); } unsafe { sp1_lib::syscall_uint256_add_with_carry( - out[k + 1].as_ptr() as *const [u32; 8], - hi.as_ptr() as *const [u32; 8], - tmp_hi.as_ptr() as *const [u32; 8], - out[k + 1].as_mut_ptr() as *mut [u32; 8], - tmp_hi.as_mut_ptr() as *mut [u32; 8], + out[k + 1].as_ptr() as *const Chunk, + hi.as_ptr() as *const Chunk, + tmp_hi.as_ptr() as *const Chunk, + out[k + 1].as_mut_ptr() as *mut Chunk, + tmp_hi.as_mut_ptr() as *mut Chunk, ); } @@ -234,11 +247,11 @@ mod zkvm { while tmp_hi[0] != 0 && idx < N2 { unsafe { sp1_lib::syscall_uint256_add_with_carry( - out[idx].as_ptr() as *const [u32; 8], - zero_carry.as_ptr() as *const [u32; 8], - tmp_hi.as_ptr() as *const [u32; 8], - out[idx].as_mut_ptr() as *mut [u32; 8], - tmp_hi.as_mut_ptr() as *mut [u32; 8], + out[idx].as_ptr() as *const Chunk, + zero_carry.as_ptr() as *const Chunk, + tmp_hi.as_ptr() as *const Chunk, + out[idx].as_mut_ptr() as *mut Chunk, + tmp_hi.as_mut_ptr() as *mut Chunk, ); } idx += 1; @@ -251,18 +264,18 @@ mod zkvm { /// Generic addition of two chunk arrays with different sizes /// Adds smaller array to the lower part of larger array, handling carries - fn add_generic_chunks(larger: &mut [[u32; 8]], smaller: &[[u32; 8]]) { - let mut carry = [0u32; 8]; - let zero_carry = [0u32; 8]; + fn add_generic_chunks(larger: &mut [Chunk], smaller: &[Chunk]) { + let mut carry = chunk_zero(); + let zero_carry = chunk_zero(); for i in 0..smaller.len() { unsafe { sp1_lib::syscall_uint256_add_with_carry( - larger[i].as_ptr() as *const [u32; 8], - smaller[i].as_ptr() as *const [u32; 8], - carry.as_ptr() as *const [u32; 8], - larger[i].as_mut_ptr() as *mut [u32; 8], - carry.as_mut_ptr() as *mut [u32; 8], + larger[i].as_ptr() as *const Chunk, + smaller[i].as_ptr() as *const Chunk, + carry.as_ptr() as *const Chunk, + larger[i].as_mut_ptr() as *mut Chunk, + carry.as_mut_ptr() as *mut Chunk, ); } } @@ -271,11 +284,11 @@ mod zkvm { while idx < larger.len() && carry[0] != 0 { unsafe { sp1_lib::syscall_uint256_add_with_carry( - larger[idx].as_ptr() as *const [u32; 8], - zero_carry.as_ptr() as *const [u32; 8], - carry.as_ptr() as *const [u32; 8], - larger[idx].as_mut_ptr() as *mut [u32; 8], - carry.as_mut_ptr() as *mut [u32; 8], + larger[idx].as_ptr() as *const Chunk, + zero_carry.as_ptr() as *const Chunk, + carry.as_ptr() as *const Chunk, + larger[idx].as_mut_ptr() as *mut Chunk, + carry.as_mut_ptr() as *mut Chunk, ); } idx += 1; @@ -283,9 +296,9 @@ mod zkvm { } /// Assert that the result is less than the modulus - fn assert_less_than(result_chunk: &[[u32; 8]; N], modulus_chunk: &[[u32; 8]; N]) { + fn assert_less_than(result_chunk: &[Chunk; N], modulus_chunk: &[Chunk; N]) { for i in (0..N).rev() { - for j in (0..8).rev() { + for j in (0..CHUNK_SIZE).rev() { if result_chunk[i][j] < modulus_chunk[i][j] { return; } @@ -296,34 +309,31 @@ mod zkvm { } /// Generic helper to convert bytes to chunks - fn bytes_to_chunks(bytes: &[u8]) -> [[u32; 8]; N] { - let mut chunks = [[0u32; 8]; N]; + fn bytes_to_chunks(bytes: &[u8]) -> [Chunk; N] { + let mut chunks = [chunk_zero(); N]; + let word_size = std::mem::size_of::(); assert!(bytes.len() == 32 * N, "incorrect length"); for (i, chunk) in chunks.iter_mut().enumerate() { for (j, word) in chunk.iter_mut().enumerate() { - let byte_idx = (i * 8 + j) * 4; - *word = u32::from_le_bytes([ - bytes[byte_idx], - bytes[byte_idx + 1], - bytes[byte_idx + 2], - bytes[byte_idx + 3], - ]); + let byte_idx = (i * CHUNK_SIZE + j) * word_size; + let word_bytes = &bytes[byte_idx..byte_idx + word_size]; + *word = ChunkWord::from_le_bytes(word_bytes.try_into().unwrap()); } } chunks } /// Generic helper to convert chunks to bytes - fn chunks_to_bytes(chunks: &[[u32; 8]; N]) -> Vec { + fn chunks_to_bytes(chunks: &[Chunk; N]) -> Vec { chunks.iter() .flat_map(|chunk| chunk.iter().flat_map(|&word| word.to_le_bytes())) .collect() } /// Check if chunk array is zero - fn chunks_is_zero(chunks: &[[u32; 8]; N]) -> bool { + fn chunks_is_zero(chunks: &[Chunk; N]) -> bool { for i in 0..N { - for j in 0..8 { + for j in 0..CHUNK_SIZE { if chunks[i][j] != 0 { return false; } @@ -333,14 +343,19 @@ mod zkvm { } /// Get chunk array representing one - fn chunks_one() -> [[u32; 8]; N] { - let mut chunks = [[0u32; 8]; N]; + fn chunks_one() -> [Chunk; N] { + let mut chunks = [chunk_zero(); N]; chunks[0][0] = 1; chunks } + + /// Helper to create zero chunk + fn chunk_zero() -> Chunk { + [ChunkWord::default(); CHUNK_SIZE] + } /// Convert BigUint to chunks for arbitrary key sizes - pub(super) fn from_biguint_to_chunks(value: &BigUint) -> [[u32; 8]; N] { + pub(super) fn from_biguint_to_chunks(value: &BigUint) -> [Chunk; N] { let mut padded_bytes = vec![0u8; N * 32]; let value_bytes = value.to_bytes_le(); for (i, &byte) in value_bytes.iter().enumerate() { From 36fd5d57ccf1627ba01705c26859e61ee20dc8bf Mon Sep 17 00:00:00 2001 From: rkm0959 Date: Fri, 27 Jun 2025 05:17:26 +0000 Subject: [PATCH 21/27] ok --- Cargo.lock | 2 ++ Cargo.toml | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc142f4..32e9ca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,6 +937,7 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "sp1-lib" version = "5.0.0" +source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/merge-v6-to-rv64#bc8885e8f093abed188d82be53a3f4b027ef4dbf" dependencies = [ "bincode", "elliptic-curve", @@ -947,6 +948,7 @@ dependencies = [ [[package]] name = "sp1-primitives" version = "5.0.0" +source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/merge-v6-to-rv64#bc8885e8f093abed188d82be53a3f4b027ef4dbf" dependencies = [ "bincode", "blake3", diff --git a/Cargo.toml b/Cargo.toml index cea6a2b..7260765 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,9 +56,12 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur "derive", ] } -[target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] +# [target.'cfg(all(target_os = "zkvm", target_pointer_width = "32", target_vendor = "succinct"))'.dependencies] # sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "multilinear_v6" } -sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } + +[target.'cfg(all(target_os = "zkvm", target_pointer_width = "64", target_vendor = "succinct"))'.dependencies] +sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "min/merge-v6-to-rv64" } +# sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } From 170c5130e4d5d456ab2da97b8b291fe142ab252f Mon Sep 17 00:00:00 2001 From: Tej Qu Nair Date: Tue, 8 Jul 2025 21:46:06 +0000 Subject: [PATCH 22/27] set sp1-lib to version 5 --- Cargo.lock | 73 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32e9ca3..5a6cdc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -381,15 +381,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "keccak" version = "0.1.4" @@ -492,8 +483,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "p3-baby-bear" -version = "0.1.0" -source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" +version = "0.2.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7521838ecab2ddf4f7bc4ceebad06ec02414729598485c1ada516c39900820e8" dependencies = [ "num-bigint", "p3-field", @@ -506,8 +498,9 @@ dependencies = [ [[package]] name = "p3-dft" -version = "0.1.0" -source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" +version = "0.2.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46414daedd796f1eefcdc1811c0484e4bced5729486b6eaba9521c572c76761a" dependencies = [ "p3-field", "p3-matrix", @@ -518,10 +511,11 @@ dependencies = [ [[package]] name = "p3-field" -version = "0.1.0" -source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" +version = "0.2.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48948a0516b349e9d1cdb95e7236a6ee010c44e68c5cc78b4b92bf1c4022a0d9" dependencies = [ - "itertools 0.12.1", + "itertools", "num-bigint", "num-traits", "p3-util", @@ -531,10 +525,11 @@ dependencies = [ [[package]] name = "p3-matrix" -version = "0.1.0" -source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" +version = "0.2.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4de3f373589477cb735ea58e125898ed20935e03664b4614c7fac258b3c42f" dependencies = [ - "itertools 0.12.1", + "itertools", "p3-field", "p3-maybe-rayon", "p3-util", @@ -545,15 +540,17 @@ dependencies = [ [[package]] name = "p3-maybe-rayon" -version = "0.1.0" -source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" +version = "0.2.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3968ad1160310296eb04f91a5f4edfa38fe1d6b2b8cd6b5c64e6f9b7370979e" [[package]] name = "p3-mds" -version = "0.1.0" -source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" +version = "0.2.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2356b1ed0add6d5dfbf7a338ce534a6fde827374394a52cec16a0840af6e97c9" dependencies = [ - "itertools 0.12.1", + "itertools", "p3-dft", "p3-field", "p3-matrix", @@ -564,8 +561,9 @@ dependencies = [ [[package]] name = "p3-poseidon2" -version = "0.1.0" -source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" +version = "0.2.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da1eec7e1b6900581bedd95e76e1ef4975608dd55be9872c9d257a8a9651c3a" dependencies = [ "gcd", "p3-field", @@ -577,18 +575,20 @@ dependencies = [ [[package]] name = "p3-symmetric" -version = "0.1.0" -source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" +version = "0.2.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb439bea1d822623b41ff4b51e3309e80d13cadf8b86d16ffd5e6efb9fdc360" dependencies = [ - "itertools 0.12.1", + "itertools", "p3-field", "serde", ] [[package]] name = "p3-util" -version = "0.1.0" -source = "git+https://github.com/erabinov/Plonky3/?branch=clone_config#09f54843af7b3c63cba090d20419585c57fae468" +version = "0.2.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c2c2010678b9332b563eaa38364915b585c1a94b5ca61e2c7541c087ddda5c" dependencies = [ "serde", ] @@ -936,8 +936,9 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "sp1-lib" -version = "5.0.0" -source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/merge-v6-to-rv64#bc8885e8f093abed188d82be53a3f4b027ef4dbf" +version = "5.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0fd8bc101e5603ccf2dc1836ea06410f25ce2298755b2dac626add9be2424b4" dependencies = [ "bincode", "elliptic-curve", @@ -947,14 +948,14 @@ dependencies = [ [[package]] name = "sp1-primitives" -version = "5.0.0" -source = "git+https://github.com/succinctlabs/sp1-wip.git?branch=min/merge-v6-to-rv64#bc8885e8f093abed188d82be53a3f4b027ef4dbf" +version = "5.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "699935774a5131c1a8b371108d0666c0c80c43611045fb77fae43f2f242676d5" dependencies = [ "bincode", "blake3", "cfg-if", "hex", - "itertools 0.13.0", "lazy_static", "num-bigint", "p3-baby-bear", diff --git a/Cargo.toml b/Cargo.toml index 7260765..8ba8974 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur # sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "multilinear_v6" } [target.'cfg(all(target_os = "zkvm", target_pointer_width = "64", target_vendor = "succinct"))'.dependencies] -sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "min/merge-v6-to-rv64" } +sp1-lib = "5.0.0" # sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } [dev-dependencies] From c38b89263da6133df656fd6916e7dd3b73a644c0 Mon Sep 17 00:00:00 2001 From: Tej Qu Nair Date: Fri, 11 Jul 2025 23:10:21 +0000 Subject: [PATCH 23/27] set sp1-lib to version 6 --- Cargo.toml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8ba8974..456df20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,12 +56,8 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur "derive", ] } -# [target.'cfg(all(target_os = "zkvm", target_pointer_width = "32", target_vendor = "succinct"))'.dependencies] -# sp1-lib = { git = "https://github.com/succinctlabs/sp1-wip.git", branch = "multilinear_v6" } - -[target.'cfg(all(target_os = "zkvm", target_pointer_width = "64", target_vendor = "succinct"))'.dependencies] -sp1-lib = "5.0.0" -# sp1-lib = { path = "../../sp1-wip/crates/zkvm/lib" } +[target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] +sp1-lib = "6.0.0" [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } From 71a4075fefd21ed92f62ffdd7de4f4b237fc647d Mon Sep 17 00:00:00 2001 From: tamirhemo Date: Tue, 5 May 2026 17:34:55 -0700 Subject: [PATCH 24/27] halt with exit code 3 on invalid hint instead of panicking --- src/algorithms/rsa.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 6ac5e2c..62d4846 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -86,6 +86,13 @@ pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { mod zkvm { use super::*; + /// On invalid prover hints, halt the zkVM with exit code 3 instead of panicking. + /// This prevents a malicious prover from forging a regular `panic` (exit code 1). + #[inline(never)] + pub(super) fn halt_invalid_hint() -> ! { + unsafe { sp1_lib::syscall_halt(3) } + } + // Macro to generate mul_mod functions for different bit sizes macro_rules! impl_mul_mod { ($name:ident, $chunks:expr, $bytes:expr, $fd_type:expr) => { @@ -110,8 +117,14 @@ mod zkvm { &prod_bytes.into_iter().chain(modulus_bytes.into_iter()).collect::>(), ); - let result_bytes: [u8; $bytes] = sp1_lib::io::read_vec().try_into().unwrap(); - let quotient_bytes: [u8; $bytes] = sp1_lib::io::read_vec().try_into().unwrap(); + let result_bytes: [u8; $bytes] = match sp1_lib::io::read_vec().try_into() { + Ok(b) => b, + Err(_) => halt_invalid_hint(), + }; + let quotient_bytes: [u8; $bytes] = match sp1_lib::io::read_vec().try_into() { + Ok(b) => b, + Err(_) => halt_invalid_hint(), + }; // Convert back to chunks let result_chunks: [Chunk; $chunks] = unsafe { @@ -129,7 +142,9 @@ mod zkvm { for i in 0..($chunks * 2) { for j in 0..CHUNK_SIZE { - assert_eq!(prod_chunks[i][j], verification_prod[i][j], "equality check failed"); + if prod_chunks[i][j] != verification_prod[i][j] { + halt_invalid_hint(); + } } } @@ -295,17 +310,20 @@ mod zkvm { } } - /// Assert that the result is less than the modulus + /// Verify that the hinted result is less than the modulus. + /// Halts the zkVM with exit code 3 on failure (invalid hint). fn assert_less_than(result_chunk: &[Chunk; N], modulus_chunk: &[Chunk; N]) { for i in (0..N).rev() { for j in (0..CHUNK_SIZE).rev() { if result_chunk[i][j] < modulus_chunk[i][j] { return; } - assert!(result_chunk[i][j] == modulus_chunk[i][j], "result < modulus check failed"); + if result_chunk[i][j] != modulus_chunk[i][j] { + halt_invalid_hint(); + } } } - assert!(false, "result < modulus check failed"); + halt_invalid_hint(); } /// Generic helper to convert bytes to chunks From 0f836d8a21266766b7804ea1a8dfe7f857a7ead1 Mon Sep 17 00:00:00 2001 From: tamirhemo Date: Wed, 6 May 2026 14:53:16 -0700 Subject: [PATCH 25/27] use sp1_lib::halt_invalid_hint (sp1-lib 6.2.0) --- Cargo.toml | 2 +- src/algorithms/rsa.rs | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 456df20..779edc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ serde = { version = "1.0.184", optional = true, default-features = false, featur ] } [target.'cfg(all(target_os = "zkvm", target_vendor = "succinct"))'.dependencies] -sp1-lib = "6.0.0" +sp1-lib = "6.2.0" [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 62d4846..1147cf4 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -86,13 +86,6 @@ pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { mod zkvm { use super::*; - /// On invalid prover hints, halt the zkVM with exit code 3 instead of panicking. - /// This prevents a malicious prover from forging a regular `panic` (exit code 1). - #[inline(never)] - pub(super) fn halt_invalid_hint() -> ! { - unsafe { sp1_lib::syscall_halt(3) } - } - // Macro to generate mul_mod functions for different bit sizes macro_rules! impl_mul_mod { ($name:ident, $chunks:expr, $bytes:expr, $fd_type:expr) => { @@ -119,11 +112,11 @@ mod zkvm { let result_bytes: [u8; $bytes] = match sp1_lib::io::read_vec().try_into() { Ok(b) => b, - Err(_) => halt_invalid_hint(), + Err(_) => sp1_lib::halt_invalid_hint(), }; let quotient_bytes: [u8; $bytes] = match sp1_lib::io::read_vec().try_into() { Ok(b) => b, - Err(_) => halt_invalid_hint(), + Err(_) => sp1_lib::halt_invalid_hint(), }; // Convert back to chunks @@ -143,7 +136,7 @@ mod zkvm { for i in 0..($chunks * 2) { for j in 0..CHUNK_SIZE { if prod_chunks[i][j] != verification_prod[i][j] { - halt_invalid_hint(); + sp1_lib::halt_invalid_hint(); } } } @@ -319,11 +312,11 @@ mod zkvm { return; } if result_chunk[i][j] != modulus_chunk[i][j] { - halt_invalid_hint(); + sp1_lib::halt_invalid_hint(); } } } - halt_invalid_hint(); + sp1_lib::halt_invalid_hint(); } /// Generic helper to convert bytes to chunks From 28de3ed21f92399f78657f24915ba3ba36a95b3c Mon Sep 17 00:00:00 2001 From: tamirhemo Date: Wed, 6 May 2026 20:35:03 -0700 Subject: [PATCH 26/27] use sp1_lib::invalid_hint! with contextual messages --- src/algorithms/rsa.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 1147cf4..9c8d9ec 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -112,11 +112,17 @@ mod zkvm { let result_bytes: [u8; $bytes] = match sp1_lib::io::read_vec().try_into() { Ok(b) => b, - Err(_) => sp1_lib::halt_invalid_hint(), + Err(_) => sp1_lib::invalid_hint!( + "RSA modmul: result hint is not {} bytes", + $bytes + ), }; let quotient_bytes: [u8; $bytes] = match sp1_lib::io::read_vec().try_into() { Ok(b) => b, - Err(_) => sp1_lib::halt_invalid_hint(), + Err(_) => sp1_lib::invalid_hint!( + "RSA modmul: quotient hint is not {} bytes", + $bytes + ), }; // Convert back to chunks @@ -136,7 +142,9 @@ mod zkvm { for i in 0..($chunks * 2) { for j in 0..CHUNK_SIZE { if prod_chunks[i][j] != verification_prod[i][j] { - sp1_lib::halt_invalid_hint(); + sp1_lib::invalid_hint!( + "RSA modmul: prod != quotient * modulus + result" + ); } } } @@ -312,11 +320,11 @@ mod zkvm { return; } if result_chunk[i][j] != modulus_chunk[i][j] { - sp1_lib::halt_invalid_hint(); + sp1_lib::invalid_hint!("RSA modmul: result >= modulus"); } } } - sp1_lib::halt_invalid_hint(); + sp1_lib::invalid_hint!("RSA modmul: result == modulus"); } /// Generic helper to convert bytes to chunks From 21465e8c64e921a9ee66c171e01fbf9f289acc15 Mon Sep 17 00:00:00 2001 From: sergey-melnychuk Date: Fri, 15 May 2026 12:46:24 +0200 Subject: [PATCH 27/27] fix: use core::mem instead of std::mem in no_std zkvm module The zkvm precompile module compiles under a no_std target (SP1 zkvm), so std::mem is unavailable. Replace with core::mem which is always available regardless of std. --- src/algorithms/rsa.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 9c8d9ec..3fe4a31 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -98,10 +98,10 @@ mod zkvm { // Convert to bytes for SP1 I/O using direct transmute let prod_bytes: [u8; $bytes * 2] = unsafe { - std::mem::transmute::<[Chunk; $chunks * 2], [u8; $bytes * 2]>(prod_chunks) + core::mem::transmute::<[Chunk; $chunks * 2], [u8; $bytes * 2]>(prod_chunks) }; let modulus_bytes: [u8; $bytes] = unsafe { - std::mem::transmute::<[Chunk; $chunks], [u8; $bytes]>(*modulus_chunks) + core::mem::transmute::<[Chunk; $chunks], [u8; $bytes]>(*modulus_chunks) }; // Call the hook to perform the modmul operation in the executor @@ -127,10 +127,10 @@ mod zkvm { // Convert back to chunks let result_chunks: [Chunk; $chunks] = unsafe { - std::mem::transmute::<[u8; $bytes], [Chunk; $chunks]>(result_bytes) + core::mem::transmute::<[u8; $bytes], [Chunk; $chunks]>(result_bytes) }; let quotient_chunks: [Chunk; $chunks] = unsafe { - std::mem::transmute::<[u8; $bytes], [Chunk; $chunks]>(quotient_bytes) + core::mem::transmute::<[u8; $bytes], [Chunk; $chunks]>(quotient_bytes) }; // Verify: prod == quotient * modulus + result and 0 <= result < modulus. @@ -330,7 +330,7 @@ mod zkvm { /// Generic helper to convert bytes to chunks fn bytes_to_chunks(bytes: &[u8]) -> [Chunk; N] { let mut chunks = [chunk_zero(); N]; - let word_size = std::mem::size_of::(); + let word_size = core::mem::size_of::(); assert!(bytes.len() == 32 * N, "incorrect length"); for (i, chunk) in chunks.iter_mut().enumerate() { for (j, word) in chunk.iter_mut().enumerate() {