feat: use crypto-bigint crate
This commit is contained in:
79
Cargo.lock
generated
79
Cargo.lock
generated
@@ -37,7 +37,7 @@ name = "common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -65,6 +65,17 @@ version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96272c2ff28b807e09250b180ad1fb7889a3258f7455759b5c3c58b719467130"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cryptopal_rs"
|
||||
version = "0.1.0"
|
||||
@@ -110,26 +121,6 @@ version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[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",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@@ -167,7 +158,7 @@ name = "p11"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"common",
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -177,7 +168,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"common",
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -195,7 +186,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"common",
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -240,6 +231,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "p20"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p21"
|
||||
@@ -250,14 +244,14 @@ name = "p22"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"common",
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p23"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -281,10 +275,8 @@ name = "p33"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"crypto-bigint",
|
||||
"once_cell",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -369,37 +361,16 @@ version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
@@ -448,6 +419,12 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.103"
|
||||
|
||||
@@ -15,3 +15,4 @@ num-bigint = { version = "0.4", features = ["rand"] }
|
||||
num-traits = "0.2"
|
||||
once_cell = "1.21"
|
||||
rayon = "1.11.0"
|
||||
crypto-bigint = "0.6"
|
||||
|
||||
@@ -4,8 +4,6 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
num-bigint = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
rand = { version = "0.8" } # rand_biguint need 0.8
|
||||
crypto-bigint = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
|
||||
@@ -1,58 +1,80 @@
|
||||
use anyhow::{Ok, Result, anyhow};
|
||||
use num_bigint::{BigUint, RandBigInt};
|
||||
use num_traits::{One, Zero};
|
||||
use crypto_bigint::{
|
||||
NonZero, Odd, Random, U2048, Zero,
|
||||
modular::{MontyForm, MontyParams},
|
||||
rand_core::OsRng,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use rand::thread_rng;
|
||||
|
||||
static NIST_P: Lazy<BigUint> = Lazy::new(|| {
|
||||
BigUint::parse_bytes(b"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff",16).unwrap()
|
||||
static NIST_P: Lazy<U2048> = Lazy::new(|| {
|
||||
U2048::from_be_hex(
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff",
|
||||
)
|
||||
});
|
||||
static NIST_G: Lazy<U2048> = Lazy::new(|| U2048::from(2u8));
|
||||
|
||||
static NIST_G: Lazy<BigUint> = Lazy::new(|| BigUint::from(2u8));
|
||||
|
||||
fn mod_exp(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> Result<BigUint> {
|
||||
if modulus.is_zero() {
|
||||
fn mod_exp(base: &U2048, exp: &U2048, modulus: &U2048) -> Result<U2048> {
|
||||
let modulus = if NonZero::new(*modulus).is_some().into() {
|
||||
NonZero::new(*modulus).unwrap()
|
||||
} else {
|
||||
return Err(anyhow!("modulus should greater than 0"));
|
||||
}
|
||||
let mut base = base % modulus;
|
||||
if exp.is_zero() {
|
||||
return Ok(BigUint::one());
|
||||
}
|
||||
if base.is_zero() {
|
||||
return Ok(base);
|
||||
}
|
||||
let mut result = BigUint::one();
|
||||
let mut exp = exp.clone();
|
||||
while !exp.is_zero() {
|
||||
if exp.bit(0) {
|
||||
result = (&result * &base) % modulus;
|
||||
}
|
||||
base = (&base * &base) % modulus;
|
||||
exp >>= 1;
|
||||
};
|
||||
|
||||
if exp.is_zero().into() {
|
||||
return Ok(U2048::ONE);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
if base.is_zero().into() {
|
||||
return Ok(*base);
|
||||
}
|
||||
|
||||
// let mut result = U2048::ONE;
|
||||
// let mut exp = *exp;
|
||||
//
|
||||
// let mut base = *base; // 在循环前解引用
|
||||
// while (!exp.is_zero()).into() {
|
||||
// if exp.bit(0).into() {
|
||||
// result = result.mul_mod(&base, &modulus);
|
||||
// }
|
||||
// base = base.mul_mod(&base, &modulus);
|
||||
// exp >>= 1;
|
||||
// }
|
||||
// Ok(result)
|
||||
|
||||
let modulus_odd = if Odd::new(*modulus).is_some().into() {
|
||||
Odd::new(*modulus).unwrap()
|
||||
} else {
|
||||
return Err(anyhow!("modulus should greater than 0"));
|
||||
};
|
||||
let params = MontyParams::new(modulus_odd);
|
||||
let base_monty = MontyForm::new(base, params);
|
||||
let result_monty = base_monty.pow(exp);
|
||||
|
||||
Ok(result_monty.retrieve())
|
||||
}
|
||||
|
||||
type PublicKey = BigUint;
|
||||
type PrivateKey = BigUint;
|
||||
type PublicKey = U2048;
|
||||
type PrivateKey = U2048;
|
||||
|
||||
fn gen_dh_keypair() -> Result<(PrivateKey, PublicKey)> {
|
||||
let mut rng = thread_rng();
|
||||
let private_key = rng.gen_biguint_range(&BigUint::zero(), &NIST_P);
|
||||
let private_key = U2048::random(&mut OsRng);
|
||||
let nist_p_nonzero = NonZero::new(*NIST_P).unwrap();
|
||||
let private_key = private_key.rem(&nist_p_nonzero);
|
||||
let public_key = mod_exp(&NIST_G, &private_key, &NIST_P)?;
|
||||
Ok((private_key, public_key))
|
||||
}
|
||||
|
||||
fn gen_secret(public_key: &BigUint, privite_key: &BigUint) -> Result<BigUint> {
|
||||
mod_exp(public_key, privite_key, &NIST_P)
|
||||
fn gen_secret(public_key: &U2048, private_key: &U2048) -> Result<U2048> {
|
||||
mod_exp(public_key, private_key, &NIST_P)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let (sk_alice, pk_alice) = gen_dh_keypair()?;
|
||||
let (sk_bob, pk_bob) = gen_dh_keypair()?;
|
||||
|
||||
let secret_alice = gen_secret(&pk_bob, &sk_alice)?;
|
||||
let secret_bob = gen_secret(&pk_alice, &sk_bob)?;
|
||||
|
||||
if secret_alice == secret_bob {
|
||||
println!("We share the same secret");
|
||||
} else {
|
||||
@@ -61,149 +83,3 @@ fn main() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use num_bigint::BigUint;
|
||||
use num_traits::{One, Zero};
|
||||
|
||||
#[test]
|
||||
fn test_basic_modexp() {
|
||||
// 3^4 mod 5 = 81 mod 5 = 1
|
||||
let result = mod_exp(
|
||||
&BigUint::from(3u32),
|
||||
&BigUint::from(4u32),
|
||||
&BigUint::from(5u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::from(1u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_large_numbers() {
|
||||
// 2^100 mod 17
|
||||
let result = mod_exp(
|
||||
&BigUint::from(2u32),
|
||||
&BigUint::from(100u32),
|
||||
&BigUint::from(17u32),
|
||||
)
|
||||
.unwrap();
|
||||
// 2^100 mod 17 = 16 (can verify with smaller calculation)
|
||||
assert_eq!(result, BigUint::from(16u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_exponent() {
|
||||
// Any number^0 mod m = 1 (except 0^0)
|
||||
let result = mod_exp(
|
||||
&BigUint::from(123u32),
|
||||
&BigUint::zero(),
|
||||
&BigUint::from(456u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_base() {
|
||||
// 0^n mod m = 0 (for n > 0)
|
||||
let result = mod_exp(&BigUint::zero(), &BigUint::from(5u32), &BigUint::from(7u32)).unwrap();
|
||||
assert_eq!(result, BigUint::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one_base() {
|
||||
// 1^n mod m = 1
|
||||
let result = mod_exp(
|
||||
&BigUint::one(),
|
||||
&BigUint::from(999u32),
|
||||
&BigUint::from(123u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_modulus_one() {
|
||||
// Any number mod 1 = 0
|
||||
let result = mod_exp(
|
||||
&BigUint::from(123u32),
|
||||
&BigUint::from(456u32),
|
||||
&BigUint::one(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rsa_example() {
|
||||
// RSA-like calculation: 42^17 mod 77
|
||||
let result = mod_exp(
|
||||
&BigUint::from(42u32),
|
||||
&BigUint::from(17u32),
|
||||
&BigUint::from(77u32),
|
||||
)
|
||||
.unwrap();
|
||||
// Can verify this manually or with known RSA test vectors
|
||||
assert_eq!(result, BigUint::from(70u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fermat_little_theorem() {
|
||||
// p = 7 (prime), a = 3
|
||||
// 3^6 mod 7 should equal 1 (Fermat's Little Theorem: a^(p-1) ≡ 1 mod p)
|
||||
let result = mod_exp(
|
||||
&BigUint::from(3u32),
|
||||
&BigUint::from(6u32),
|
||||
&BigUint::from(7u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_very_large_numbers() {
|
||||
// Test with very large numbers
|
||||
let base = BigUint::parse_bytes(b"123456789012345678901234567890", 10).unwrap();
|
||||
let exp = BigUint::from(1000u32);
|
||||
let modulus = BigUint::parse_bytes(b"987654321098765432109876543210987654321", 10).unwrap();
|
||||
|
||||
let result = mod_exp(&base, &exp, &modulus);
|
||||
assert!(result.is_ok());
|
||||
// Result should be less than modulus
|
||||
assert!(
|
||||
result.unwrap()
|
||||
< BigUint::parse_bytes(b"987654321098765432109876543210987654321", 10).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_zero_modulus() {
|
||||
// Division by zero should return error
|
||||
let result = mod_exp(&BigUint::from(5u32), &BigUint::from(3u32), &BigUint::zero());
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_zero_case() {
|
||||
// 0^0 is mathematically undefined, should handle appropriately
|
||||
let result = mod_exp(&BigUint::zero(), &BigUint::zero(), &BigUint::from(5u32));
|
||||
// Depending on your implementation, this might return 1 or error
|
||||
// Common convention is 0^0 = 1 in many contexts
|
||||
assert!(result.is_ok() || result.is_err()); // Just ensure it's handled
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equal_base_modulus() {
|
||||
// base == modulus, so base mod modulus = 0
|
||||
// 0^exp mod modulus = 0 (for exp > 0)
|
||||
let result = mod_exp(
|
||||
&BigUint::from(7u32),
|
||||
&BigUint::from(3u32),
|
||||
&BigUint::from(7u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::zero());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user