Skip to content

Update RSA signature traits implementations #179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 7, 2022
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
11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,17 @@ digest = { version = "0.10.0", default-features = false, features = ["alloc"] }
pkcs1 = { version = "0.4", default-features = false, features = ["pkcs8", "alloc"] }
pkcs8 = { version = "0.9", default-features = false, features = ["alloc"] }
#To keep the rand_core versions properly pinnen, specify exact version
signature = { version = ">=1.5, <1.7", default-features = false , features = ["rand-preview"] }
signature = { version = ">=1.5, <1.7", default-features = false , features = ["digest-preview", "rand-preview"] }
zeroize = { version = "1", features = ["alloc"] }

# Temporary workaround until https://github.com/dignifiedquire/num-bigint/pull/42 lands
smallvec = { version = "1.6.1", default-features = false }

# Temporary until the link from Digest to OID is moved to corresponding crates
sha1 = { version = "0.10.1", default-features = false, optional = true }
sha2 = { version = "0.10.2", default-features = false, optional = true }
sha3 = { version = "0.10.1", default-features = false, optional = true }

[dependencies.serde_crate]
package = "serde"
optional = true
Expand All @@ -53,7 +58,7 @@ sha3 = { version = "0.10.1", default-features = false }
name = "key"

[features]
default = ["std", "pem"]
default = ["std", "pem", "sha2"]
nightly = ["num-bigint/nightly"]
serde = ["num-bigint/serde", "serde_crate"]
expose-internals = []
Expand All @@ -63,7 +68,7 @@ pkcs5 = ["pkcs8/encryption"]
getrandom = ["rand_core/getrandom"]

[package.metadata.docs.rs]
features = ["std", "pem", "serde", "expose-internals"]
features = ["std", "pem", "serde", "expose-internals", "sha1", "sha2", "sha3"]
rustdoc-args = ["--cfg", "docsrs"]

[profile.dev]
Expand Down
33 changes: 32 additions & 1 deletion src/algorithms.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloc::vec;
use digest::DynDigest;
use digest::{Digest, DynDigest, FixedOutputReset};
use num_bigint::traits::ModInverse;
use num_bigint::{BigUint, RandPrime};
#[allow(unused_imports)]
Expand Down Expand Up @@ -165,6 +165,37 @@ pub fn mgf1_xor(out: &mut [u8], digest: &mut dyn DynDigest, seed: &[u8]) {
}
}

/// 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_digest<D>(out: &mut [u8], digest: &mut D, seed: &[u8])
where
D: Digest + FixedOutputReset,
{
let mut counter = [0u8; 4];
let mut i = 0;

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

while i < out.len() {
Digest::update(digest, seed);
Digest::update(digest, counter);

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);
Expand Down
52 changes: 52 additions & 0 deletions src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,55 @@ impl Hash {
}
}
}

/* FIXME: This trait should be refactored into per-digest implementations returning OID */
pub trait AssociatedHash {
const HASH: Hash;
}
Comment on lines +87 to +90
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This trait could probably use a doc comment, even if the intent is to remove/replace it


#[cfg(feature = "sha1")]
impl AssociatedHash for sha1::Sha1 {
const HASH: Hash = Hash::SHA1;
}

#[cfg(feature = "sha2")]
impl AssociatedHash for sha2::Sha224 {
const HASH: Hash = Hash::SHA2_224;
}

#[cfg(feature = "sha2")]
impl AssociatedHash for sha2::Sha256 {
const HASH: Hash = Hash::SHA2_256;
}

#[cfg(feature = "sha2")]
impl AssociatedHash for sha2::Sha384 {
const HASH: Hash = Hash::SHA2_384;
}

#[cfg(feature = "sha2")]
impl AssociatedHash for sha2::Sha512 {
const HASH: Hash = Hash::SHA2_512;
}

/*
#[cfg(feature = "sha3")]
impl AssociatedHash for sha3::Sha3_224 {
const HASH: Hash = Hash::SHA3_224;
}
*/

#[cfg(feature = "sha3")]
impl AssociatedHash for sha3::Sha3_256 {
const HASH: Hash = Hash::SHA3_256;
}

#[cfg(feature = "sha3")]
impl AssociatedHash for sha3::Sha3_384 {
const HASH: Hash = Hash::SHA3_384;
}

#[cfg(feature = "sha3")]
impl AssociatedHash for sha3::Sha3_512 {
const HASH: Hash = Hash::SHA3_512;
}
58 changes: 30 additions & 28 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,32 @@
//! assert_eq!(&data[..], &dec_data[..]);
//! ```
//!
//! Using PKCS1v15 signatures
//! ```
//! use rsa::{Hash, RsaPrivateKey};
//! use rsa::pkcs1v15::{SigningKey, VerifyingKey};
//! use sha2::{Digest, Sha256};
//! use signature::{RandomizedSigner, Signature, Verifier};
//!
//! let mut rng = rand::thread_rng();
//!
//! let bits = 2048;
//! let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
//! let signing_key = SigningKey::new_with_hash(private_key, Hash::SHA2_256);
//! let verifying_key: VerifyingKey = (&signing_key).into();
//!
//! // Sign
//! let data = b"hello world";
//! let digest = Sha256::digest(data).to_vec();
//! let signature = signing_key.sign_with_rng(&mut rng, &digest);
//! assert_ne!(signature.as_bytes(), data);
//!
//! // Verify
//! verifying_key.verify(&digest, &signature).expect("failed to verify");
//! ```
#![cfg_attr(
feature = "sha2",
doc = r#"
Using PKCS1v15 signatures
```
use rsa::{Hash, RsaPrivateKey};
use rsa::pkcs1v15::{SigningKey, VerifyingKey};
use sha2::{Digest, Sha256};
use signature::{RandomizedSigner, Signature, Verifier};

let mut rng = rand::thread_rng();

let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let signing_key = SigningKey::<Sha256>::new_with_prefix(private_key);
let verifying_key: VerifyingKey<_> = (&signing_key).into();

// Sign
let data = b"hello world";
let signature = signing_key.sign_with_rng(&mut rng, data);
assert_ne!(signature.as_bytes(), data);

// Verify
verifying_key.verify(data, &signature).expect("failed to verify");
```"#
)]
//!
//! Using PSS signatures
//! ```
Expand All @@ -82,17 +85,16 @@
//!
//! let bits = 2048;
//! let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
//! let signing_key = BlindedSigningKey::new(private_key, Box::new(Sha256::new()));
//! let verifying_key: VerifyingKey = (&signing_key).into();
//! let signing_key = BlindedSigningKey::<Sha256>::new(private_key);
//! let verifying_key: VerifyingKey<_> = (&signing_key).into();
//!
//! // Sign
//! let data = b"hello world";
//! let digest = Sha256::digest(data).to_vec();
//! let signature = signing_key.sign_with_rng(&mut rng, &digest);
//! let signature = signing_key.sign_with_rng(&mut rng, data);
//! assert_ne!(signature.as_bytes(), data);
//!
//! // Verify
//! verifying_key.verify(&digest, &signature).expect("failed to verify");
//! verifying_key.verify(data, &signature).expect("failed to verify");
//! ```
//!
//! ## PKCS#1 RSA Key Encoding
Expand Down
Loading