1
0

feat: 实现多个挑战并改进测试

- 重写p1实现纯Rust的hex到base64转换
- 完成p13 ECB剪切粘贴攻击和破解脚本
- 实现p33 Diffie-Hellman密钥交换算法
- 修复p9的PKCS#7测试用例
- 在common库添加gen_random_key和pkcs7_unpadding函数
- 更新workspace依赖管理
This commit is contained in:
2025-08-01 16:00:16 +08:00
parent 23d016407c
commit e400b87e9f
17 changed files with 664 additions and 68 deletions

11
problems/p33/Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "p33"
version = "0.1.0"
edition = "2024"
[dependencies]
num-bigint = { workspace = true }
num-traits = { workspace = true }
rand = { workspace = true }
anyhow = { workspace = true }
once_cell = { workspace = true }

209
problems/p33/src/main.rs Normal file
View File

@@ -0,0 +1,209 @@
use anyhow::{Ok, Result, anyhow};
use num_bigint::{BigUint, RandBigInt};
use num_traits::{One, Zero};
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_G: Lazy<BigUint> = Lazy::new(|| BigUint::from(2u8));
fn mod_exp(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> Result<BigUint> {
if modulus.is_zero() {
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;
}
Ok(result)
}
type PublicKey = BigUint;
type PrivateKey = BigUint;
fn gen_dh_keypair() -> Result<(PrivateKey, PublicKey)> {
let mut rng = thread_rng();
let private_key = rng.gen_biguint_range(&BigUint::zero(), &NIST_P);
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 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 {
println!("Secret incorrect");
}
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());
}
}