Skip to content

Commit 5a96fa8

Browse files
committed
Factor out RSA padding message digest computation.
Instead of every padding verification implementation and every padding encoding implementation doing the digesting, have `sign()` and `verify()` to it themselves.
1 parent a38d9a4 commit 5a96fa8

File tree

3 files changed

+51
-32
lines changed

3 files changed

+51
-32
lines changed

src/rsa/padding.rs

+43-28
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,30 @@ use untrusted;
1919
#[cfg(feature = "rsa_signing")]
2020
use rand;
2121

22+
/// Common features of both RSA padding encoding and RSA padding verification.
23+
pub trait RSAPadding: 'static + Sync + ::private::Private {
24+
// The digest algorithm used for digesting the message (and maybe for
25+
// other things).
26+
fn digest_alg(&self) -> &'static digest::Algorithm;
27+
}
28+
2229
/// An RSA signature encoding as described in [RFC 3447 Section 8].
2330
///
2431
/// [RFC 3447 Section 8]: https://tools.ietf.org/html/rfc3447#section-8
2532
#[cfg(feature = "rsa_signing")]
26-
pub trait RSAEncoding: 'static + Sync + ::private::Private {
33+
pub trait RSAEncoding: RSAPadding {
2734
#[doc(hidden)]
28-
fn encode(&self, msg: &[u8], m_out: &mut [u8], mod_bits: bits::BitLength,
29-
rng: &rand::SecureRandom) -> Result<(), error::Unspecified>;
35+
fn encode(&self, m_hash: &digest::Digest, m_out: &mut [u8],
36+
mod_bits: bits::BitLength, rng: &rand::SecureRandom)
37+
-> Result<(), error::Unspecified>;
3038
}
3139

