diff --git a/src/main.rs b/src/main.rs index ef22466..07a06dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,8 +62,9 @@ fn main() { set5::challenge34(); set5::challenge35(); set5::challenge36(); + set5::challenge37().unwrap_or_else(|| println!("[FAIL] challenge 37")); } - set5::challenge37(); - set5::challenge38(); - set5::challenge39(); + set5::challenge38().unwrap_or_else(|| println!("[FAIL] challenge 38")); + set5::challenge39().unwrap_or_else(|| println!("[FAIL] challenge 39")); + set5::challenge40().unwrap_or_else(|| println!("[FAIL] challenge 40")); } diff --git a/src/rsa.rs b/src/rsa.rs index d482f4a..b4e7c2a 100644 --- a/src/rsa.rs +++ b/src/rsa.rs @@ -1,5 +1,8 @@ use num_bigint::BigUint; use num_bigint::RandBigInt; +use openssl::bn::BigNum; +use openssl::bn::BigNumContext; +use openssl::error::ErrorStack; #[derive(Clone)] pub struct PublicKey(pub BigUint); @@ -24,3 +27,94 @@ impl Keypair { } } } + +pub struct RsaPublicKey { + e: BigNum, + n: BigNum, +} + +pub struct RsaPrivateKey { + d: BigNum, + n: BigNum, +} + +pub fn rsa_gen_keys() -> Result<(RsaPublicKey, RsaPrivateKey), ErrorStack> { + let mut ctx = BigNumContext::new()?; + + // Generate 2 random primes. We'll use small numbers to start, so you can just pick them out of a prime table. Call them "p" and "q". + let mut p = BigNum::new()?; + let mut q = BigNum::new()?; + p.generate_prime(256, true, None, None)?; + q.generate_prime(256, true, None, None)?; + + // Let n be p * q. Your RSA math is modulo n. + let mut n = BigNum::new()?; + n.checked_mul(&p, &q, &mut ctx)?; + // This is stupid but I couldn't figure out how to clone a bignum so we do this. + let mut n2 = BigNum::new()?; + n2.checked_mul(&p, &q, &mut ctx)?; + + // Let et be (p-1)*(q-1) (the "totient"). You need this value only for keygen. + let mut et = BigNum::new()?; + q.sub_word(1)?; + p.sub_word(1)?; + et.checked_mul(&p, &q, &mut ctx)?; + + // Let e be 3. + // Compute d = invmod(e, et). invmod(17, 3120) is 2753. + let e = BigNum::from_u32(3)?; + let d = invmod(&e, &et)?; + + // Your public key is [e, n]. Your private key is [d, n]. + Ok((RsaPublicKey { e, n }, RsaPrivateKey { d, n: n2 })) +} + +pub fn invmod(a: &BigNum, n: &BigNum) -> Result { + //let zero = BigNum::from_u32(0)?; + + //let gcd_extended = |a: &mut BigNum, b: &mut BigNum| -> (BigNum, BigNum, BigNum) { + // if *a == zero { + // return ( + // BigNum::from_u32(0).unwrap(), + // BigNum::from_u32(1).unwrap(), + // b); + // } + // ( + // BigNum::from_u32(0).unwrap(), + // BigNum::from_u32(1).unwrap(), + // b) + //} + + let mut ctx = BigNumContext::new()?; + let mut r = BigNum::new()?; + r.mod_inverse(&a, &n, &mut ctx)?; + Ok(r) +} + +pub fn rsa_encrypt(m: &BigNum, p: &RsaPublicKey) -> Result { + let mut ctx = BigNumContext::new()?; + let mut c = BigNum::new()?; + // To encrypt: c = m**e%n. + c.mod_exp(&m, &p.e, &p.n, &mut ctx)?; + Ok(c) +} + +pub fn rsa_decrypt(c: &BigNum, p: &RsaPrivateKey) -> Result { + let mut ctx = BigNumContext::new()?; + let mut m = BigNum::new()?; + // To decrypt: m = c**d%n. + m.mod_exp(&c, &p.d, &p.n, &mut ctx)?; + Ok(m) +} + +pub fn rsa_encrypt_str(m: &str, p: &RsaPublicKey) -> Result { + // Finally, to encrypt a string, do something cheesy. + let m = BigNum::from_slice(&m.as_bytes())?; + assert!(m < p.n); + rsa_encrypt(&m, p) +} + +pub fn rsa_decrypt_str(c: &BigNum, p: &RsaPrivateKey) -> Result { + let m = rsa_decrypt(c, p)?; + Ok(String::from(std::str::from_utf8(&m.to_vec()).unwrap())) +} diff --git a/src/set5.rs b/src/set5.rs index c5d70b9..67309c3 100644 --- a/src/set5.rs +++ b/src/set5.rs @@ -5,6 +5,7 @@ use crate::srp; use num_bigint::BigUint; use num_bigint::RandBigInt; use num_bigint::ToBigUint; +use openssl::bn::BigNum; use openssl::sha::sha256; use rand::Rng; @@ -509,6 +510,33 @@ pub fn challenge38() -> Option<()> { Some(()) } -pub fn challenge39() { - println!("[xxxx] Challenge 39: Implement RSA"); +pub fn challenge39() -> Option<()> { + // I recommend you not bother with primegen, + // but do take the time to get your own EGCD and + // invmod algorithm working. + let a = BigNum::from_u32(17).ok()?; + let n = BigNum::from_u32(3120).ok()?; + let r = BigNum::from_u32(2753).ok()?; + assert_eq!(rsa::invmod(&a, &n).ok()?, r, "invmod does not work"); + + let (public_key, private_key) = rsa::rsa_gen_keys().ok()?; + + // Test this out with a number, like "42". + let i = BigNum::from_u32(1337).ok()?; + let c = rsa::rsa_encrypt(&i, &public_key).ok()?; + let m = rsa::rsa_decrypt(&c, &private_key).ok()?; + assert_eq!(i, m, "rsa is broken"); + + let i = "Party, party, party, all night long. Yuah!"; + let c = rsa::rsa_encrypt_str(&i, &public_key).ok()?; + let m = rsa::rsa_decrypt_str(&c, &private_key).ok()?; + assert_eq!(i, &m, "string rsa is broken"); + + println!("[okay] Challenge 39: implement RSA"); + Some(()) +} + +pub fn challenge40() -> Option<()> { + // println!("[xxxx] Challenge 40: implement an E=3 RSA Broadcast attack"); + None }