diff --git a/ssh-key/Cargo.toml b/ssh-key/Cargo.toml index 389e266..3ca3af6 100644 --- a/ssh-key/Cargo.toml +++ b/ssh-key/Cargo.toml @@ -80,6 +80,7 @@ p256 = ["dep:p256", "ecdsa"] p384 = ["dep:p384", "ecdsa"] p521 = ["dep:p521", "ecdsa"] rsa = ["dep:bigint", "dep:rsa", "alloc", "rand_core"] +rsa-sha1 = ["rsa", "dep:sha1", "sha1/oid"] tdes = ["cipher/tdes", "encryption"] [package.metadata.docs.rs] diff --git a/ssh-key/src/signature.rs b/ssh-key/src/signature.rs index 50f0668..5b030d5 100644 --- a/ssh-key/src/signature.rs +++ b/ssh-key/src/signature.rs @@ -13,7 +13,6 @@ use crate::{private::Ed25519Keypair, public::Ed25519PublicKey}; use { crate::{private::DsaKeypair, public::DsaPublicKey}, bigint::BigUint, - sha1::Sha1, signature::{DigestSigner, DigestVerifier}, }; @@ -32,6 +31,9 @@ use { sha2::Sha512, }; +#[cfg(any(feature = "rsa-sha1", feature = "dsa"))] +use sha1::Sha1; + #[cfg(any(feature = "ed25519", feature = "rsa", feature = "p256"))] use sha2::Sha256; @@ -661,34 +663,54 @@ impl Verifier for EcdsaPublicKey { } #[cfg(feature = "rsa")] -impl Signer for RsaKeypair { +impl Signer for (&RsaKeypair, Option) { fn try_sign(&self, message: &[u8]) -> signature::Result { - let data = rsa::pkcs1v15::SigningKey::::try_from(self)? - .try_sign(message) - .map_err(|_| signature::Error::new())?; + let data = match self.1 { + Some(HashAlg::Sha512) => { + rsa::pkcs1v15::SigningKey::::try_from(self.0)?.try_sign(message) + } + Some(HashAlg::Sha256) => { + rsa::pkcs1v15::SigningKey::::try_from(self.0)?.try_sign(message) + } + #[cfg(feature = "rsa-sha1")] + None => rsa::pkcs1v15::SigningKey::::try_from(self.0)?.try_sign(message), + #[cfg(not(feature = "rsa-sha1"))] + None => return Err(Algorithm::Rsa { hash: None }.unsupported_error().into()), + } + .map_err(|_| signature::Error::new())?; Ok(Signature { - algorithm: Algorithm::Rsa { - hash: Some(HashAlg::Sha512), - }, + algorithm: Algorithm::Rsa { hash: self.1 }, data: data.to_vec(), }) } } +#[cfg(feature = "rsa")] +impl Signer for RsaKeypair { + fn try_sign(&self, message: &[u8]) -> signature::Result { + (self, Some(HashAlg::Sha512)).try_sign(message) + } +} + #[cfg(feature = "rsa")] impl Verifier for RsaPublicKey { fn verify(&self, message: &[u8], signature: &Signature) -> signature::Result<()> { match signature.algorithm { - // TODO(tarcieri): optional off-by-default support for legacy SHA1 signatures? - Algorithm::Rsa { hash: Some(hash) } => { + Algorithm::Rsa { hash } => { let signature = rsa::pkcs1v15::Signature::try_from(signature.data.as_ref())?; match hash { - HashAlg::Sha256 => rsa::pkcs1v15::VerifyingKey::::try_from(self)? + #[cfg(not(feature = "rsa-sha1"))] + None => Err(Algorithm::Rsa { hash: None }.unsupported_error().into()), + #[cfg(feature = "rsa-sha1")] + None => rsa::pkcs1v15::VerifyingKey::::try_from(self)? + .verify(message, &signature) + .map_err(|_| signature::Error::new()), + Some(HashAlg::Sha256) => rsa::pkcs1v15::VerifyingKey::::try_from(self)? .verify(message, &signature) .map_err(|_| signature::Error::new()), - HashAlg::Sha512 => rsa::pkcs1v15::VerifyingKey::::try_from(self)? + Some(HashAlg::Sha512) => rsa::pkcs1v15::VerifyingKey::::try_from(self)? .verify(message, &signature) .map_err(|_| signature::Error::new()), }