Skip to content

Commit 3bd9795

Browse files
Merge pull request #43 from RustCrypto/oaep-dig
2 parents 94ce39d + a2bafc0 commit 3bd9795

14 files changed

+945
-167
lines changed

Cargo.toml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ byteorder = "1.3.1"
2222
thiserror = "1.0.11"
2323
subtle = "2.0.0"
2424
simple_asn1 = "0.4"
25-
pem = { version = "0.7", optional = true }
25+
pem = { version = "0.8", optional = true }
26+
digest = { version = "0.9.0", features = ["std"] }
27+
sha2 = "0.9.0"
2628

2729
[dependencies.zeroize]
2830
version = "1.1.0"
@@ -36,13 +38,13 @@ default-features = false
3638
features = ["std", "derive"]
3739

3840
[dev-dependencies]
39-
base64 = "0.11.0"
40-
sha-1 = "0.8.1"
41-
sha2 = "0.8.0"
41+
base64 = "0.12.0"
4242
hex = "0.4.0"
4343
serde_test = "1.0.89"
4444
rand_xorshift = "0.2.0"
45-
pem = "0.7"
45+
pem = "0.8"
46+
sha-1 = "0.9.0"
47+
sha3 = "0.9.0"
4648

4749
[[bench]]
4850
name = "key"

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,21 @@ A portable RSA implementation in pure Rust.
99
## Example
1010

1111
```rust
12-
extern crate rsa;
13-
extern crate rand;
14-
1512
use rsa::{PublicKey, RSAPrivateKey, PaddingScheme};
1613
use rand::rngs::OsRng;
1714

1815
let mut rng = OsRng;
1916
let bits = 2048;
20-
let key = RSAPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
17+
let priv_key = RSAPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
18+
let pub_key = RSAPublicKey::from(&private_key);
2119

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

2725
// Decrypt
28-
let dec_data = key.decrypt(PaddingScheme::PKCS1v15, &enc_data).expect("failed to decrypt");
26+
let dec_data = priv_key.decrypt(PaddingScheme::new_pkcs1v15(), &enc_data).expect("failed to decrypt");
2927
assert_eq!(&data[..], &dec_data[..]);
3028
```
3129

@@ -41,8 +39,8 @@ There will be three phases before `1.0` :ship: can be released.
4139
- [x] PKCS1v1.5: Encryption & Decryption :white_check_mark:
4240
- [x] PKCS1v1.5: Sign & Verify :white_check_mark:
4341
- [ ] PKCS1v1.5 (session key): Encryption & Decryption
44-
- [ ] OAEP: Encryption & Decryption
45-
- [ ] PSS: Sign & Verify
42+
- [x] OAEP: Encryption & Decryption
43+
- [x] PSS: Sign & Verify
4644
- [x] Key import & export
4745
2. :rocket: Make it fast
4846
- [x] Benchmarks :white_check_mark:

benches/key.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ use base64;
66
use num_bigint::BigUint;
77
use num_traits::{FromPrimitive, Num};
88
use rand::{rngs::StdRng, SeedableRng};
9-
use rsa::hash::Hashes;
10-
use rsa::padding::PaddingScheme;
11-
use rsa::RSAPrivateKey;
9+
use rsa::{Hash, PaddingScheme, RSAPrivateKey};
1210
use sha2::{Digest, Sha256};
1311
use test::Bencher;
1412

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

3533
b.iter(|| {
36-
let res = priv_key.decrypt(PaddingScheme::PKCS1v15, &x).unwrap();
34+
let res = priv_key
35+
.decrypt(PaddingScheme::new_pkcs1v15_encrypt(), &x)
36+
.unwrap();
3737
test::black_box(res);
3838
});
3939
}
@@ -48,8 +48,7 @@ fn bench_rsa_2048_pkcsv1_sign_blinded(b: &mut Bencher) {
4848
let res = priv_key
4949
.sign_blinded(
5050
&mut rng,
51-
PaddingScheme::PKCS1v15,
52-
Some(&Hashes::SHA2_256),
51+
PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA2_256)),
5352
&digest,
5453
)
5554
.unwrap();

src/algorithms.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use digest::DynDigest;
12
use num_bigint::traits::ModInverse;
23
use num_bigint::{BigUint, RandPrime};
34
use num_traits::{FromPrimitive, One, Zero};
@@ -110,3 +111,44 @@ pub fn generate_multi_prime_key<R: Rng>(
110111
primes,
111112
))
112113
}
114+
115+
/// Mask generation function.
116+
///
117+
/// Panics if out is larger than 2**32. This is in accordance with RFC 8017 - PKCS #1 B.2.1
118+
pub fn mgf1_xor(out: &mut [u8], digest: &mut dyn DynDigest, seed: &[u8]) {
119+
let mut counter = [0u8; 4];
120+
let mut i = 0;
121+
122+
const MAX_LEN: u64 = std::u32::MAX as u64 + 1;
123+
assert!(out.len() as u64 <= MAX_LEN);
124+
125+
while i < out.len() {
126+
let mut digest_input = vec![0u8; seed.len() + 4];
127+
digest_input[0..seed.len()].copy_from_slice(seed);
128+
digest_input[seed.len()..].copy_from_slice(&counter);
129+
130+
digest.update(digest_input.as_slice());
131+
let digest_output = &*digest.finalize_reset();
132+
let mut j = 0;
133+
loop {
134+
if j >= digest_output.len() || i >= out.len() {
135+
break;
136+
}
137+
138+
out[i] ^= digest_output[j];
139+
j += 1;
140+
i += 1;
141+
}
142+
inc_counter(&mut counter);
143+
}
144+
}
145+
146+
fn inc_counter(counter: &mut [u8; 4]) {
147+
for i in (0..4).rev() {
148+
counter[i] = counter[i].wrapping_add(1);
149+
if counter[i] != 0 {
150+
// No overflow
151+
return;
152+
}
153+
}
154+
}

