Checkpoint; not sure where this code is, but I'm rethinking.
This commit is contained in:
48
src/rsa/core.rs
Normal file
48
src/rsa/core.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use num::bigint::BigUint;
|
||||
use rsa::errors::RSAError;
|
||||
use simple_asn1::{ASN1Block,ASN1DecodeErr};
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn drop0s(a: &[u8]) -> &[u8] {
|
||||
let mut idx = 0;
|
||||
|
||||
while (idx < a.len()) && (a[idx] == 0) {
|
||||
idx = idx + 1;
|
||||
}
|
||||
|
||||
&a[idx..]
|
||||
}
|
||||
|
||||
pub fn xor_vecs(a: &Vec<u8>, b: &Vec<u8>) -> Vec<u8> {
|
||||
a.iter().zip(b.iter()).map(|(a,b)| a^b).collect()
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
src/rsa/errors.rs
Normal file
27
src/rsa/errors.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use simple_asn1::ASN1DecodeErr;
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RSAError {
|
||||
BadMessageSize,
|
||||
KeyTooSmallForHash,
|
||||
DecryptionError,
|
||||
DecryptHashMismatch,
|
||||
InvalidKey,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
92
src/rsa/mod.rs
Normal file
92
src/rsa/mod.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
//! A simple RSA library.
|
||||
//!
|
||||
//! This library performs all the standard bits and pieces that you'd expect
|
||||
//! from an RSA library, and does so using only Rust. It's a bit slow at the
|
||||
//! moment, but it gets the job done.
|
||||
//!
|
||||
//! 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;
|
||||
mod errors;
|
||||
mod oaep;
|
||||
mod private;
|
||||
mod public;
|
||||
mod signing_hashes;
|
||||
|
||||
pub use self::signing_hashes::{SigningHash,
|
||||
SIGNING_HASH_NULL,
|
||||
SIGNING_HASH_SHA1,
|
||||
SIGNING_HASH_SHA224,
|
||||
SIGNING_HASH_SHA256,
|
||||
SIGNING_HASH_SHA384,
|
||||
SIGNING_HASH_SHA512};
|
||||
pub use self::oaep::OAEPParams;
|
||||
pub use self::private::{RSAPrivate, RSAPrivateKey,
|
||||
RSA512Private, RSA1024Private, RSA2048Private,
|
||||
RSA3072Private, RSA4096Private, RSA8192Private,
|
||||
RSA15360Private};
|
||||
pub use self::public::{RSAPublic, RSAPublicKey,
|
||||
RSA512Public, RSA1024Public, RSA2048Public,
|
||||
RSA3072Public, RSA4096Public, RSA8192Public,
|
||||
RSA15360Public};
|
||||
|
||||
use cryptonum::*;
|
||||
use rand::Rng;
|
||||
|
||||
macro_rules! generate_rsa_pair
|
||||
{
|
||||
($pair: ident, $pub: ident, $priv: ident, $uint: ident, $half: ident) => {
|
||||
pub struct $pair {
|
||||
pub public: $pub,
|
||||
pub private: $priv
|
||||
}
|
||||
|
||||
impl $pair {
|
||||
pub fn new(pu: $pub, pr: $priv) -> $pair {
|
||||
$pair {
|
||||
public: pu,
|
||||
private: pr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate<G: Rng>(rng: &mut G) -> $pair {
|
||||
loop {
|
||||
let e = $uint::from(65537u32);
|
||||
let (p, q) = $pair::generate_pq(rng, &e);
|
||||
let one = $half::from(1u32);
|
||||
let phi = &(&p - &one) * &(&q - &one);
|
||||
let n = &p * &q;
|
||||
if let Some(d) = e.modinv(phi) {
|
||||
let public = $pub::new(n.clone(), e);
|
||||
let private = $priv::new(n, d);
|
||||
return $pair::new(public, private);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_pq<G: Rng>(_rng: &mut G, _e: &$uint) -> ($half, $half)
|
||||
{
|
||||
panic!("generate_pq")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_rsa_pair!(RSA512KeyPair, RSA512Public, RSA512Private, U512, U256);
|
||||
generate_rsa_pair!(RSA1024KeyPair, RSA1024Public, RSA1024Private, U1024, U512);
|
||||
generate_rsa_pair!(RSA2048KeyPair, RSA2048Public, RSA2048Private, U2048, U1024);
|
||||
generate_rsa_pair!(RSA3072KeyPair, RSA3072Public, RSA3072Private, U3072, U1536);
|
||||
generate_rsa_pair!(RSA4096KeyPair, RSA4096Public, RSA4096Private, U4096, U2048);
|
||||
generate_rsa_pair!(RSA8192KeyPair, RSA8192Public, RSA8192Private, U8192, U4096);
|
||||
generate_rsa_pair!(RSA15360KeyPair, RSA15360Public, RSA15360Private, U15360, U7680);
|
||||
48
src/rsa/oaep.rs
Normal file
48
src/rsa/oaep.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use byteorder::{BigEndian,ByteOrder};
|
||||
use digest::{FixedOutput,Input};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// 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: Default + Input + FixedOutput> {
|
||||
pub label: String,
|
||||
phantom: PhantomData<H>
|
||||
}
|
||||
|
||||
impl<H: Default + Input + FixedOutput> OAEPParams<H> {
|
||||
pub fn new(label: String)
|
||||
-> OAEPParams<H>
|
||||
{
|
||||
OAEPParams { label: label, phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub fn hash_len(&self) -> usize {
|
||||
H::default().fixed_result().as_slice().len()
|
||||
}
|
||||
|
||||
pub fn hash(&self, input: &[u8]) -> Vec<u8> {
|
||||
let mut digest = H::default();
|
||||
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 = 0u32;
|
||||
|
||||
while res.len() < len {
|
||||
let mut buffer = [0; 4];
|
||||
BigEndian::write_u32(&mut buffer, counter);
|
||||
let mut digest = H::default();
|
||||
digest.process(input);
|
||||
digest.process(&buffer);
|
||||
let chunk = digest.fixed_result();
|
||||
res.extend_from_slice(chunk.as_slice());
|
||||
counter = counter + 1;
|
||||
}
|
||||
|
||||
res.truncate(len);
|
||||
res
|
||||
}
|
||||
}
|
||||
263
src/rsa/private.rs
Normal file
263
src/rsa/private.rs
Normal file
@@ -0,0 +1,263 @@
|
||||
use cryptonum::*;
|
||||
use digest::{FixedOutput,Input};
|
||||
use rsa::core::{drop0s,pkcs1_pad,xor_vecs};
|
||||
use rsa::errors::RSAError;
|
||||
use rsa::oaep::OAEPParams;
|
||||
use rsa::signing_hashes::SigningHash;
|
||||
|
||||
pub trait RSAPrivateKey<N> {
|
||||
/// Generate a new private key using the given modulus and private
|
||||
/// exponent. You probably don't want to use this function directly
|
||||
/// unless you're writing your own key generation routine or key
|
||||
/// parsing library.
|
||||
fn new(n: N, d: N) -> Self;
|
||||
|
||||
/// Sign the given message with the given private key.
|
||||
fn sign(&self, signhash: &SigningHash, msg: &[u8]) -> Vec<u8>;
|
||||
|
||||
/// Decrypt the provided message using the given OAEP parameters. As
|
||||
/// mentioned in the comment for encryption, RSA decryption is really,
|
||||
/// really slow. So if your plaintext is larger than about half the
|
||||
/// bit size of the key, it's almost certainly a better idea to generate
|
||||
/// a fresh symmetric encryption key, encrypt only the key with RSA, and
|
||||
/// then encrypt the message with that key.
|
||||
fn decrypt<H>(&self, oaep: &OAEPParams<H>, msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where H: Default + Input + FixedOutput;
|
||||
}
|
||||
|
||||
pub enum RSAPrivate {
|
||||
Key512(RSA512Private),
|
||||
Key1024(RSA1024Private),
|
||||
Key2048(RSA2048Private),
|
||||
Key3072(RSA3072Private),
|
||||
Key4096(RSA4096Private),
|
||||
Key8192(RSA8192Private),
|
||||
Key15360(RSA15360Private)
|
||||
}
|
||||
|
||||
// fn print_vector(name: &'static str, bytes: &[u8])
|
||||
// {
|
||||
// print!("{}: (length {}) ", name, bytes.len());
|
||||
// for x in bytes.iter() {
|
||||
// print!("{:02X}", *x);
|
||||
// }
|
||||
// println!("");
|
||||
// }
|
||||
|
||||
macro_rules! generate_rsa_private
|
||||
{
|
||||
($rsa: ident, $num: ident, $bar: ident, $size: expr) => {
|
||||
pub struct $rsa {
|
||||
nu: $bar,
|
||||
d: $num
|
||||
}
|
||||
|
||||
impl RSAPrivateKey<$num> for $rsa {
|
||||
fn new(n: $num, d: $num) -> $rsa {
|
||||
let nu = $bar::new(&n);
|
||||
$rsa { nu: nu, d: d }
|
||||
}
|
||||
|
||||
fn sign(&self, signhash: &SigningHash, msg: &[u8])
|
||||
-> Vec<u8>
|
||||
{
|
||||
let hash = (signhash.run)(msg);
|
||||
let em = pkcs1_pad(&signhash.ident, &hash, $size/8);
|
||||
let m = $num::from_bytes(&em);
|
||||
let s = self.sp1(&m);
|
||||
let sig = s.to_bytes();
|
||||
sig
|
||||
}
|
||||
|
||||
fn decrypt<H>(&self, oaep: &OAEPParams<H>, msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where H: Default + Input + FixedOutput
|
||||
{
|
||||
let mut res = Vec::new();
|
||||
|
||||
for chunk in msg.chunks($size/8) {
|
||||
let mut dchunk = self.oaep_decrypt(oaep, chunk)?;
|
||||
res.append(&mut dchunk);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl $rsa {
|
||||
fn sp1(&self, m: &$num) -> $num {
|
||||
m.modexp(&self.d, &self.nu)
|
||||
}
|
||||
|
||||
fn dp(&self, c: &$num) -> $num {
|
||||
c.modexp(&self.d, &self.nu)
|
||||
}
|
||||
|
||||
fn oaep_decrypt<H>(&self, oaep: &OAEPParams<H>, c: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where
|
||||
H: Default + Input + FixedOutput
|
||||
{
|
||||
let byte_len = $size / 8;
|
||||
// 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 = $num::from_bytes(&c);
|
||||
// Step 2b
|
||||
let m_ip = self.dp(&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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_rsa_private!(RSA512Private, U512, BarrettU512, 512);
|
||||
generate_rsa_private!(RSA1024Private, U1024, BarrettU1024, 1024);
|
||||
generate_rsa_private!(RSA2048Private, U2048, BarrettU2048, 2048);
|
||||
generate_rsa_private!(RSA3072Private, U3072, BarrettU3072, 3072);
|
||||
generate_rsa_private!(RSA4096Private, U4096, BarrettU4096, 4096);
|
||||
generate_rsa_private!(RSA8192Private, U8192, BarrettU8192, 8192);
|
||||
generate_rsa_private!(RSA15360Private, U15360, BarrettU15360, 15360);
|
||||
|
||||
macro_rules! generate_tests {
|
||||
( $( ($mod: ident, $rsa: ident, $num: ident, $size: expr) ),* ) => {
|
||||
$(
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod $mod {
|
||||
use cryptonum::Decoder;
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
use rsa::signing_hashes::*;
|
||||
use sha1::Sha1;
|
||||
use sha2::{Sha224,Sha256,Sha384,Sha512};
|
||||
|
||||
#[test]
|
||||
fn sign() {
|
||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
||||
run_test(fname.to_string(), 6, |case| {
|
||||
let (neg0, dbytes) = case.get("d").unwrap();
|
||||
let (neg1, nbytes) = case.get("n").unwrap();
|
||||
let (neg2, hbytes) = case.get("h").unwrap();
|
||||
let (neg3, mbytes) = case.get("m").unwrap();
|
||||
let (neg4, sbytes) = case.get("s").unwrap();
|
||||
|
||||
assert!(!neg0&&!neg1&&!neg2&&!neg3&&!neg4);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let d = $num::from_bytes(dbytes);
|
||||
let privkey = $rsa::new(n, d);
|
||||
let hashnum = ((hbytes[0] as u16)<<8) + (hbytes[1] as u16);
|
||||
let sighash = match hashnum {
|
||||
0x160 => &SIGNING_HASH_SHA1,
|
||||
0x224 => &SIGNING_HASH_SHA224,
|
||||
0x256 => &SIGNING_HASH_SHA256,
|
||||
0x384 => &SIGNING_HASH_SHA384,
|
||||
0x512 => &SIGNING_HASH_SHA512,
|
||||
_ => panic!("Bad signing hash: {}", hashnum)
|
||||
};
|
||||
let sig = privkey.sign(sighash, &mbytes);
|
||||
assert_eq!(sig, *sbytes);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decrypt() {
|
||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
||||
run_test(fname.to_string(), 6, |case| {
|
||||
let (neg0, dbytes) = case.get("d").unwrap();
|
||||
let (neg1, nbytes) = case.get("n").unwrap();
|
||||
let (neg2, hbytes) = case.get("h").unwrap();
|
||||
let (neg3, mbytes) = case.get("m").unwrap();
|
||||
let (neg4, cbytes) = case.get("c").unwrap();
|
||||
|
||||
assert!(!neg0&&!neg1&&!neg2&&!neg3&&!neg4);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let d = $num::from_bytes(dbytes);
|
||||
let privkey = $rsa::new(n, d);
|
||||
let hashnum = ((hbytes[0] as u16)<<8) + (hbytes[1] as u16);
|
||||
let empty = "".to_string();
|
||||
match hashnum {
|
||||
0x160 => {
|
||||
let oaep = OAEPParams::<Sha1>::new(empty);
|
||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
||||
assert!(plain.is_ok());
|
||||
assert_eq!(*mbytes, plain.unwrap());
|
||||
}
|
||||
0x224 =>{
|
||||
let oaep = OAEPParams::<Sha224>::new(empty);
|
||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
||||
assert!(plain.is_ok());
|
||||
assert_eq!(*mbytes, plain.unwrap());
|
||||
}
|
||||
0x256 => {
|
||||
let oaep = OAEPParams::<Sha256>::new(empty);
|
||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
||||
assert!(plain.is_ok());
|
||||
assert_eq!(*mbytes, plain.unwrap());
|
||||
}
|
||||
0x384 => {
|
||||
let oaep = OAEPParams::<Sha384>::new(empty);
|
||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
||||
assert!(plain.is_ok());
|
||||
assert_eq!(*mbytes, plain.unwrap());
|
||||
}
|
||||
0x512 => {
|
||||
let oaep = OAEPParams::<Sha512>::new(empty);
|
||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
||||
assert!(plain.is_ok());
|
||||
assert_eq!(*mbytes, plain.unwrap());
|
||||
}
|
||||
_ => panic!("Bad signing hash: {}", hashnum)
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
generate_tests!( (RSA512, RSA512Private, U512, 512),
|
||||
(RSA1024, RSA1024Private, U1024, 1024),
|
||||
(RSA2048, RSA2048Private, U2048, 2048),
|
||||
(RSA3072, RSA3072Private, U3072, 3072),
|
||||
(RSA4096, RSA4096Private, U4096, 4096),
|
||||
(RSA8192, RSA8192Private, U8192, 8192),
|
||||
(RSA15360, RSA15360Private, U15360, 15360)
|
||||
);
|
||||
404
src/rsa/public.rs
Normal file
404
src/rsa/public.rs
Normal file
@@ -0,0 +1,404 @@
|
||||
use cryptonum::*;
|
||||
use digest::{FixedOutput,Input};
|
||||
use num::{BigInt,BigUint};
|
||||
use rand::{OsRng,Rng};
|
||||
use rsa::core::{decode_biguint,pkcs1_pad,xor_vecs};
|
||||
use rsa::errors::RSAError;
|
||||
use rsa::oaep::OAEPParams;
|
||||
use rsa::signing_hashes::SigningHash;
|
||||
use simple_asn1::{ASN1Block,ASN1DecodeErr,ASN1EncodeErr,
|
||||
ASN1Class,FromASN1,ToASN1};
|
||||
|
||||
pub trait RSAPublicKey<N> {
|
||||
/// Generate a new public key pair for the given modulus and
|
||||
/// exponent. You should probably not call this directly unless
|
||||
/// you're writing a key generation function or writing your own
|
||||
/// public key parser.
|
||||
fn new(n: N, e: N) -> Self;
|
||||
|
||||
/// Verify that the provided signature is valid; that the private
|
||||
/// key associated with this public key sent exactly this message.
|
||||
/// The hash used here must exactly match the hash used to sign
|
||||
/// the message, including its ASN.1 metadata.
|
||||
fn verify(&self, signhash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool;
|
||||
|
||||
/// Encrypt the message with a hash function, given the appropriate
|
||||
/// label. Please note that RSA encryption is not particularly fast,
|
||||
/// and decryption is very slow indeed. Thus, most crypto systems that
|
||||
/// need asymmetric encryption should generate a symmetric key, encrypt
|
||||
/// that key with RSA encryption, and then encrypt the actual message
|
||||
/// with that symmetric key.
|
||||
///
|
||||
/// In this variant of the function, we use an explicit random number
|
||||
/// generator, just in case you have one you really like. It better be
|
||||
/// cryptographically strong, though, as some of the padding protections
|
||||
/// are relying on it.
|
||||
fn encrypt_rng<G,H>(&self, g: &mut G, oaep: &OAEPParams<H>, msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where
|
||||
G: Rng,
|
||||
H: Default + Input + FixedOutput;
|
||||
|
||||
/// Encrypt the message with a hash function, given the appropriate
|
||||
/// label. Please note that RSA encryption is not particularly fast,
|
||||
/// and decryption is very slow indeed. Thus, most crypto systems that
|
||||
/// need asymmetric encryption should generate a symmetric key, encrypt
|
||||
/// that key with RSA encryption, and then encrypt the actual message
|
||||
/// with that symmetric key.
|
||||
///
|
||||
/// This variant will just use the system RNG for its randomness.
|
||||
fn encrypt<H>(&self,oaep:&OAEPParams<H>,msg:&[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where
|
||||
H: Default + Input + FixedOutput
|
||||
{
|
||||
let mut g = OsRng::new()?;
|
||||
self.encrypt_rng(&mut g, oaep, msg)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RSAPublic {
|
||||
Key512(RSA512Public),
|
||||
Key1024(RSA1024Public),
|
||||
Key2048(RSA2048Public),
|
||||
Key3072(RSA3072Public),
|
||||
Key4096(RSA4096Public),
|
||||
Key8192(RSA8192Public),
|
||||
Key15360(RSA15360Public)
|
||||
}
|
||||
|
||||
impl FromASN1 for RSAPublic {
|
||||
type Error = RSAError;
|
||||
|
||||
fn from_asn1(bs: &[ASN1Block])
|
||||
-> Result<(RSAPublic,&[ASN1Block]),RSAError>
|
||||
{
|
||||
match bs.split_first() {
|
||||
None =>
|
||||
Err(RSAError::ASN1DecodeErr(ASN1DecodeErr::EmptyBuffer)),
|
||||
Some((&ASN1Block::Sequence(_, _, ref items), rest))
|
||||
if items.len() == 2 =>
|
||||
{
|
||||
let n = decode_biguint(&items[0])?;
|
||||
let e = decode_biguint(&items[1])?;
|
||||
let nsize = n.bits();
|
||||
let mut rsa_size = 512;
|
||||
|
||||
while rsa_size < nsize {
|
||||
rsa_size = rsa_size + 256;
|
||||
}
|
||||
match rsa_size {
|
||||
512 => {
|
||||
let n2 = U512::from(n);
|
||||
let e2 = U512::from(e);
|
||||
let res = RSA512Public::new(n2, e2);
|
||||
Ok((RSAPublic::Key512(res), rest))
|
||||
}
|
||||
1024 => {
|
||||
let n2 = U1024::from(n);
|
||||
let e2 = U1024::from(e);
|
||||
let res = RSA1024Public::new(n2, e2);
|
||||
Ok((RSAPublic::Key1024(res), rest))
|
||||
}
|
||||
2048 => {
|
||||
let n2 = U2048::from(n);
|
||||
let e2 = U2048::from(e);
|
||||
let res = RSA2048Public::new(n2, e2);
|
||||
Ok((RSAPublic::Key2048(res), rest))
|
||||
}
|
||||
3072 => {
|
||||
let n2 = U3072::from(n);
|
||||
let e2 = U3072::from(e);
|
||||
let res = RSA3072Public::new(n2, e2);
|
||||
Ok((RSAPublic::Key3072(res), rest))
|
||||
}
|
||||
4096 => {
|
||||
let n2 = U4096::from(n);
|
||||
let e2 = U4096::from(e);
|
||||
let res = RSA4096Public::new(n2, e2);
|
||||
Ok((RSAPublic::Key4096(res), rest))
|
||||
}
|
||||
8192 => {
|
||||
let n2 = U8192::from(n);
|
||||
let e2 = U8192::from(e);
|
||||
let res = RSA8192Public::new(n2, e2);
|
||||
Ok((RSAPublic::Key8192(res), rest))
|
||||
}
|
||||
15360 => {
|
||||
let n2 = U15360::from(n);
|
||||
let e2 = U15360::from(e);
|
||||
let res = RSA15360Public::new(n2, e2);
|
||||
Ok((RSAPublic::Key15360(res), rest))
|
||||
}
|
||||
_ =>
|
||||
Err(RSAError::InvalidKey)
|
||||
}
|
||||
}
|
||||
Some(_) =>
|
||||
Err(RSAError::InvalidKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for RSAPublic {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,Self::Error>
|
||||
{
|
||||
match self {
|
||||
RSAPublic::Key512(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key1024(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key2048(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key3072(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key4096(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key8192(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key15360(x) => x.to_asn1_class(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn print_vector(name: &'static str, bytes: &[u8])
|
||||
// {
|
||||
// print!("{}: (length {}) ", name, bytes.len());
|
||||
// for x in bytes.iter() {
|
||||
// print!("{:02X}", *x);
|
||||
// }
|
||||
// println!("");
|
||||
// }
|
||||
|
||||
macro_rules! generate_rsa_public
|
||||
{
|
||||
($rsa: ident, $num: ident, $bar: ident, $var: ident, $size: expr) => {
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct $rsa {
|
||||
nu: $bar,
|
||||
e: $num
|
||||
}
|
||||
|
||||
impl RSAPublicKey<$num> for $rsa {
|
||||
fn new(n: $num, e: $num) -> $rsa {
|
||||
let nu = $bar::new(&n);
|
||||
$rsa { nu: nu, e: e }
|
||||
}
|
||||
|
||||
fn verify(&self, signhash: &SigningHash, msg: &[u8], sig: &[u8])
|
||||
-> bool
|
||||
{
|
||||
let hash: Vec<u8> = (signhash.run)(msg);
|
||||
let s = $num::from_bytes(&sig);
|
||||
let m = self.vp1(&s);
|
||||
let em = m.to_bytes();
|
||||
let em_ = pkcs1_pad(signhash.ident, &hash, $size/8);
|
||||
em == em_
|
||||
}
|
||||
|
||||
fn encrypt_rng<G,H>(&self,g: &mut G,oaep: &OAEPParams<H>,msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where
|
||||
G: Rng,
|
||||
H: Default + Input + FixedOutput
|
||||
{
|
||||
let byte_len = $size / 8;
|
||||
let mut res = Vec::new();
|
||||
|
||||
if byte_len <= ((2 * oaep.hash_len()) + 2) {
|
||||
return Err(RSAError::KeyTooSmallForHash);
|
||||
}
|
||||
for chunk in msg.chunks(byte_len - (2 * oaep.hash_len()) - 2) {
|
||||
let mut newchunk = self.oaep_encrypt(g, oaep, chunk)?;
|
||||
res.append(&mut newchunk);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl $rsa {
|
||||
fn vp1(&self, s: &$num) -> $num {
|
||||
s.modexp(&self.e, &self.nu)
|
||||
}
|
||||
|
||||
fn ep(&self, m: &$num) -> $num {
|
||||
m.modexp(&self.e, &self.nu)
|
||||
}
|
||||
|
||||
fn oaep_encrypt<G,H>(&self,g: &mut G,oaep: &OAEPParams<H>,m: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where
|
||||
G: Rng,
|
||||
H: Default + Input + FixedOutput
|
||||
{
|
||||
let byte_len = $size / 8;
|
||||
// Step 1b
|
||||
if m.len() > (byte_len - (2 * oaep.hash_len()) - 2) {
|
||||
return Err(RSAError::BadMessageSize)
|
||||
}
|
||||
// Step 2a
|
||||
let mut lhash = oaep.hash(oaep.label.as_bytes());
|
||||
// Step 2b
|
||||
let num0s = byte_len - m.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(m);
|
||||
// Step 2d
|
||||
let seed : Vec<u8> = g.gen_iter().take(oaep.hash_len()).collect();
|
||||
// Step 2e
|
||||
let db_mask = oaep.mgf1(&seed, byte_len - 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 = $num::from_bytes(&em);
|
||||
// Step 3b
|
||||
let c_i = self.ep(&m_i);
|
||||
// Step 3c
|
||||
let c = c_i.to_bytes();
|
||||
Ok(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for $rsa {
|
||||
type Error = RSAError;
|
||||
|
||||
fn from_asn1(bs: &[ASN1Block])
|
||||
-> Result<($rsa,&[ASN1Block]),RSAError>
|
||||
{
|
||||
let (core, rest) = RSAPublic::from_asn1(bs)?;
|
||||
|
||||
match core {
|
||||
RSAPublic::$var(x) => Ok((x, rest)),
|
||||
_ => Err(RSAError::InvalidKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for $rsa {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,Self::Error>
|
||||
{
|
||||
let n = BigInt::from(BigUint::from(self.nu.m.clone()));
|
||||
let e = BigInt::from(BigUint::from(self.e.clone()));
|
||||
let enc_n = ASN1Block::Integer(c, 0, n);
|
||||
let enc_e = ASN1Block::Integer(c, 0, e);
|
||||
let seq = ASN1Block::Sequence(c, 0, vec![enc_n, enc_e]);
|
||||
Ok(vec![seq])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_rsa_public!(RSA512Public, U512, BarrettU512, Key512, 512);
|
||||
generate_rsa_public!(RSA1024Public, U1024, BarrettU1024, Key1024, 1024);
|
||||
generate_rsa_public!(RSA2048Public, U2048, BarrettU2048, Key2048, 2048);
|
||||
generate_rsa_public!(RSA3072Public, U3072, BarrettU3072, Key3072, 3072);
|
||||
generate_rsa_public!(RSA4096Public, U4096, BarrettU4096, Key4096, 4096);
|
||||
generate_rsa_public!(RSA8192Public, U8192, BarrettU8192, Key8192, 8192);
|
||||
generate_rsa_public!(RSA15360Public, U15360, BarrettU15360, Key15360, 15360);
|
||||
|
||||
macro_rules! generate_tests {
|
||||
( $( ($mod: ident, $rsa: ident, $num: ident, $size: expr) ),* ) => {
|
||||
$(
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod $mod {
|
||||
use cryptonum::Decoder;
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
use rsa::signing_hashes::*;
|
||||
|
||||
#[test]
|
||||
fn encode() {
|
||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
||||
run_test(fname.to_string(), 6, |case| {
|
||||
let (neg0, nbytes) = case.get("n").unwrap();
|
||||
|
||||
assert!(!neg0);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let e = $num::from(65537u64);
|
||||
let pubkey = $rsa::new(n, e);
|
||||
let asn1 = pubkey.to_asn1().unwrap();
|
||||
let (pubkey2, _) = $rsa::from_asn1(&asn1).unwrap();
|
||||
assert_eq!(pubkey, pubkey2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify() {
|
||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
||||
run_test(fname.to_string(), 6, |case| {
|
||||
let (neg0, nbytes) = case.get("n").unwrap();
|
||||
let (neg1, hbytes) = case.get("h").unwrap();
|
||||
let (neg2, mbytes) = case.get("m").unwrap();
|
||||
let (neg3, sbytes) = case.get("s").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1 && !neg2 && !neg3);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let e = $num::from(65537u64);
|
||||
let pubkey = $rsa::new(n, e);
|
||||
let hashnum = ((hbytes[0] as u16)<<8) + (hbytes[1] as u16);
|
||||
let sighash = match hashnum {
|
||||
0x160 => &SIGNING_HASH_SHA1,
|
||||
0x224 => &SIGNING_HASH_SHA224,
|
||||
0x256 => &SIGNING_HASH_SHA256,
|
||||
0x384 => &SIGNING_HASH_SHA384,
|
||||
0x512 => &SIGNING_HASH_SHA512,
|
||||
_ => panic!("Bad signing hash: {}", hashnum)
|
||||
};
|
||||
assert!(pubkey.verify(sighash, &mbytes, &sbytes));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypt() {
|
||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
||||
run_test(fname.to_string(), 6, |case| {
|
||||
let (neg0, nbytes) = case.get("n").unwrap();
|
||||
let (neg1, hbytes) = case.get("h").unwrap();
|
||||
let (neg2, mbytes) = case.get("m").unwrap();
|
||||
let (neg3, sbytes) = case.get("s").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1 && !neg2 && !neg3);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let e = $num::from(65537u64);
|
||||
let pubkey = $rsa::new(n, e);
|
||||
let hashnum = ((hbytes[0] as u16)<<8) + (hbytes[1] as u16);
|
||||
let sighash = match hashnum {
|
||||
0x160 => &SIGNING_HASH_SHA1,
|
||||
0x224 => &SIGNING_HASH_SHA224,
|
||||
0x256 => &SIGNING_HASH_SHA256,
|
||||
0x384 => &SIGNING_HASH_SHA384,
|
||||
0x512 => &SIGNING_HASH_SHA512,
|
||||
_ => panic!("Bad signing hash: {}", hashnum)
|
||||
};
|
||||
assert!(pubkey.verify(sighash, &mbytes, &sbytes));
|
||||
});
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
generate_tests!( (RSA512, RSA512Public, U512, 512),
|
||||
(RSA1024, RSA1024Public, U1024, 1024),
|
||||
(RSA2048, RSA2048Public, U2048, 2048),
|
||||
(RSA3072, RSA3072Public, U3072, 3072),
|
||||
(RSA4096, RSA4096Public, U4096, 4096),
|
||||
(RSA8192, RSA8192Public, U8192, 8192),
|
||||
(RSA15360, RSA15360Public, U15360, 15360)
|
||||
);
|
||||
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