From 49b2c92d8744a8e8610ecbad6c6bdd795b3d5284 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 17 Mar 2018 14:47:44 -0700 Subject: [PATCH] Starting to add RSA back in, to see if some of this stuff will work together. --- Cargo.toml | 6 + src/cryptonum/builder.rs | 46 ++++++- src/cryptonum/extended_math.rs | 23 ++-- src/cryptonum/mod.rs | 19 ++- src/cryptonum/primes.rs | 120 +++++++++-------- src/cryptonum/signed.rs | 1 - src/cryptonum/traits.rs | 26 ++++ src/cryptonum/unsigned.rs | 2 + src/lib.rs | 10 ++ src/rsa/core.rs | 59 +++++++++ src/rsa/errors.rs | 39 ++++++ src/rsa/mod.rs | 228 +++++++++++++++++++++++++++++++++ src/rsa/oaep.rs | 47 +++++++ src/rsa/private.rs | 117 +++++++++++++++++ src/rsa/public.rs | 196 ++++++++++++++++++++++++++++ src/rsa/signing_hashes.rs | 119 +++++++++++++++++ 16 files changed, 977 insertions(+), 81 deletions(-) create mode 100644 src/rsa/core.rs create mode 100644 src/rsa/errors.rs create mode 100644 src/rsa/mod.rs create mode 100644 src/rsa/oaep.rs create mode 100644 src/rsa/private.rs create mode 100644 src/rsa/public.rs create mode 100644 src/rsa/signing_hashes.rs diff --git a/Cargo.toml b/Cargo.toml index d710d2b..8fc2c56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,13 @@ license-file = "LICENSE" repository = "https://github.com/acw/simple_crypto" [dependencies] +byteorder = "^1.2.1" +digest = "^0.7.1" +num = "^0.1.39" rand = "^0.3" +sha-1 = "^0.7.0" +sha2 = "^0.7.0" +simple_asn1 = "^0.1.0" [dev-dependencies] quickcheck = "^0.4.1" diff --git a/src/cryptonum/builder.rs b/src/cryptonum/builder.rs index d846cfa..3bf3fe3 100644 --- a/src/cryptonum/builder.rs +++ b/src/cryptonum/builder.rs @@ -441,7 +441,6 @@ macro_rules! construct_unsigned { from_to!($type, $count, u64, from_u64, to_u64); } - impl CryptoNumSerialization for $type { fn bit_size(&self) -> usize { $count * 64 @@ -595,6 +594,51 @@ macro_rules! construct_unsigned { } } + impl CryptoNumModOps for $type { + fn modinv(&self, _b: &Self) -> Self { + panic!("modinv"); + } + fn modexp(&self, _a: &Self, _b: &Self) -> Self { + panic!("modexp"); + } + fn modsq(&self, _v: &Self) -> Self { + panic!("modsq"); + } + } + + impl CryptoNumPrimes for $type { + fn probably_prime(_g: &mut G, _iters: usize) -> bool { + panic!("probably_prime"); + } + fn generate_prime(_g: &mut G, _iters: usize, _e: &Self, _min: &Self) -> Self { + panic!("generate_prime"); + } + } + + impl Into for $type { + fn into(self) -> BigInt { + panic!("into bigint") + } + } + + impl Into for $type { + fn into(self) -> BigUint { + panic!("into big uint") + } + } + + impl From for $type { + fn from(_x: BigInt) -> Self { + panic!("from bigint") + } + } + + impl From for $type { + fn from(_x: BigUint) -> Self { + panic!("from biguint") + } + } + #[cfg(test)] mod $modname { use quickcheck::{Arbitrary,Gen}; diff --git a/src/cryptonum/extended_math.rs b/src/cryptonum/extended_math.rs index 1d86d2d..cfb6cba 100644 --- a/src/cryptonum/extended_math.rs +++ b/src/cryptonum/extended_math.rs @@ -1,18 +1,16 @@ use cryptonum::traits::*; use std::ops::*; -pub fn modinv(e: &U, phi: &U) -> U +pub fn modinv(e: U, phi: U) -> U where S: Clone + CryptoNumBase + CryptoNumSigned, - S: Div + Mul + Neg + Sub, - S: AddAssign, + S: CryptoNumExtended, U: Clone { - let (_, mut x, _): (S, S, S) = extended_euclidean(e, phi); - let int_phi: S = S::new(phi.clone()); + let (_, mut x, _): (S, S, S) = extended_euclidean(e, phi.clone()); + let int_phi: S = S::new(phi); while x.is_negative() { - // FIXME: Unnecessary clone - x += int_phi.clone(); + x += &int_phi; } x.abs() } @@ -22,15 +20,14 @@ pub fn modexp(b: &T, e: &T, m: &T) -> T panic!("modexp") } -pub fn extended_euclidean(a: &U, b: &U) -> (S, S, S) +pub fn extended_euclidean(a: U, b: U) -> (S, S, S) where S: Clone + CryptoNumBase + CryptoNumSigned, - S: Div + Mul + Neg + Sub, - U: Clone + S: CryptoNumExtended { - let posinta = S::new(a.clone()); - let posintb = S::new(b.clone()); - let (mut d, mut x, mut y) = egcd(&posinta, &posintb); + let posinta: S = S::new(a); + let posintb: S = S::new(b); + let (mut d, mut x, mut y) = egcd(posinta, posintb); if d.is_negative() { d = -d; diff --git a/src/cryptonum/mod.rs b/src/cryptonum/mod.rs index 3dce0a5..7ce611e 100644 --- a/src/cryptonum/mod.rs +++ b/src/cryptonum/mod.rs @@ -3,16 +3,27 @@ //! This module is designed to provide large, fixed-width number support for //! the rest of the Simple-Crypto libraries. Feel free to use it other places, //! of course, but that's its origin. +//! +//! Key generation is supported, using either the native `OsRng` or a random +//! number generator of your choice. Obviously, you should be careful to use +//! a cryptographically-sound random number generator sufficient for the +//! security level you're going for. +//! +//! Signing and verification are via standard PKCS1 padding, but can be +//! adjusted based on the exact hash you want. This library also supports +//! somewhat arbitrary signing mechanisms used by your weirder network +//! protocols. (I'm looking at you, Tor.) +//! +//! Encryption and decryption are via the OAEP mechanism, as described in +//! NIST documents. +//! mod core; #[macro_use] mod builder; -mod extended_math; -// mod primes; mod signed; mod traits; mod unsigned; -pub use self::extended_math::{modexp,modinv,extended_euclidean,egcd}; -// pub use self::primes::{probably_prime}; pub use self::signed::{I512,I1024,I2048,I3072,I4096,I7680,I8192,I15360}; pub use self::unsigned::{U512,U1024,U2048,U3072,U4096,U7680,U8192,U15360}; +pub use self::traits::*; diff --git a/src/cryptonum/primes.rs b/src/cryptonum/primes.rs index 27ae90a..1f9ebc3 100644 --- a/src/cryptonum/primes.rs +++ b/src/cryptonum/primes.rs @@ -1,65 +1,64 @@ -use cryptonum::extended_math::modexp; use cryptonum::traits::*; use rand::Rng; use std::ops::*; -static SMALL_PRIMES: [u64; 310] = [ - 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, - 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, - 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, - 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, - 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, - 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, - 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, - 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, - 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, - 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, - 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, - 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, - 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, - 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, - 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, - 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, - 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, - 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, - 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, - 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, - 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, - 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, - 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, - 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, - 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, - 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, - 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, - 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, - 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, - 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, - 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053]; - - -pub fn probably_prime(x: &T, g: &mut G, iters: usize) -> bool - where - G: Rng, - T: Clone + PartialOrd + Rem + Sub, - T: CryptoNumBase + CryptoNumSerialization, -{ - for tester in SMALL_PRIMES.iter() { - if (x % T::from_u64(*tester)) == T::zero() { - return false; - } - } - miller_rabin(g, x, iters) -} - +//static SMALL_PRIMES: [u64; 310] = [ +// 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, +// 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, +// 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, +// 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, +// 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, +// 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, +// 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, +// 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, +// 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, +// 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, +// 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, +// 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, +// 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, +// 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, +// 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, +// 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, +// 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, +// 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, +// 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, +// 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, +// 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, +// 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, +// 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, +// 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, +// 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, +// 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, +// 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, +// 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, +// 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, +// 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, +// 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053]; +// +// +//pub fn probably_prime(x: &T, g: &mut G, iters: usize) -> bool +// where +// G: Rng, +// T: Clone + PartialOrd + Rem + Sub, +// T: CryptoNumBase + CryptoNumSerialization, +//{ +// for tester in SMALL_PRIMES.iter() { +// if (x % T::from_u64(*tester)) == T::zero() { +// return false; +// } +// } +// miller_rabin(g, x, iters) +//} +// fn miller_rabin(g: &mut G, n: T, iters: usize) -> bool where G: Rng, - T: Clone + PartialEq + PartialOrd + Sub, - T: CryptoNumBase + CryptoNumSerialization, + T: Clone + PartialEq + PartialOrd + Sub, + T: CryptoNumBase + CryptoNumSerialization + CryptoNumExtended + ShrAssign, { let one = T::from_u8(1); let two = T::from_u8(2); - let nm1 = n - one; + let nm1 = &n - &one; // Quoth Wikipedia: // write n - 1 as 2^r*d with d odd by factoring powers of 2 from n - 1 let mut d = nm1.clone(); @@ -74,7 +73,7 @@ fn miller_rabin(g: &mut G, n: T, iters: usize) -> bool // pick a random integer a in the range [2, n - 2] let a = random_in_range(g, &two, &nm1); // x <- a^d mod n - let mut x = modexp(&a, &d, &n); + let mut x = a.modexp(&d, &n); // if x = 1 or x = n - 1 then if (&x == &one) || (&x == &nm1) { // continue WitnessLoop @@ -83,7 +82,7 @@ fn miller_rabin(g: &mut G, n: T, iters: usize) -> bool // repeat r - 1 times: for _i in 0..r { // x <- x^2 mod n - x = modexp(&x, &two, &n); + x = x.modexp(&two, &n); // if x = 1 then if &x == &one { // return composite @@ -102,10 +101,8 @@ fn miller_rabin(g: &mut G, n: T, iters: usize) -> bool true } -fn random_in_range(rng: &mut G, min: &T, max: &T) -> T - where - G: Rng, - T: CryptoNumSerialization + PartialOrd +fn random_in_range(rng: &mut G, min: &U, max: &U) -> U + where G: Rng, U: CryptoNumSerialization + PartialOrd { assert_eq!(min.byte_size(), max.byte_size()); loop { @@ -117,13 +114,12 @@ fn random_in_range(rng: &mut G, min: &T, max: &T) -> T } } -fn random_number(rng: &mut G, bytelen: usize) -> T +fn random_number(rng: &mut G, bytelen: usize) -> U where - G: Rng, - T: CryptoNumSerialization + G: Rng, U: CryptoNumSerialization { let components: Vec = rng.gen_iter().take(bytelen).collect(); - T::from_bytes(&components) + U::from_bytes(&components) } diff --git a/src/cryptonum/signed.rs b/src/cryptonum/signed.rs index d8f6e2f..0a8775d 100644 --- a/src/cryptonum/signed.rs +++ b/src/cryptonum/signed.rs @@ -1,4 +1,3 @@ -use cryptonum::core::*; use cryptonum::traits::*; use cryptonum::unsigned::*; use std::cmp::Ordering; diff --git a/src/cryptonum/traits.rs b/src/cryptonum/traits.rs index 8e9e1d1..7af294d 100644 --- a/src/cryptonum/traits.rs +++ b/src/cryptonum/traits.rs @@ -1,3 +1,5 @@ +use rand::Rng; + pub trait CryptoNumBase { /// Generate the zero value for this type. fn zero() -> Self; @@ -71,3 +73,27 @@ pub trait CryptoNumSigned { /// Test if the number is positive. fn is_positive(&self) -> bool; } + +pub trait CryptoNumModOps: Sized +{ + /// Compute the modular inverse of the number. + fn modinv(&self, b: &Self) -> Self; + /// Raise the number to the power of the first value, mod the second. + fn modexp(&self, a: &Self, b: &Self) -> Self; + /// Square the number, mod the given value. + fn modsq(&self, v: &Self) -> Self; +} + +pub trait CryptoNumPrimes +{ + /// Determine if the given number is probably prime using a quick spot + /// check and Miller-Rabin, using the given random number generator + /// and number of iterations. + fn probably_prime(g: &mut G, iters: usize) -> bool; + /// Generate a prime using the given random number generator, using + /// the given number of rounds to determine if the number is probably + /// prime. The other two numbers are a number for which the generator + /// should have a GCD of 1, and the minimum value for the number. + fn generate_prime(g: &mut G, iters: usize, e: &Self, min: &Self) + -> Self; +} diff --git a/src/cryptonum/unsigned.rs b/src/cryptonum/unsigned.rs index 066e096..9e7bf76 100644 --- a/src/cryptonum/unsigned.rs +++ b/src/cryptonum/unsigned.rs @@ -1,5 +1,7 @@ use cryptonum::core::*; use cryptonum::traits::*; +use num::{BigUint,BigInt}; +use rand::Rng; use std::cmp::Ordering; use std::fmt::{Debug,Error,Formatter}; use std::ops::*; diff --git a/src/lib.rs b/src/lib.rs index a7e37fb..0876056 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,14 +11,24 @@ //! when they should use it, and examples. For now, it mostly just fowards //! off to more detailed modules. Help requested! +extern crate byteorder; +extern crate digest; +extern crate num; #[cfg(test)] #[macro_use] extern crate quickcheck; extern crate rand; +extern crate sha1; +extern crate sha2; +extern crate simple_asn1; /// The cryptonum module provides support for large numbers at fixed, /// cryptographically-relevant sizes. pub mod cryptonum; +/// The RSA module performs the basic operations for RSA, and should +/// be used directly only if you're fairly confident about what you're +/// doing. +pub mod rsa; #[cfg(test)] mod test { diff --git a/src/rsa/core.rs b/src/rsa/core.rs new file mode 100644 index 0000000..7693e5d --- /dev/null +++ b/src/rsa/core.rs @@ -0,0 +1,59 @@ +use cryptonum::{CryptoNumModOps}; +use num::BigUint; +use rsa::errors::RSAError; +use simple_asn1::{ASN1DecodeErr,ASN1Block}; + +// encoding PKCS1 stuff +pub fn pkcs1_pad(ident: &[u8], hash: &[u8], keylen: usize) -> Vec { + 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 +} + +// the RSA encryption function +pub fn ep(n: &U, e: &U, m: &U) -> U { + m.modexp(e, n) +} + +// the RSA decryption function +pub fn dp(n: &U, d: &U, c: &U) -> U { + c.modexp(d, n) +} + +// the RSA signature generation function +pub fn sp1(n: &U, d: &U, m: &U) -> U { + m.modexp(d, n) +} + +pub fn decode_biguint(b: &ASN1Block) -> Result { + match b { + &ASN1Block::Integer(_, _, ref v) => { + match v.to_biguint() { + Some(sn) => Ok(sn), + _ => Err(RSAError::InvalidKey) + } + } + _ => + Err(RSAError::ASN1DecodeErr(ASN1DecodeErr::EmptyBuffer)) + } +} + + +// the RSA signature verification function +pub fn vp1(n: &U, e: &U, s: &U) -> U { + s.modexp(e, n) +} + +pub fn xor_vecs(a: &Vec, b: &Vec) -> Vec { + a.iter().zip(b.iter()).map(|(a,b)| a^b).collect() +} + diff --git a/src/rsa/errors.rs b/src/rsa/errors.rs new file mode 100644 index 0000000..8ba9e0a --- /dev/null +++ b/src/rsa/errors.rs @@ -0,0 +1,39 @@ +use simple_asn1::{ASN1DecodeErr}; +use std::io; + +#[derive(Debug)] +pub enum RSAKeyGenError { + InvalidKeySize(usize), RngFailure(io::Error) +} + +impl From for RSAKeyGenError { + fn from(e: io::Error) -> RSAKeyGenError { + RSAKeyGenError::RngFailure(e) + } +} + +#[derive(Debug)] +pub enum RSAError { + BadMessageSize, + KeyTooSmallForHash, + DecryptionError, + DecryptHashMismatch, + InvalidKey, + KeySizeMismatch, + RandomGenError(io::Error), + ASN1DecodeErr(ASN1DecodeErr) +} + +impl From for RSAError { + fn from(e: io::Error) -> RSAError { + RSAError::RandomGenError(e) + } +} + +impl From for RSAError { + fn from(e: ASN1DecodeErr) -> RSAError { + RSAError::ASN1DecodeErr(e) + } +} + + diff --git a/src/rsa/mod.rs b/src/rsa/mod.rs new file mode 100644 index 0000000..d210cd7 --- /dev/null +++ b/src/rsa/mod.rs @@ -0,0 +1,228 @@ +//! # An implementation of RSA. +//! +//! This module is designed to provide implementations of the core routines +//! used for asymmetric cryptography using RSA. It probably provides a bit +//! more flexibility than beginners should play with, and definitely includes +//! some capabilities largely targeted at legacy systems. New users should +//! probably stick with the stuff in the root of this crate. +mod core; +mod errors; +mod oaep; +mod private; +mod public; +mod signing_hashes; + +use cryptonum::*; +use rand::{OsRng,Rng}; +use std::cmp::PartialOrd; +use std::ops::*; + +pub use self::errors::{RSAKeyGenError,RSAError}; +pub use self::oaep::{OAEPParams}; +pub use self::private::RSAPrivateKey; +pub use self::public::RSAPublicKey; +pub use self::signing_hashes::{SigningHash, + SIGNING_HASH_NULL, SIGNING_HASH_SHA1, + SIGNING_HASH_SHA224, SIGNING_HASH_SHA256, + SIGNING_HASH_SHA384, SIGNING_HASH_SHA512}; + +/// An RSA public and private key. +#[derive(Clone,Debug,PartialEq)] +pub struct RSAKeyPair + where + Size: CryptoNumBase + CryptoNumSerialization +{ + pub private: RSAPrivateKey, + pub public: RSAPublicKey +} + +impl RSAKeyPair + where + T: Clone + Sized + PartialOrd, + T: CryptoNumBase + CryptoNumModOps + CryptoNumPrimes + CryptoNumSerialization, + T: Sub + Mul + Shl +{ + /// Generates a fresh RSA key pair. 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() -> Result,RSAKeyGenError> { + let mut rng = OsRng::new()?; + RSAKeyPair::::generate_w_rng(&mut rng) + } + + /// 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(rng: &mut G) + -> Result,RSAKeyGenError> + { + let e = T::from_u32(65537); + let len_bits = e.bit_size(); + match generate_pq(rng, &e) { + None => + return Err(RSAKeyGenError::InvalidKeySize(len_bits)), + Some((p, q)) => { + let n = p.clone() * q.clone(); + let phi = (p - T::from_u64(1)) * (q - T::from_u64(1)); + let d = e.modinv(&phi); + let public_key = RSAPublicKey::new(n.clone(), e); + let private_key = RSAPrivateKey::new(n, d); + return Ok(RSAKeyPair{ private: private_key, public: public_key }) + } + } + } +} + +pub fn generate_pq<'a,G,T>(rng: &mut G, e: &T) -> Option<(T,T)> + where + G: Rng, + T: Clone + PartialOrd + Shl + Sub, + T: CryptoNumBase + CryptoNumPrimes + CryptoNumSerialization +{ + let bitlen = T::zero().bit_size(); + let mindiff = T::from_u8(1) << ((bitlen/2)-101); + let minval = T::from_u64(6074001000) << ((mindiff.bit_size()/2) - 33); + let p = T::generate_prime(rng, 7, e, &minval); + + loop { + let q = T::generate_prime(rng, 7, e, &minval); + + if diff(p.clone(), q.clone()) >= mindiff { + return Some((p, q)); + } + } +} + +fn diff>(a: T, b: T) -> T +{ + if a > b { + a - b + } else { + b - a + } +} + +#[cfg(test)] +mod tests { + use quickcheck::{Arbitrary,Gen}; + use rsa::core::{dp,ep,sp1,vp1}; + use sha2::Sha224; + use simple_asn1::{der_decode,der_encode}; + use super::*; + + impl Arbitrary for RSAKeyPair { + fn arbitrary(g: &mut G) -> RSAKeyPair { + RSAKeyPair::generate_w_rng(g).unwrap() + } + } + + // Core primitive checks + quickcheck! { + fn ep_dp_inversion(kp: RSAKeyPair, m: U512) -> bool { + let realm = &m % &kp.public.n; + let ciphertext = ep(&kp.public.n, &kp.public.e, &realm); + let mprime = dp(&kp.private.n, &kp.private.d, &ciphertext); + mprime == m + } + fn sp_vp_inversion(kp: RSAKeyPair, m: U512) -> bool { + let realm = &m % &kp.public.n; + let sig = sp1(&kp.private.n, &kp.private.d, &realm); + let mprime = vp1(&kp.public.n, &kp.public.e, &sig); + mprime == m + } + } + + // Public key serialization + quickcheck! { + fn asn1_encoding_inverts(kp: RSAKeyPair) -> bool { + let bytes = der_encode(&kp.public).unwrap(); + let pubkey: RSAPublicKey = der_decode(&bytes).unwrap(); + (pubkey.n == kp.public.n) && (pubkey.e == kp.public.e) + } + } + + #[derive(Clone,Debug)] + struct Message { + m: Vec + } + + impl Arbitrary for Message { + fn arbitrary(g: &mut G) -> Message { + let len = 1 + (g.gen::() % 3); + let mut storage = Vec::new(); + for _ in 0..len { + storage.push(g.gen::()); + } + Message{ m: storage } + } + } + + #[derive(Clone,Debug)] + struct KeyPairAndSigHash + where + T: CryptoNumSerialization + CryptoNumBase + { + kp: RSAKeyPair, + sh: &'static SigningHash + } + + impl Arbitrary for KeyPairAndSigHash + where + T: Clone + Sized + PartialOrd, + T: CryptoNumBase + CryptoNumModOps, + T: CryptoNumPrimes + CryptoNumSerialization, + T: Sub + Mul + Shl, + RSAKeyPair: Arbitrary + { + fn arbitrary(g: &mut G) -> KeyPairAndSigHash { + let kp = RSAKeyPair::generate_w_rng(g).unwrap(); + let size = kp.public.n.bit_size(); + let mut hashes = vec![&SIGNING_HASH_SHA1]; + + if size >= 1024 { + hashes.push(&SIGNING_HASH_SHA224); + } + + if size >= 2048 { + hashes.push(&SIGNING_HASH_SHA256); + } + + if size >= 4096 { + hashes.push(&SIGNING_HASH_SHA384); + hashes.push(&SIGNING_HASH_SHA512); + } + + let hash = g.choose(&hashes).unwrap().clone(); + KeyPairAndSigHash{ kp: kp, sh: hash } + } + } + + quickcheck! { + fn sign_verifies(kpsh: KeyPairAndSigHash, m: Message) -> bool { + let sig = kpsh.kp.private.sign(kpsh.sh, &m.m); + kpsh.kp.public.verify(kpsh.sh, &m.m, &sig) + } + + fn enc_dec_roundtrips(kp: RSAKeyPair, m: Message) -> bool { + let oaep = OAEPParams { + hash: Sha224::default(), + label: "test".to_string() + }; + let c = kp.public.encrypt(&oaep, &m.m).unwrap(); + let mp = kp.private.decrypt(&oaep, &c).unwrap(); + + mp == m.m + } + } +} diff --git a/src/rsa/oaep.rs b/src/rsa/oaep.rs new file mode 100644 index 0000000..af3c0e7 --- /dev/null +++ b/src/rsa/oaep.rs @@ -0,0 +1,47 @@ +use byteorder::{BigEndian,ByteOrder}; +use digest::{FixedOutput,Input}; + +/// Parameters for OAEP encryption and decryption: a hash function to use +/// as part of the message generation function (MGF1, if you're curious), +/// and any labels you want to include as part of the encryption. +pub struct OAEPParams { + pub hash: H, + pub label: String +} + +impl OAEPParams { + pub fn new(hash: H, label: String) + -> OAEPParams + { + OAEPParams { hash: hash, label: label } + } + + pub fn hash_len(&self) -> usize { + self.hash.clone().fixed_result().as_slice().len() + } + + pub fn hash(&self, input: &[u8]) -> Vec { + let mut digest = self.hash.clone(); + digest.process(input); + digest.fixed_result().as_slice().to_vec() + } + + pub fn mgf1(&self, input: &[u8], len: usize) -> Vec { + let mut res = Vec::with_capacity(len); + let mut counter: u32 = 0; + + while res.len() < len { + let mut c: [u8; 4] = [0; 4]; + BigEndian::write_u32(&mut c, counter); + let mut digest = self.hash.clone(); + digest.process(input); + digest.process(&c); + let chunk = digest.fixed_result(); + res.extend_from_slice(chunk.as_slice()); + counter += 1; + } + + res.truncate(len); + res + } +} diff --git a/src/rsa/private.rs b/src/rsa/private.rs new file mode 100644 index 0000000..37415df --- /dev/null +++ b/src/rsa/private.rs @@ -0,0 +1,117 @@ +use cryptonum::{CryptoNumBase,CryptoNumModOps,CryptoNumSerialization}; +use digest::{FixedOutput,Input}; +use rsa::core::{dp,sp1,pkcs1_pad,xor_vecs}; +use rsa::oaep::{OAEPParams}; +use rsa::errors::{RSAError}; +use rsa::signing_hashes::SigningHash; + +/// A RSA private key. As with public keys, I've left the size as a +/// parameter: 2048-4096 is standard practice, 512-1024 is weak, and +/// >4096 is going to be slow. +#[derive(Clone,Debug,PartialEq)] +pub struct RSAPrivateKey + where + Size: CryptoNumBase + CryptoNumSerialization +{ + pub(crate) n: Size, + pub(crate) d: Size +} + +impl RSAPrivateKey + where + U: CryptoNumBase + CryptoNumModOps + CryptoNumSerialization +{ + /// Generate a private key, using the given `n` and `d` parameters + /// gathered from some other source. The length should be given in + /// bits. + pub fn new(n: U, d: U) -> RSAPrivateKey { + RSAPrivateKey { + n: n, + d: d + } + } + + /// Sign a message using the given hash. + pub fn sign(&self, sighash: &SigningHash, msg: &[u8]) -> Vec { + let hash = (sighash.run)(msg); + let em = pkcs1_pad(&sighash.ident, &hash, self.d.byte_size()); + let m = U::from_bytes(&em); + let s = sp1(&self.n, &self.d, &m); + let sig = s.to_bytes(); + sig + } + + /// Decrypt a message with the given parameters. + pub fn decrypt(&self, oaep: &OAEPParams, msg: &[u8]) + -> Result,RSAError> + { + let mut res = Vec::new(); + let byte_len = self.d.byte_size(); + + for chunk in msg.chunks(byte_len) { + let mut dchunk = self.oaep_decrypt(oaep, chunk)?; + res.append(&mut dchunk); + } + + Ok(res) + } + + fn oaep_decrypt(&self, oaep: &OAEPParams, c: &[u8]) + -> Result,RSAError> + { + let byte_len = self.d.byte_size(); + // Step 1b + if c.len() != byte_len { + return Err(RSAError::DecryptionError); + } + // Step 1c + if byte_len < ((2 * oaep.hash_len()) + 2) { + return Err(RSAError::DecryptHashMismatch); + } + // Step 2a + let c_ip = U::from_bytes(&c); + // Step 2b + let m_ip = dp(&self.n, &self.d, &c_ip); + // Step 2c + let em = m_ip.to_bytes(); + // Step 3a + let l_hash = oaep.hash(oaep.label.as_bytes()); + // Step 3b + let (y, rest) = em.split_at(1); + let (masked_seed, masked_db) = rest.split_at(oaep.hash_len()); + // Step 3c + let seed_mask = oaep.mgf1(masked_db, oaep.hash_len()); + // Step 3d + let seed = xor_vecs(&masked_seed.to_vec(), &seed_mask); + // Step 3e + let db_mask = oaep.mgf1(&seed, byte_len - oaep.hash_len() - 1); + // Step 3f + let db = xor_vecs(&masked_db.to_vec(), &db_mask); + // Step 3g + let (l_hash2, ps_o_m) = db.split_at(oaep.hash_len()); + let o_m = drop0s(ps_o_m); + let (o, m) = o_m.split_at(1); + // Checks! + if o != [1] { + return Err(RSAError::DecryptionError); + } + if l_hash != l_hash2 { + return Err(RSAError::DecryptionError); + } + if y != [0] { + return Err(RSAError::DecryptionError); + } + + Ok(m.to_vec()) + } +} + +fn drop0s(a: &[u8]) -> &[u8] { + let mut idx = 0; + + while (idx < a.len()) && (a[idx] == 0) { + idx = idx + 1; + } + + &a[idx..] +} diff --git a/src/rsa/public.rs b/src/rsa/public.rs new file mode 100644 index 0000000..fddbf1b --- /dev/null +++ b/src/rsa/public.rs @@ -0,0 +1,196 @@ +use cryptonum::{CryptoNumBase,CryptoNumModOps,CryptoNumSerialization}; +use digest::{FixedOutput,Input}; +use num::{BigInt,BigUint}; +use rand::{OsRng,Rng}; +use rsa::core::{ep,vp1,pkcs1_pad,xor_vecs,decode_biguint}; +use rsa::oaep::{OAEPParams}; +use rsa::errors::{RSAError}; +use rsa::signing_hashes::SigningHash; +use simple_asn1::{FromASN1,ToASN1,ASN1DecodeErr,ASN1EncodeErr}; +use simple_asn1::{ASN1Block,ASN1Class}; + +/// An RSA public key with the given modulus size. I've left the size as a +/// parameter, instead of hardcoding particular values. That being said, +/// you almost certainly want one of `U2048`, `U3072`, or `U4096` if you're +/// being pretty standard; `U512` or `U1024` if you're interfacing with +/// legacy code or want to build intentionally weak systems; or `U7680`, +/// `U8192`, or `U15360` if you like things running very slowly. +#[derive(Clone,Debug,PartialEq)] +pub struct RSAPublicKey + where + Size: CryptoNumBase + CryptoNumSerialization +{ + pub(crate) n: Size, + pub(crate) e: Size +} + +impl RSAPublicKey + where + U: CryptoNumBase + CryptoNumModOps + CryptoNumSerialization +{ + /// Create a new RSA public key from the given components, which you found + /// via some other mechanism. + pub fn new(n: U, e: U) -> RSAPublicKey { + RSAPublicKey{ n: n, e: e } + } + + /// Verify the signature for a given message, using the given signing hash, + /// return true iff the signature validates. + pub fn verify(&self, sighash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool + { + let hash = (sighash.run)(msg); + let s = U::from_bytes(sig); + let m = vp1(&self.n, &self.e, &s); + let em = s.to_bytes(); + let em_ = pkcs1_pad(&sighash.ident, &hash, m.byte_size()); + em == em_ + } + + /// Encrypt the given data with the public key and parameters, returning + /// the encrypted blob or an error encountered during encryption. + /// + /// OAEP encoding is used for this process, which requires a random number + /// generator. This version of the function uses `OsRng`. If you want to + /// use your own RNG, use `encrypt_w_rng`. + pub fn encrypt(&self, oaep: &OAEPParams, + msg: &[u8]) + -> Result,RSAError> + { + let mut g = OsRng::new()?; + self.encrypt_with_rng(&mut g, oaep, msg) + } + + /// Encrypt the given data with the public key and parameters, returning + /// the encrypted blob or an error encountered during encryption. This + /// version also allows you to provide your own RNG, if you really feel + /// like shooting yourself in the foot. + pub fn encrypt_with_rng(&self, g: &mut G, oaep: &OAEPParams, + msg: &[u8]) + -> Result,RSAError> + where G: Rng, H: Clone + Input + FixedOutput + { + let mylen = self.e.byte_size(); + + if mylen <= ((2 * oaep.hash_len()) + 2) { + return Err(RSAError::KeyTooSmallForHash); + } + + let mut res = Vec::new(); + + for chunk in msg.chunks(mylen - (2 * oaep.hash_len()) - 2) { + let mut newchunk = self.oaep_encrypt(g, oaep, chunk)?; + res.append(&mut newchunk) + } + + Ok(res) + } + + fn oaep_encrypt(&self, g: &mut G, oaep: &OAEPParams, msg: &[u8]) + -> Result,RSAError> + where + G: Rng, H:Clone + Input + FixedOutput + { + let mylen = self.e.byte_size(); + + // Step 1b + if msg.len() > (mylen - (2 * oaep.hash_len()) - 2) { + return Err(RSAError::BadMessageSize) + } + // Step 2a + let mut lhash = oaep.hash(oaep.label.as_bytes()); + // Step 2b + let num0s = mylen - msg.len() - (2 * oaep.hash_len()) - 2; + let mut ps = Vec::new(); + ps.resize(num0s, 0); + // Step 2c + let mut db = Vec::new(); + db.append(&mut lhash); + db.append(&mut ps); + db.push(1); + db.extend_from_slice(msg); + // Step 2d + let seed : Vec = g.gen_iter().take(oaep.hash_len()).collect(); + // Step 2e + let db_mask = oaep.mgf1(&seed, mylen - oaep.hash_len() - 1); + // Step 2f + let mut masked_db = xor_vecs(&db, &db_mask); + // Step 2g + let seed_mask = oaep.mgf1(&masked_db, oaep.hash_len()); + // Step 2h + let mut masked_seed = xor_vecs(&seed, &seed_mask); + // Step 2i + let mut em = Vec::new(); + em.push(0); + em.append(&mut masked_seed); + em.append(&mut masked_db); + // Step 3a + let m_i = U::from_bytes(&em); + // Step 3b + let c_i = ep(&self.n, &self.e, &m_i); + // Step 3c + let c = c_i.to_bytes(); + Ok(c) + } +} + +impl FromASN1 for RSAPublicKey + where + T: CryptoNumBase + CryptoNumSerialization, + T: From +{ + type Error = RSAError; + + fn from_asn1(bs: &[ASN1Block]) + -> Result<(RSAPublicKey,&[ASN1Block]),RSAError> + { + match bs.split_first() { + None => + Err(RSAError::ASN1DecodeErr(ASN1DecodeErr::EmptyBuffer)), + Some((&ASN1Block::Sequence(_, _, ref items), rest)) + if items.len() == 2 => + { + let numn = decode_biguint(&items[0])?; + let nume = decode_biguint(&items[1])?; + let nsize = numn.bits(); + let mut rsa_size = 512; + + while rsa_size < nsize { + rsa_size = rsa_size + 256; + } + rsa_size /= 8; + + if rsa_size != (T::from_u8(0)).bit_size() { + return Err(RSAError::KeySizeMismatch); + } + + let n = T::from(numn); + let e = T::from(nume); + + let res = RSAPublicKey{ n: n, e: e }; + + Ok((res, rest)) + } + Some(_) => + Err(RSAError::InvalidKey) + } + } +} + +impl ToASN1 for RSAPublicKey + where + T: Clone + Into, + T: CryptoNumBase + CryptoNumSerialization +{ + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, c: ASN1Class) + -> Result,Self::Error> + { + let enc_n = ASN1Block::Integer(c, 0, self.n.clone().into()); + let enc_e = ASN1Block::Integer(c, 0, self.e.clone().into()); + let seq = ASN1Block::Sequence(c, 0, vec![enc_n, enc_e]); + Ok(vec![seq]) + } +} + + diff --git a/src/rsa/signing_hashes.rs b/src/rsa/signing_hashes.rs new file mode 100644 index 0000000..0b40955 --- /dev/null +++ b/src/rsa/signing_hashes.rs @@ -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 +} + +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 { + 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 { + 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 { + 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 { + 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 { + 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 { + let mut d = Sha512::default(); + d.process(i); + d.fixed_result().as_slice().to_vec() +} + +