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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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