3240
/// Verification of an RSA signature encoding as described in
3341
/// [RFC 3447 Section 8].
3442
///
3543
/// [RFC 3447 Section 8]: https://tools.ietf.org/html/rfc3447#section-8
36-
pub trait RSAVerification: 'static + Sync + ::private::Private {
37-
fn verify(&self, msg: untrusted::Input, m: &mut untrusted::Reader,
44+
pub trait RSAVerification: RSAPadding {
45+
fn verify(&self, m_hash: &digest::Digest, m: &mut untrusted::Reader,
3846
mod_bits: bits::BitLength) -> Result<(), error::Unspecified>;
3947
}
4048

@@ -51,24 +59,29 @@ pub struct PKCS1 {
5159

5260
impl ::private::Private for PKCS1 { }
5361

62+
impl RSAPadding for PKCS1 {
63+
fn digest_alg(&self) -> &'static digest::Algorithm { self.digest_alg }
64+
}
65+
5466
#[cfg(feature ="rsa_signing")]
5567
impl RSAEncoding for PKCS1 {
56-
fn encode(&self, msg: &[u8], m_out: &mut [u8], _mod_bits: bits::BitLength,
57-
_rng: &rand::SecureRandom) -> Result<(), error::Unspecified> {
58-
pkcs1_encode(&self, msg, m_out);
68+
fn encode(&self, m_hash: &digest::Digest, m_out: &mut [u8],
69+
_mod_bits: bits::BitLength, _rng: &rand::SecureRandom)
70+
-> Result<(), error::Unspecified> {
71+
pkcs1_encode(&self, m_hash, m_out);
5972
Ok(())
6073
}
6174
}
6275

6376
impl RSAVerification for PKCS1 {
64-
fn verify(&self, msg: untrusted::Input, m: &mut untrusted::Reader,
77+
fn verify(&self, m_hash: &digest::Digest, m: &mut untrusted::Reader,
6578
mod_bits: bits::BitLength) -> Result<(), error::Unspecified> {
6679
// `mod_bits.as_usize_bytes_rounded_up() <=
6780
// PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN` is ensured by `verify_rsa()`.
6881
let mut calculated = [0u8; PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN];
6982
let calculated =
7083
&mut calculated[..mod_bits.as_usize_bytes_rounded_up()];
71-
pkcs1_encode(&self, msg.as_slice_less_safe(), calculated);
84+
pkcs1_encode(&self, m_hash, calculated);
7285
if m.skip_to_end() != polyfill::ref_from_mut_ref(calculated) {
7386
return Err(error::Unspecified);
7487
}
@@ -80,7 +93,7 @@ impl RSAVerification for PKCS1 {
8093
// https://tools.ietf.org/html/rfc3447#section-9.2. This is used by both
8194
// verification and signing so it needs to be able to handle moduli of the
8295
// minimum and maximum sizes for both operations.
83-
fn pkcs1_encode(pkcs1: &PKCS1, msg: &[u8], m_out: &mut [u8]) {
96+
fn pkcs1_encode(pkcs1: &PKCS1, m_hash: &digest::Digest, m_out: &mut [u8]) {
8497
let em = m_out;
8598

8699
let digest_len =
@@ -100,7 +113,7 @@ fn pkcs1_encode(pkcs1: &PKCS1, msg: &[u8], m_out: &mut [u8]) {
100113
let (digest_prefix, digest_dst) = em[3 + pad_len..]
101114
.split_at_mut(pkcs1.digestinfo_prefix.len());
102115
digest_prefix.copy_from_slice(pkcs1.digestinfo_prefix);
103-
digest_dst.copy_from_slice(digest::digest(pkcs1.digest_alg, msg).as_ref());
116+
digest_dst.copy_from_slice(m_hash.as_ref());
104117
}
105118

106119
macro_rules! rsa_pkcs1_padding {
@@ -174,12 +187,17 @@ impl ::private::Private for PSS { }
174187
// In practice, this is constrained by the maximum digest length.
175188
const MAX_SALT_LEN: usize = digest::MAX_OUTPUT_LEN;
176189

190+
impl RSAPadding for PSS {
191+
fn digest_alg(&self) -> &'static digest::Algorithm { self.digest_alg }
192+
}
193+
177194
#[cfg(feature = "rsa_signing")]
178195
impl RSAEncoding for PSS {
179196
// Implement padding procedure per EMSA-PSS,
180197
// https://tools.ietf.org/html/rfc3447#section-9.1.
181-
fn encode(&self, msg: &[u8], m_out: &mut [u8], mod_bits: bits::BitLength,
182-
rng: &rand::SecureRandom) -> Result<(), error::Unspecified> {
198+
fn encode(&self, m_hash: &digest::Digest, m_out: &mut [u8],
199+
mod_bits: bits::BitLength, rng: &rand::SecureRandom)
200+
-> Result<(), error::Unspecified> {
183201
let metrics = try!(PSSMetrics::new(self.digest_alg, mod_bits));
184202

185203
// The `m_out` this function fills is the big-endian-encoded value of `m`
@@ -196,7 +214,7 @@ impl RSAEncoding for PSS {
196214
};
197215
assert_eq!(em.len(), metrics.em_len);
198216

199-
// Steps 1 and 2 are done later, out of order.
217+
// Steps 1 and 2 are done by the caller to produce `m_hash`.
200218

201219
// Step 3 is done by `PSSMetrics::new()` above.
202220

@@ -206,7 +224,7 @@ impl RSAEncoding for PSS {
206224
try!(rng.fill(salt));
207225

208226
// Step 5 and 6.
209-
let h_hash = pss_digest(self.digest_alg, msg, salt);
227+
let h_hash = pss_digest(self.digest_alg, m_hash, salt);
210228

211229
// Re-order steps 7, 8, 9 and 10 so that we first output the db mask
212230
// into `em`, and then XOR the value of db.
@@ -246,7 +264,7 @@ impl RSAEncoding for PSS {
246264
impl RSAVerification for PSS {
247265
// RSASSA-PSS-VERIFY from https://tools.ietf.org/html/rfc3447#section-8.1.2
248266
// where steps 1, 2(a), and 2(b) have been done for us.
249-
fn verify(&self, msg: untrusted::Input, m: &mut untrusted::Reader,
267+
fn verify(&self, m_hash: &digest::Digest, m: &mut untrusted::Reader,
250268
mod_bits: bits::BitLength) -> Result<(), error::Unspecified> {
251269
let metrics = try!(PSSMetrics::new(self.digest_alg, mod_bits));
252270

@@ -269,7 +287,7 @@ impl RSAVerification for PSS {
269287
// The rest of this function is EMSA-PSS-VERIFY from
270288
// https://tools.ietf.org/html/rfc3447#section-9.1.2.
271289

272-
// Steps 1 and 2 are done later, out of order.
290+
// Steps 1 and 2 are done by the caller to produce `m_hash`.
273291

274292
// Step 3 is done by `PSSMetrics::new()` above.
275293

@@ -321,8 +339,7 @@ impl RSAVerification for PSS {
321339
let salt = &db[(db.len() - metrics.s_len)..];
322340

323341
// Step 12 and 13.
324-
let h_prime =
325-
pss_digest(self.digest_alg, msg.as_slice_less_safe(), salt);
342+
let h_prime = pss_digest(self.digest_alg, m_hash, salt);
326343

327344
// Step 14.
328345
if h_hash != h_prime.as_ref() {
@@ -402,15 +419,11 @@ fn mgf1(digest_alg: &'static digest::Algorithm, seed: &[u8], mask: &mut [u8])
402419
Ok(())
403420
}
404421

405-
fn pss_digest(digest_alg: &'static digest::Algorithm, msg: &[u8], salt: &[u8])
406-
-> digest::Digest {
422+
fn pss_digest(digest_alg: &'static digest::Algorithm, m_hash: &digest::Digest,
423+
salt: &[u8]) -> digest::Digest {
407424
// Fixed prefix.
408425
const PREFIX_ZEROS: [u8; 8] = [0u8; 8];
409426

410-
// Steps 1 & 2 for both encoding and verification. Step 1 is delegated to
411-
// the digest implementation.
412-
let m_hash = digest::digest(digest_alg, msg);
413-
414427
// Encoding step 5 and 6, Verification step 12 and 13.
415428
let mut ctx = digest::Context::new(digest_alg);
416429
ctx.update(&PREFIX_ZEROS);
@@ -444,7 +457,7 @@ rsa_pss_padding!(RSA_PSS_SHA512, &digest::SHA512,
444457

445458
#[cfg(test)]
446459
mod test {
447-
use {error, test};
460+
use {digest, error, test};
448461
use super::*;
449462
use untrusted;
450463

@@ -464,6 +477,8 @@ mod test {
464477

465478
let msg = test_case.consume_bytes("Msg");
466479
let msg = untrusted::Input::from(&msg);
480+
let m_hash = digest::digest(alg.digest_alg(),
481+
msg.as_slice_less_safe());
467482

468483
let encoded = test_case.consume_bytes("Encoded");
469484
let encoded = untrusted::Input::from(&encoded);
@@ -473,7 +488,7 @@ mod test {
473488

474489
let actual_result =
475490
encoded.read_all(error::Unspecified,
476-
|m| alg.verify(msg, m, bit_len));
491+
|m| alg.verify(&m_hash, m, bit_len));
477492
assert_eq!(actual_result.is_ok(), expected_result == "P");
478493

479494
Ok(())

src/rsa/signing.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
/// RSA PKCS#1 1.5 signatures.
1616
17-
use {bits, bssl, c, der, error};
17+
use {bits, bssl, c, der, digest, error};
1818
use rand;
1919
use std;
2020
use super::{BIGNUM, GFp_BN_free, BN_MONT_CTX, GFp_BN_MONT_CTX_free,
@@ -224,7 +224,8 @@ impl RSASigningState {
224224
return Err(error::Unspecified);
225225
}
226226

227-
try!(padding_alg.encode(msg, signature, mod_bits, rng));
227+
let m_hash = digest::digest(padding_alg.digest_alg(), msg);
228+
try!(padding_alg.encode(&m_hash, signature, mod_bits, rng));
228229
let mut rand = rand::RAND::new(rng);
229230
bssl::map_result(unsafe {
230231
GFp_rsa_private_transform(&self.key_pair.rsa,

src/rsa/verification.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
/// RSA PKCS#1 1.5 signatures.
1616
17-
use {bssl, c, error, private, signature};
17+
use {bssl, c, digest, error, private, signature};
1818
use super::{BIGNUM, PositiveInteger, PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN,
1919
RSAParameters, parse_public_key};
2020
use untrusted;
@@ -125,9 +125,12 @@ pub fn verify_rsa(params: &RSAParameters,
125125
PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN * 8)
126126
}));
127127

128+
let m_hash = digest::digest(params.padding_alg.digest_alg(),
129+
msg.as_slice_less_safe());
130+
128131
untrusted::Input::from(decoded).read_all(
129132
error::Unspecified,
130-
|m| params.padding_alg.verify(msg, m, n.bit_length()))
133+
|m| params.padding_alg.verify(&m_hash, m, n.bit_length()))
131134
}
132135

133136
extern {

0 commit comments

Comments
 (0)