Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ byteorder = "1.3.1"
thiserror = "1.0.11"
subtle = "2.0.0"
simple_asn1 = "0.4"
pem = { version = "0.7", optional = true }
pem = { version = "0.8", optional = true }
digest = { version = "0.9.0", features = ["std"] }
sha2 = "0.9.0"

[dependencies.zeroize]
version = "1.1.0"
Expand All @@ -36,13 +38,13 @@ default-features = false
features = ["std", "derive"]

[dev-dependencies]
base64 = "0.11.0"
sha-1 = "0.8.1"
sha2 = "0.8.0"
base64 = "0.12.0"
hex = "0.4.0"
serde_test = "1.0.89"
rand_xorshift = "0.2.0"
pem = "0.7"
pem = "0.8"
sha-1 = "0.9.0"
sha3 = "0.9.0"

[[bench]]
name = "key"
Expand Down
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,21 @@ A portable RSA implementation in pure Rust.
## Example

```rust
extern crate rsa;
extern crate rand;

use rsa::{PublicKey, RSAPrivateKey, PaddingScheme};
use rand::rngs::OsRng;

let mut rng = OsRng;
let bits = 2048;
let key = RSAPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let priv_key = RSAPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let pub_key = RSAPublicKey::from(&private_key);

// Encrypt
let data = b"hello world";
let enc_data = key.encrypt(&mut rng, PaddingScheme::PKCS1v15, &data[..]).expect("failed to encrypt");
let enc_data = pub_key.encrypt(&mut rng, PaddingScheme::new_pkcs1v15(), &data[..]).expect("failed to encrypt");
assert_ne!(&data[..], &enc_data[..]);

// Decrypt
let dec_data = key.decrypt(PaddingScheme::PKCS1v15, &enc_data).expect("failed to decrypt");
let dec_data = priv_key.decrypt(PaddingScheme::new_pkcs1v15(), &enc_data).expect("failed to decrypt");
assert_eq!(&data[..], &dec_data[..]);
```

Expand All @@ -41,8 +39,8 @@ There will be three phases before `1.0` :ship: can be released.
- [x] PKCS1v1.5: Encryption & Decryption :white_check_mark:
- [x] PKCS1v1.5: Sign & Verify :white_check_mark:
- [ ] PKCS1v1.5 (session key): Encryption & Decryption
- [ ] OAEP: Encryption & Decryption
- [ ] PSS: Sign & Verify
- [x] OAEP: Encryption & Decryption
- [x] PSS: Sign & Verify
- [x] Key import & export
2. :rocket: Make it fast
- [x] Benchmarks :white_check_mark:
Expand Down
11 changes: 5 additions & 6 deletions benches/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ use base64;
use num_bigint::BigUint;
use num_traits::{FromPrimitive, Num};
use rand::{rngs::StdRng, SeedableRng};
use rsa::hash::Hashes;
use rsa::padding::PaddingScheme;
use rsa::RSAPrivateKey;
use rsa::{Hash, PaddingScheme, RSAPrivateKey};
use sha2::{Digest, Sha256};
use test::Bencher;

Expand All @@ -33,7 +31,9 @@ fn bench_rsa_2048_pkcsv1_decrypt(b: &mut Bencher) {
let x = base64::decode(DECRYPT_VAL).unwrap();

b.iter(|| {
let res = priv_key.decrypt(PaddingScheme::PKCS1v15, &x).unwrap();
let res = priv_key
.decrypt(PaddingScheme::new_pkcs1v15_encrypt(), &x)
.unwrap();
test::black_box(res);
});
}
Expand All @@ -48,8 +48,7 @@ fn bench_rsa_2048_pkcsv1_sign_blinded(b: &mut Bencher) {
let res = priv_key
.sign_blinded(
&mut rng,
PaddingScheme::PKCS1v15,
Some(&Hashes::SHA2_256),
PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA2_256)),
&digest,
)
.unwrap();
Expand Down
42 changes: 42 additions & 0 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use digest::DynDigest;
use num_bigint::traits::ModInverse;
use num_bigint::{BigUint, RandPrime};
use num_traits::{FromPrimitive, One, Zero};
Expand Down Expand Up @@ -110,3 +111,44 @@ pub fn generate_multi_prime_key<R: Rng>(
primes,
))
}