src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,6 @@ pub enum Error {
3535
ParseError { reason: String },
3636
#[error("internal error")]
3737
Internal,
38+
#[error("label too long")]
39+
LabelTooLong,
3840
}

src/hash.rs

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
1-
/// A generic trait that exposes the information that is needed for a hash function to be
2-
/// used in `sign` and `verify.`.
3-
pub trait Hash {
4-
/// Returns the length in bytes of a digest.
5-
fn size(&self) -> usize;
6-
7-
/// Returns the ASN1 DER prefix for the the hash function.
8-
fn asn1_prefix(&self) -> Vec<u8>;
9-
}
10-
111
/// A list of provided hashes, implementing `Hash`.
122
#[derive(Debug, Clone, Copy)]
13-
pub enum Hashes {
3+
pub enum Hash {
144
MD5,
155
SHA1,
166
SHA2_224,
@@ -24,67 +14,69 @@ pub enum Hashes {
2414
RIPEMD160,
2515
}
2616

27-
impl Hash for Hashes {
28-
fn size(&self) -> usize {
17+
impl Hash {
18+
/// Returns the length in bytes of a digest.
19+
pub fn size(&self) -> usize {
2920
match *self {
30-
Hashes::MD5 => 16,
31-
Hashes::SHA1 => 20,
32-
Hashes::SHA2_224 => 28,
33-
Hashes::SHA2_256 => 32,
34-
Hashes::SHA2_384 => 48,
35-
Hashes::SHA2_512 => 64,
36-
Hashes::SHA3_256 => 32,
37-
Hashes::SHA3_384 => 48,
38-
Hashes::SHA3_512 => 64,
39-
Hashes::MD5SHA1 => 36,
40-
Hashes::RIPEMD160 => 20,
21+
Hash::MD5 => 16,
22+
Hash::SHA1 => 20,
23+
Hash::SHA2_224 => 28,
24+
Hash::SHA2_256 => 32,
25+
Hash::SHA2_384 => 48,
26+
Hash::SHA2_512 => 64,
27+
Hash::SHA3_256 => 32,
28+
Hash::SHA3_384 => 48,
29+
Hash::SHA3_512 => 64,
30+
Hash::MD5SHA1 => 36,
31+
Hash::RIPEMD160 => 20,
4132
}
4233
}
4334

44-
fn asn1_prefix(&self) -> Vec<u8> {
35+
/// Returns the ASN1 DER prefix for the the hash function.
36+
pub fn asn1_prefix(&self) -> &'static [u8] {
4537
match *self {
46-
Hashes::MD5 => vec![
38+
Hash::MD5 => &[
4739
0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05,
4840
0x05, 0x00, 0x04, 0x10,
4941
],
50-
Hashes::SHA1 => vec![
42+
Hash::SHA1 => &[
5143
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04,
5244
0x14,
5345
],
54-
Hashes::SHA2_224 => vec![
46+
Hash::SHA2_224 => &[
5547
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
5648
0x04, 0x05, 0x00, 0x04, 0x1c,
5749
],
58-
Hashes::SHA2_256 => vec![
50+
Hash::SHA2_256 => &[
5951
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
6052
0x01, 0x05, 0x00, 0x04, 0x20,
6153
],
62-
Hashes::SHA2_384 => vec![
54+
Hash::SHA2_384 => &[
6355
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
6456
0x02, 0x05, 0x00, 0x04, 0x30,
6557
],
6658

67-
Hashes::SHA2_512 => vec![
59+
Hash::SHA2_512 => &[
6860
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
6961
0x03, 0x05, 0x00, 0x04, 0x40,
7062
],
7163

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

78-
Hashes::SHA3_256 => vec![
70+
Hash::SHA3_256 => &[
7971
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
8072
0x08, 0x05, 0x00, 0x04, 0x20,
8173
],
82-
Hashes::SHA3_384 => vec![
74+
Hash::SHA3_384 => &[
8375
30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
8476
0x08, 0x05, 0x00, 0x04, 0x20,
8577
],
8678

87-
Hashes::SHA3_512 => vec![
79+
Hash::SHA3_512 => &[
8880
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
8981
0x0a, 0x05, 0x00, 0x04, 0x40,
9082
],

0 commit comments

Comments
 (0)