Skip to content

Commit 52ad0a5

Browse files
committed
RSA: add support for signature traits
Implement Signature, Signer and Verifier traits from signature crate. Signed-off-by: Dmitry Baryshkov <[email protected]>
1 parent 15a2982 commit 52ad0a5

File tree

5 files changed

+601
-10
lines changed

5 files changed

+601
-10
lines changed

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ subtle = { version = "2.1.1", default-features = false }
2323
digest = { version = "0.10.0", default-features = false, features = ["alloc"] }
2424
pkcs1 = { version = "0.4", default-features = false, features = ["pkcs8", "alloc"] }
2525
pkcs8 = { version = "0.9", default-features = false, features = ["alloc"] }
26+
signature = { version = "1.5", default-features = false , features = ["rand-preview"] }
2627
zeroize = { version = "1", features = ["alloc"] }
2728

2829
# Temporary workaround until https://github.com/dignifiedquire/num-bigint/pull/42 lands
@@ -55,7 +56,7 @@ default = ["std", "pem"]
5556
nightly = ["num-bigint/nightly"]
5657
serde = ["num-bigint/serde", "serde_crate"]
5758
expose-internals = []
58-
std = ["digest/std", "pkcs1/std", "pkcs8/std", "rand_core/std"]
59+
std = ["digest/std", "pkcs1/std", "pkcs8/std", "rand_core/std", "signature/std"]
5960
pem = ["pkcs1/pem", "pkcs8/pem"]
6061
pkcs5 = ["pkcs8/encryption"]
6162
getrandom = ["rand_core/getrandom"]

src/errors.rs

+14
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,17 @@ impl From<pkcs8::Error> for Error {
6464
Error::Pkcs8(err)
6565
}
6666
}
67+
68+
#[cfg(feature = "std")]
69+
impl From<Error> for signature::Error {
70+
fn from(err: Error) -> Self {
71+
Self::from_source(err)
72+
}
73+
}
74+
75+
#[cfg(not(feature = "std"))]
76+
impl From<Error> for signature::Error {
77+
fn from(_err: Error) -> Self {
78+
Self::new()
79+
}
80+
}

src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,15 @@ pub mod errors;
158158
pub mod hash;
159159
/// Supported padding schemes.
160160
pub mod padding;
161+
/// RSASSA-PKCS1-v1_5 Signature support
162+
pub mod pkcs1v15;
163+
/// RSASSA-PSS Signature support
164+
pub mod pss;
161165

162166
mod dummy_rng;
163167
mod encoding;
164168
mod key;
165169
mod oaep;
166-
mod pkcs1v15;
167-
mod pss;
168170
mod raw;
169171

170172
pub use pkcs1;

src/pkcs1v15.rs

+258-4
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,89 @@
11
use alloc::vec;
22
use alloc::vec::Vec;
3+
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
34
use rand_core::{CryptoRng, RngCore};
5+
use signature::{RandomizedSigner, Signature as SignSignature, Signer, Verifier};
46
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
57
use zeroize::Zeroizing;
68

9+
use crate::dummy_rng::DummyRng;
710
use crate::errors::{Error, Result};
811
use crate::hash::Hash;
912
use crate::key::{self, PrivateKey, PublicKey};
13+
use crate::{RsaPrivateKey, RsaPublicKey};
14+
15+
#[derive(Clone)]
16+
pub struct Signature {
17+
bytes: Vec<u8>,
18+
}
19+
20+
impl signature::Signature for Signature {
21+
fn from_bytes(bytes: &[u8]) -> signature::Result<Self> {
22+
Ok(Signature {
23+
bytes: bytes.into(),
24+
})
25+
}
26+
27+
fn as_bytes(&self) -> &[u8] {
28+
&self.bytes.as_slice()
29+
}
30+
}
31+
32+
impl From<Vec<u8>> for Signature {
33+
fn from(bytes: Vec<u8>) -> Self {
34+
Self { bytes }
35+
}
36+
}
37+
38+
impl PartialEq for Signature {
39+
fn eq(&self, other: &Self) -> bool {
40+
self.as_bytes() == other.as_bytes()
41+
}
42+
}
43+
44+
impl Eq for Signature {}
45+
46+
impl Debug for Signature {
47+
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
48+
fmt.debug_list().entries(self.as_bytes().iter()).finish()
49+
}
50+
}
51+
52+
impl AsRef<[u8]> for Signature {
53+
fn as_ref(&self) -> &[u8] {
54+
&self.as_bytes()
55+
}
56+
}
57+
58+
impl LowerHex for Signature {
59+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
60+
for byte in self.as_bytes() {
61+
write!(f, "{:02x}", byte)?;
62+
}
63+
Ok(())
64+
}
65+
}
66+
67+
impl UpperHex for Signature {
68+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
69+
for byte in self.as_bytes() {
70+
write!(f, "{:02X}", byte)?;
71+
}
72+
Ok(())
73+
}
74+
}
75+
76+
impl Display for Signature {
77+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
78+
write!(f, "{:X}", self)
79+
}
80+
}
1081

1182
// Encrypts the given message with RSA and the padding
1283
// scheme from PKCS#1 v1.5. The message must be no longer than the
1384
// length of the public modulus minus 11 bytes.
1485
#[inline]
15-
pub fn encrypt<R: RngCore + CryptoRng, PK: PublicKey>(
86+
pub(crate) fn encrypt<R: RngCore + CryptoRng, PK: PublicKey>(
1687
rng: &mut R,
1788
pub_key: &PK,
1889
msg: &[u8],
@@ -43,7 +114,7 @@ pub fn encrypt<R: RngCore + CryptoRng, PK: PublicKey>(
43114
// forge signatures as if they had the private key. See
44115
// `decrypt_session_key` for a way of solving this problem.
45116
#[inline]
46-
pub fn decrypt<R: RngCore + CryptoRng, SK: PrivateKey>(
117+
pub(crate) fn decrypt<R: RngCore + CryptoRng, SK: PrivateKey>(
47118
rng: Option<&mut R>,
48119
priv_key: &SK,
49120
ciphertext: &[u8],
@@ -72,7 +143,7 @@ pub fn decrypt<R: RngCore + CryptoRng, SK: PrivateKey>(
72143
// messages to signatures and identify the signed messages. As ever,
73144
// signatures provide authenticity, not confidentiality.
74145
#[inline]
75-
pub fn sign<R: RngCore + CryptoRng, SK: PrivateKey>(
146+
pub(crate) fn sign<R: RngCore + CryptoRng, SK: PrivateKey>(
76147
rng: Option<&mut R>,
77148
priv_key: &SK,
78149
hash: Option<&Hash>,
@@ -99,7 +170,7 @@ pub fn sign<R: RngCore + CryptoRng, SK: PrivateKey>(
99170

100171
/// Verifies an RSA PKCS#1 v1.5 signature.
101172
#[inline]
102-
pub fn verify<PK: PublicKey>(
173+
pub(crate) fn verify<PK: PublicKey>(
103174
pub_key: &PK,
104175
hash: Option<&Hash>,
105176
hashed: &[u8],
@@ -214,6 +285,100 @@ fn non_zero_random_bytes<R: RngCore + CryptoRng>(rng: &mut R, data: &mut [u8]) {
214285
}
215286
}
216287

288+
pub struct SigningKey {
289+
inner: RsaPrivateKey,
290+
hash: Option<Hash>,
291+
}
292+
293+
impl SigningKey {
294+
pub(crate) fn key(&self) -> &RsaPrivateKey {
295+
&self.inner
296+
}
297+
298+
pub(crate) fn hash(&self) -> Option<Hash> {
299+
self.hash
300+
}
301+
302+
pub fn new(key: RsaPrivateKey) -> Self {
303+
Self {
304+
inner: key,
305+
hash: None,
306+
}
307+
}
308+
309+
pub fn new_with_hash(key: RsaPrivateKey, hash: Hash) -> Self {
310+
Self {
311+
inner: key,
312+
hash: Some(hash),
313+
}
314+
}
315+
}
316+
317+
impl Signer<Signature> for SigningKey {
318+
fn try_sign(&self, digest: &[u8]) -> signature::Result<Signature> {
319+
sign::<DummyRng, _>(None, &self.inner, self.hash.as_ref(), digest)
320+
.map(|v| v.into())
321+
.map_err(|e| e.into())
322+
}
323+
}
324+
325+
impl RandomizedSigner<Signature> for SigningKey {
326+
fn try_sign_with_rng(
327+
&self,
328+
mut rng: impl CryptoRng + RngCore,
329+
digest: &[u8],
330+
) -> signature::Result<Signature> {
331+
sign(Some(&mut rng), &self.inner, self.hash.as_ref(), digest)
332+
.map(|v| v.into())
333+
.map_err(|e| e.into())
334+
}
335+
}
336+
337+
pub struct VerifyingKey {
338+
inner: RsaPublicKey,
339+
hash: Option<Hash>,
340+
}
341+
342+
impl VerifyingKey {
343+
pub fn new(key: RsaPublicKey) -> Self {
344+
Self {
345+
inner: key,
346+
hash: None,
347+
}
348+
}
349+
350+
pub fn new_with_hash(key: RsaPublicKey, hash: Hash) -> Self {
351+
Self {
352+
inner: key,
353+
hash: Some(hash),
354+
}
355+
}
356+
}
357+
358+
impl From<SigningKey> for VerifyingKey {
359+
fn from(key: SigningKey) -> Self {
360+
Self {
361+
inner: key.key().into(),
362+
hash: key.hash(),
363+
}
364+
}
365+
}
366+
367+
impl From<&SigningKey> for VerifyingKey {
368+
fn from(key: &SigningKey) -> Self {
369+
Self {
370+
inner: key.key().into(),
371+
hash: key.hash(),
372+
}
373+
}
374+
}
375+
376+
impl Verifier<Signature> for VerifyingKey {
377+
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
378+
verify(&self.inner, self.hash.as_ref(), msg, signature.as_ref()).map_err(|e| e.into())
379+
}
380+
}
381+
217382
#[cfg(test)]
218383
mod tests {
219384
use super::*;
@@ -224,6 +389,7 @@ mod tests {
224389
use num_traits::Num;
225390
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
226391
use sha1::{Digest, Sha1};
392+
use signature::{RandomizedSigner, Signature, Signer, Verifier};
227393

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

@@ -348,6 +514,33 @@ mod tests {
348514
}
349515
}
350516

517+
#[test]
518+
fn test_sign_pkcs1v15_signer() {
519+
let priv_key = get_private_key();
520+
521+
let tests = [(
522+
"Test.\n",
523+
hex!(
524+
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
525+
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
526+
),
527+
)];
528+
529+
let signing_key = SigningKey::new_with_hash(priv_key, Hash::SHA1);
530+
531+
for (text, expected) in &tests {
532+
let digest = Sha1::digest(text.as_bytes()).to_vec();
533+
534+
let out = signing_key.sign(&digest);
535+
assert_ne!(out.as_ref(), digest);
536+
assert_eq!(out.as_ref(), expected);
537+
538+
let mut rng = ChaCha8Rng::from_seed([42; 32]);
539+
let out2 = signing_key.sign_with_rng(&mut rng, &digest);
540+
assert_eq!(out2.as_ref(), expected);
541+
}
542+
}
543+
351544
#[test]
352545
fn test_verify_pkcs1v15() {
353546
let priv_key = get_private_key();
@@ -390,6 +583,45 @@ mod tests {
390583
}
391584
}
392585

586+
#[test]
587+
fn test_verify_pkcs1v15_signer() {
588+
let priv_key = get_private_key();
589+
590+
let tests = [
591+
(
592+
"Test.\n",
593+
hex!(
594+
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
595+
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
596+
),
597+
true,
598+
),
599+
(
600+
"Test.\n",
601+
hex!(
602+
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
603+
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af"
604+
),
605+
false,
606+
),
607+
];
608+
let pub_key: RsaPublicKey = priv_key.into();
609+
let verifying_key = VerifyingKey::new_with_hash(pub_key, Hash::SHA1);
610+
611+
for (text, sig, expected) in &tests {
612+
let digest = Sha1::digest(text.as_bytes()).to_vec();
613+
614+
let result = verifying_key.verify(&digest, &Signature::from_bytes(sig).unwrap());
615+
match expected {
616+
true => result.expect("failed to verify"),
617+
false => {
618+
result.expect_err("expected verifying error");
619+
()
620+
}
621+
}
622+
}
623+
}
624+
393625
#[test]
394626
fn test_unpadded_signature() {
395627
let msg = b"Thu Dec 19 18:06:16 EST 2013\n";
@@ -406,4 +638,26 @@ mod tests {
406638
.verify(PaddingScheme::new_pkcs1v15_sign(None), msg, &sig)
407639
.expect("failed to verify");
408640
}
641+
642+
#[test]
643+
fn test_unpadded_signature_signer() {
644+
let msg = b"Thu Dec 19 18:06:16 EST 2013\n";
645+
let expected_sig = Base64::decode_vec("pX4DR8azytjdQ1rtUiC040FjkepuQut5q2ZFX1pTjBrOVKNjgsCDyiJDGZTCNoh9qpXYbhl7iEym30BWWwuiZg==").unwrap();
646+
let priv_key = get_private_key();
647+
648+
let signing_key = SigningKey::new(priv_key);
649+
let sig = signing_key.sign(msg);
650+
assert_eq!(sig.as_ref(), expected_sig);
651+
652+
let verifying_key: VerifyingKey = (&signing_key).into();
653+
verifying_key
654+
.verify(msg, &Signature::from_bytes(&expected_sig).unwrap())
655+
.expect("failed to verify");
656+
657+
let mut rng = ChaCha8Rng::from_seed([42; 32]);
658+
let sig = signing_key.sign_with_rng(&mut rng, msg);
659+
assert_eq!(sig.as_ref(), expected_sig);
660+
661+
verifying_key.verify(msg, &sig).expect("failed to verify");
662+
}
409663
}

0 commit comments

Comments
 (0)