Rust and RSA

Many of our software vulnerabilities are caused by the C programming, and these are typically caused by the usage of memory pointers. If…

Rust and RSA

Many of our software vulnerabilities are caused by the C programming language, and these are typically caused by the usage of memory pointers. If we want to create more robust code, we must thus move to a programming language which checks the code before it is built. Thus Rust is proposed as one of the most robust around, so let’s do some RSA code with Rust. The great advantage with Rust over Python is that it produces robust machine level executables.

How RSA Works

With RSA, we create two prime numbers (P and Q), and then compute:

N=PQ

And then select e and d. Our public key is:

Pub=(e,N)

and private key:

Priv=(d,N)

If we have a message of M, then the encrypted content is:

Enc=M^e (mod N)

and we decrypt with:

Dec=Enc^d (modN)

Implementing RSA in Rust

First we create the Rust project with:

cargo new rsa

We then go into the rsa folder, and add the following to the cargo.toml file:

[package]
name = "rsa"
version = "0.1.0"
authors = ["billatnapier"]
edition = "2018"
[dependencies]
rsa = "0.2.0"
rand = "0.7.0"
hex = "0.4.0"
num-bigint = "0.2.5"
num-traits = "0.2.11"

Next we go into the src folder, and edit the main.rs file with [here]

extern crate rsa;
extern crate rand;
extern crate hex;
use rsa::{PublicKey, RSAPrivateKey, PaddingScheme};
use rand::rngs::OsRng;
use std::str;
use std::env;
// use num_bigint::{BigUint};
use num_traits::{One};
fn main() {
let mut rng = OsRng;
let mut bits = 512;
let mut string = String::from("Hello world!");
let args: Vec<String>  = env::args().collect();
if args.len() >1 { string = args[1].clone();}
if args.len()> 2 { bits = args[2].clone().parse::<usize>().unwrap(); }

let key = RSAPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
println!("Message:\t{}",string);
println!("Number of bits:\t{}",bits);
let data = string.as_bytes();
println!("\nN:\t{} (Hex: {:x})",key.n(),key.n());
println!("E:\t{} (Hex: {:x})",key.e(),key.e());
println!("D:\t{} (Hex: {:x})",key.d(),key.d() );
println!("\nPrimes (P and Q):");
for prime in key.primes() {
println!("\t{} (Hex:{:x})",prime,prime);

}
let enc_data = key.encrypt(&mut rng, PaddingScheme::PKCS1v15, &data[..]).expect("failed to encrypt");
let hex_string = hex::encode(enc_data.clone());
println!("\n\nEncrypted:\t{}",hex_string);
let dec_data = key.decrypt(PaddingScheme::PKCS1v15, &enc_data).expect("failed to decrypt");
let mystr = str::from_utf8(&dec_data).unwrap();
println!("\nDecrypted :\t{}",mystr);
// Final check for (d x e) mod (p-1)*(q-1)
let p=  key.primes()[0].clone();
let q= key.primes()[1].clone();
let val1: rsa::BigUint = One::one();
let phi = (p - val1.clone()) * (q - val1.clone());
let val = (key.d()*key.e()) % phi;
println!("\n(d*e) mod (p-1)(q-1):\t{}",val);
}

Finally we simply build with:

cargo build

and run with:

cargo run

A sample run is [here]:

Message: Hello World!!!!
Number of bits: 256
N: 96761243117646436262536763827585347236071401749752263964809064532291214661297 (Hex: d5ecec3d338bba52b0af43e448db755e9aefe6585011eef341b1721e5ae856b1)
E: 65537 (Hex: 10001)
D: 32322155947503178092672152123434066583433006726619771630870751016469025046529 (Hex: 4775b12470878861feeb4cfe80e7ee3863957435e751a029cb11dc0af74cc001)
Primes (P and Q):
320831318569542044621117914910010848513 (Hex:f15ddd463d8559e5e017c3b7bbff3501)
301595378995622825889736797980030120369 (Hex:e2e5283af599d35d0c555a6e10cfb1b1)
Encrypted: 464a94f0fdffea2e2749ee4398b29becc7d0685390feddcd9b3bd6d98862c3e0
Decrypted : Hello World!!!!

We can see here we have 256 bit RSA, and where each prime number is 128 bits long (32 hex characters). The value of e is typically set at 65,537, and we determine the value of d as:

(d x e) mod N = 1

For this we determine the inverse of e mod N. Go try it here:

https://asecuritysite.com/rust/rust_rsa

and here is the code:

Conclusions

C was the first real programming language I learnt, and it’s the one I know best. It’s great that Rust aims to match the power of C, but add in a whole lot more robustness and security. Like Go, it is well matched to a world of open source software, and not hung up with complex DLLs. With a “cargo build”, we pull down the required modules we need in our code, and make the whole thing dynamic. For more Rust examples, try here:

https://asecuritysite.com/rust

Please consider supporting Asecuritysite.com, with a subscription to this blog, here: