use num_bigint::{BigInt, BigUint};
use num_integer::Integer;
use num_traits::{One, Signed, Zero};
use rand::Rng;
use crate::base64::base64url_encode;
use crate::crypto::aes::aes128_ecb_encrypt;
#[derive(Debug, Clone)]
pub struct MegaRsaKey {
pub p: BigUint,
pub q: BigUint,
pub d: BigUint,
pub u: BigUint,
pub m: BigUint,
pub e: BigUint,
}
impl MegaRsaKey {
pub fn is_valid_private_key(&self) -> bool {
self.e == BigUint::from(3u32)
&& !self.p.is_zero()
&& !self.q.is_zero()
&& !self.d.is_zero()
&& !self.u.is_zero()
&& !self.m.is_zero()
&& self.m == (&self.p * &self.q)
&& self.p.bits() >= 512
&& self.q.bits() >= 512
}
pub fn generate() -> Result<Self, String> {
let mut rng = rand::thread_rng();
let e = BigUint::from(3u32);
let p = generate_prime_for_e3(&mut rng, 1024)?;
let q = generate_prime_for_e3(&mut rng, 1024)?;
let m = &p * &q;
let p_minus_1 = &p - 1u32;
let q_minus_1 = &q - 1u32;
let phi = &p_minus_1 * &q_minus_1;
let d = mod_inverse(&e, &phi).ok_or("Failed to compute private exponent")?;
let u = mod_inverse(&p, &q).ok_or("Failed to compute CRT coefficient")?;
Ok(Self { p, q, d, u, m, e })
}
pub fn encode_public_key(&self) -> String {
let mut data = Vec::new();
append_mpi(&mut data, &self.m);
append_mpi(&mut data, &self.e);
base64url_encode(&data)
}
pub fn public_key_bytes(&self) -> Vec<u8> {
let mut data = Vec::new();
append_mpi(&mut data, &self.m);
append_mpi(&mut data, &self.e);
data
}
pub fn from_encoded_public_key(b64: &str) -> Result<Self, String> {
let data =
crate::base64::base64url_decode(b64).map_err(|_| "Invalid base64".to_string())?;
let mut pos = 0;
let m = read_mpi(&data, &mut pos).map_err(|e| e)?;
let e = read_mpi(&data, &mut pos).map_err(|e| e)?;
Ok(Self {
m,
e,
p: BigUint::zero(),
q: BigUint::zero(),
d: BigUint::zero(),
u: BigUint::zero(),
})
}
pub fn encode_private_key(&self, master_key: &[u8; 16]) -> String {
let mut data = Vec::new();
append_mpi(&mut data, &self.p);
append_mpi(&mut data, &self.q);
append_mpi(&mut data, &self.d);
append_mpi(&mut data, &self.u);
let padding = (16 - (data.len() % 16)) % 16;
data.extend(vec![0u8; padding]);
let encrypted = aes128_ecb_encrypt(&data, master_key);
base64url_encode(&encrypted)
}
pub fn private_key_bytes(&self) -> Vec<u8> {
let mut data = Vec::new();
append_mpi(&mut data, &self.p);
append_mpi(&mut data, &self.q);
append_mpi(&mut data, &self.d);
append_mpi(&mut data, &self.u);
data
}
pub fn decrypt(&self, ciphertext: &[u8]) -> Option<Vec<u8>> {
if ciphertext.is_empty() || !self.is_valid_private_key() {
return None;
}
let c = BigUint::from_bytes_be(ciphertext);
let m = mod_pow(&c, &self.d, &self.m);
let result = m.to_bytes_be();
if result.is_empty() {
return None;
}
Some(result)
}
pub fn encrypt(&self, plaintext: &[u8]) -> Vec<u8> {
let m = BigUint::from_bytes_be(plaintext);
let c = mod_pow(&m, &self.e, &self.m);
c.to_bytes_be()
}
}
fn append_mpi(buf: &mut Vec<u8>, n: &BigUint) {
let bytes = n.to_bytes_be();
let bit_len = if bytes.is_empty() {
0u16
} else {
((bytes.len() - 1) * 8 + (8 - bytes[0].leading_zeros() as usize)) as u16
};
buf.extend_from_slice(&bit_len.to_be_bytes());
buf.extend_from_slice(&bytes);
}
pub fn read_mpi(data: &[u8], pos: &mut usize) -> Result<BigUint, String> {
if *pos + 2 > data.len() {
return Err("MPI truncated".to_string());
}
let bit_len = u16::from_be_bytes([data[*pos], data[*pos + 1]]) as usize;
let byte_len = (bit_len + 7) / 8;
*pos += 2;
if *pos + byte_len > data.len() {
return Err("MPI data truncated".to_string());
}
let bytes = &data[*pos..*pos + byte_len];
*pos += byte_len;
Ok(BigUint::from_bytes_be(bytes))
}
fn generate_prime_for_e3(rng: &mut impl Rng, bits: usize) -> Result<BigUint, String> {
for _ in 0..10000 {
let mut bytes = vec![0u8; bits / 8];
rng.fill(&mut bytes[..]);
bytes[0] |= 0x80;
let last_idx = bytes.len() - 1;
bytes[last_idx] |= 0x01;
let candidate = BigUint::from_bytes_be(&bytes);
let remainder = &candidate % 3u32;
let p = if remainder == BigUint::from(0u32) {
&candidate + 2u32
} else if remainder == BigUint::from(1u32) {
&candidate + 1u32
} else {
candidate
};
if is_probably_prime(&p, 20) {
return Ok(p);
}
}
Err("Failed to generate prime after 10000 attempts".to_string())
}
fn is_probably_prime(n: &BigUint, rounds: usize) -> bool {
if n <= &BigUint::from(1u32) {
return false;
}
if n == &BigUint::from(2u32) {
return true;
}
if n.is_even() {
return false;
}
let n_minus_1 = n - 1u32;
let mut d = n_minus_1.clone();
let mut r = 0u32;
while d.is_even() {
d >>= 1;
r += 1;
}
let mut rng = rand::thread_rng();
'witness: for _ in 0..rounds {
let a = loop {
let bytes: Vec<u8> = (0..n.to_bytes_be().len()).map(|_| rng.r#gen()).collect();
let candidate = BigUint::from_bytes_be(&bytes) % n;
if candidate >= BigUint::from(2u32) && candidate <= &n_minus_1 - 1u32 {
break candidate;
}
};
let mut x = mod_pow(&a, &d, n);
if x == BigUint::one() || x == n_minus_1 {
continue 'witness;
}
for _ in 0..r - 1 {
x = mod_pow(&x, &BigUint::from(2u32), n);
if x == n_minus_1 {
continue 'witness;
}
}
return false;
}
true
}
fn mod_pow(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
if modulus.is_one() {
return BigUint::zero();
}
let mut result = BigUint::one();
let mut base = base % modulus;
let mut exp = exp.clone();
while !exp.is_zero() {
if exp.is_odd() {
result = (&result * &base) % modulus;
}
exp >>= 1;
base = (&base * &base) % modulus;
}
result
}
fn mod_inverse(a: &BigUint, m: &BigUint) -> Option<BigUint> {
let a = BigInt::from(a.clone());
let m = BigInt::from(m.clone());
let mut old_r = a;
let mut r = m.clone();
let mut old_s = BigInt::one();
let mut s = BigInt::zero();
while !r.is_zero() {
let quotient = &old_r / &r;
let temp_r = r.clone();
r = &old_r - "ient * &r;
old_r = temp_r;
let temp_s = s.clone();
s = &old_s - "ient * &s;
old_s = temp_s;
}
if old_r > BigInt::one() {
return None; }
if old_s.is_negative() {
old_s = old_s + &m;
}
Some(old_s.to_biguint().unwrap())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_rsa_key() {
let key = MegaRsaKey::generate();
assert!(key.is_ok());
let key = key.unwrap();
assert_eq!(key.e, BigUint::from(3u32));
assert_eq!(key.m, &key.p * &key.q);
assert!(key.m.bits() >= 2040 && key.m.bits() <= 2056);
}
#[test]
fn test_encode_public_key() {
let key = MegaRsaKey::generate().unwrap();
let encoded = key.encode_public_key();
assert!(!encoded.is_empty());
assert!(!encoded.contains('='));
assert!(!encoded.contains('+'));
assert!(!encoded.contains('/'));
}
#[test]
fn test_encode_private_key() {
let key = MegaRsaKey::generate().unwrap();
let master_key = [0u8; 16];
let encoded = key.encode_private_key(&master_key);
assert!(!encoded.is_empty());
}
#[test]
fn test_mod_inverse() {
let a = BigUint::from(3u32);
let m = BigUint::from(11u32);
let inv = mod_inverse(&a, &m);
assert!(inv.is_some());
let inv = inv.unwrap();
assert_eq!((&a * &inv) % &m, BigUint::one());
}
#[test]
fn test_encrypt_decrypt() {
let key = MegaRsaKey::generate().expect("Failed to generate key");
let plaintext = b"Hello MEGA RSA!";
let ciphertext = key.encrypt(plaintext);
assert!(!ciphertext.is_empty());
assert_ne!(ciphertext, plaintext);
let decrypted = key.decrypt(&ciphertext).expect("Failed to decrypt");
assert_eq!(decrypted, plaintext);
}
}