From fa18521e8eaf243ec45c3d93c359c2740e8eb8e4 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 16 Sep 2022 09:49:13 +0300 Subject: [PATCH 1/4] pkcs1v15: Also test SHA1 and SHA3-256 prefixes Add tests using RSA-SHA1 and RSA-SHA3-256 signature schemes. Signed-off-by: Dmitry Baryshkov --- src/pkcs1v15.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index fb60aac3..f83e18ca 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -507,6 +507,8 @@ mod tests { use sha1::{Digest, Sha1}; #[cfg(feature = "sha2")] use sha2::Sha256; + #[cfg(feature = "sha3")] + use sha3::Sha3_256; use signature::{RandomizedSigner, Signature, Signer, Verifier}; use crate::{Hash, PaddingScheme, PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey}; @@ -632,9 +634,36 @@ mod tests { } } + #[cfg(feature = "sha1")] + #[test] + fn test_sign_pkcs1v15_signer_sha1() { + let priv_key = get_private_key(); + + let tests = [( + "Test.\n", + hex!( + "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" + "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" + ), + )]; + + let signing_key = SigningKey::::new_with_prefix(priv_key); + + for (text, expected) in &tests { + let out = signing_key.sign(text.as_bytes()); + assert_ne!(out.as_ref(), text.as_bytes()); + assert_ne!(out.as_ref(), &Sha1::digest(text.as_bytes()).to_vec()); + assert_eq!(out.as_ref(), expected); + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let out2 = signing_key.sign_with_rng(&mut rng, text.as_bytes()); + assert_eq!(out2.as_ref(), expected); + } + } + #[cfg(feature = "sha2")] #[test] - fn test_sign_pkcs1v15_signer() { + fn test_sign_pkcs1v15_signer_sha2_256() { let priv_key = get_private_key(); let tests = [( @@ -650,7 +679,32 @@ mod tests { for (text, expected) in &tests { let out = signing_key.sign(text.as_bytes()); assert_ne!(out.as_ref(), text.as_bytes()); - assert_ne!(out.as_ref(), &Sha1::digest(text.as_bytes()).to_vec()); + assert_eq!(out.as_ref(), expected); + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let out2 = signing_key.sign_with_rng(&mut rng, text.as_bytes()); + assert_eq!(out2.as_ref(), expected); + } + } + + #[cfg(feature = "sha3")] + #[test] + fn test_sign_pkcs1v15_signer_sha3_256() { + let priv_key = get_private_key(); + + let tests = [( + "Test.\n", + hex!( + "55e9fba3354dfb51d2c8111794ea552c86afc2cab154652c03324df8c2c51ba7" + "2ff7c14de59a6f9ba50d90c13a7537cc3011948369f1f0ec4a49d21eb7e723f9" + ), + )]; + + let signing_key = SigningKey::::new_with_prefix(priv_key); + + for (text, expected) in &tests { + let out = signing_key.sign(text.as_bytes()); + assert_ne!(out.as_ref(), text.as_bytes()); assert_eq!(out.as_ref(), expected); let mut rng = ChaCha8Rng::from_seed([42; 32]); From 5617d9cbdc18d8851329ede5c9363e34c9d7227f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 9 Sep 2022 15:57:27 +0300 Subject: [PATCH 2/4] Cargo.toml: bump versions Bump digest, sha1 and sha2 crates versions to resolve the OID/AssociatedOId implementations. Signed-off-by: Dmitry Baryshkov --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a9397ab6..d36de1f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ num-iter = { version = "0.1.37", default-features = false } rand_core = { version = "0.6", default-features = false } byteorder = { version = "1.3.1", default-features = false } subtle = { version = "2.1.1", default-features = false } -digest = { version = "0.10.0", default-features = false, features = ["alloc"] } +digest = { version = "0.10.5", default-features = false, features = ["alloc", "oid"] } 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 @@ -50,9 +50,9 @@ rand_xorshift = "0.3" rand_chacha = "0.3" rand = "0.8" rand_core = { version = "0.6", default-features = false } -sha1 = { version = "0.10.1", default-features = false } -sha2 = { version = "0.10.2", default-features = false } -sha3 = { version = "0.10.1", default-features = false } +sha1 = { version = "0.10.5", default-features = false, features = ["oid"] } +sha2 = { version = "0.10.6", default-features = false, features = ["oid"] } +sha3 = { version = "0.10.5", default-features = false, features = ["oid"] } [[bench]] name = "key" From cdb5c2e87a9971b6da0b0f8b9b4618251401aaf3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 16 Sep 2022 10:01:27 +0300 Subject: [PATCH 3/4] pkcs1v15: use AssociatedOID for getting the RSA prefix Drop internal implementation of AssociatedHash and use AssociatedOID trait to get the OID corresponding to the Digest and to format the ASN.1 prefix. Signed-off-by: Dmitry Baryshkov --- Cargo.toml | 9 +--- src/hash.rs | 52 ------------------- src/key.rs | 26 ++++++++-- src/lib.rs | 48 ++++++++---------- src/pkcs1v15.rs | 131 ++++++++++++++++++++++-------------------------- 5 files changed, 108 insertions(+), 158 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d36de1f0..0558bfac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,11 +30,6 @@ 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 @@ -58,7 +53,7 @@ sha3 = { version = "0.10.5", default-features = false, features = ["oid"] } name = "key" [features] -default = ["std", "pem", "sha2"] +default = ["std", "pem"] nightly = ["num-bigint/nightly"] serde = ["num-bigint/serde", "serde_crate"] expose-internals = [] @@ -68,7 +63,7 @@ pkcs5 = ["pkcs8/encryption"] getrandom = ["rand_core/getrandom"] [package.metadata.docs.rs] -features = ["std", "pem", "serde", "expose-internals", "sha1", "sha2", "sha3"] +features = ["std", "pem", "serde", "expose-internals"] rustdoc-args = ["--cfg", "docsrs"] [profile.dev] diff --git a/src/hash.rs b/src/hash.rs index ffa8433f..aa753187 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -83,55 +83,3 @@ impl Hash { } } } - -/* FIXME: This trait should be refactored into per-digest implementations returning OID */ -pub trait AssociatedHash { - const HASH: Hash; -} - -#[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; -} diff --git a/src/key.rs b/src/key.rs index 3d65d5c3..256e2d5d 100644 --- a/src/key.rs +++ b/src/key.rs @@ -12,6 +12,7 @@ use zeroize::Zeroize; use crate::algorithms::{generate_multi_prime_key, generate_multi_prime_key_with_exp}; use crate::dummy_rng::DummyRng; use crate::errors::{Error, Result}; +use crate::hash::Hash; use crate::padding::PaddingScheme; use crate::raw::{DecryptionPrimitive, EncryptionPrimitive}; @@ -217,7 +218,8 @@ impl PublicKey for RsaPublicKey { fn verify(&self, padding: PaddingScheme, hashed: &[u8], sig: &[u8]) -> Result<()> { match padding { PaddingScheme::PKCS1v15Sign { ref hash } => { - pkcs1v15::verify(self, hash.as_ref(), hashed, sig) + let prefix = hash_info(*hash, hashed.len())?; + pkcs1v15::verify(self, prefix, hashed, sig) } PaddingScheme::PSS { mut digest, .. } => pss::verify(self, hashed, sig, &mut *digest), _ => Err(Error::InvalidPaddingScheme), @@ -510,7 +512,8 @@ impl RsaPrivateKey { match padding { // need to pass any Rng as the type arg, so the type checker is happy, it is not actually used for anything PaddingScheme::PKCS1v15Sign { ref hash } => { - pkcs1v15::sign::(None, self, hash.as_ref(), digest_in) + let prefix = hash_info(*hash, digest_in.len())?; + pkcs1v15::sign::(None, self, prefix, digest_in) } _ => Err(Error::InvalidPaddingScheme), } @@ -545,7 +548,8 @@ impl RsaPrivateKey { ) -> Result> { match padding { PaddingScheme::PKCS1v15Sign { ref hash } => { - pkcs1v15::sign(Some(rng), self, hash.as_ref(), digest_in) + let prefix = hash_info(*hash, digest_in.len())?; + pkcs1v15::sign(Some(rng), self, prefix, digest_in) } PaddingScheme::PSS { mut digest, @@ -556,6 +560,22 @@ impl RsaPrivateKey { } } +#[inline] +fn hash_info(hash: Option, digest_len: usize) -> Result<&'static [u8]> { + match hash { + Some(hash) => { + let hash_len = hash.size(); + if digest_len != hash_len { + return Err(Error::InputNotHashed); + } + + Ok(hash.asn1_prefix()) + } + // this means the data is signed directly + None => Ok(&[]), + } +} + /// Check that the public key is well formed and has an exponent within acceptable bounds. #[inline] pub fn check_public(public_key: &impl PublicKeyParts) -> Result<()> { diff --git a/src/lib.rs b/src/lib.rs index 4d2c7768..ade3fa7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,32 +47,28 @@ //! assert_eq!(&data[..], &dec_data[..]); //! ``` //! -#![cfg_attr( - feature = "sha2", - doc = r#" -Using PKCS1v15 signatures -``` -use rsa::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_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 PKCS1v15 signatures +//! ``` +//! use rsa::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_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 //! ``` diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index f83e18ca..59c14779 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -4,6 +4,7 @@ use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use core::marker::PhantomData; use core::ops::Deref; use digest::Digest; +use pkcs8::AssociatedOid; use rand_core::{CryptoRng, RngCore}; use signature::{ DigestSigner, DigestVerifier, RandomizedDigestSigner, RandomizedSigner, @@ -14,7 +15,6 @@ use zeroize::Zeroizing; use crate::dummy_rng::DummyRng; use crate::errors::{Error, Result}; -use crate::hash::{AssociatedHash, Hash}; use crate::key::{self, PrivateKey, PublicKey}; use crate::{RsaPrivateKey, RsaPublicKey}; @@ -160,12 +160,11 @@ pub(crate) fn decrypt( pub(crate) fn sign( rng: Option<&mut R>, priv_key: &SK, - hash: Option<&Hash>, + prefix: &[u8], hashed: &[u8], ) -> Result> { - let (hash_len, prefix) = hash_info(hash, hashed.len())?; - - let t_len = prefix.len() + hash_len; + let hash_len = hashed.len(); + let t_len = prefix.len() + hashed.len(); let k = priv_key.size(); if k < t_len + 11 { return Err(Error::MessageTooLong); @@ -186,13 +185,12 @@ pub(crate) fn sign( #[inline] pub(crate) fn verify( pub_key: &PK, - hash: Option<&Hash>, + prefix: &[u8], hashed: &[u8], sig: &[u8], ) -> Result<()> { - let (hash_len, prefix) = hash_info(hash, hashed.len())?; - - let t_len = prefix.len() + hash_len; + let hash_len = hashed.len(); + let t_len = prefix.len() + hashed.len(); let k = pub_key.size(); if k < t_len + 11 { return Err(Error::Verification); @@ -218,20 +216,26 @@ pub(crate) fn verify( Ok(()) } +// prefix = 0x30 0x30 0x06 oid 0x05 0x00 0x04 #[inline] -fn hash_info(hash: Option<&Hash>, digest_len: usize) -> Result<(usize, &'static [u8])> { - match hash { - Some(hash) => { - let hash_len = hash.size(); - if digest_len != hash_len { - return Err(Error::InputNotHashed); - } - - Ok((hash_len, hash.asn1_prefix())) - } - // this means the data is signed directly - None => Ok((digest_len, &[])), - } +fn generate_prefix() -> Vec +where + D: Digest + AssociatedOid, +{ + let oid = D::OID.as_bytes(); + let oid_len = oid.len() as u8; + let digest_len = ::output_size() as u8; + let mut v = vec![ + 0x30, + oid_len + 8 + digest_len, + 0x30, + oid_len + 4, + 0x6, + oid_len, + ]; + v.extend_from_slice(oid); + v.extend_from_slice(&[0x05, 0x00, 0x04, digest_len]); + v } /// Decrypts ciphertext using `priv_key` and blinds the operation if @@ -304,7 +308,7 @@ where D: Digest, { inner: RsaPrivateKey, - hash: Option, + prefix: Vec, phantom: PhantomData, } @@ -316,14 +320,14 @@ where &self.inner } - pub(crate) fn hash(&self) -> Option { - self.hash + pub(crate) fn prefix(&self) -> Vec { + self.prefix.clone() } pub fn new(key: RsaPrivateKey) -> Self { Self { inner: key, - hash: None, + prefix: Vec::new(), phantom: Default::default(), } } @@ -331,12 +335,12 @@ where impl SigningKey where - D: Digest + AssociatedHash, + D: Digest + AssociatedOid, { pub fn new_with_prefix(key: RsaPrivateKey) -> Self { Self { inner: key, - hash: Some(D::HASH), + prefix: generate_prefix::(), phantom: Default::default(), } } @@ -347,7 +351,7 @@ where D: Digest, { fn try_sign(&self, msg: &[u8]) -> signature::Result { - sign::(None, &self.inner, self.hash.as_ref(), &D::digest(msg)) + sign::(None, &self.inner, &self.prefix, &D::digest(msg)) .map(|v| v.into()) .map_err(|e| e.into()) } @@ -362,14 +366,9 @@ where mut rng: impl CryptoRng + RngCore, msg: &[u8], ) -> signature::Result { - sign( - Some(&mut rng), - &self.inner, - self.hash.as_ref(), - &D::digest(msg), - ) - .map(|v| v.into()) - .map_err(|e| e.into()) + sign(Some(&mut rng), &self.inner, &self.prefix, &D::digest(msg)) + .map(|v| v.into()) + .map_err(|e| e.into()) } } @@ -378,7 +377,7 @@ where D: Digest, { fn try_sign_digest(&self, digest: D) -> signature::Result { - sign::(None, &self.inner, self.hash.as_ref(), &digest.finalize()) + sign::(None, &self.inner, &self.prefix, &digest.finalize()) .map(|v| v.into()) .map_err(|e| e.into()) } @@ -396,7 +395,7 @@ where sign( Some(&mut rng), &self.inner, - self.hash.as_ref(), + &self.prefix, &digest.finalize(), ) .map(|v| v.into()) @@ -409,7 +408,7 @@ where D: Digest, { inner: RsaPublicKey, - hash: Option, + prefix: Vec, phantom: PhantomData, } @@ -420,7 +419,7 @@ where pub fn new(key: RsaPublicKey) -> Self { Self { inner: key, - hash: None, + prefix: Vec::new(), phantom: Default::default(), } } @@ -428,12 +427,12 @@ where impl VerifyingKey where - D: Digest + AssociatedHash, + D: Digest + AssociatedOid, { pub fn new_with_prefix(key: RsaPublicKey) -> Self { Self { inner: key, - hash: Some(D::HASH), + prefix: generate_prefix::(), phantom: Default::default(), } } @@ -446,7 +445,7 @@ where fn from(key: SigningKey) -> Self { Self { inner: key.key().into(), - hash: key.hash(), + prefix: key.prefix(), phantom: Default::default(), } } @@ -459,7 +458,7 @@ where fn from(key: &SigningKey) -> Self { Self { inner: key.key().into(), - hash: key.hash(), + prefix: key.prefix(), phantom: Default::default(), } } @@ -472,7 +471,7 @@ where fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> { verify( &self.inner, - self.hash.as_ref(), + &self.prefix, &D::digest(msg), signature.as_ref(), ) @@ -487,7 +486,7 @@ where fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> { verify( &self.inner, - self.hash.as_ref(), + &self.prefix, &digest.finalize(), signature.as_ref(), ) @@ -505,9 +504,7 @@ mod tests { use num_traits::Num; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; use sha1::{Digest, Sha1}; - #[cfg(feature = "sha2")] use sha2::Sha256; - #[cfg(feature = "sha3")] use sha3::Sha3_256; use signature::{RandomizedSigner, Signature, Signer, Verifier}; @@ -634,9 +631,8 @@ mod tests { } } - #[cfg(feature = "sha1")] #[test] - fn test_sign_pkcs1v15_signer_sha1() { + fn test_sign_pkcs1v15_signer() { let priv_key = get_private_key(); let tests = [( @@ -661,7 +657,6 @@ mod tests { } } - #[cfg(feature = "sha2")] #[test] fn test_sign_pkcs1v15_signer_sha2_256() { let priv_key = get_private_key(); @@ -687,7 +682,6 @@ mod tests { } } - #[cfg(feature = "sha3")] #[test] fn test_sign_pkcs1v15_signer_sha3_256() { let priv_key = get_private_key(); @@ -713,7 +707,6 @@ mod tests { } } - #[cfg(feature = "sha2")] #[test] fn test_sign_pkcs1v15_digest_signer() { let priv_key = get_private_key(); @@ -721,15 +714,15 @@ mod tests { let tests = [( "Test.\n", hex!( - "2ffae3f3e130287b3a1dcb320e46f52e8f3f7969b646932273a7e3a6f2a182ea" - "02d42875a7ffa4a148aa311f9e4b562e4e13a2223fb15f4e5bf5f2b206d9451b" + "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" + "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" ), )]; let signing_key = SigningKey::new_with_prefix(priv_key); for (text, expected) in &tests { - let mut digest = Sha256::new(); + let mut digest = Sha1::new(); digest.update(text.as_bytes()); let out = signing_key.sign_digest(digest); assert_ne!(out.as_ref(), text.as_bytes()); @@ -737,7 +730,7 @@ mod tests { assert_eq!(out.as_ref(), expected); let mut rng = ChaCha8Rng::from_seed([42; 32]); - let mut digest = Sha256::new(); + let mut digest = Sha1::new(); digest.update(text.as_bytes()); let out2 = signing_key.sign_digest_with_rng(&mut rng, digest); assert_eq!(out2.as_ref(), expected); @@ -786,7 +779,6 @@ mod tests { } } - #[cfg(feature = "sha2")] #[test] fn test_verify_pkcs1v15_signer() { let priv_key = get_private_key(); @@ -795,22 +787,22 @@ mod tests { ( "Test.\n", hex!( - "2ffae3f3e130287b3a1dcb320e46f52e8f3f7969b646932273a7e3a6f2a182ea" - "02d42875a7ffa4a148aa311f9e4b562e4e13a2223fb15f4e5bf5f2b206d9451b" + "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" + "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" ), true, ), ( "Test.\n", hex!( - "2ffae3f3e130287b3a1dcb320e46f52e8f3f7969b646932273a7e3a6f2a182ea" - "02d42875a7ffa4a148aa311f9e4b562e4e13a2223fb15f4e5bf5f2b206d9451c" + "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" + "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af" ), false, ), ]; let pub_key: RsaPublicKey = priv_key.into(); - let verifying_key = VerifyingKey::::new_with_prefix(pub_key); + let verifying_key = VerifyingKey::::new_with_prefix(pub_key); for (text, sig, expected) in &tests { let result = @@ -825,7 +817,6 @@ mod tests { } } - #[cfg(feature = "sha2")] #[test] fn test_verify_pkcs1v15_digest_signer() { let priv_key = get_private_key(); @@ -834,16 +825,16 @@ mod tests { ( "Test.\n", hex!( - "2ffae3f3e130287b3a1dcb320e46f52e8f3f7969b646932273a7e3a6f2a182ea" - "02d42875a7ffa4a148aa311f9e4b562e4e13a2223fb15f4e5bf5f2b206d9451b" + "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" + "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" ), true, ), ( "Test.\n", hex!( - "2ffae3f3e130287b3a1dcb320e46f52e8f3f7969b646932273a7e3a6f2a182ea" - "02d42875a7ffa4a148aa311f9e4b562e4e13a2223fb15f4e5bf5f2b206d9451c" + "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" + "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af" ), false, ), @@ -852,7 +843,7 @@ mod tests { let verifying_key = VerifyingKey::new_with_prefix(pub_key); for (text, sig, expected) in &tests { - let mut digest = Sha256::new(); + let mut digest = Sha1::new(); digest.update(text.as_bytes()); let result = verifying_key.verify_digest(digest, &Signature::from_bytes(sig).unwrap()); match expected { From 53a085a36edc5e4f2a1e2e900520fb58ae6167be Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 10 Sep 2022 20:19:24 +0300 Subject: [PATCH 4/4] Get rid of rsa::Hash Fully replace rsa::Hash with AssociatedOid usage. Signed-off-by: Dmitry Baryshkov --- src/hash.rs | 85 ------------------------------------------------- src/key.rs | 47 ++++++++++++--------------- src/lib.rs | 3 -- src/padding.rs | 29 +++++++++++++---- src/pkcs1v15.rs | 18 ++++------- 5 files changed, 51 insertions(+), 131 deletions(-) delete mode 100644 src/hash.rs diff --git a/src/hash.rs b/src/hash.rs deleted file mode 100644 index aa753187..00000000 --- a/src/hash.rs +++ /dev/null @@ -1,85 +0,0 @@ -/// A list of provided hashes, implementing `Hash`. -#[derive(Debug, Clone, Copy)] -pub enum Hash { - MD5, - SHA1, - SHA2_224, - SHA2_256, - SHA2_384, - SHA2_512, - SHA3_256, - SHA3_384, - SHA3_512, - MD5SHA1, - RIPEMD160, -} - -impl Hash { - /// Returns the length in bytes of a digest. - pub fn size(&self) -> usize { - match *self { - 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, - } - } - - /// Returns the ASN1 DER prefix for the the hash function. - pub fn asn1_prefix(&self) -> &'static [u8] { - match *self { - Hash::MD5 => &[ - 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, - 0x05, 0x00, 0x04, 0x10, - ], - Hash::SHA1 => &[ - 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, - 0x14, - ], - Hash::SHA2_224 => &[ - 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, - 0x04, 0x05, 0x00, 0x04, 0x1c, - ], - Hash::SHA2_256 => &[ - 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, - 0x01, 0x05, 0x00, 0x04, 0x20, - ], - Hash::SHA2_384 => &[ - 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, - 0x02, 0x05, 0x00, 0x04, 0x30, - ], - - 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 - Hash::MD5SHA1 => &[], - Hash::RIPEMD160 => &[ - 0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14, - ], - - Hash::SHA3_256 => &[ - 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, - 0x08, 0x05, 0x00, 0x04, 0x20, - ], - Hash::SHA3_384 => &[ - 30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, - 0x08, 0x05, 0x00, 0x04, 0x20, - ], - - Hash::SHA3_512 => &[ - 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, - 0x0a, 0x05, 0x00, 0x04, 0x40, - ], - } - } -} diff --git a/src/key.rs b/src/key.rs index 256e2d5d..02c88791 100644 --- a/src/key.rs +++ b/src/key.rs @@ -12,7 +12,6 @@ use zeroize::Zeroize; use crate::algorithms::{generate_multi_prime_key, generate_multi_prime_key_with_exp}; use crate::dummy_rng::DummyRng; use crate::errors::{Error, Result}; -use crate::hash::Hash; use crate::padding::PaddingScheme; use crate::raw::{DecryptionPrimitive, EncryptionPrimitive}; @@ -217,9 +216,13 @@ impl PublicKey for RsaPublicKey { fn verify(&self, padding: PaddingScheme, hashed: &[u8], sig: &[u8]) -> Result<()> { match padding { - PaddingScheme::PKCS1v15Sign { ref hash } => { - let prefix = hash_info(*hash, hashed.len())?; - pkcs1v15::verify(self, prefix, hashed, sig) + PaddingScheme::PKCS1v15Sign { hash_len, prefix } => { + if let Some(hash_len) = hash_len { + if hashed.len() != hash_len { + return Err(Error::InputNotHashed); + } + } + pkcs1v15::verify(self, prefix.as_ref(), hashed, sig) } PaddingScheme::PSS { mut digest, .. } => pss::verify(self, hashed, sig, &mut *digest), _ => Err(Error::InvalidPaddingScheme), @@ -511,9 +514,13 @@ impl RsaPrivateKey { pub fn sign(&self, padding: PaddingScheme, digest_in: &[u8]) -> Result> { match padding { // need to pass any Rng as the type arg, so the type checker is happy, it is not actually used for anything - PaddingScheme::PKCS1v15Sign { ref hash } => { - let prefix = hash_info(*hash, digest_in.len())?; - pkcs1v15::sign::(None, self, prefix, digest_in) + PaddingScheme::PKCS1v15Sign { hash_len, prefix } => { + if let Some(hash_len) = hash_len { + if digest_in.len() != hash_len { + return Err(Error::InputNotHashed); + } + } + pkcs1v15::sign::(None, self, prefix.as_ref(), digest_in) } _ => Err(Error::InvalidPaddingScheme), } @@ -547,9 +554,13 @@ impl RsaPrivateKey { digest_in: &[u8], ) -> Result> { match padding { - PaddingScheme::PKCS1v15Sign { ref hash } => { - let prefix = hash_info(*hash, digest_in.len())?; - pkcs1v15::sign(Some(rng), self, prefix, digest_in) + PaddingScheme::PKCS1v15Sign { hash_len, prefix } => { + if let Some(hash_len) = hash_len { + if digest_in.len() != hash_len { + return Err(Error::InputNotHashed); + } + } + pkcs1v15::sign(Some(rng), self, prefix.as_ref(), digest_in) } PaddingScheme::PSS { mut digest, @@ -560,22 +571,6 @@ impl RsaPrivateKey { } } -#[inline] -fn hash_info(hash: Option, digest_len: usize) -> Result<&'static [u8]> { - match hash { - Some(hash) => { - let hash_len = hash.size(); - if digest_len != hash_len { - return Err(Error::InputNotHashed); - } - - Ok(hash.asn1_prefix()) - } - // this means the data is signed directly - None => Ok(&[]), - } -} - /// Check that the public key is well formed and has an exponent within acceptable bounds. #[inline] pub fn check_public(public_key: &impl PublicKeyParts) -> Result<()> { diff --git a/src/lib.rs b/src/lib.rs index ade3fa7f..14f33e92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -200,8 +200,6 @@ pub use rand_core; pub mod algorithms; /// Error types. pub mod errors; -/// Supported hash functions. -pub mod hash; /// Supported padding schemes. pub mod padding; /// RSASSA-PKCS1-v1_5 Signature support @@ -218,7 +216,6 @@ mod raw; pub use pkcs1; pub use pkcs8; -pub use self::hash::Hash; pub use self::key::{PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey}; pub use self::padding::PaddingScheme; diff --git a/src/padding.rs b/src/padding.rs index 8655cb63..49d3a41c 100644 --- a/src/padding.rs +++ b/src/padding.rs @@ -3,15 +3,19 @@ use alloc::string::{String, ToString}; use core::fmt; use digest::{Digest, DynDigest}; +use pkcs8::AssociatedOid; -use crate::hash::Hash; +use crate::pkcs1v15; /// Available padding schemes. pub enum PaddingScheme { /// Encryption and Decryption using PKCS1v15 padding. PKCS1v15Encrypt, /// Sign and Verify using PKCS1v15 padding. - PKCS1v15Sign { hash: Option }, + PKCS1v15Sign { + hash_len: Option, + prefix: Box<[u8]>, + }, /// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc3447#section-7.1.1). /// /// - `digest` is used to hash the label. The maximum possible plaintext length is `m = k - 2 * h_len - 2`, @@ -38,8 +42,8 @@ impl fmt::Debug for PaddingScheme { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { PaddingScheme::PKCS1v15Encrypt => write!(f, "PaddingScheme::PKCS1v15Encrypt"), - PaddingScheme::PKCS1v15Sign { ref hash } => { - write!(f, "PaddingScheme::PKCS1v15Sign({:?})", hash) + PaddingScheme::PKCS1v15Sign { prefix, .. } => { + write!(f, "PaddingScheme::PKCS1v15Sign({:?})", prefix) } PaddingScheme::OAEP { ref label, .. } => { // TODO: How to print the digest name? @@ -58,8 +62,21 @@ impl PaddingScheme { PaddingScheme::PKCS1v15Encrypt } - pub fn new_pkcs1v15_sign(hash: Option) -> Self { - PaddingScheme::PKCS1v15Sign { hash } + pub fn new_pkcs1v15_sign_raw() -> Self { + PaddingScheme::PKCS1v15Sign { + hash_len: None, + prefix: Box::new([]), + } + } + + pub fn new_pkcs1v15_sign() -> Self + where + D: Digest + AssociatedOid, + { + PaddingScheme::PKCS1v15Sign { + hash_len: Some(::output_size()), + prefix: pkcs1v15::generate_prefix::().into_boxed_slice(), + } } /// Create a new OAEP `PaddingScheme`, using `T` as the hash function for the default (empty) label, and `U` as the hash function for MGF1. diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index 59c14779..b5ec29dc 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -218,7 +218,7 @@ pub(crate) fn verify( // prefix = 0x30 0x30 0x06 oid 0x05 0x00 0x04 #[inline] -fn generate_prefix() -> Vec +pub(crate) fn generate_prefix() -> Vec where D: Digest + AssociatedOid, { @@ -508,7 +508,7 @@ mod tests { use sha3::Sha3_256; use signature::{RandomizedSigner, Signature, Signer, Verifier}; - use crate::{Hash, PaddingScheme, PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey}; + use crate::{PaddingScheme, PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey}; #[test] fn test_non_zero_bytes() { @@ -614,7 +614,7 @@ mod tests { let digest = Sha1::digest(text.as_bytes()).to_vec(); let out = priv_key - .sign(PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA1)), &digest) + .sign(PaddingScheme::new_pkcs1v15_sign::(), &digest) .unwrap(); assert_ne!(out, digest); assert_eq!(out, expected); @@ -623,7 +623,7 @@ mod tests { let out2 = priv_key .sign_blinded( &mut rng, - PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA1)), + PaddingScheme::new_pkcs1v15_sign::(), &digest, ) .unwrap(); @@ -764,11 +764,7 @@ mod tests { for (text, sig, expected) in &tests { let digest = Sha1::digest(text.as_bytes()).to_vec(); - let result = pub_key.verify( - PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA1)), - &digest, - sig, - ); + let result = pub_key.verify(PaddingScheme::new_pkcs1v15_sign::(), &digest, sig); match expected { true => result.expect("failed to verify"), false => { @@ -862,13 +858,13 @@ mod tests { let priv_key = get_private_key(); let sig = priv_key - .sign(PaddingScheme::new_pkcs1v15_sign(None), msg) + .sign(PaddingScheme::new_pkcs1v15_sign_raw(), msg) .unwrap(); assert_eq!(expected_sig, sig); let pub_key: RsaPublicKey = priv_key.into(); pub_key - .verify(PaddingScheme::new_pkcs1v15_sign(None), msg, &sig) + .verify(PaddingScheme::new_pkcs1v15_sign_raw(), msg, &sig) .expect("failed to verify"); }