Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tiny rsa #68

Merged
merged 28 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e23bf01
feat: basic setup
Autoparallel May 15, 2024
669cea5
add: `README` in curve module
Autoparallel May 16, 2024
9ea0233
impl Tonelli-Shanks sqrt
Autoparallel May 16, 2024
6bd95b0
feat: 17-torsion generators
Autoparallel May 16, 2024
9c90a02
bug: `curve::tests::fields::double_distorted_generator()` fails
Autoparallel May 16, 2024
be464e2
fix: `GaloisField<2, 59>::inverse()`
Autoparallel May 16, 2024
dcc5805
feat: vertical line functions
Autoparallel May 16, 2024
b513b90
fix: lhs scalar mul
Autoparallel May 16, 2024
e143352
feat: tangent line
Autoparallel May 16, 2024
c568e18
test: `line_from_p_to_2p`
Autoparallel May 16, 2024
055df04
fix: `point_doubling()`
Autoparallel May 16, 2024
957ac6c
bug: `miller_loop_check()` does not pass yet
Autoparallel May 16, 2024
800e1ba
fix: `panic!`s on `AffinePoint::Infinity`
Autoparallel May 16, 2024
57d71e6
wip
Autoparallel May 16, 2024
6c4e368
wip: working tiny rsa
0xJepsen May 16, 2024
13b6f13
wip: working tiny rsa
0xJepsen May 16, 2024
90d1b58
feat: miller loop test passes
Autoparallel May 17, 2024
9781e75
update generator
Autoparallel May 17, 2024
d706c7f
cleanup pairing
Autoparallel May 17, 2024
1db5b3c
clean lint
Autoparallel May 17, 2024
e1a2e81
Merge branch 'feat/pairing' into tiny_rsa
0xJepsen May 17, 2024
f388f53
feat: use our field trait
0xJepsen May 17, 2024
68072a8
chore: todo comments
0xJepsen May 17, 2024
96a583e
feat: tiny_rsa
0xJepsen May 20, 2024
c789f6a
Merge branch 'main' into tiny_rsa
0xJepsen May 20, 2024
fbe0166
Merge branch 'main' into tiny_rsa
0xJepsen May 22, 2024
aa95952
tiny rsa
0xJepsen May 31, 2024
f9d7d1b
Merge branch 'main' into tiny_rsa
0xJepsen May 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub mod ecdsa;
pub mod field;
pub mod kzg;
pub mod polynomial;
pub mod tiny_rsa;

