Rusty RSA

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

Photo by Shaun Bell on Unsplash

Rusty 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 the modulus (N):

N=PQ

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

Pub=(e,N)

and private key (d is the inverse of e, mod N):

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
Number of bits: 256
N: 88784581785652878820034186370624314588994295185617668374956124269591217255721 (Hex: c44a4ae0749e4f10766eba7b3118528925dac668f9c00c9c09073c4ac4133529)
E: 65537 (Hex: 10001)
D: 48068335915872491055319178491577153824946719614772150427580544411717337514985 (Hex: 6a45b6c8531b2abf44ffaa22ec6fb8aee94b092f01cce0df9220209798e683e9)
Primes (P and Q):
286102593091902216489634142755351342163 (Hex:d73d5ae929314cacd75941fe789eac53)
310324282021216737003603617881490491667 (Hex:e97648606fe492da2d4f3d961f1c8913)
Encrypted: 0844d0b3ef240d9e943921250246918b92a92541afab2c78f3b74af882ce7315
Decrypted : Hello
(d*e) mod (p-1)(q-1): 1

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: