Skip to content

Commit

Permalink
RSA: add support for signature traits
Browse files Browse the repository at this point in the history
Implement Signature, Signer and Verifier traits from signature crate.

Signed-off-by: Dmitry Baryshkov <[email protected]>
  • Loading branch information
lumag committed Aug 14, 2022
1 parent 9212322 commit f1e596b
Show file tree
Hide file tree
Showing 6 changed files with 588 additions and 4 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ subtle = { version = "2.1.1", default-features = false }
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"] }
signature = { version = "1.5", default-features = false , features = ["rand-preview"] }
zeroize = { version = "1", features = ["alloc"] }

# Temporary workaround until https://github.com/dignifiedquire/num-bigint/pull/42 lands
Expand Down Expand Up @@ -55,7 +56,7 @@ default = ["std", "pem"]
nightly = ["num-bigint/nightly"]
serde = ["num-bigint/serde", "serde_crate"]
expose-internals = []
std = ["digest/std", "pkcs1/std", "pkcs8/std", "rand_core/std"]
std = ["digest/std", "pkcs1/std", "pkcs8/std", "rand_core/std", "signature/std"]
pem = ["pkcs1/pem", "pkcs8/pem"]
pkcs5 = ["pkcs8/encryption"]
getrandom = ["rand_core/getrandom"]
Expand Down
14 changes: 14 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,17 @@ impl From<pkcs8::Error> for Error {
Error::Pkcs8(err)
}
}

#[cfg(feature = "std")]
impl From<Error> for signature::Error {
fn from(err: Error) -> Self {
Self::from_source(err)
}
}

#[cfg(not(feature = "std"))]
impl From<Error> for signature::Error {
fn from(_err: Error) -> Self {
Self::new()
}
}
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,12 @@ mod dummy_rng;
mod encoding;
mod key;
mod oaep;
mod pkcs1v15;
mod pss;
pub mod pkcs1v15;
pub mod pss;
mod raw;
mod signature;

pub use self::signature::Signature;

pub use pkcs1;
pub use pkcs8;
Expand Down
249 changes: 249 additions & 0 deletions src/pkcs1v15.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use alloc::vec;
use alloc::vec::Vec;
use rand_core::{CryptoRng, RngCore};
use signature::{RandomizedSigner, Signer, Verifier};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
use zeroize::Zeroizing;

use crate::dummy_rng::DummyRng;
use crate::errors::{Error, Result};
use crate::hash::Hash;
use crate::key::{self, PrivateKey, PublicKey};
use crate::{PublicKeyParts, RsaPrivateKey, RsaPublicKey};

pub use crate::Signature;

// Encrypts the given message with RSA and the padding
// scheme from PKCS#1 v1.5. The message must be no longer than the
Expand Down Expand Up @@ -214,6 +219,162 @@ fn non_zero_random_bytes<R: RngCore + CryptoRng>(rng: &mut R, data: &mut [u8]) {
}
}

pub struct RsaPkcs1v15SigningKey {
inner: RsaPrivateKey,
hash: Option<Hash>,
}

impl<'a> RsaPkcs1v15SigningKey {
pub(crate) fn key(&'a self) -> &'a RsaPrivateKey {
&self.inner
}

pub(crate) fn hash(&self) -> Option<Hash> {
self.hash
}
}

impl From<RsaPrivateKey> for RsaPkcs1v15SigningKey {
fn from(key: RsaPrivateKey) -> Self {
Self {
inner: key,
hash: None,
}
}
}

impl From<&RsaPrivateKey> for RsaPkcs1v15SigningKey {
fn from(key: &RsaPrivateKey) -> Self {
Self {
inner: RsaPrivateKey::from_components(
key.n().clone(),
key.e().clone(),
key.d().clone(),
key.primes().clone().to_vec(),
)
.unwrap(),
hash: None,
}
}
}

impl From<(RsaPrivateKey, Hash)> for RsaPkcs1v15SigningKey {
fn from(data: (RsaPrivateKey, Hash)) -> Self {
let key = data.0;
let hash = data.1;
Self {
inner: key,
hash: Some(hash),
}
}
}

impl From<(&RsaPrivateKey, Hash)> for RsaPkcs1v15SigningKey {
fn from(data: (&RsaPrivateKey, Hash)) -> Self {
let key = data.0;
let hash = data.1;
Self {
inner: RsaPrivateKey::from_components(
key.n().clone(),
key.e().clone(),
key.d().clone(),
key.primes().clone().to_vec(),
)
.unwrap(),
hash: Some(hash),
}
}
}

impl Signer<Signature> for RsaPkcs1v15SigningKey {
fn try_sign(&self, digest: &[u8]) -> signature::Result<Signature> {
sign::<DummyRng, _>(None, &self.inner, self.hash.as_ref(), digest)
.map(|v| v.into())
.map_err(|e| e.into())
}
}

impl RandomizedSigner<Signature> for RsaPkcs1v15SigningKey {
fn try_sign_with_rng(
&self,
mut rng: impl CryptoRng + RngCore,
digest: &[u8],
) -> signature::Result<Signature> {
sign(Some(&mut rng), &self.inner, self.hash.as_ref(), digest)
.map(|v| v.into())
.map_err(|e| e.into())
}
}

pub struct RsaPkcs1v15VerifyingKey {
inner: RsaPublicKey,
hash: Option<Hash>,
}

impl From<RsaPublicKey> for RsaPkcs1v15VerifyingKey {
fn from(key: RsaPublicKey) -> Self {
Self {
inner: key,
hash: None,
}
}
}

impl From<&RsaPublicKey> for RsaPkcs1v15VerifyingKey {
fn from(key: &RsaPublicKey) -> Self {
Self {
inner: RsaPublicKey::new(key.n().clone(), key.e().clone()).unwrap(),
hash: None,
}
}
}

impl From<(RsaPublicKey, Hash)> for RsaPkcs1v15VerifyingKey {
fn from(data: (RsaPublicKey, Hash)) -> Self {
let key = data.0;
let hash = data.1;
Self {
inner: key,
hash: Some(hash),
}
}
}

impl From<(&RsaPublicKey, Hash)> for RsaPkcs1v15VerifyingKey {
fn from(data: (&RsaPublicKey, Hash)) -> Self {
let key = data.0;
let hash = data.1;
Self {
inner: RsaPublicKey::new(key.n().clone(), key.e().clone()).unwrap(),
hash: Some(hash),
}
}
}

impl From<RsaPkcs1v15SigningKey> for RsaPkcs1v15VerifyingKey {
fn from(key: RsaPkcs1v15SigningKey) -> Self {
Self {
inner: key.key().into(),
hash: key.hash(),
}
}
}

impl From<&RsaPkcs1v15SigningKey> for RsaPkcs1v15VerifyingKey {
fn from(key: &RsaPkcs1v15SigningKey) -> Self {
Self {
inner: key.key().into(),
hash: key.hash(),
}
}
}

impl Verifier<Signature> for RsaPkcs1v15VerifyingKey {
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
verify(&self.inner, self.hash.as_ref(), msg, signature.as_ref()).map_err(|e| e.into())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -224,6 +385,7 @@ mod tests {
use num_traits::Num;
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
use sha1::{Digest, Sha1};
use signature::{RandomizedSigner, Signature, Signer, Verifier};

use crate::{Hash, PaddingScheme, PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey};

Expand Down Expand Up @@ -348,6 +510,32 @@ mod tests {
}
}

#[test]
fn test_sign_pkcs1v15_signer() {
let priv_key = get_private_key();

let tests = [(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
),
)];

for (text, expected) in &tests {
let digest = Sha1::digest(text.as_bytes()).to_vec();

let signing_key: RsaPkcs1v15SigningKey = (&priv_key, Hash::SHA1).into();
let out = signing_key.sign(&digest);
assert_ne!(out.as_ref(), digest);
assert_eq!(out.as_ref(), expected);

let mut rng = ChaCha8Rng::from_seed([42; 32]);
let out2 = signing_key.sign_with_rng(&mut rng, &digest);
assert_eq!(out2.as_ref(), expected);
}
}

#[test]
fn test_verify_pkcs1v15() {
let priv_key = get_private_key();
Expand Down Expand Up @@ -390,6 +578,45 @@ mod tests {
}
}

#[test]
fn test_verify_pkcs1v15_signer() {
let priv_key = get_private_key();

let tests = [
(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
),
true,
),
(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af"
),
false,
),
];
let pub_key: RsaPublicKey = priv_key.into();
let verifying_key: RsaPkcs1v15VerifyingKey = (&pub_key, Hash::SHA1).into();

for (text, sig, expected) in &tests {
let digest = Sha1::digest(text.as_bytes()).to_vec();

let result = verifying_key.verify(&digest, &Signature::from_bytes(sig).unwrap());
match expected {
true => result.expect("failed to verify"),
false => {
result.expect_err("expected verifying error");
()
}
}
}
}

#[test]
fn test_unpadded_signature() {
let msg = b"Thu Dec 19 18:06:16 EST 2013\n";
Expand All @@ -406,4 +633,26 @@ mod tests {
.verify(PaddingScheme::new_pkcs1v15_sign(None), msg, &sig)
.expect("failed to verify");
}

#[test]
fn test_unpadded_signature_signer() {
let msg = b"Thu Dec 19 18:06:16 EST 2013\n";
let expected_sig = Base64::decode_vec("pX4DR8azytjdQ1rtUiC040FjkepuQut5q2ZFX1pTjBrOVKNjgsCDyiJDGZTCNoh9qpXYbhl7iEym30BWWwuiZg==").unwrap();
let priv_key = get_private_key();

let signing_key: RsaPkcs1v15SigningKey = (&priv_key).into();
let sig = signing_key.sign(msg);
assert_eq!(sig.as_ref(), expected_sig);

let verifying_key: RsaPkcs1v15VerifyingKey = (&signing_key).into();
verifying_key
.verify(msg, &Signature::from_bytes(&expected_sig).unwrap())
.expect("failed to verify");

let mut rng = ChaCha8Rng::from_seed([42; 32]);
let sig = signing_key.sign_with_rng(&mut rng, msg);
assert_eq!(sig.as_ref(), expected_sig);

verifying_key.verify(msg, &sig).expect("failed to verify");
}
}
Loading

0 comments on commit f1e596b

Please sign in to comment.