use core::{
fmt::{self, Display, Formatter},
Expand Down
117 changes: 117 additions & 0 deletions src/tiny_rsa/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//! Tiny RSA implementation
//! TinyRSA module for educational cryptographic implementations.
//! RSA Security Assumptions:
//! - The security of RSA relies on the difficulty of factoring large integers.
//! - The implementation is secure if the modulus is the product of two large random primes (they
//! are not random or big in these tests).
//! - The size of the modulus should be at least 2048 bits.
#[cfg(test)] mod tests;

#[allow(dead_code)]
/// Computes the modular inverse of e mod totient
0xJepsen marked this conversation as resolved.
Show resolved Hide resolved
const fn mod_inverse(e: u64, totient: u64) -> u64 {
let mut d = 1;
while (d * e) % totient != 1 {
d += 1;
}
d
}
/// RSAKey struct
pub struct RSA {
/// pub key (e,n)
pub private_key: PrivateKey,
/// priv key (d,n)
pub public_key: PublicKey,
}

/// private key
pub struct PrivateKey {
/// gcd(e, totient) = 1
e: usize,
/// modulus
n: usize,
}

/// public key
pub struct PublicKey {
/// d x e mod totient = 1
d: usize,
/// modulus
n: usize,
}

impl RSA {
/// Encrypts a message using the RSA algorithm
#[allow(dead_code)]
/// Encrypts a message using the RSA algorithm
/// C = P^e mod n
const fn encrypt(&self, message: u32) -> u32 {
message.pow(self.private_key.e as u32) % self.private_key.n as u32
}

#[allow(dead_code)]
/// Decrypts a cipher using the RSA algorithm
/// P = C^d mod n
const fn decrypt(&self, cipher: u32) -> u32 {
cipher.pow(self.public_key.d as u32) % self.public_key.n as u32
}
}
/// Key generation for the RSA algorithm
/// TODO: Implement a secure key generation algorithm using miller rabin primality test
pub fn rsa_key_gen(p: usize, q: usize) -> RSA {
assert!(is_prime(p));
assert!(is_prime(q));
let n = p * q;
let e = generate_e(p, q);
let totient = euler_totient(p as u64, q as u64);
let d = mod_inverse(e, totient);
RSA { private_key: PrivateKey { e: e as usize, n }, public_key: PublicKey { d: d as usize, n } }
}

/// Generates e value for the RSA algorithm
/// gcd of totient and e must be 1 which is equivalent to: e and totient must be coprime
#[allow(dead_code)]
const fn generate_e(p: usize, q: usize) -> u64 {
assert!(p > 1 && q > 2, "P and Q must be greater than 1");
let totient = euler_totient(p as u64, q as u64);
let mut e = 2;
while e < totient {
if gcd(totient, e) == 1 {
// This check ensures e and totient are coprime
return e;
}
e += 1;
}
panic!("Failed to find coprime e; totient should be greater than 1")
}

/// Generates a random prime number bigger than 1_000_000
pub fn random_prime(first_prime: usize) -> usize {
let mut n = 1_000_000;
while !is_prime(n) && n != first_prime {
n += 1;
}
n
}

fn is_prime(n: usize) -> bool {
if n <= 1 {
return false;
}
for i in 2..=((n as f64).sqrt() as usize) {
if n % i == 0 {
return false;
}
}
true
}

const fn euler_totient(prime_1: u64, prime_2: u64) -> u64 { (prime_1 - 1) * (prime_2 - 1) }

const fn gcd(a: u64, b: u64) -> u64 {
if b == 0 {
a
} else {
gcd(b, a % b)
}
}
90 changes: 90 additions & 0 deletions src/tiny_rsa/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use super::*;

const PRIME_1: usize = 5;
const PRIME_2: usize = 3;
const PRIME_3: usize = 7;

#[test]
fn test_euler_totient() {
assert_eq!(euler_totient(PRIME_1 as u64, PRIME_2 as u64), 8);
assert_eq!(euler_totient(PRIME_2 as u64, PRIME_3 as u64), 12);
assert_eq!(euler_totient(PRIME_3 as u64, PRIME_1 as u64), 24);
}

#[test]
fn key_gen() {
let key = rsa_key_gen(PRIME_1, PRIME_2);
assert_eq!(key.public_key.n, PRIME_1 * PRIME_2);
assert_eq!(gcd(key.private_key.e as u64, euler_totient(PRIME_1 as u64, PRIME_2 as u64)), 1);

let key = rsa_key_gen(PRIME_2, PRIME_3);
assert_eq!(key.public_key.n, PRIME_2 * PRIME_3);
assert_eq!(gcd(key.private_key.e as u64, euler_totient(PRIME_2 as u64, PRIME_3 as u64)), 1);

let key = rsa_key_gen(PRIME_3, PRIME_1);
assert_eq!(key.public_key.n, PRIME_3 * PRIME_1);
assert_eq!(gcd(key.private_key.e as u64, euler_totient(PRIME_3 as u64, PRIME_1 as u64)), 1);
}

#[test]
#[should_panic]
fn non_prime_key_gen() { rsa_key_gen(100, 200); }

#[test]
fn test_gcd() {
assert_eq!(gcd(10, 5), 5);
assert_eq!(gcd(10, 3), 1);
}

#[test]
fn test_generate_e() {
let e = generate_e(PRIME_1, PRIME_2);
assert_eq!(e, 3);

let e = generate_e(PRIME_2, PRIME_3);
assert_eq!(e, 5);

let e = generate_e(PRIME_3, PRIME_1);
assert_eq!(e, 5);
}

#[test]
fn test_mod_inverse() {
assert_eq!(mod_inverse(3, 8), 3);
assert_eq!(mod_inverse(5, 12), 5);
assert_eq!(mod_inverse(5, 24), 5);
}

#[test]
fn test_encrypt_decrypt() {
let message = 10;
let key = rsa_key_gen(PRIME_1, PRIME_2);
let cipher = key.encrypt(message);
let decrypted = key.decrypt(cipher);
assert_eq!(decrypted, message);

let key = rsa_key_gen(PRIME_2, PRIME_3);
let cipher = key.encrypt(message);
let decrypted = key.decrypt(cipher);
assert_eq!(decrypted, message);

let message = 10;
let key = rsa_key_gen(PRIME_3, PRIME_1);
let cipher = key.encrypt(message);
let decrypted = key.decrypt(cipher);
assert_eq!(decrypted, message);
}

#[test]
fn test_random_prime() {
let prime = random_prime(2);
assert!(is_prime(prime));
assert!(prime >= 1_000_000);
}

#[test]
fn test_random_prime_generation() {
let prime = random_prime(2);
assert!(is_prime(prime));
assert!(prime >= 1_000_000);
}