From ca5cc46cc62b9149afa3c3bd01d3e65fd4856ac5 Mon Sep 17 00:00:00 2001 From: theBreadCompany Date: Thu, 5 Dec 2024 18:15:26 +0100 Subject: [PATCH] add elliptic curves, diffie hellman and 'messaging' --- Cargo.lock | 80 +++++++++++++++++++++++++++++ Cargo.toml | 1 + src/keygen.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 10 ++++ src/pollard_rho.rs | 31 +---------- src/utils.rs | 42 +++++++++++++++ 6 files changed, 259 insertions(+), 30 deletions(-) create mode 100644 src/keygen.rs create mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index efc010e..69d485f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + [[package]] name = "byteorder" version = "1.5.0" @@ -19,6 +25,7 @@ name = "ec_crypto" version = "0.1.0" dependencies = [ "gcd", + "num", "rand", ] @@ -45,6 +52,79 @@ version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "ppv-lite86" version = "0.2.20" diff --git a/Cargo.toml b/Cargo.toml index 84d9883..10fa14e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] rand = "0.8.5" gcd = "2.3.0" +num = "0.4.3" diff --git a/src/keygen.rs b/src/keygen.rs new file mode 100644 index 0000000..653988a --- /dev/null +++ b/src/keygen.rs @@ -0,0 +1,125 @@ +use core::str; + +use num::{pow::Pow, Integer}; +use rand::{distributions::Standard, prelude::Distribution}; + +use crate::utils::*; + +struct EllipticCurve +where + BigInt: Integer, +{ + a: BigInt, + b: BigInt, + r#mod: BigInt, +} +impl EllipticCurve +where + BigInt: Integer + std::marker::Copy + Pow, + Standard: Distribution, +{ + pub fn new(a: BigInt, b: BigInt, r#mod: BigInt) -> Self { + EllipticCurve { a, b, r#mod } + } + + pub fn y(&self, x: BigInt) -> BigInt { + (x.pow(3) + self.b * x + self.a) % self.r#mod + } + pub fn random(&self) -> (BigInt, BigInt) { + let mut start = rand::random::() % self.r#mod; + let i_count = rand::random::(); + + for _ in 0..i_count { + start = self.y(start); + } + + (start, self.y(start)) + } +} + +pub struct Person { + pub name: String, + private_key: Option, + pub public_key: Option, + pub shared_key: Option, +} +impl Person { + pub fn new(name: &str) -> Self { + Person { + name: name.to_string(), + private_key: None, + public_key: None, + shared_key: None, + } + } + + pub fn gen_keys(&mut self, start: u32, r#mod: u32) { + let private = rand::random::(); + self.private_key = Some(private.into()); + let public = mod_pow(start, private.into(), r#mod); + self.public_key = Some(public); + eprintln!( + "[{}] private: {} -> public: {}", + self.name, + self.private_key.unwrap(), + self.public_key.unwrap() + ); + } + + pub fn gen_shared(&mut self, public: u32, r#mod: u32) { + self.shared_key = Some(mod_pow(public, self.private_key.unwrap(), r#mod)) + } + + pub fn diffie_hellman(p1: &mut Self, p2: &mut Self) { + let m: u32 = 17; + let curve = EllipticCurve::new(0, 7, m); + let gen = curve.random().1; + + eprintln!( + "Start params: x³+{}x+{}, mod {}, generator {}", + 0, 7, m, gen + ); + + p1.gen_keys(gen, m.into()); + p2.gen_keys(gen, m.into()); + p1.gen_shared(p2.public_key.unwrap(), m.into()); + p2.gen_shared(p1.public_key.unwrap(), m.into()); + assert_eq!(p1.shared_key, p2.shared_key); + eprintln!( + "[{}] Shared key for {}: {}", + p1.name, + p2.name, + p1.shared_key.unwrap() + ); + } + + fn xor_cipher(msg: &[u8], key: u32) -> Vec { + let key_bytes = key.to_le_bytes(); + let key_len = key_bytes.len(); + + msg.iter() + .enumerate() + .map(|(i, &byte)| byte ^ key_bytes[i % key_len]) + .collect() + } + + pub fn send(&self, msg_raw: &[u8], target: &Self) { + let encrypted_raw = Self::xor_cipher(msg_raw, self.shared_key.unwrap()); + let encrypted = str::from_utf8(&encrypted_raw).unwrap_or("not displayable"); + let msg = str::from_utf8(&msg_raw).unwrap_or("not displayable"); + eprintln!( + "[{}] Sending message '{}' ({}) to {}", + self.name, msg, encrypted, target.name + ); + target.recv(&encrypted_raw, self); + } + fn recv(&self, msg_raw: &[u8], source: &Self) { + let decrypted_raw = Self::xor_cipher(msg_raw, self.shared_key.unwrap()); + let decrypted = str::from_utf8(&decrypted_raw).unwrap_or("not displayable"); + let msg = str::from_utf8(&msg_raw).unwrap_or("not displayable"); + eprintln!( + "[{}] Received message '{}' ({}) from {}", + self.name, decrypted, msg, source.name + ); + } +} diff --git a/src/main.rs b/src/main.rs index 831a179..a6aff1a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,15 @@ +use keygen::Person; + +mod keygen; mod pollard_rho; +mod utils; fn main() { + let mut alice = Person::new("alice"); + let mut bob = Person::new("bob"); + Person::diffie_hellman(&mut alice, &mut bob); + alice.send(b"Hello World", &bob); + /* let mut n = 0; while n % 2 == 0 { n = rand::random::(); @@ -10,4 +19,5 @@ fn main() { "Generated random number {}, got prime divisor {}", n, n_primediv ); + */ } diff --git a/src/pollard_rho.rs b/src/pollard_rho.rs index 3dff6f9..c49d11e 100644 --- a/src/pollard_rho.rs +++ b/src/pollard_rho.rs @@ -1,5 +1,5 @@ +use crate::utils::{is_prime, mod_pow}; use gcd::Gcd; - /** * Calculate the prime divisor for some number `n` * @@ -40,32 +40,3 @@ pub fn pollard_rho(n: u32) -> u32 { div } - -/** - * Discrete/Modular exponentiation - * - * Highly memory efficient because the full result is never stored, but shortened by defined modulo instead. - * We can use that because the prime divisor required for our algorithm is guarenteed to be smaller - * than n. - * - * Counterpart function to the discrete logarithm. - */ -fn mod_pow(base: u32, exp: u32, r#mod: u32) -> u32 { - let mut result = 1; - for _ in 0..exp - 1 { - result = (result * base) % r#mod; - } - result -} - -/** - * very primitive prime checker - */ -fn is_prime(n: u32) -> bool { - for i in (3..=(n as f32).sqrt() as u32).step_by(2) { - if n % i == 0 { - return false; - } - } - true -} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..ca9b931 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,42 @@ +use std::ops::Div; + +use num::Integer; + +/** + * Discrete/Modular exponentiation + * + * Highly memory efficient because the full result is never stored, but shortened by defined modulo instead. + * We can use that because the prime divisor required for our algorithm is guarenteed to be smaller + * than n. + * + * Counterpart function to the discrete logarithm. + */ +pub fn mod_pow(mut base: T, mut exp: T, r#mod: T) -> T +where + T: Integer + Copy + std::ops::DivAssign + From, + u64: From, + u128: From, +{ + let mut result: T = T::one(); + base = base % r#mod; + while exp > T::zero() { + if exp.is_odd() { + result = (result * base) % r#mod; + } + base = (base * base) % r#mod; + exp /= T::from(2); + } + result +} + +/** + * very primitive prime checker + */ +pub fn is_prime(n: u32) -> bool { + for i in (3..=(n as f32).sqrt() as u32).step_by(2) { + if n % i == 0 { + return false; + } + } + true +}