Skip to content

Commit 9f85c94

Browse files
authored
Tiny rsa (#68)
* wip: working tiny rsa * feat: tiny_rsa
1 parent efb0225 commit 9f85c94

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub mod ecdsa;
2727
pub mod field;
2828
pub mod kzg;
2929
pub mod polynomial;
30+
pub mod tiny_rsa;
3031

3132
use core::{
3233
fmt::{self, Display, Formatter},

src/tiny_rsa/mod.rs

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//! Tiny RSA implementation
2+
//! TinyRSA module for educational cryptographic implementations.
3+
//! RSA Security Assumptions:
4+
//! - The security of RSA relies on the difficulty of factoring large integers.
5+
//! - The implementation is secure if the modulus is the product of two large random primes (they
6+
//! are not random or big in these tests).
7+
//! - The size of the modulus should be at least 2048 bits.
8+
#[cfg(test)] mod tests;
9+
10+
#[allow(dead_code)]
11+
/// Computes the modular inverse of e mod totient
12+
const fn mod_inverse(e: u64, totient: u64) -> u64 {
13+
let mut d = 1;
14+
while (d * e) % totient != 1 {
15+
d += 1;
16+
}
17+
d
18+
}
19+
/// RSAKey struct
20+
pub struct RSA {
21+
/// pub key (e,n)
22+
pub private_key: PrivateKey,
23+
/// priv key (d,n)
24+
pub public_key: PublicKey,
25+
}
26+
27+
/// private key
28+
pub struct PrivateKey {
29+
/// gcd(e, totient) = 1
30+
e: usize,
31+
/// modulus
32+
n: usize,
33+
}
34+
35+
/// public key
36+
pub struct PublicKey {
37+
/// d x e mod totient = 1
38+
d: usize,
39+
/// modulus
40+
n: usize,
41+
}
42+
43+
impl RSA {
44+
/// Encrypts a message using the RSA algorithm
45+
#[allow(dead_code)]
46+
/// Encrypts a message using the RSA algorithm
47+
/// C = P^e mod n
48+
const fn encrypt(&self, message: u32) -> u32 {
49+
message.pow(self.private_key.e as u32) % self.private_key.n as u32
50+
}
51+
52+
#[allow(dead_code)]
53+
/// Decrypts a cipher using the RSA algorithm
54+
/// P = C^d mod n
55+
const fn decrypt(&self, cipher: u32) -> u32 {
56+
cipher.pow(self.public_key.d as u32) % self.public_key.n as u32
57+
}
58+
}
59+
/// Key generation for the RSA algorithm
60+
/// TODO: Implement a secure key generation algorithm using miller rabin primality test
61+
pub fn rsa_key_gen(p: usize, q: usize) -> RSA {
62+
assert!(is_prime(p));
63+
assert!(is_prime(q));
64+
let n = p * q;
65+
let e = generate_e(p, q);
66+
let totient = euler_totient(p as u64, q as u64);
67+
let d = mod_inverse(e, totient);
68+
RSA { private_key: PrivateKey { e: e as usize, n }, public_key: PublicKey { d: d as usize, n } }
69+
}
70+
71+
/// Generates e value for the RSA algorithm
72+
/// gcd of totient and e must be 1 which is equivalent to: e and totient must be coprime
73+
#[allow(dead_code)]
74+
const fn generate_e(p: usize, q: usize) -> u64 {
75+
assert!(p > 1 && q > 2, "P and Q must be greater than 1");
76+
let totient = euler_totient(p as u64, q as u64);
77+
let mut e = 2;
78+
while e < totient {
79+
if gcd(totient, e) == 1 {
80+
// This check ensures e and totient are coprime
81+
return e;
82+
}
83+
e += 1;
84+
}
85+
panic!("Failed to find coprime e; totient should be greater than 1")
86+
}
87+
88+
/// Generates a random prime number bigger than 1_000_000
89+
pub fn random_prime(first_prime: usize) -> usize {
90+
let mut n = 1_000_000;
91+
while !is_prime(n) && n != first_prime {
92+
n += 1;
93+
}
94+
n
95+
}
96+
97+
fn is_prime(n: usize) -> bool {
98+
if n <= 1 {
99+
return false;
100+
}
101+
for i in 2..=((n as f64).sqrt() as usize) {
102+
if n % i == 0 {
103+
return false;
104+
}
105+
}
106+
true
107+
}
108+
109+
const fn euler_totient(prime_1: u64, prime_2: u64) -> u64 { (prime_1 - 1) * (prime_2 - 1) }
110+
111+
const fn gcd(a: u64, b: u64) -> u64 {
112+
if b == 0 {
113+
a
114+
} else {
115+
gcd(b, a % b)
116+
}
117+
}

src/tiny_rsa/tests.rs

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use super::*;
2+
3+
const PRIME_1: usize = 5;
4+
const PRIME_2: usize = 3;
5+
const PRIME_3: usize = 7;
6+
7+
#[test]
8+
fn test_euler_totient() {
9+
assert_eq!(euler_totient(PRIME_1 as u64, PRIME_2 as u64), 8);
10+
assert_eq!(euler_totient(PRIME_2 as u64, PRIME_3 as u64), 12);
11+
assert_eq!(euler_totient(PRIME_3 as u64, PRIME_1 as u64), 24);
12+
}
13+
14+
#[test]
15+
fn key_gen() {
16+
let key = rsa_key_gen(PRIME_1, PRIME_2);
17+
assert_eq!(key.public_key.n, PRIME_1 * PRIME_2);
18+
assert_eq!(gcd(key.private_key.e as u64, euler_totient(PRIME_1 as u64, PRIME_2 as u64)), 1);
19+
20+
let key = rsa_key_gen(PRIME_2, PRIME_3);
21+
assert_eq!(key.public_key.n, PRIME_2 * PRIME_3);
22+
assert_eq!(gcd(key.private_key.e as u64, euler_totient(PRIME_2 as u64, PRIME_3 as u64)), 1);
23+
24+
let key = rsa_key_gen(PRIME_3, PRIME_1);
25+
assert_eq!(key.public_key.n, PRIME_3 * PRIME_1);
26+
assert_eq!(gcd(key.private_key.e as u64, euler_totient(PRIME_3 as u64, PRIME_1 as u64)), 1);
27+
}
28+
29+
#[test]
30+
#[should_panic]
31+
fn non_prime_key_gen() { rsa_key_gen(100, 200); }
32+
33+
#[test]
34+
fn test_gcd() {
35+
assert_eq!(gcd(10, 5), 5);
36+
assert_eq!(gcd(10, 3), 1);
37+
}
38+
39+
#[test]
40+
fn test_generate_e() {
41+
let e = generate_e(PRIME_1, PRIME_2);
42+
assert_eq!(e, 3);
43+
44+
let e = generate_e(PRIME_2, PRIME_3);
45+
assert_eq!(e, 5);
46+
47+
let e = generate_e(PRIME_3, PRIME_1);
48+
assert_eq!(e, 5);
49+
}
50+
51+
#[test]
52+
fn test_mod_inverse() {
53+
assert_eq!(mod_inverse(3, 8), 3);
54+
assert_eq!(mod_inverse(5, 12), 5);
55+
assert_eq!(mod_inverse(5, 24), 5);
56+
}
57+
58+
#[test]
59+
fn test_encrypt_decrypt() {
60+
let message = 10;
61+
let key = rsa_key_gen(PRIME_1, PRIME_2);
62+
let cipher = key.encrypt(message);
63+
let decrypted = key.decrypt(cipher);
64+
assert_eq!(decrypted, message);
65+
66+
let key = rsa_key_gen(PRIME_2, PRIME_3);
67+
let cipher = key.encrypt(message);
68+
let decrypted = key.decrypt(cipher);
69+
assert_eq!(decrypted, message);
70+
71+
let message = 10;
72+
let key = rsa_key_gen(PRIME_3, PRIME_1);
73+
let cipher = key.encrypt(message);
74+
let decrypted = key.decrypt(cipher);
75+
assert_eq!(decrypted, message);
76+
}
77+
78+
#[test]
79+
fn test_random_prime() {
80+
let prime = random_prime(2);
81+
assert!(is_prime(prime));
82+
assert!(prime >= 1_000_000);
83+
}
84+
85+
#[test]
86+
fn test_random_prime_generation() {
87+
let prime = random_prime(2);
88+
assert!(is_prime(prime));
89+
assert!(prime >= 1_000_000);
90+
}

0 commit comments

Comments
 (0)