Starting to include RSA crypto.

This commit is contained in:
2018-04-14 10:45:11 -07:00
parent b5a5cbdd98
commit baa70a6ce6
11 changed files with 512 additions and 9 deletions

View File

@@ -9,5 +9,5 @@ mod unsigned;
mod gold_tests;
pub use self::signed::SCN;
pub use self::unsigned::UCN;
pub use self::unsigned::{BarrettUCN,UCN};
pub use self::primes::*;

View File

@@ -53,24 +53,26 @@ impl UCN {
let candidate = base | &one;
if let Some(proposed) = check_value(candidate) {
if proposed.probably_prime(rng, iterations) {
if proposed.probably_prime(rng, bitlen, iterations) {
return proposed;
}
}
}
}
pub fn probably_prime<G: Rng>(&self, g: &mut G, iters: usize) -> bool {
fn probably_prime<G: Rng>(&self, g: &mut G, size: usize, iters: usize)
-> bool
{
for tester in SMALL_PRIMES.iter() {
if (self % UCN::from(*tester)).is_zero() {
return false;
}
}
miller_rabin(g, &self, iters)
miller_rabin(g, &self, size, iters)
}
}
fn miller_rabin<G: Rng>(g: &mut G, n: &UCN, iters: usize) -> bool {
fn miller_rabin<G: Rng>(g: &mut G, n: &UCN, size: usize, iters: usize) -> bool {
let one = UCN::from(1 as u8);
let two = UCN::from(2 as u8);
let nm1 = n - &one;
@@ -86,7 +88,7 @@ fn miller_rabin<G: Rng>(g: &mut G, n: &UCN, iters: usize) -> bool {
// WitnessLoop: repeat k times
'WitnessLoop: for _k in 0..iters {
// pick a random integer a in the range [2, n - 2]
let a = random_in_range(g, &two, &nm1);
let a = random_in_range(g, size, &two, &nm1);
// x <- a^d mod n
let mut x = a.modexp(&d, &n);
// if x = 1 or x = n - 1 then
@@ -116,8 +118,9 @@ fn miller_rabin<G: Rng>(g: &mut G, n: &UCN, iters: usize) -> bool {
true
}
fn random_in_range<G: Rng>(rng: &mut G, min: &UCN, max: &UCN) -> UCN {
let bitlen = ((max.bits() + 63) / 64) * 64;
fn random_in_range<G: Rng>(rng: &mut G, bitlen: usize, min: &UCN, max: &UCN)
-> UCN
{
loop {
let candidate = random_number(rng, bitlen);

View File

@@ -11,6 +11,7 @@ pub struct UCN {
pub(crate) contents: Vec<u64>
}
#[derive(Clone,Debug,PartialEq,Eq)]
pub struct BarrettUCN {
pub(crate) u: UCN,
pub(crate) k: usize,
@@ -185,7 +186,6 @@ impl UCN {
let mylen = self.contents.len() * 8;
let mut res = Vec::with_capacity(len);
println!("mylen: {} len: {}", mylen, len);
assert!( (len % 8) == 0 );
assert!( mylen <= len );
for _ in 0..(len - mylen) {

View File

@@ -10,15 +10,22 @@
//! when they should use it, and examples. For now, it mostly just fowards
//! off to more detailed modules. Help requested!
extern crate digest;
extern crate num;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
extern crate rand;
extern crate sha1;
extern crate sha2;
/// The cryptonum module provides support for large numbers for use in various
/// cryptographically-relevant algorithms.
pub mod cryptonum;
/// The rsa module provides support for RSA-related core algorithms, including
/// signing / verification and encryption / decryption. You can also generate
/// key material there.
pub mod rsa;
#[cfg(test)]
mod test {

159
src/rsa/core.rs Normal file
View File

@@ -0,0 +1,159 @@
use cryptonum::{BarrettUCN,UCN};
use rand::Rng;
pub static ACCEPTABLE_KEY_SIZES: [(usize,usize); 8] =
[(512, 7),
(1024, 7),
(2048, 4),
(3072, 3),
(4096, 3),
(7680, 3),
(8192, 3),
(15360, 3)];
fn iterations_for_size(l: usize) -> usize {
for &(m, i) in ACCEPTABLE_KEY_SIZES.iter() {
if m == l {
return i;
}
}
panic!("Bad key size, can't get M-R iterations")
}
pub fn generate_pq<G: Rng>(rng: &mut G, e: &UCN, bitlen: usize) -> (UCN, UCN) {
let iterations = iterations_for_size(bitlen);
let sqrt2 = UCN::from(6074001000 as u64);
let topbit = UCN::from(1 as u64) << ((bitlen / 2) - 1);
let minval = sqrt2 << ((bitlen / 2) - 33);
let mindiff = UCN::from(1 as u64) << ((bitlen / 2) - 101);
let validate = |inval| {
let x = &inval | &topbit;
if x < minval {
return None
}
if !gcd_is_one(&e, &x) {
return None
}
Some(x)
};
let p = UCN::generate_prime(rng, bitlen / 2, iterations, validate);
loop {
let q = UCN::generate_prime(rng, bitlen / 2, iterations, validate);
if diff(&p, &q) >= mindiff {
return (p, q);
}
}
}
fn diff(a: &UCN, b: &UCN) -> UCN {
if a > b {
a - b
} else {
b - a
}
}
fn gcd_is_one(a: &UCN, b: &UCN) -> bool {
let mut u = a.clone();
let mut v = b.clone();
if u.is_zero() {
return v == UCN::from(1 as u8);
}
if v.is_zero() {
return u == UCN::from(1 as u8);
}
if u.is_even() && v.is_even() {
return false;
}
while u.is_even() {
u >>= 1;
}
loop {
while v.is_even() {
v >>= 1;
}
// u and v guaranteed to be odd right now.
if u > v {
// make sure that v > u, so that our subtraction works
// out.
let t = u;
u = v;
v = t;
}
v = v - &u;
if v.is_zero() {
return u == UCN::from(1 as u64);
}
}
}
// the RSA encryption function
pub fn ep(nu: &BarrettUCN, e: &UCN, m: &UCN) -> UCN {
m.fastmodexp(e, nu)
}
// the RSA decryption function
pub fn dp(nu: &BarrettUCN, d: &UCN, c: &UCN) -> UCN {
c.fastmodexp(d, nu)
}
// the RSA signature generation function
pub fn sp1(nu: &BarrettUCN, d: &UCN, m: &UCN) -> UCN {
m.fastmodexp(d, nu)
}
// the RSA signature verification function
pub fn vp1(nu: &BarrettUCN, e: &UCN, s: &UCN) -> UCN {
s.fastmodexp(e, nu)
}
// encoding PKCS1 stuff
pub fn pkcs1_pad(ident: &[u8], hash: &[u8], keylen: usize) -> Vec<u8> {
let mut idhash = Vec::new();
idhash.extend_from_slice(ident);
idhash.extend_from_slice(hash);
let tlen = idhash.len();
assert!(keylen > (tlen + 3));
let mut padding = Vec::new();
padding.resize(keylen - tlen - 3, 0xFF);
let mut result = vec![0x00, 0x01];
result.append(&mut padding);
result.push(0x00);
result.append(&mut idhash);
result
}
#[cfg(test)]
mod tests {
use rand::OsRng;
use super::*;
#[test]
fn can_get_p_and_q() {
let mut rng = OsRng::new().unwrap();
let e = UCN::from(65537 as u64);
for &(size, _) in ACCEPTABLE_KEY_SIZES.iter().take(3) {
let (p,q) = generate_pq(&mut rng, &e, size);
let minval = UCN::from(1 as u8) << ((size / 2) - 1);
assert!(p > minval);
assert!(q > minval);
assert!(p != q);
assert!(p.is_odd());
assert!(q.is_odd());
let phi = (p - UCN::from(1 as u64)) * (q - UCN::from(1 as u64));
let d = e.modinv(&phi);
assert_eq!( (&e * &d) % phi, UCN::from(1 as u64) );
}
}
}

14
src/rsa/errors.rs Normal file
View File

@@ -0,0 +1,14 @@
use std::io;
#[derive(Debug)]
pub enum RSAKeyGenError {
InvalidKeySize(usize), RngFailure(io::Error)
}
impl From<io::Error> for RSAKeyGenError {
fn from(e: io::Error) -> RSAKeyGenError {
RSAKeyGenError::RngFailure(e)
}
}

104
src/rsa/mod.rs Normal file
View File

@@ -0,0 +1,104 @@
mod core;
mod errors;
mod public;
mod private;
mod signing_hashes;
pub use self::public::RSAPublic;
pub use self::private::RSAPrivate;
pub use self::signing_hashes::{SigningHash,
SIGNING_HASH_NULL, SIGNING_HASH_SHA1,
SIGNING_HASH_SHA224, SIGNING_HASH_SHA256,
SIGNING_HASH_SHA384, SIGNING_HASH_SHA512};
use cryptonum::UCN;
use rand::{OsRng,Rng};
use self::core::{ACCEPTABLE_KEY_SIZES,generate_pq};
use self::errors::*;
#[derive(Clone,Debug)]
pub struct RSAKeyPair {
pub public: RSAPublic,
pub private: RSAPrivate
}
impl RSAKeyPair {
/// Generates a fresh RSA key pair of the given bit size. Valid bit sizes
/// are 512, 1024, 2048, 3072, 4096, 7680, 8192, and 15360. If you
/// actually want to protect data, use a value greater than or equal to
/// 2048. If you don't want to spend all day waiting for RSA computations
/// to finish, choose a value less than or equal to 4096.
///
/// This routine will use `OsRng` for entropy. If you want to use your
/// own random number generator, use `generate_w_rng`.
pub fn generate(len_bits: usize) -> Result<RSAKeyPair,RSAKeyGenError> {
let mut rng = OsRng::new()?;
RSAKeyPair::generate_w_rng(&mut rng, len_bits)
}
/// Generates a fresh RSA key pair of the given bit size. Valid bit sizes
/// are 512, 1024, 2048, 3072, 4096, 7680, 8192, and 15360. If you
/// actually want to protect data, use a value greater than or equal to
/// 2048. If you don't want to spend all day waiting for RSA computations
/// to finish, choose a value less than or equal to 4096.
///
/// If you provide your own random number generator that is not `OsRng`,
/// you should know what you're doing, and be using a cryptographically-
/// strong RNG of your own choosing. We've warned you. Use a good one.
/// So now it's on you.
pub fn generate_w_rng<G: Rng>(rng: &mut G, len_bits: usize)
-> Result<RSAKeyPair,RSAKeyGenError>
{
let e = UCN::from(65537 as u32);
for &(length, _) in ACCEPTABLE_KEY_SIZES.iter() {
if length == len_bits {
let (p, q) = generate_pq(rng, &e, len_bits);
let n = &p * &q;
let one = UCN::from(1 as u8);
let phi = (p - &one) * (q - &one);
let d = e.modinv(&phi);
let public_key = RSAPublic::new(n.clone(), e);
let private_key = RSAPrivate::new(n, d);
return Ok(RSAKeyPair{
private: private_key,
public: public_key
})
}
}
Err(RSAKeyGenError::InvalidKeySize(len_bits))
}
}
#[cfg(test)]
mod tests {
use quickcheck::{Arbitrary,Gen};
use rsa::core::{ep,dp,sp1,vp1};
use super::*;
const TEST_KEY_SIZES: [usize; 2] = [512, 1024];
impl Arbitrary for RSAKeyPair {
fn arbitrary<G: Gen>(g: &mut G) -> RSAKeyPair {
let size = g.choose(&TEST_KEY_SIZES).unwrap();
RSAKeyPair::generate_w_rng(g, *size).unwrap()
}
}
quickcheck! {
fn rsa_ep_dp_inversion(kp: RSAKeyPair, n: UCN) -> bool {
let m = n.reduce(&kp.public.nu);
let ciphertext = ep(&kp.public.nu, &kp.public.e, &m);
let mprime = dp(&kp.private.nu, &kp.private.d, &ciphertext);
mprime == m
}
fn rsa_sp_vp_inversion(kp: RSAKeyPair, n: UCN) -> bool {
let m = n.reduce(&kp.public.nu);
let sig = sp1(&kp.private.nu, &kp.private.d, &m);
let mprime = vp1(&kp.public.nu, &kp.public.e, &sig);
mprime == m
}
}
}

64
src/rsa/private.rs Normal file
View File

@@ -0,0 +1,64 @@
use cryptonum::{BarrettUCN,UCN};
use rsa::core::{ACCEPTABLE_KEY_SIZES,pkcs1_pad,sp1};
use rsa::signing_hashes::SigningHash;
use std::fmt;
#[derive(Clone)]
pub struct RSAPrivate {
pub(crate) byte_len: usize,
pub(crate) n: UCN,
pub(crate) nu: BarrettUCN,
pub(crate) d: UCN
}
#[cfg(test)]
impl fmt::Debug for RSAPrivate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("RSAPrivate")
.field("byte_len", &self.byte_len)
.field("n", &self.n)
.field("nu", &self.nu)
.field("d", &self.d)
.finish()
}
}
#[cfg(test)]
impl PartialEq for RSAPrivate {
fn eq(&self, rhs: &RSAPrivate) -> bool {
(self.byte_len == rhs.byte_len) &&
(self.n == rhs.n) &&
(self.nu == rhs.nu) &&
(self.d == rhs.d)
}
}
#[cfg(test)]
impl Eq for RSAPrivate {}
#[cfg(not(test))]
impl fmt::Debug for RSAPrivate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("RSAPrivateKey<PRIVATE>")
}
}
impl RSAPrivate {
/// Create a new RSA public key from the given components, which you found
/// via some other mechanism.
pub fn new(n: UCN, d: UCN) -> RSAPrivate {
let len = n.bits();
for &(valid_bits, _) in ACCEPTABLE_KEY_SIZES.iter() {
if valid_bits > len {
return RSAPrivate {
byte_len: valid_bits / 8,
n: n.clone(),
nu: n.barrett_u(),
d: d.clone()
};
}
}
panic!("Invalid RSA key size in new()")
}
}

30
src/rsa/public.rs Normal file
View File

@@ -0,0 +1,30 @@
use cryptonum::{BarrettUCN,UCN};
use rsa::core::ACCEPTABLE_KEY_SIZES;
#[derive(Clone,Debug,PartialEq,Eq)]
pub struct RSAPublic {
pub(crate) byte_len: usize,
pub(crate) n: UCN,
pub(crate) nu: BarrettUCN,
pub(crate) e: UCN
}
impl RSAPublic {
/// Create a new RSA public key from the given components, which you found
/// via some other mechanism.
pub fn new(n: UCN, e: UCN) -> RSAPublic {
let len = n.bits();
for &(valid_bits, _) in ACCEPTABLE_KEY_SIZES.iter() {
if valid_bits > len {
return RSAPublic{
byte_len: valid_bits / 8,
n: n.clone(),
nu: n.barrett_u(),
e: e.clone()
};
}
}
panic!("Invalid RSA key size in new()")
}
}

119
src/rsa/signing_hashes.rs Normal file
View File

@@ -0,0 +1,119 @@
use digest::{FixedOutput,Input};
use sha1::Sha1;
use sha2::{Sha224,Sha256,Sha384,Sha512};
use std::fmt;
/// A hash that can be used to sign a message.
#[derive(Clone)]
pub struct SigningHash {
/// The name of this hash (only used for display purposes)
pub name: &'static str,
/// The approved identity string for the hash.
pub ident: &'static [u8],
/// The hash
pub run: fn(&[u8]) -> Vec<u8>
}
impl fmt::Debug for SigningHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
/// The "null" signing hash. This signing hash has no identity, and will
/// simply pass the data through unhashed. You really should know what
/// you're doing if you use this, and probably using a somewhat strange
/// signing protocol. There's no good reason to use this in new code
/// for a new protocol or system.
pub static SIGNING_HASH_NULL: SigningHash = SigningHash {
name: "NULL",
ident: &[],
run: nohash
};
fn nohash(i: &[u8]) -> Vec<u8> {
i.to_vec()
}
/// Sign a hash based on SHA1. You shouldn't use this unless you're using
/// very small keys, and this is the only one available to you. Even then,
/// why are you using such small keys?!
pub static SIGNING_HASH_SHA1: SigningHash = SigningHash {
name: "SHA1",
ident: &[0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,
0x02,0x1a,0x05,0x00,0x04,0x14],
run: runsha1
};
fn runsha1(i: &[u8]) -> Vec<u8> {
let mut d = Sha1::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}
/// Sign a hash based on SHA2-224. This is the first reasonable choice
/// we've come across, and is useful when you have smaller RSA key sizes.
/// I wouldn't recommend it, though.
pub static SIGNING_HASH_SHA224: SigningHash = SigningHash {
name: "SHA224",
ident: &[0x30,0x2d,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
0x01,0x65,0x03,0x04,0x02,0x04,0x05,0x00,0x04,
0x1c],
run: runsha224
};
fn runsha224(i: &[u8]) -> Vec<u8> {
let mut d = Sha224::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}
/// Sign a hash based on SHA2-256. The first one I'd recommend!
pub static SIGNING_HASH_SHA256: SigningHash = SigningHash {
name: "SHA256",
ident: &[0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,
0x20],
run: runsha256
};
fn runsha256(i: &[u8]) -> Vec<u8> {
let mut d = Sha256::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}
/// Sign a hash based on SHA2-384. Approximately 50% better than
/// SHA-256.
pub static SIGNING_HASH_SHA384: SigningHash = SigningHash {
name: "SHA384",
ident: &[0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,
0x30],
run: runsha384
};
fn runsha384(i: &[u8]) -> Vec<u8> {
let mut d = Sha384::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}
/// Sign a hash based on SHA2-512. At this point, you're getting a bit
/// silly. But if you want to through 8kbit RSA keys with a 512 bit SHA2
/// signing hash, we're totally behind you.
pub static SIGNING_HASH_SHA512: SigningHash = SigningHash {
name: "SHA512",
ident: &[0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,
0x40],
run: runsha512
};
fn runsha512(i: &[u8]) -> Vec<u8> {
let mut d = Sha512::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}