Skip to content

Commit a38d9a4

Browse files
committed
Verify RSA PKCS#1 1.5 signatures by encode-then-check.
When we first implemented PKCS#1 1.5 signature verification we had not implemented signature generation, so we implemented verification by parsing the padding. Now that we have generation we can save some code and arguably make verification safer. Also, this is the way RFC 3447 recommends to do it.
1 parent dcba8a8 commit a38d9a4

File tree

2 files changed

+41
-61
lines changed

2 files changed

+41
-61
lines changed

src/polyfill.rs

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
1818
use core;
1919

20+
// A better name for the `&*` idiom for removing the mutability from a
21+
// reference.
22+
#[inline(always)]
23+
pub fn ref_from_mut_ref<'a, T: ?Sized>(x: &'a mut T) -> &'a T { x }
24+
2025
#[inline(always)]
2126
pub fn u64_from_usize(x: usize) -> u64 { x as u64 }
2227

src/rsa/padding.rs

+36-61
Original file line numberDiff line numberDiff line change
@@ -53,81 +53,56 @@ impl ::private::Private for PKCS1 { }
5353

5454
#[cfg(feature ="rsa_signing")]
5555
impl RSAEncoding for PKCS1 {
56-
// Implement padding procedure per EMSA-PKCS1-v1_5,
57-
// https://tools.ietf.org/html/rfc3447#section-9.2.
5856
fn encode(&self, msg: &[u8], m_out: &mut [u8], _mod_bits: bits::BitLength,
5957
_rng: &rand::SecureRandom) -> Result<(), error::Unspecified> {
60-
let em = m_out;
61-
62-
let digest_len = self.digestinfo_prefix.len() +
63-
self.digest_alg.output_len;
64-
65-
// Require at least 8 bytes of padding. Since we disallow keys smaller
66-
// than 2048 bits, this should never happen anyway.
67-
debug_assert!(em.len() >= digest_len + 11);
68-
let pad_len = em.len() - digest_len - 3;
69-
em[0] = 0;
70-
em[1] = 1;
71-
for i in 0..pad_len {
72-
em[2 + i] = 0xff;
73-
}
74-
em[2 + pad_len] = 0;
75-
76-
let (digest_prefix, digest_dst) = em[3 + pad_len..]
77-
.split_at_mut(self.digestinfo_prefix.len());
78-
digest_prefix.copy_from_slice(self.digestinfo_prefix);
79-
digest_dst.copy_from_slice(
80-
digest::digest(self.digest_alg, msg).as_ref());
58+
pkcs1_encode(&self, msg, m_out);
8159
Ok(())
8260
}
8361
}
8462

8563
impl RSAVerification for PKCS1 {
8664
fn verify(&self, msg: untrusted::Input, m: &mut untrusted::Reader,
87-
_mod_bits: bits::BitLength) -> Result<(), error::Unspecified> {
88-
let em = m;
89-
90-
if try!(em.read_byte()) != 0 ||
91-
try!(em.read_byte()) != 1 {
92-
return Err(error::Unspecified);
93-
}
94-
95-
let mut ps_len = 0;
96-
loop {
97-
match try!(em.read_byte()) {
98-
0xff => {
99-
ps_len += 1;
100-
},
101-
0x00 => {
102-
break;
103-
},
104-
_ => {
105-
return Err(error::Unspecified);
106-
},
107-
}
108-
}
109-
if ps_len < 8 {
110-
return Err(error::Unspecified);
111-
}
112-
113-
let em_digestinfo_prefix = try!(em.skip_and_get_input(
114-
self.digestinfo_prefix.len()));
115-
if em_digestinfo_prefix != self.digestinfo_prefix {
116-
return Err(error::Unspecified);
117-
}
118-
119-
let digest_alg = self.digest_alg;
120-
let decoded_digest =
121-
try!(em.skip_and_get_input(digest_alg.output_len));
122-
let digest = digest::digest(digest_alg, msg.as_slice_less_safe());
123-
if decoded_digest != digest.as_ref() {
65+
mod_bits: bits::BitLength) -> Result<(), error::Unspecified> {
66+
// `mod_bits.as_usize_bytes_rounded_up() <=
67+
// PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN` is ensured by `verify_rsa()`.
68+
let mut calculated = [0u8; PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN];
69+
let calculated =
70+
&mut calculated[..mod_bits.as_usize_bytes_rounded_up()];
71+
pkcs1_encode(&self, msg.as_slice_less_safe(), calculated);
72+
if m.skip_to_end() != polyfill::ref_from_mut_ref(calculated) {
12473
return Err(error::Unspecified);
12574
}
126-
12775
Ok(())
12876
}
12977
}
13078

79+
// Implement padding procedure per EMSA-PKCS1-v1_5,
80+
// https://tools.ietf.org/html/rfc3447#section-9.2. This is used by both
81+
// verification and signing so it needs to be able to handle moduli of the
82+
// minimum and maximum sizes for both operations.
83+
fn pkcs1_encode(pkcs1: &PKCS1, msg: &[u8], m_out: &mut [u8]) {
84+
let em = m_out;
85+
86+
let digest_len =
87+
pkcs1.digestinfo_prefix.len() + pkcs1.digest_alg.output_len;
88+
89+
// The specification requires at least 8 bytes of padding. Since we
90+
// disallow keys smaller than 2048 bits, this should always be true.
91+
assert!(em.len() >= digest_len + 11);
92+
let pad_len = em.len() - digest_len - 3;
93+
em[0] = 0;
94+
em[1] = 1;
95+
for i in 0..pad_len {
96+
em[2 + i] = 0xff;
97+
}
98+
em[2 + pad_len] = 0;
99+
100+
let (digest_prefix, digest_dst) = em[3 + pad_len..]
101+
.split_at_mut(pkcs1.digestinfo_prefix.len());
102+
digest_prefix.copy_from_slice(pkcs1.digestinfo_prefix);
103+
digest_dst.copy_from_slice(digest::digest(pkcs1.digest_alg, msg).as_ref());
104+
}
105+
131106
macro_rules! rsa_pkcs1_padding {
132107
( $PADDING_ALGORITHM:ident, $digest_alg:expr, $digestinfo_prefix:expr,
133108
$doc_str:expr ) => {

0 commit comments

Comments
 (0)