Fix (mod_bits - 1) % == 0 case in RSA PSS verification.

Output the extra leading zero byte when necessary.

Rename parameters in the `Encoding` interface to match the
specification and update the implementations to use the updated
variable names.
This commit is contained in:
Brian Smith 2016-11-12 14:33:58 -10:00
parent 4e5145157c
commit 7520d283eb
2 changed files with 44 additions and 32 deletions

View File

@ -24,9 +24,8 @@ pub trait Encoding: Sync {
/// The term "Verification" comes from RFC 3447.
pub trait Verification: Sync {
fn verify(&self, msg: untrusted::Input, encoded: untrusted::Input,
public_modulus_len_in_bits: usize)
-> Result<(), error::Unspecified>;
fn verify(&self, msg: untrusted::Input, m: untrusted::Input,
mod_bits: usize) -> Result<(), error::Unspecified>;
}
pub struct PKCS1 {
@ -64,17 +63,17 @@ impl Encoding for PKCS1 {
}
impl Verification for PKCS1 {
fn verify(&self, msg: untrusted::Input, encoded: untrusted::Input, _: usize)
-> Result<(), error::Unspecified> {
encoded.read_all(error::Unspecified, |decoded| {
if try!(decoded.read_byte()) != 0 ||
try!(decoded.read_byte()) != 1 {
fn verify(&self, msg: untrusted::Input, m: untrusted::Input,
_mod_bits: usize) -> Result<(), error::Unspecified> {
m.read_all(error::Unspecified, |em| {
if try!(em.read_byte()) != 0 ||
try!(em.read_byte()) != 1 {
return Err(error::Unspecified);
}
let mut ps_len = 0;
loop {
match try!(decoded.read_byte()) {
match try!(em.read_byte()) {
0xff => {
ps_len += 1;
},
@ -90,15 +89,15 @@ impl Verification for PKCS1 {
return Err(error::Unspecified);
}
let decoded_digestinfo_prefix = try!(decoded.skip_and_get_input(
let em_digestinfo_prefix = try!(em.skip_and_get_input(
self.digestinfo_prefix.len()));
if decoded_digestinfo_prefix != self.digestinfo_prefix {
if em_digestinfo_prefix != self.digestinfo_prefix {
return Err(error::Unspecified);
}
let digest_alg = self.digest_alg;
let decoded_digest =
try!(decoded.skip_and_get_input(digest_alg.output_len));
try!(em.skip_and_get_input(digest_alg.output_len));
let digest = digest::digest(digest_alg, msg.as_slice_less_safe());
if decoded_digest != digest.as_ref() {
return Err(error::Unspecified);
@ -162,10 +161,10 @@ pkcs1_digestinfo_prefix!(
[ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 ]);
/// PSS Padding as described in https://tools.ietf.org/html/rfc3447#section-9.1.
/// It generates a random salt equal in length to the output of the specified
/// digest algorithm and uses MGF1 with that digest algorihtm as the mask
/// generating function.
/// PSS padding as described in [RFC 3447 Section 8.1]. The mask generation
/// function is MGF1 using the signature's digest's algorithm.
///
/// [RFC 3447 Section 8.1]: https://tools.ietf.org/html/rfc3447#section-8.1
pub struct PSS {
digest_alg: &'static digest::Algorithm,
}
@ -174,22 +173,35 @@ pub struct PSS {
const PSS_PREFIX_ZEROS: [u8; 8] = [0u8; 8];
impl Verification for PSS {
// PSS verification as specified in
// https://tools.ietf.org/html/rfc3447#section-9.1.2
fn verify(&self, msg: untrusted::Input, encoded: untrusted::Input,
public_modulus_len_in_bits: usize)
-> Result<(), error::Unspecified> {
// Number of bytes required to encode message. The maximum length is
// given by the length of the public modulus in bits minus 1.
let em_len = 1 + (public_modulus_len_in_bits - 2) / 8;
assert_eq!(encoded.len(), em_len);
// Create a bit mask to match the size of the modulus used.
let top_byte_mask = 0xffu8 >>
(7 - ((public_modulus_len_in_bits - 2) % 8));
encoded.read_all(error::Unspecified, |em| {
let digest_len = self.digest_alg.output_len;
// RSASSA-PSS-VERIFY from https://tools.ietf.org/html/rfc3447#section-8.1.2
// where steps 1, 2(a), and 2(b) have been done for us.
fn verify(&self, msg: untrusted::Input, m: untrusted::Input,
mod_bits: usize) -> Result<(), error::Unspecified> {
m.read_all(error::Unspecified, |m| {
// RSASSA-PSS-VERIFY Step 2(c). The `m` this function is given is
// the big-endian-encoded value of `m` from the specification,
// padded to `k` bytes, where `k` is the length in bytes of the
// public modulus. The spec says "Note that emLen will be one less
// than k if modBits - 1 is divisible by 8 and equal to k
// otherwise," where `k` is the length in octets of the RSA public
// modulus `n`. In other words, `em` might have an extra leading
// zero byte that we need to strip before we start the PSS decoding
// steps which is an artifact of the `Verification` interface.
if (mod_bits - 1) % 8 == 0 {
if try!(m.read_byte()) != 0 {
return Err(error::Unspecified);
}
};
let em = m;
let em_bits = mod_bits - 1;
let em_len = (em_bits + 7) / 8;
let top_byte_mask = 0xffu8 >> ((8 * em_len) - em_bits);
// Step 2.
// The rest of this function is EMSA-PSS-VERIFY from
// https://tools.ietf.org/html/rfc3447#section-9.1.2.
// Steps 1 and 2.
let digest_len = self.digest_alg.output_len;
let m_hash = digest::digest(self.digest_alg,
msg.as_slice_less_safe());

View File

@ -12,7 +12,7 @@ Result = P
Digest = SHA256
Msg = e002377affb04f0fe4598de9d92d31d6c786040d5776976556a2cfc55e54a1dcb3cb1b126bd6a4bed2a184990ccea773fcc79d246553e6c64f686d21ad4152673cafec22aeb40f6a084e8a5b4991f4c64cf8a927effd0fd775e71e8329e41fdd4457b3911173187b4f09a817d79ea2397fc12dfe3d9c9a0290c8ead31b6690a6
Encoded = c9cbb20ff49558e10505fea39e85e30f64f7b71a6307e51d58d1b09139e623ca00800224f6024fb84c7f5407f3d4045e4474def670e32d62590861e10612567eb8f51ba7d8b095553c214d75bb9960f33aa1aa173d020558e35d2b6d7385bd04223b707083d3311b37fea10f73fcb88ec5a9f1920fdef4dba6965756cf78a89ba4cea0fc29b834849c05fa6f8ba823e9c47df8b563f56aaa9a20f69dbee63bb56306c467963703e6671e1eb2f33021404f8ee9332b364f33b6468272afe85dd0ff8f94e7eb2a28876a975d51ec87af3e5fba4a50374daa2a0cde9eb8b675d9fab3a8ec5deee1cace4acb6e0df2ae12f6c63cf28c96492f1a742dcf91186cc0bc
Encoded = 00c9cbb20ff49558e10505fea39e85e30f64f7b71a6307e51d58d1b09139e623ca00800224f6024fb84c7f5407f3d4045e4474def670e32d62590861e10612567eb8f51ba7d8b095553c214d75bb9960f33aa1aa173d020558e35d2b6d7385bd04223b707083d3311b37fea10f73fcb88ec5a9f1920fdef4dba6965756cf78a89ba4cea0fc29b834849c05fa6f8ba823e9c47df8b563f56aaa9a20f69dbee63bb56306c467963703e6671e1eb2f33021404f8ee9332b364f33b6468272afe85dd0ff8f94e7eb2a28876a975d51ec87af3e5fba4a50374daa2a0cde9eb8b675d9fab3a8ec5deee1cace4acb6e0df2ae12f6c63cf28c96492f1a742dcf91186cc0bc
Len = 2049
Result = P