Starting to add RSA back in, to see if some of this stuff will work together.
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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: Rng>(_g: &mut G, _iters: usize) -> bool {
|
||||
panic!("probably_prime");
|
||||
}
|
||||
fn generate_prime<G: Rng>(_g: &mut G, _iters: usize, _e: &Self, _min: &Self) -> Self {
|
||||
panic!("generate_prime");
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BigInt> for $type {
|
||||
fn into(self) -> BigInt {
|
||||
panic!("into bigint")
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BigUint> for $type {
|
||||
fn into(self) -> BigUint {
|
||||
panic!("into big uint")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BigInt> for $type {
|
||||
fn from(_x: BigInt) -> Self {
|
||||
panic!("from bigint")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BigUint> for $type {
|
||||
fn from(_x: BigUint) -> Self {
|
||||
panic!("from biguint")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod $modname {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
use cryptonum::traits::*;
|
||||
use std::ops::*;
|
||||
|
||||
pub fn modinv<S,U>(e: &U, phi: &U) -> U
|
||||
pub fn modinv<S,U>(e: U, phi: U) -> U
|
||||
where
|
||||
S: Clone + CryptoNumBase + CryptoNumSigned<Unsigned=U>,
|
||||
S: Div<Output=S> + Mul<Output=S> + Neg<Output=S> + Sub<Output=S>,
|
||||
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<T>(b: &T, e: &T, m: &T) -> T
|
||||
panic!("modexp")
|
||||
}
|
||||
|
||||
pub fn extended_euclidean<U,S>(a: &U, b: &U) -> (S, S, S)
|
||||
pub fn extended_euclidean<U,S>(a: U, b: U) -> (S, S, S)
|
||||
where
|
||||
S: Clone + CryptoNumBase + CryptoNumSigned<Unsigned=U>,
|
||||
S: Div<Output=S> + Mul<Output=S> + Neg<Output=S> + Sub<Output=S>,
|
||||
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;
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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<G,T>(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<G,T>(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,T>(g: &mut G, n: T, iters: usize) -> bool
|
||||
where
|
||||
G: Rng,
|
||||
T: Clone + PartialEq + PartialOrd + Sub,
|
||||
T: CryptoNumBase + CryptoNumSerialization,
|
||||
T: Clone + PartialEq + PartialOrd + Sub<Output=T>,
|
||||
T: CryptoNumBase + CryptoNumSerialization + CryptoNumExtended + ShrAssign<u32>,
|
||||
{
|
||||
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,T>(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,T>(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,T>(g: &mut G, n: T, iters: usize) -> bool
|
||||
true
|
||||
}
|
||||
|
||||
fn random_in_range<G,T>(rng: &mut G, min: &T, max: &T) -> T
|
||||
where
|
||||
G: Rng,
|
||||
T: CryptoNumSerialization + PartialOrd
|
||||
fn random_in_range<G,U>(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<G,T>(rng: &mut G, min: &T, max: &T) -> T
|
||||
}
|
||||
}
|
||||
|
||||
fn random_number<G,T>(rng: &mut G, bytelen: usize) -> T
|
||||
fn random_number<G,U>(rng: &mut G, bytelen: usize) -> U
|
||||
where
|
||||
G: Rng,
|
||||
T: CryptoNumSerialization
|
||||
G: Rng, U: CryptoNumSerialization
|
||||
{
|
||||
let components: Vec<u8> = rng.gen_iter().take(bytelen).collect();
|
||||
T::from_bytes(&components)
|
||||
U::from_bytes(&components)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use cryptonum::core::*;
|
||||
use cryptonum::traits::*;
|
||||
use cryptonum::unsigned::*;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
@@ -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: Rng>(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: Rng>(g: &mut G, iters: usize, e: &Self, min: &Self)
|
||||
-> Self;
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
10
src/lib.rs
10
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 {
|
||||
|
||||
59
src/rsa/core.rs
Normal file
59
src/rsa/core.rs
Normal file
@@ -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<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
|
||||
}
|
||||
|
||||
// the RSA encryption function
|
||||
pub fn ep<U: CryptoNumModOps>(n: &U, e: &U, m: &U) -> U {
|
||||
m.modexp(e, n)
|
||||
}
|
||||
|
||||
// the RSA decryption function
|
||||
pub fn dp<U: CryptoNumModOps>(n: &U, d: &U, c: &U) -> U {
|
||||
c.modexp(d, n)
|
||||
}
|
||||
|
||||
// the RSA signature generation function
|
||||
pub fn sp1<U: CryptoNumModOps>(n: &U, d: &U, m: &U) -> U {
|
||||
m.modexp(d, n)
|
||||
}
|
||||
|
||||
pub fn decode_biguint(b: &ASN1Block) -> Result<BigUint,RSAError> {
|
||||
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<U: CryptoNumModOps>(n: &U, e: &U, s: &U) -> U {
|
||||
s.modexp(e, n)
|
||||
}
|
||||
|
||||
pub fn xor_vecs(a: &Vec<u8>, b: &Vec<u8>) -> Vec<u8> {
|
||||
a.iter().zip(b.iter()).map(|(a,b)| a^b).collect()
|
||||
}
|
||||
|
||||
39
src/rsa/errors.rs
Normal file
39
src/rsa/errors.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use simple_asn1::{ASN1DecodeErr};
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RSAError {
|
||||
BadMessageSize,
|
||||
KeyTooSmallForHash,
|
||||
DecryptionError,
|
||||
DecryptHashMismatch,
|
||||
InvalidKey,
|
||||
KeySizeMismatch,
|
||||
RandomGenError(io::Error),
|
||||
ASN1DecodeErr(ASN1DecodeErr)
|
||||
}
|
||||
|
||||
impl From<io::Error> for RSAError {
|
||||
fn from(e: io::Error) -> RSAError {
|
||||
RSAError::RandomGenError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ASN1DecodeErr> for RSAError {
|
||||
fn from(e: ASN1DecodeErr) -> RSAError {
|
||||
RSAError::ASN1DecodeErr(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
228
src/rsa/mod.rs
Normal file
228
src/rsa/mod.rs
Normal file
@@ -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<Size>
|
||||
where
|
||||
Size: CryptoNumBase + CryptoNumSerialization
|
||||
{
|
||||
pub private: RSAPrivateKey<Size>,
|
||||
pub public: RSAPublicKey<Size>
|
||||
}
|
||||
|
||||
impl<T> RSAKeyPair<T>
|
||||
where
|
||||
T: Clone + Sized + PartialOrd,
|
||||
T: CryptoNumBase + CryptoNumModOps + CryptoNumPrimes + CryptoNumSerialization,
|
||||
T: Sub<Output=T> + Mul<Output=T> + Shl<usize,Output=T>
|
||||
{
|
||||
/// 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<RSAKeyPair<T>,RSAKeyGenError> {
|
||||
let mut rng = OsRng::new()?;
|
||||
RSAKeyPair::<T>::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<G: Rng>(rng: &mut G)
|
||||
-> Result<RSAKeyPair<T>,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<usize,Output=T> + Sub<Output=T>,
|
||||
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<T: PartialOrd + Sub<Output=T>>(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<U512> {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> RSAKeyPair<U512> {
|
||||
RSAKeyPair::generate_w_rng(g).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// Core primitive checks
|
||||
quickcheck! {
|
||||
fn ep_dp_inversion(kp: RSAKeyPair<U512>, 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<U512>, 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<U512>) -> bool {
|
||||
let bytes = der_encode(&kp.public).unwrap();
|
||||
let pubkey: RSAPublicKey<U512> = der_decode(&bytes).unwrap();
|
||||
(pubkey.n == kp.public.n) && (pubkey.e == kp.public.e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
struct Message {
|
||||
m: Vec<u8>
|
||||
}
|
||||
|
||||
impl Arbitrary for Message {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> Message {
|
||||
let len = 1 + (g.gen::<u8>() % 3);
|
||||
let mut storage = Vec::new();
|
||||
for _ in 0..len {
|
||||
storage.push(g.gen::<u8>());
|
||||
}
|
||||
Message{ m: storage }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
struct KeyPairAndSigHash<T>
|
||||
where
|
||||
T: CryptoNumSerialization + CryptoNumBase
|
||||
{
|
||||
kp: RSAKeyPair<T>,
|
||||
sh: &'static SigningHash
|
||||
}
|
||||
|
||||
impl<T> Arbitrary for KeyPairAndSigHash<T>
|
||||
where
|
||||
T: Clone + Sized + PartialOrd,
|
||||
T: CryptoNumBase + CryptoNumModOps,
|
||||
T: CryptoNumPrimes + CryptoNumSerialization,
|
||||
T: Sub<Output=T> + Mul<Output=T> + Shl<usize,Output=T>,
|
||||
RSAKeyPair<T>: Arbitrary
|
||||
{
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> KeyPairAndSigHash<T> {
|
||||
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<U512>, 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<U512>, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/rsa/oaep.rs
Normal file
47
src/rsa/oaep.rs
Normal file
@@ -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<H: Clone + Input + FixedOutput> {
|
||||
pub hash: H,
|
||||
pub label: String
|
||||
}
|
||||
|
||||
impl<H: Clone + Input + FixedOutput> OAEPParams<H> {
|
||||
pub fn new(hash: H, label: String)
|
||||
-> OAEPParams<H>
|
||||
{
|
||||
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<u8> {
|
||||
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<u8> {
|
||||
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
|
||||
}
|
||||
}
|
||||
117
src/rsa/private.rs
Normal file
117
src/rsa/private.rs
Normal file
@@ -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<Size>
|
||||
where
|
||||
Size: CryptoNumBase + CryptoNumSerialization
|
||||
{
|
||||
pub(crate) n: Size,
|
||||
pub(crate) d: Size
|
||||
}
|
||||
|
||||
impl<U> RSAPrivateKey<U>
|
||||
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<U> {
|
||||
RSAPrivateKey {
|
||||
n: n,
|
||||
d: d
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign a message using the given hash.
|
||||
pub fn sign(&self, sighash: &SigningHash, msg: &[u8]) -> Vec<u8> {
|
||||
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<H: Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>, msg: &[u8])
|
||||
-> Result<Vec<u8>,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<H: Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>, c: &[u8])
|
||||
-> Result<Vec<u8>,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..]
|
||||
}
|
||||
196
src/rsa/public.rs
Normal file
196
src/rsa/public.rs
Normal file
@@ -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<Size>
|
||||
where
|
||||
Size: CryptoNumBase + CryptoNumSerialization
|
||||
{
|
||||
pub(crate) n: Size,
|
||||
pub(crate) e: Size
|
||||
}
|
||||
|
||||
impl<U> RSAPublicKey<U>
|
||||
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<U> {
|
||||
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<H:Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>,
|
||||
msg: &[u8])
|
||||
-> Result<Vec<u8>,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<G,H>(&self, g: &mut G, oaep: &OAEPParams<H>,
|
||||
msg: &[u8])
|
||||
-> Result<Vec<u8>,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<G,H>(&self, g: &mut G, oaep: &OAEPParams<H>, msg: &[u8])
|
||||
-> Result<Vec<u8>,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<u8> = 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<T> FromASN1 for RSAPublicKey<T>
|
||||
where
|
||||
T: CryptoNumBase + CryptoNumSerialization,
|
||||
T: From<BigUint>
|
||||
{
|
||||
type Error = RSAError;
|
||||
|
||||
fn from_asn1(bs: &[ASN1Block])
|
||||
-> Result<(RSAPublicKey<T>,&[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<T> ToASN1 for RSAPublicKey<T>
|
||||
where
|
||||
T: Clone + Into<BigInt>,
|
||||
T: CryptoNumBase + CryptoNumSerialization
|
||||
{
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,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])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
119
src/rsa/signing_hashes.rs
Normal file
119
src/rsa/signing_hashes.rs
Normal 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()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user