/// Mask generation function.
///
/// Panics if out is larger than 2**32. This is in accordance with RFC 8017 - PKCS #1 B.2.1
pub fn mgf1_xor(out: &mut [u8], digest: &mut dyn DynDigest, seed: &[u8]) {
let mut counter = [0u8; 4];
let mut i = 0;

const MAX_LEN: u64 = std::u32::MAX as u64 + 1;
assert!(out.len() as u64 <= MAX_LEN);

while i < out.len() {
let mut digest_input = vec![0u8; seed.len() + 4];
digest_input[0..seed.len()].copy_from_slice(seed);
digest_input[seed.len()..].copy_from_slice(&counter);

digest.update(digest_input.as_slice());
let digest_output = &*digest.finalize_reset();
let mut j = 0;
loop {
if j >= digest_output.len() || i >= out.len() {
break;
}

out[i] ^= digest_output[j];
j += 1;
i += 1;
}
inc_counter(&mut counter);
}
}

fn inc_counter(counter: &mut [u8; 4]) {
for i in (0..4).rev() {
counter[i] = counter[i].wrapping_add(1);
if counter[i] != 0 {
// No overflow
return;
}
}
}
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ pub enum Error {
ParseError { reason: String },
#[error("internal error")]
Internal,
#[error("label too long")]
LabelTooLong,
}
64 changes: 28 additions & 36 deletions src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
/// A generic trait that exposes the information that is needed for a hash function to be
/// used in `sign` and `verify.`.
pub trait Hash {
/// Returns the length in bytes of a digest.
fn size(&self) -> usize;

/// Returns the ASN1 DER prefix for the the hash function.
fn asn1_prefix(&self) -> Vec<u8>;
}

/// A list of provided hashes, implementing `Hash`.
#[derive(Debug, Clone, Copy)]
pub enum Hashes {
pub enum Hash {
MD5,
SHA1,
SHA2_224,
Expand All @@ -24,67 +14,69 @@ pub enum Hashes {
RIPEMD160,
}

impl Hash for Hashes {
fn size(&self) -> usize {
impl Hash {
/// Returns the length in bytes of a digest.
pub fn size(&self) -> usize {
match *self {
Hashes::MD5 => 16,
Hashes::SHA1 => 20,
Hashes::SHA2_224 => 28,
Hashes::SHA2_256 => 32,
Hashes::SHA2_384 => 48,
Hashes::SHA2_512 => 64,
Hashes::SHA3_256 => 32,
Hashes::SHA3_384 => 48,
Hashes::SHA3_512 => 64,
Hashes::MD5SHA1 => 36,
Hashes::RIPEMD160 => 20,
Hash::MD5 => 16,
Hash::SHA1 => 20,
Hash::SHA2_224 => 28,
Hash::SHA2_256 => 32,
Hash::SHA2_384 => 48,
Hash::SHA2_512 => 64,
Hash::SHA3_256 => 32,
Hash::SHA3_384 => 48,
Hash::SHA3_512 => 64,
Hash::MD5SHA1 => 36,
Hash::RIPEMD160 => 20,
}
}

fn asn1_prefix(&self) -> Vec<u8> {
/// Returns the ASN1 DER prefix for the the hash function.
pub fn asn1_prefix(&self) -> &'static [u8] {
match *self {
Hashes::MD5 => vec![
Hash::MD5 => &[
0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05,
0x05, 0x00, 0x04, 0x10,
],
Hashes::SHA1 => vec![
Hash::SHA1 => &[
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04,
0x14,
],
Hashes::SHA2_224 => vec![
Hash::SHA2_224 => &[
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
0x04, 0x05, 0x00, 0x04, 0x1c,
],
Hashes::SHA2_256 => vec![
Hash::SHA2_256 => &[
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
0x01, 0x05, 0x00, 0x04, 0x20,
],
Hashes::SHA2_384 => vec![
Hash::SHA2_384 => &[
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
0x02, 0x05, 0x00, 0x04, 0x30,
],

Hashes::SHA2_512 => vec![
Hash::SHA2_512 => &[
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
0x03, 0x05, 0x00, 0x04, 0x40,
],

// A special TLS case which doesn't use an ASN1 prefix
Hashes::MD5SHA1 => Vec::new(),
Hashes::RIPEMD160 => vec![
Hash::MD5SHA1 => &[],
Hash::RIPEMD160 => &[
0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14,
],

Hashes::SHA3_256 => vec![
Hash::SHA3_256 => &[
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
0x08, 0x05, 0x00, 0x04, 0x20,
],
Hashes::SHA3_384 => vec![
Hash::SHA3_384 => &[
30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
0x08, 0x05, 0x00, 0x04, 0x20,
],

Hashes::SHA3_512 => vec![
Hash::SHA3_512 => &[
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
0x0a, 0x05, 0x00, 0x04, 0x40,
],
Expand Down
Loading