Finish challenge 39 without invmod

This commit is contained in:
2022-10-14 18:23:05 -04:00
parent 6b17c66175
commit 3176f23662
3 changed files with 128 additions and 5 deletions

View File

@@ -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<BigNum, ErrorStack> {
//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<BigNum, ErrorStack> {
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<BigNum, ErrorStack> {
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<BigNum, ErrorStack> {
// 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<String, ErrorStack> {
let m = rsa_decrypt(c, p)?;
Ok(String::from(std::str::from_utf8(&m.to_vec()).unwrap()))
}