From 9968baa55dd595d3120c5290d03341b69b3e9bfa Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 27 Nov 2018 15:13:26 -1000 Subject: [PATCH] Report some details of why asymmetric keys were rejected. --- src/ec/curve25519/ed25519/signing.rs | 28 +++--- src/ec/suite_b/ecdsa/signing.rs | 6 +- src/ec/suite_b/mod.rs | 46 ++++++---- src/error.rs | 82 +++++++++++++++++- src/pkcs8.rs | 61 ++++++++----- src/rsa/bigint.rs | 44 +++++----- src/rsa/signing.rs | 91 +++++++++++++------- src/rsa/verification.rs | 9 +- src/signature.rs | 12 +-- tests/ecdsa_from_pkcs8_tests.txt | 14 +-- tests/ecdsa_tests.rs | 30 +++++-- tests/ed25519_from_pkcs8_tests.txt | 16 ++-- tests/ed25519_from_pkcs8_unchecked_tests.txt | 4 +- tests/ed25519_tests.rs | 34 ++++++-- tests/rsa_from_pkcs8_tests.txt | 12 +-- tests/rsa_tests.rs | 12 ++- 16 files changed, 342 insertions(+), 159 deletions(-) diff --git a/src/ec/curve25519/ed25519/signing.rs b/src/ec/curve25519/ed25519/signing.rs index c7cf62002..8a526e627 100644 --- a/src/ec/curve25519/ed25519/signing.rs +++ b/src/ec/curve25519/ed25519/signing.rs @@ -69,7 +69,7 @@ impl<'a> KeyPair { /// /// If you need to parse PKCS#8 v1 files (without the public key) then use /// `Ed25519KeyPair::from_pkcs8_maybe_unchecked()` instead. - pub fn from_pkcs8(input: untrusted::Input) -> Result { + pub fn from_pkcs8(input: untrusted::Input) -> Result { let (seed, public_key) = unwrap_pkcs8(pkcs8::Version::V2Only, input)?; Self::from_seed_and_public_key(seed, public_key.unwrap()) } @@ -86,7 +86,7 @@ impl<'a> KeyPair { /// between the public key and the private key. /// /// PKCS#8 v2 files are parsed exactly like `Ed25519KeyPair::from_pkcs8()`. - pub fn from_pkcs8_maybe_unchecked(input: untrusted::Input) -> Result { + pub fn from_pkcs8_maybe_unchecked(input: untrusted::Input) -> Result { let (seed, public_key) = unwrap_pkcs8(pkcs8::Version::V1OrV2, input)?; if let Some(public_key) = public_key { Self::from_seed_and_public_key(seed, public_key) @@ -107,14 +107,19 @@ impl<'a> KeyPair { /// key. pub fn from_seed_and_public_key( seed: untrusted::Input, public_key: untrusted::Input, - ) -> Result { + ) -> Result { let pair = Self::from_seed_unchecked(seed)?; // This implicitly verifies that `public_key` is the right length. // XXX: This rejects ~18 keys when they are partially reduced, though // those keys are virtually impossible to find. if public_key != pair.public_key_bytes() { - return Err(error::Unspecified); + let err = if public_key.len() != pair.public_key_bytes().len() { + error::KeyRejected::invalid_encoding() + } else { + error::KeyRejected::inconsistent_components() + }; + return Err(err); } Ok(pair) @@ -129,8 +134,9 @@ impl<'a> KeyPair { /// Since the public key is not given, the public key will be computed from /// the private key. It is not possible to detect misuse or corruption of /// the private key since the public key isn't given as input. - pub fn from_seed_unchecked(seed: untrusted::Input) -> Result { - let seed = slice_as_array_ref!(seed.as_slice_less_safe(), SEED_LEN)?; + pub fn from_seed_unchecked(seed: untrusted::Input) -> Result { + let seed = slice_as_array_ref!(seed.as_slice_less_safe(), SEED_LEN) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; Ok(Self::from_seed_(seed)) } @@ -194,11 +200,13 @@ impl<'a> KeyPair { fn unwrap_pkcs8( version: pkcs8::Version, input: untrusted::Input, -) -> Result<(untrusted::Input, Option), error::Unspecified> { +) -> Result<(untrusted::Input, Option), error::KeyRejected> { let (private_key, public_key) = pkcs8::unwrap_key(&PKCS8_TEMPLATE, version, input)?; - let private_key = private_key.read_all(error::Unspecified, |input| { - der::expect_tag_and_get_value(input, der::Tag::OctetString) - })?; + let private_key = private_key + .read_all(error::Unspecified, |input| { + der::expect_tag_and_get_value(input, der::Tag::OctetString) + }) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; Ok((private_key, public_key)) } diff --git a/src/ec/suite_b/ecdsa/signing.rs b/src/ec/suite_b/ecdsa/signing.rs index 60e299bae..5a76d7df0 100644 --- a/src/ec/suite_b/ecdsa/signing.rs +++ b/src/ec/suite_b/ecdsa/signing.rs @@ -55,7 +55,7 @@ impl private::Sealed for Algorithm {} impl signature::SigningAlgorithm for Algorithm { fn from_pkcs8( &'static self, input: untrusted::Input, - ) -> Result { + ) -> Result { Key::from_pkcs8(self, input).map(signature::KeyPair::new) } } @@ -106,7 +106,7 @@ impl Key { /// algorithm identifier in the PKCS#8 header. pub fn from_pkcs8( alg: &'static Algorithm, input: untrusted::Input, - ) -> Result { + ) -> Result { let key_pair = ec::suite_b::key_pair_from_pkcs8(alg.curve, alg.pkcs8_template, input)?; Ok(Self::new(alg, key_pair)) } @@ -119,7 +119,7 @@ impl Key { /// key) instead. pub fn from_private_key_and_public_key( alg: &'static Algorithm, private_key: untrusted::Input, public_key: untrusted::Input, - ) -> Result { + ) -> Result { let key_pair = ec::suite_b::key_pair_from_bytes(alg.curve, private_key, public_key)?; Ok(Self::new(alg, key_pair)) } diff --git a/src/ec/suite_b/mod.rs b/src/ec/suite_b/mod.rs index 3452025ab..25a867d30 100644 --- a/src/ec/suite_b/mod.rs +++ b/src/ec/suite_b/mod.rs @@ -152,33 +152,40 @@ fn verify_affine_point_is_on_the_curve_scaled( pub(crate) fn key_pair_from_pkcs8( curve: &ec::Curve, template: &pkcs8::Template, input: untrusted::Input, -) -> Result { +) -> Result { let (ec_private_key, _) = pkcs8::unwrap_key(template, pkcs8::Version::V1Only, input)?; - let (private_key, public_key) = ec_private_key.read_all(error::Unspecified, |input| { - // https://tools.ietf.org/html/rfc5915#section-3 - der::nested(input, der::Tag::Sequence, error::Unspecified, |input| { - key_pair_from_pkcs8_(template, input) - }) - })?; + let (private_key, public_key) = + ec_private_key.read_all(error::KeyRejected::invalid_encoding(), |input| { + // https://tools.ietf.org/html/rfc5915#section-3 + der::nested( + input, + der::Tag::Sequence, + error::KeyRejected::invalid_encoding(), + |input| key_pair_from_pkcs8_(template, input), + ) + })?; key_pair_from_bytes(curve, private_key, public_key) } fn key_pair_from_pkcs8_<'a>( template: &pkcs8::Template, input: &mut untrusted::Reader<'a>, -) -> Result<(untrusted::Input<'a>, untrusted::Input<'a>), error::Unspecified> { - let version = der::small_nonnegative_integer(input)?; +) -> Result<(untrusted::Input<'a>, untrusted::Input<'a>), error::KeyRejected> { + let version = der::small_nonnegative_integer(input) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; if version != 1 { - return Err(error::Unspecified); + return Err(error::KeyRejected::version_not_supported()); } - let private_key = der::expect_tag_and_get_value(input, der::Tag::OctetString)?; + let private_key = der::expect_tag_and_get_value(input, der::Tag::OctetString) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; // [0] parameters (optional). if input.peek(der::Tag::ContextSpecificConstructed0 as u8) { let actual_alg_id = - der::expect_tag_and_get_value(input, der::Tag::ContextSpecificConstructed0)?; + der::expect_tag_and_get_value(input, der::Tag::ContextSpecificConstructed0) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; if actual_alg_id != template.curve_oid() { - return Err(error::Unspecified); + return Err(error::KeyRejected::wrong_algorithm()); } } @@ -189,23 +196,26 @@ fn key_pair_from_pkcs8_<'a>( der::Tag::ContextSpecificConstructed1, error::Unspecified, der::bit_string_with_no_unused_bits, - )?; + ) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; Ok((private_key, public_key)) } pub fn key_pair_from_bytes( curve: &ec::Curve, private_key_bytes: untrusted::Input, public_key_bytes: untrusted::Input, -) -> Result { - let private_key = ec::PrivateKey::from_bytes(curve, private_key_bytes)?; +) -> Result { + let private_key = ec::PrivateKey::from_bytes(curve, private_key_bytes) + .map_err(|error::Unspecified| error::KeyRejected::invalid_component())?; let mut public_key_check = [0; ec::PUBLIC_KEY_MAX_LEN]; { // Borrow `public_key_check`. let public_key_check = &mut public_key_check[..curve.public_key_len]; - (curve.public_from_private)(public_key_check, &private_key)?; + (curve.public_from_private)(public_key_check, &private_key) + .map_err(|error::Unspecified| error::KeyRejected::unexpected_error())?; if public_key_bytes != &*public_key_check { - return Err(error::Unspecified); + return Err(error::KeyRejected::inconsistent_components()); } } diff --git a/src/error.rs b/src/error.rs index 62aeb159d..744f725a6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -82,10 +82,14 @@ use std; #[derive(Clone, Copy, Debug, PartialEq)] pub struct Unspecified; +impl Unspecified { + fn description_() -> &'static str { "ring::error::Unspecified" } +} + // This is required for the implementation of `std::error::Error`. impl core::fmt::Display for Unspecified { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.write_str("ring::error::Unspecified") + f.write_str(Self::description_()) } } @@ -94,10 +98,82 @@ impl std::error::Error for Unspecified { #[inline] fn cause(&self) -> Option<&std::error::Error> { None } - #[inline] - fn description(&self) -> &str { "ring::error::Unspecified" } + fn description(&self) -> &str { Self::description_() } } impl From for Unspecified { fn from(_: untrusted::EndOfInput) -> Self { Unspecified } } + +/// An error parsing or validating a key. +/// +/// The `Display` implementation and `::description()` +/// will return a string that will help you better understand why a key was +/// rejected change which errors are reported in which situations while +/// minimizing the likelihood that any applications will be broken. +/// +/// Here is an incomplete list of reasons a key may be unsupported: +/// +/// * Invalid or Inconsistent Components: A component of the key has an +/// invalid value, or the mathematical relationship between two (or more) +/// components required for a valid key does not hold. +/// +/// * The encoding of the key is invalid. Perhaps the key isn't in the correct +/// format; e.g. it may be Base64 ("PEM") encoded, in which case the Base64 +/// encoding needs to be undone first. +/// +/// * The encoding includes a versioning mechanism and that mechanism indicates +/// that the key is encoded in a version of the encoding that isn't supported. +/// This might happen for multi-prime RSA keys (keys with more than two +/// private prime factors), which aren't supported, for example. +/// +/// * Too small or too Large: One of the primary components of the key is too +/// small or two large. Too-small keys are rejected for security reasons. +/// Some unnecessarily large keys are rejected for performance reasons. +/// +/// * Wrong algorithm: The key is not valid for the algorithm in which it +/// was being used. +/// +/// * Unexpected errors: Report this as a bug. +#[derive(Copy, Clone, Debug)] +pub struct KeyRejected(&'static str); + +impl KeyRejected { + fn description_(&self) -> &'static str { self.0 } + + pub(crate) fn inconsistent_components() -> Self { KeyRejected("InconsistentComponents") } + + pub(crate) fn invalid_component() -> Self { KeyRejected("InvalidComponent") } + + #[inline] + pub(crate) fn invalid_encoding() -> Self { KeyRejected("InvalidEncoding") } + + pub(crate) fn public_key_is_missing() -> Self { KeyRejected("PublicKeyIsMissing") } + + pub(crate) fn too_small() -> Self { KeyRejected("TooSmall") } + + pub(crate) fn too_large() -> Self { KeyRejected("TooLarge") } + + pub(crate) fn version_not_supported() -> Self { KeyRejected("VersionNotSupported") } + + pub(crate) fn wrong_algorithm() -> Self { KeyRejected("WrongAlgorithm") } + + pub(crate) fn unexpected_error() -> Self { KeyRejected("UnexpectedError") } +} + +#[cfg(feature = "use_heap")] +impl std::error::Error for KeyRejected { + fn cause(&self) -> Option<&std::error::Error> { None } + + fn description(&self) -> &str { self.description_() } +} + +impl std::fmt::Display for KeyRejected { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.write_str(self.description_()) + } +} + +impl From for Unspecified { + fn from(_: KeyRejected) -> Self { Unspecified } +} diff --git a/src/pkcs8.rs b/src/pkcs8.rs index 8c2539406..dce878826 100644 --- a/src/pkcs8.rs +++ b/src/pkcs8.rs @@ -63,7 +63,7 @@ impl Template { /// [RFC 5958]: https://tools.ietf.org/html/rfc5958. pub(crate) fn unwrap_key<'a>( template: &Template, version: Version, input: untrusted::Input<'a>, -) -> Result<(untrusted::Input<'a>, Option>), error::Unspecified> { +) -> Result<(untrusted::Input<'a>, Option>), error::KeyRejected> { unwrap_key_(template.alg_id_value(), version, input) } @@ -79,47 +79,68 @@ pub(crate) fn unwrap_key<'a>( /// [RFC 5958]: https://tools.ietf.org/html/rfc5958. pub(crate) fn unwrap_key_<'a>( alg_id: &[u8], version: Version, input: untrusted::Input<'a>, -) -> Result<(untrusted::Input<'a>, Option>), error::Unspecified> { - input.read_all(error::Unspecified, |input| { - der::nested(input, der::Tag::Sequence, error::Unspecified, |input| { - unwrap_key__(alg_id, version, input) - }) +) -> Result<(untrusted::Input<'a>, Option>), error::KeyRejected> { + input.read_all(error::KeyRejected::invalid_encoding(), |input| { + der::nested( + input, + der::Tag::Sequence, + error::KeyRejected::invalid_encoding(), + |input| unwrap_key__(alg_id, version, input), + ) }) } fn unwrap_key__<'a>( alg_id: &[u8], version: Version, input: &mut untrusted::Reader<'a>, -) -> Result<(untrusted::Input<'a>, Option>), error::Unspecified> { - // Currently we only support algorithms that should only be encoded - // in v1 form, so reject v2 and any later form. - let require_public_key = match (der::small_nonnegative_integer(input)?, version) { +) -> Result<(untrusted::Input<'a>, Option>), error::KeyRejected> { + let actual_version = der::small_nonnegative_integer(input) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; + + // Do things in a specific order to return more useful errors: + // 1. Check for completely unsupported version. + // 2. Check for algorithm mismatch. + // 3. Check for algorithm-specific version mismatch. + + if actual_version > 1 { + return Err(error::KeyRejected::version_not_supported()); + }; + + let actual_alg_id = der::expect_tag_and_get_value(input, der::Tag::Sequence) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; + if actual_alg_id != alg_id { + return Err(error::KeyRejected::wrong_algorithm()); + } + + let require_public_key = match (actual_version, version) { (0, Version::V1Only) => false, (0, Version::V1OrV2) => false, (1, Version::V1OrV2) | (1, Version::V2Only) => true, _ => { - return Err(error::Unspecified); + return Err(error::KeyRejected::version_not_supported()); }, }; - let actual_alg_id = der::expect_tag_and_get_value(input, der::Tag::Sequence)?; - if actual_alg_id != alg_id { - return Err(error::Unspecified); - } - - let private_key = der::expect_tag_and_get_value(input, der::Tag::OctetString)?; + let private_key = der::expect_tag_and_get_value(input, der::Tag::OctetString) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; // Ignore any attributes that are present. if input.peek(der::Tag::ContextSpecificConstructed0 as u8) { - let _ = der::expect_tag_and_get_value(input, der::Tag::ContextSpecificConstructed0)?; + let _ = der::expect_tag_and_get_value(input, der::Tag::ContextSpecificConstructed0) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; } let public_key = if require_public_key { - Some(der::nested( + if input.at_end() { + return Err(error::KeyRejected::public_key_is_missing()); + } + let public_key = der::nested( input, der::Tag::ContextSpecificConstructed1, error::Unspecified, der::bit_string_with_no_unused_bits, - )?) + ) + .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; + Some(public_key) } else { None }; diff --git a/src/rsa/bigint.rs b/src/rsa/bigint.rs index 4237de134..e473c7c17 100644 --- a/src/rsa/bigint.rs +++ b/src/rsa/bigint.rs @@ -98,18 +98,19 @@ impl Clone for BoxedLimbs { impl BoxedLimbs { fn positive_minimal_width_from_be_bytes( input: untrusted::Input, - ) -> Result { + ) -> Result { // Reject leading zeros. Also reject the value zero ([0]) because zero // isn't positive. if untrusted::Reader::new(input).peek(0) { - return Err(error::Unspecified); + return Err(error::KeyRejected::invalid_encoding()); } let num_limbs = (input.len() + LIMB_BYTES - 1) / LIMB_BYTES; let mut r = Self::zero(Width { num_limbs, m: PhantomData, }); - limb::parse_big_endian_and_pad_consttime(input, &mut r)?; + limb::parse_big_endian_and_pad_consttime(input, &mut r) + .map_err(|error::Unspecified| error::KeyRejected::unexpected_error())?; Ok(r) } @@ -227,14 +228,14 @@ impl core::fmt::Debug for Modulus { impl Modulus { pub fn from_be_bytes_with_bit_length( input: untrusted::Input, - ) -> Result<(Self, bits::BitLength), error::Unspecified> { + ) -> Result<(Self, bits::BitLength), error::KeyRejected> { let limbs = BoxedLimbs::positive_minimal_width_from_be_bytes(input)?; let bits = limb::limbs_minimal_bits(&limbs); Ok((Self::from_boxed_limbs(limbs)?, bits)) } #[cfg(feature = "rsa_signing")] - pub fn from(n: Nonnegative) -> Result { + pub fn from(n: Nonnegative) -> Result { let limbs = BoxedLimbs { limbs: n.limbs.into_boxed_slice(), m: PhantomData, @@ -242,16 +243,17 @@ impl Modulus { Self::from_boxed_limbs(limbs) } - fn from_boxed_limbs(n: BoxedLimbs) -> Result { + fn from_boxed_limbs(n: BoxedLimbs) -> Result { if n.len() > MODULUS_MAX_LIMBS { - return Err(error::Unspecified); + return Err(error::KeyRejected::too_large()); } - Result::from(unsafe { GFp_bn_mul_mont_check_num_limbs(n.len()) })?; + Result::from(unsafe { GFp_bn_mul_mont_check_num_limbs(n.len()) }) + .map_err(|error::Unspecified| error::KeyRejected::too_small())?; if limb::limbs_are_even_constant_time(&n) != LimbMask::False { - return Err(error::Unspecified); + return Err(error::KeyRejected::invalid_component()); } if limb::limbs_less_than_limb_constant_time(&n, 3) != LimbMask::False { - return Err(error::Unspecified); + return Err(error::KeyRejected::unexpected_error()); } // n_mod_r = n % r. As explained in the documentation for `n0`, this is @@ -427,7 +429,7 @@ impl Elem { } #[cfg(feature = "rsa_signing")] - pub fn into_modulus(self) -> Result, error::Unspecified> { + pub fn into_modulus(self) -> Result, error::KeyRejected> { Modulus::from_boxed_limbs(BoxedLimbs::minimal_width_from_unpadded(&self.limbs)) } @@ -647,19 +649,21 @@ pub struct PublicExponent(u64); impl PublicExponent { pub fn from_be_bytes( input: untrusted::Input, min_value: u64, - ) -> Result { + ) -> Result { if input.len() > 5 { - return Err(error::Unspecified); + return Err(error::KeyRejected::too_large()); } - let value = input.read_all_mut(error::Unspecified, |input| { + let value = input.read_all_mut(error::KeyRejected::invalid_encoding(), |input| { // The exponent can't be zero and it can't be prefixed with // zero-valued bytes. if input.peek(0) { - return Err(error::Unspecified); + return Err(error::KeyRejected::invalid_encoding()); } let mut value = 0u64; loop { - let byte = input.read_byte()?; + let byte = input + .read_byte() + .map_err(|untrusted::EndOfInput| error::KeyRejected::invalid_encoding())?; value = (value << 8) | u64::from(byte); if input.at_end() { return Ok(value); @@ -672,18 +676,18 @@ impl PublicExponent { // verification, for compatibility. Only small public exponents are // supported. if value & 1 != 1 { - return Err(error::Unspecified); + return Err(error::KeyRejected::invalid_component()); } debug_assert!(min_value & 1 == 1); debug_assert!(min_value <= PUBLIC_EXPONENT_MAX_VALUE); if min_value < 3 { - return Err(error::Unspecified); + return Err(error::KeyRejected::invalid_component()); } if value < min_value { - return Err(error::Unspecified); + return Err(error::KeyRejected::too_small()); } if value > PUBLIC_EXPONENT_MAX_VALUE { - return Err(error::Unspecified); + return Err(error::KeyRejected::too_large()); } Ok(PublicExponent(value)) diff --git a/src/rsa/signing.rs b/src/rsa/signing.rs index 8ad0bccc4..64335d3a6 100644 --- a/src/rsa/signing.rs +++ b/src/rsa/signing.rs @@ -18,7 +18,11 @@ use super::{ }; use arithmetic::montgomery::R; /// RSA PKCS#1 1.5 signatures. -use crate::{bits, der, digest, error, pkcs8, rand}; +use crate::{ + bits, der, digest, + error::{self, KeyRejected}, + pkcs8, rand, +}; use std; use untrusted; @@ -136,7 +140,7 @@ impl KeyPair { /// /// [RFC 5958]: /// https://tools.ietf.org/html/rfc5958 - pub fn from_pkcs8(input: untrusted::Input) -> Result { + pub fn from_pkcs8(input: untrusted::Input) -> Result { const RSA_ENCRYPTION: &[u8] = include_bytes!("../data/alg-rsa-encryption.der"); let (der, _) = pkcs8::unwrap_key_(&RSA_ENCRYPTION, pkcs8::Version::V1Only, input)?; Self::from_der(der) @@ -157,33 +161,44 @@ impl KeyPair { /// /// [NIST SP-800-56B rev. 1]: /// http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br1.pdf - pub fn from_der(input: untrusted::Input) -> Result { - input.read_all(error::Unspecified, |input| { + pub fn from_der(input: untrusted::Input) -> Result { + input.read_all(KeyRejected::invalid_encoding(), |input| { der::nested( input, der::Tag::Sequence, - error::Unspecified, + error::KeyRejected::invalid_encoding(), Self::from_der_reader, ) }) } - fn from_der_reader(input: &mut untrusted::Reader) -> Result { - let version = der::small_nonnegative_integer(input)?; + fn from_der_reader(input: &mut untrusted::Reader) -> Result { + let version = der::small_nonnegative_integer(input) + .map_err(|error::Unspecified| KeyRejected::invalid_encoding())?; if version != 0 { - return Err(error::Unspecified); + return Err(KeyRejected::version_not_supported()); } - let n = der::positive_integer(input)?; - let e = der::positive_integer(input)?; - let d = der::positive_integer(input)?; - let p = der::positive_integer(input)?; - let q = der::positive_integer(input)?; - let dP = der::positive_integer(input)?; - let dQ = der::positive_integer(input)?; - let qInv = der::positive_integer(input)?; - let (p, p_bits) = bigint::Nonnegative::from_be_bytes_with_bit_length(p)?; - let (q, q_bits) = bigint::Nonnegative::from_be_bytes_with_bit_length(q)?; + fn positive_integer<'a>( + input: &mut untrusted::Reader<'a>, + ) -> Result, KeyRejected> { + der::positive_integer(input) + .map_err(|error::Unspecified| KeyRejected::invalid_encoding()) + } + + let n = positive_integer(input)?; + let e = positive_integer(input)?; + let d = positive_integer(input)?; + let p = positive_integer(input)?; + let q = positive_integer(input)?; + let dP = positive_integer(input)?; + let dQ = positive_integer(input)?; + let qInv = positive_integer(input)?; + + let (p, p_bits) = bigint::Nonnegative::from_be_bytes_with_bit_length(p) + .map_err(|error::Unspecified| KeyRejected::invalid_encoding())?; + let (q, q_bits) = bigint::Nonnegative::from_be_bytes_with_bit_length(q) + .map_err(|error::Unspecified| KeyRejected::invalid_encoding())?; // Our implementation of CRT-based modular exponentiation used requires // that `p > q` so swap them if `p < q`. If swapped, `qInv` is @@ -191,7 +206,7 @@ impl KeyPair { // `q_mod_p` is constructed. let ((p, p_bits, dP), (q, q_bits, dQ, qInv)) = match q.verify_less_than(&p) { Ok(_) => ((p, p_bits, dP), (q, q_bits, dQ, Some(qInv))), - Err(_) => { + Err(error::Unspecified) => { // TODO: verify `q` and `qInv` are inverses (mod p). ((q, q_bits, dQ), (p, p_bits, dP, None)) }, @@ -244,7 +259,7 @@ impl KeyPair { // Second, stop if `p > 2**(nBits/2) - 1`. let half_n_bits = public_key.n_bits.half_rounded_up(); if p_bits != half_n_bits { - return Err(error::Unspecified); + return Err(KeyRejected::inconsistent_components()); } // TODO: Step 5.d: Verify GCD(p - 1, e) == 1. @@ -257,12 +272,14 @@ impl KeyPair { // // Second, stop if `q > 2**(nBits/2) - 1`. if p_bits != q_bits { - return Err(error::Unspecified); + return Err(KeyRejected::inconsistent_components()); } // TODO: Step 5.h: Verify GCD(p - 1, e) == 1. - let q_mod_n_decoded = q.to_elem(&public_key.n)?; + let q_mod_n_decoded = q + .to_elem(&public_key.n) + .map_err(|error::Unspecified| KeyRejected::inconsistent_components())?; // TODO: Step 5.i // @@ -280,10 +297,12 @@ impl KeyPair { q_mod_n_decoded.clone(), &public_key.n, ); - let p_mod_n = p.to_elem(&public_key.n)?; + let p_mod_n = p + .to_elem(&public_key.n) + .map_err(|error::Unspecified| KeyRejected::inconsistent_components())?; let pq_mod_n = bigint::elem_mul(&q_mod_n, p_mod_n, &public_key.n); if !pq_mod_n.is_zero() { - return Err(error::Unspecified); + return Err(KeyRejected::inconsistent_components()); } // 6.4.1.4.3/6.4.1.2.1 - Step 6. @@ -293,15 +312,17 @@ impl KeyPair { // First, validate `2**half_n_bits < d`. Since 2**half_n_bits has a bit // length of half_n_bits + 1, this check gives us 2**half_n_bits <= d, // and knowing d is odd makes the inequality strict. - let (d, d_bits) = bigint::Nonnegative::from_be_bytes_with_bit_length(d)?; + let (d, d_bits) = bigint::Nonnegative::from_be_bytes_with_bit_length(d) + .map_err(|_| error::KeyRejected::invalid_encoding())?; if !(half_n_bits < d_bits) { - return Err(error::Unspecified); + return Err(KeyRejected::inconsistent_components()); } // XXX: This check should be `d < LCM(p - 1, q - 1)`, but we don't have // a good way of calculating LCM, so it is omitted, as explained above. - d.verify_less_than_modulus(&public_key.n)?; + d.verify_less_than_modulus(&public_key.n) + .map_err(|error::Unspecified| KeyRejected::inconsistent_components())?; if !d.is_odd() { - return Err(error::Unspecified); + return Err(KeyRejected::invalid_component()); } // Step 6.b is omitted as explained above. @@ -318,12 +339,14 @@ impl KeyPair { // Step 7.c. let qInv = if let Some(qInv) = qInv { - bigint::Elem::from_be_bytes_padded(qInv, &p.modulus)? + bigint::Elem::from_be_bytes_padded(qInv, &p.modulus) + .map_err(|error::Unspecified| KeyRejected::invalid_component())? } else { // We swapped `p` and `q` above, so we need to calculate `qInv`. // Step 7.f below will verify `qInv` is correct. let q_mod_p = bigint::elem_mul(p.modulus.oneRR().as_ref(), q_mod_p.clone(), &p.modulus); - bigint::elem_inverse_consttime(q_mod_p, &p.modulus)? + bigint::elem_inverse_consttime(q_mod_p, &p.modulus) + .map_err(|error::Unspecified| KeyRejected::unexpected_error())? }; // Steps 7.d and 7.e are omitted per the documentation above, and @@ -332,7 +355,8 @@ impl KeyPair { // Step 7.f. let qInv = bigint::elem_mul(p.modulus.oneRR().as_ref(), qInv, &p.modulus); - bigint::verify_inverses_consttime(&qInv, q_mod_p, &p.modulus)?; + bigint::verify_inverses_consttime(&qInv, q_mod_p, &p.modulus) + .map_err(|error::Unspecified| KeyRejected::inconsistent_components())?; let qq = bigint::elem_mul(&q_mod_n, q_mod_n_decoded, &public_key.n).into_modulus::()?; @@ -360,11 +384,12 @@ struct PrivatePrime { impl PrivatePrime { /// Constructs a `PrivatePrime` from the private prime `p` and `dP` where /// dP == d % (p - 1). - fn new(p: bigint::Nonnegative, dP: untrusted::Input) -> Result { + fn new(p: bigint::Nonnegative, dP: untrusted::Input) -> Result { let p = bigint::Modulus::from(p)?; // [NIST SP-800-56B rev. 1] 6.4.1.4.3 - Steps 7.a & 7.b. - let dP = bigint::PrivateExponent::from_be_bytes_padded(dP, &p)?; + let dP = bigint::PrivateExponent::from_be_bytes_padded(dP, &p) + .map_err(|error::Unspecified| KeyRejected::invalid_component())?; // XXX: Steps 7.d and 7.e are omitted. We don't check that // `dP == d % (p - 1)` because we don't (in the long term) have a good diff --git a/src/rsa/verification.rs b/src/rsa/verification.rs index 288eb5a14..4d13a6558 100644 --- a/src/rsa/verification.rs +++ b/src/rsa/verification.rs @@ -29,7 +29,7 @@ impl Key { pub fn from_modulus_and_exponent( n: untrusted::Input, e: untrusted::Input, n_min_bits: bits::BitLength, n_max_bits: bits::BitLength, e_min_value: u64, - ) -> Result { + ) -> Result { // This is an incomplete implementation of NIST SP800-56Br1 Section // 6.4.2.2, "Partial Public-Key Validation for RSA." That spec defers // to NIST SP800-89 Section 5.3.3, "(Explicit) Partial Public Key @@ -52,12 +52,13 @@ impl Key { // flexible to be compatible with other commonly-used crypto libraries. assert!(n_min_bits >= N_MIN_BITS); let n_bits_rounded_up = - bits::BitLength::from_usize_bytes(n_bits.as_usize_bytes_rounded_up())?; + bits::BitLength::from_usize_bytes(n_bits.as_usize_bytes_rounded_up()) + .map_err(|error::Unspecified| error::KeyRejected::unexpected_error())?; if n_bits_rounded_up < n_min_bits { - return Err(error::Unspecified); + return Err(error::KeyRejected::too_small()); } if n_bits > n_max_bits { - return Err(error::Unspecified); + return Err(error::KeyRejected::too_large()); } // Step 2 / Step b. diff --git a/src/signature.rs b/src/signature.rs index f7ee2bbfd..555b683b7 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -213,12 +213,12 @@ //! let private_key_der = read_file(private_key_path)?; //! let private_key_der = untrusted::Input::from(&private_key_der); //! let key_pair = signature::RSAKeyPair::from_der(private_key_der) -//! .map_err(|ring::error::Unspecified| MyError::BadPrivateKey)?; +//! .map_err(|_| MyError::BadPrivateKey)?; //! //! // Create a signing state. //! let key_pair = std::sync::Arc::new(key_pair); //! let mut signing_state = signature::RSASigningState::new(key_pair) -//! .map_err(|ring::error::Unspecified| MyError::OOM)?; +//! .map_err(|_| MyError::OOM)?; //! //! // Sign the message "hello, world", using PKCS#1 v1.5 padding and the //! // SHA256 digest algorithm. @@ -227,7 +227,7 @@ //! let mut signature = vec![0; signing_state.key_pair().public_modulus_len()]; //! signing_state.sign(&signature::RSA_PKCS1_SHA256, &rng, MESSAGE, //! &mut signature) -//! .map_err(|ring::error::Unspecified| MyError::OOM)?; +//! .map_err(|_| MyError::OOM)?; //! //! // Verify the signature. //! let public_key_der = read_file(public_key_path)?; @@ -236,7 +236,7 @@ //! let signature = untrusted::Input::from(&signature); //! signature::verify(&signature::RSA_PKCS1_2048_8192_SHA256, //! public_key_der, message, signature) -//! .map_err(|ring::error::Unspecified| MyError::BadSignature)?; +//! .map_err(|_| MyError::BadSignature)?; //! //! Ok(()) //! } @@ -373,7 +373,7 @@ pub(crate) trait KeyPairImpl: core::fmt::Debug + Send + 'static { pub trait SigningAlgorithm: core::fmt::Debug + Sync + 'static + private::Sealed { /// Parses the key out of the given PKCS#8 document, verifying that it is /// valid for the algorithm. - fn from_pkcs8(&'static self, input: untrusted::Input) -> Result; + fn from_pkcs8(&'static self, input: untrusted::Input) -> Result; } /// Returns a key for signing that is parsed from a PKCS#8 document. @@ -383,7 +383,7 @@ pub trait SigningAlgorithm: core::fmt::Debug + Sync + 'static + private::Sealed #[inline] pub fn key_pair_from_pkcs8( alg: &'static SigningAlgorithm, input: untrusted::Input, -) -> Result { +) -> Result { alg.from_pkcs8(input) } diff --git a/tests/ecdsa_from_pkcs8_tests.txt b/tests/ecdsa_from_pkcs8_tests.txt index d4993f105..f78288ca6 100644 --- a/tests/ecdsa_from_pkcs8_tests.txt +++ b/tests/ecdsa_from_pkcs8_tests.txt @@ -15,29 +15,29 @@ Input = 3081bf020100301006072a8648ce3d020106052b810400220481a73081a40201010430fc # A P-256 key where the ECPrivateKey contains a parameters field identifying P-384. Curve = P-256 Input = 308190020100301306072a8648ce3d020106082a8648ce3d030107047630740201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a00706052b81040022a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724 -Error = DECODE_ERROR +Error = WrongAlgorithm # A P-256 key where the ECPrivateKey contains a parameters field containing an entire AlgorithmIdentifier. Curve = P-256 Input = 3081a0020100301306072a8648ce3d020106082a8648ce3d0301070481853081820201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a015301306072a8648ce3d020106082a8648ce3d030107a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724 -Error = DECODE_ERROR +Error = WrongAlgorithm # The curve P-256 is encoded explicitly, instead of as a reference to a named curve. Curve = P-256 Input = 308201220201010420c6c1aada15b07661f8142c6caf0fdb241aff2efe46c0938b74f2bcc53052b077a081fa3081f7020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff305b0420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b031500c49d360886e704936a6678e1139d26b7819f7e900441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020101 -Error = DECODE_ERROR +Error = InvalidEncoding # A valid PKCS#8 P-256 private key, but without the public key in the ECPrivateKey. Curve = P-256 Input = 308141020100301306072a8648ce3d020106082a8648ce3d030107042730250201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07 -Error = DECODE_ERROR +Error = InvalidEncoding # The AlgorithmIdentifier is ecPublicKey w/ P-256, but it's an RSAPrivateKey. Curve = P-256 Input = 308189020100301306072A8648CE3D020106082A8648CE3D0301070500046d306b0201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724 -Error = DECODE_ERROR +Error = InvalidEncoding -# A P-256 ECPrivateKey, but the AlgorithmIdentifier is The AlgorithmIdentifier is rsaEncryption. +# A P-256 ECPrivateKey, but the AlgorithmIdentifier is rsaEncryption. Curve = P-256 Input = 308181020100300d06092a864886f70d0101010500046d306b0201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724 -Error = DECODE_ERROR +Error = WrongAlgorithm diff --git a/tests/ecdsa_tests.rs b/tests/ecdsa_tests.rs index 0c2355b0d..7d16b739d 100644 --- a/tests/ecdsa_tests.rs +++ b/tests/ecdsa_tests.rs @@ -41,6 +41,8 @@ use ring::{rand, signature, test}; #[test] fn ecdsa_from_pkcs8_test() { test::from_file("tests/ecdsa_from_pkcs8_tests.txt", |section, test_case| { + use std::error::Error; + assert_eq!(section, ""); let curve_name = test_case.consume_string("Curve"); @@ -73,14 +75,26 @@ fn ecdsa_from_pkcs8_test() { let error = test_case.consume_optional_string("Error"); - assert_eq!( - signature::key_pair_from_pkcs8(this_fixed, input).is_ok(), - error.is_none() - ); - assert_eq!( - signature::key_pair_from_pkcs8(this_asn1, input).is_ok(), - error.is_none() - ); + match ( + signature::key_pair_from_pkcs8(this_fixed, input), + error.clone(), + ) { + (Ok(_), None) => (), + (Err(e), None) => panic!("Failed with error \"{}\", but expected to succeed", e), + (Ok(_), Some(e)) => panic!("Succeeded, but expected error \"{}\"", e), + (Err(actual), Some(expected)) => assert_eq!(actual.description(), expected), + }; + + match ( + signature::key_pair_from_pkcs8(this_asn1, input), + error.clone(), + ) { + (Ok(_), None) => (), + (Err(e), None) => panic!("Failed with error \"{}\", but expected to succeed", e), + (Ok(_), Some(e)) => panic!("Succeeded, but expected error \"{}\"", e), + (Err(actual), Some(expected)) => assert_eq!(actual.description(), expected), + }; + assert!(signature::key_pair_from_pkcs8(other_fixed, input).is_err()); assert!(signature::key_pair_from_pkcs8(other_asn1, input).is_err()); diff --git a/tests/ed25519_from_pkcs8_tests.txt b/tests/ed25519_from_pkcs8_tests.txt index 7e35a4b42..5831869ad 100644 --- a/tests/ed25519_from_pkcs8_tests.txt +++ b/tests/ed25519_from_pkcs8_tests.txt @@ -3,43 +3,43 @@ Input = 3053020101300506032b657004220420a22efdb713f0e1600d2a5ce948e321ca3a18137c # v2. The private key's last byte, zero, is omitted. Input = 3052020101300506032b65700421041fa22efdb713f0e1600d2a5ce948e321ca3a18137c47f15091a12c7126c1749aa1230321001aeb8e3ee5ba5afd91113466d19f4ea77fa0feffbd8c5adcb499927f12535f77 -Error = DECODE_ERROR +Error = InvalidEncoding # v2. The private key starts with a zero byte. Input = 3053020101300506032b65700422042000b1a7c20b2b4ed9c78f3686db82f854734cdc95be51def304d98e0cd30bf490a12303210063457cd4dfdd0e98a53796265831d46ac6a5a685f2a54c9697a38b2c800d60ba # v2. The private key's first byte, zero, is omitted. Input = 3052020101300506032b65700421041fb1a7c20b2b4ed9c78f3686db82f854734cdc95be51def304d98e0cd30bf490a12303210063457cd4dfdd0e98a53796265831d46ac6a5a685f2a54c9697a38b2c800d60ba -Error = DECODE_ERROR +Error = InvalidEncoding # v2. The public key's first byte is zero. Input = 3053020101300506032b6570042204202dc67de5186d9193021c0b104d9c6ef24bee2bd395ccb5ed5a2db5f37a2fc1f0a12303210000c17e4d8bbff27c1fb618c23fce988703c7efa3cd590aacac12d3f1e3c90c8c # v2. The public key's first byte, zero, is omitted. Input = 3052020101300506032b6570042204202dc67de5186d9193021c0b104d9c6ef24bee2bd395ccb5ed5a2db5f37a2fc1f0a122032000c17e4d8bbff27c1fb618c23fce988703c7efa3cd590aacac12d3f1e3c90c8c -Error = DECODE_ERROR +Error = InvalidEncoding # v2. The public key's last byte is zero. Input = 3053020101300506032b657004220420b2579f555a2eabdabac8d46997b1c08fe8ce63858df124efc29c60dfbb86c349a1230321009d421270ce2fcc08672c41e427214876245c9b0f14ab671b8bb9d266a492e400 # v2. The public key's last byte, zero, is omitted (valid ASN.1 DER). Input = 3052020101300506032b657004220420b2579f555a2eabdabac8d46997b1c08fe8ce63858df124efc29c60dfbb86c349a1220320009d421270ce2fcc08672c41e427214876245c9b0f14ab671b8bb9d266a492e4 -Error = DECODE_ERROR +Error = InvalidEncoding # v2. The public key's last byte, zero, has been truncated (invalid ASN.1 DER). Input = 3053020101300506032b657004220420b2579f555a2eabdabac8d46997b1c08fe8ce63858df124efc29c60dfbb86c349a1230321009d421270ce2fcc08672c41e427214876245c9b0f14ab671b8bb9d266a492e4 -Error = DECODE_ERROR +Error = InvalidEncoding # v2. The public key's high bit has been flipped. Ed25519 public keys don't # have their high bit masked, so this is wrong. Input = 3053020101300506032b6570042204202dc67de5186d9193021c0b104d9c6ef24bee2bd395ccb5ed5a2db5f37a2fc1f0a12303210000c17e4d8bbff27c1fb618c23fce988703c7efa3cd590aacac12d3f1e3c90c0c -Error = DECODE_ERROR +Error = InconsistentComponents # v2. Valid except the public key field is missing. Input = 302e020101300506032b657004220420a22efdb713f0e1600d2a5ce948e321ca3a18137c47f15091a12c7126c1749a00 -Error = DECODE_ERROR +Error = PublicKeyIsMissing # v2. Valid except the public key is encoded as [0] instead of [1]; i.e. the # attributes are invalid and the public key is missing. Input = 3053020101300506032b657004220420a22efdb713f0e1600d2a5ce948e321ca3a18137c47f15091a12c7126c1749a00a0230321001aeb8e3ee5ba5afd91113466d19f4ea77fa0feffbd8c5adcb499927f12535f77 -Error = DECODE_ERROR +Error = PublicKeyIsMissing diff --git a/tests/ed25519_from_pkcs8_unchecked_tests.txt b/tests/ed25519_from_pkcs8_unchecked_tests.txt index 9cc359387..61fac528f 100644 --- a/tests/ed25519_from_pkcs8_unchecked_tests.txt +++ b/tests/ed25519_from_pkcs8_unchecked_tests.txt @@ -3,11 +3,11 @@ Input = 302e020100300506032b6570042204209d61b19deffd5a60ba844af492ec2cc44449c569 # The same as the above, but with an invalid NULL parameter. (This is from BoringSSL's tests.) Input = 3030020100300706032b65700500042204209d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60 -Error = DECODE_ERROR +Error = WrongAlgorithm # Sample private key from draft-ietf-curdle-pkix-04. Input = 302e020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842 # v1. valid except it includes publicKey. Input = 3053020100300506032b657004220420a22efdb713f0e1600d2a5ce948e321ca3a18137c47f15091a12c7126c1749a00a1230321001aeb8e3ee5ba5afd91113466d19f4ea77fa0feffbd8c5adcb499927f12535f77 -Error = DECODE_ERROR +Error = InvalidEncoding diff --git a/tests/ed25519_tests.rs b/tests/ed25519_tests.rs index 5fb5240b2..e6d1428e6 100644 --- a/tests/ed25519_tests.rs +++ b/tests/ed25519_tests.rs @@ -123,13 +123,22 @@ fn test_ed25519_from_pkcs8_unchecked() { test::from_file( "tests/ed25519_from_pkcs8_unchecked_tests.txt", |section, test_case| { + use std::error::Error; + assert_eq!(section, ""); let input = test_case.consume_bytes("Input"); let error = test_case.consume_optional_string("Error"); - assert_eq!( - Ed25519KeyPair::from_pkcs8_maybe_unchecked(untrusted::Input::from(&input)).is_ok(), - error.is_none() - ); + + match ( + Ed25519KeyPair::from_pkcs8_maybe_unchecked(untrusted::Input::from(&input)), + error.clone(), + ) { + (Ok(_), None) => (), + (Err(e), None) => panic!("Failed with error \"{}\", but expected to succeed", e), + (Ok(_), Some(e)) => panic!("Succeeded, but expected error \"{}\"", e), + (Err(actual), Some(expected)) => assert_eq!(actual.description(), expected), + }; + Ok(()) }, ); @@ -141,13 +150,22 @@ fn test_ed25519_from_pkcs8() { test::from_file( "tests/ed25519_from_pkcs8_tests.txt", |section, test_case| { + use std::error::Error; + assert_eq!(section, ""); let input = test_case.consume_bytes("Input"); let error = test_case.consume_optional_string("Error"); - assert_eq!( - Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&input)).is_ok(), - error.is_none() - ); + + match ( + Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&input)), + error.clone(), + ) { + (Ok(_), None) => (), + (Err(e), None) => panic!("Failed with error \"{}\", but expected to succeed", e), + (Ok(_), Some(e)) => panic!("Succeeded, but expected error \"{}\"", e), + (Err(actual), Some(expected)) => assert_eq!(actual.description(), expected), + }; + Ok(()) }, ); diff --git a/tests/rsa_from_pkcs8_tests.txt b/tests/rsa_from_pkcs8_tests.txt index d4623dcf5..836dbbf8b 100644 --- a/tests/rsa_from_pkcs8_tests.txt +++ b/tests/rsa_from_pkcs8_tests.txt @@ -3,7 +3,7 @@ Input = 308204bd020100300d06092a864886f70d0101010500048204a7308204a3020100028201 # The same key as above, with the last byte removed. Input = 308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100b9d7af84fa4184a5f22037ec8aff2db5f78bd8c21e714e579ae57c6398c4950f3a694b17bfccf488766159aec5bb7c2c43d59c798cbd45a09c9c86933f126879ee7eadcd404f61ecfc425197cab03946ba381a49ef3b4d0f60b17f8a747cde56a834a7f6008f35ffb2f60a54ceda1974ff2a9963aba7f80d4e2916a93d8c74bb1ba5f3b189a4e8f0377bd3e94b5cc3f9c53cb8c8c7c0af394818755e968b7a76d9cada8da7af5fbe25da2a09737d5e4e4d7092aa16a0718d7322ce8aca767015128d6d35775ea9cb8bb1ac6512e1b787d34015221be780a37b1d69bc3708bfd8832591be6095a768f0fd3b3457927e6ae3641d55799a29a0a269cb4a693bc14b0203010001028201001c5fb7e69fa6dd2fd0f5e653f12ce0b7c5a1ce6864e97bc2985dad4e2f86e4133d21d25b3fe774f658cca83aace9e11d8905d62c20b6cd28a680a77357cfe1afac201f3d1532898afb40cce0560bedd2c49fc833bd98da3d1cd03cded0c637d4173e62de865b572d410f9ba83324cd7a3573359428232f1628f6d104e9e6c5f380898b5570201cf11eb5f7e0c4933139c7e7fba67582287ffb81b84fa81e9a2d9739815a25790c06ead7abcf286bd43c6e3d009d01f15fca3d720bbea48b0c8ccf8764f3c822e61159d8efcbff38c794f8afe040b45df14c976a91b1b6d886a55b8e68969bcb30c7197920d97d7721d78d954d89ffecbcc93c6ee82a86fe754102818100eba1cbe453f5cb2fb7eabc12d697267d25785a8f7b43cc2cb14555d3618c63929b19839dcd4212397ecda8ad872f97ede6ac95ebda7322bbc9409bac2b24ae56ad62202800c670365ae28671195fe934978a5987bee2fcea06561b782630b066b0a35c3f559a281f0f729fc282ef8ebdbb065d60000223da6edb732fa32d82bb02818100c9e81e353315fd88eff53763ed7b3859f419a0a158f5155851ce0fe6e43188e44fb43dd25bcdb7f3839fe84a5db88c6525e5bcbae513bae5ff54398106bd8ae4d241c082f8a64a9089531f7b57b09af52042efa097140702dda55a2141c174dd7a324761267728a6cc4ce386c034393d855ebe985c4e5f2aec2bd3f2e2123ab1028180566889dd9c50798771397a68aa1ad9b970e136cc811676ac3901c51c741c48737dbf187de8c47eec68acc05b8a4490c164230c0366a36c2c52fc075a56a3e7eecf3c39b091c0336c2b5e00913f0de5f62c5046ceb9d88188cc740d34bd44839bd4d0c346527cea93a15596727d139e53c35eed25043bc4ac18950f237c02777b0281800f9dd98049e44088efee6a8b5b19f5c0d765880c12c25a154bb6817a5d5a0b798544aea76f9c58c707fe3d4c4b3573fe7ad0eb291580d22ae9f5ccc0d311a40590d1af1f3236427c2d72f57367d3ec185b9771cb5d041a8ab93409e59a9d68f99c72f91c658a3fe5aed59f9f938c368530a4a45f4a7c7155f3906c4354030ef102818100c89e0ba805c970abd84a70770d8fc57bfaa34748a58b77fcddaf0ca285db91953ef5728c1be7470da5540df6af56bb04c0f5ec500f83b08057664cb1551e1e29c58d8b1e9d70e23ed57fdf9936c591a83c1dc954f6654d4a245b6d8676d045c2089ffce537d234fc88e98d92afa92926c75b286e8fee70e273d762bbe63cd6 -Error = DECODE_ERROR +Error = InvalidEncoding # RSA 3072-bit key with e == 65537. Input = 308206fd020100300d06092a864886f70d0101010500048206e7308206e30201000282018100ac2d26e81b524ff75bb23d79c5eef2258546a1cbc9ce00cdb453449ad3af0d38d748bab51533ff39aabe9d21a0b1113f57764a6c27d10c97212296e3033814b8b1ec1d4b8cb00a4d8174dce02a7a73149d195ef83c3ee0334a8bf94d86dc0e8ba2a5ac80343e8124ead35dda110f106436813fae364113f8136a79f7791b035549dc7f7a1492ff0cbbbae937eb7e05ed997a3023bc3622def643a13aa4b20f15b4c293ccfd64a26db61623ae4fc69bac32162953cb37c93216c009ca217bc9b4cfa463e22b9d083d1c42f321cb8c248c29090194c0e5e94957ca2a34d62bcab2a12acfbb4565bf2576583a57fd78b810a7f7c70aec118e7e9bb7fba9c80c10acb8154af0e0e21ee4e488393c9dfd977aa7c245925fb5f880bf1074d916710630362c246f0270d70cbb546bd5ef07e423c37f1f446707c4d6c3874f9c7abfe80f873884911a6a98514b34ef147b1e7ff93c9497b57c73653f391d74bce6db9699cfb3fdca698427db519f08e33b16a7105b27f1387aa9958a674d777c1403e03302030100010282018043901045627b415fea7e9dfb0bfdc0cd9267ee139ee41492a3e14cf9887997b1a76125caeb40153616d767a78b7c102d8fef9e32a4c936ac935714688e99d21fcbc9c09125eaea90116dbf42c3c87b222aa78b09d3f2445e3c318329b5f2e24e4f0bbe0e2cf21e1153888fba83992ab07866ed8b3a44fd460ab06c9dfccae9daecf9f697fc3cd692b3941bf25299884ffd3ae26b136f16edf12b06b8916a41db1dac80fd1eadffc31781c68c9f476b4cfdf68e4f105e9ab7ea0f7f87eeedb96f723e2a17e5103af96c3a508d38262577ffe3b2fb7bb4f7150cdc9dfb8d4f3d5cdf349192d1d302182df169403a066ddb28647831041d587884282392f83716b74f201d33fa5a967d5f506a93e4e9506ecf6802869ead17beff0b8903114f154fa3973067d56003409cdefa962578096d664c79d2c5df6e8dbbf67a9bf6e2c7c938608c17ae1ef6e56fce9058fb4823c5cee63e2a1c9f495dca8021fcb50cd92a35a9de5df29755b8f0d6d677d8fb8144dd106f07782e63032e6f40f3c29c0de90281c100def986933a3b7fdcb21c7096a4ef361c6631a23aacc8831f34270efd4850eac191d9284070cc910cf12864efd6d8c676491ba8f810b005dc4143e7e774d2d4fa255007ec75a98f5fc105100ed49327e90d31f01771bfe92b1f47cb36eb8c2d30067d1523980b9329e73ff7ae84a8ae11276a085a201bef0b398f934e4bdd7eba261e676a4a933a4254934f6ad8c17277a977a7782da24000eec12cf8def48d86a73ac84e9dcf5b5e8dad32ceb4d565085c2063f88df9a4b1f200375d2a58c86f0281c100c5ad84dcac6f51bfefa20c2b52611bcec8822739ff907491912b7c0d80ddcdfe1836c264969048133e76d3c4987a563a2d7d3c3b213cf47e1cd9329df1878bc4834e9742e5d0a31c663d4fa8edd8e0abad1c5fe2e6fef1f01c35b93eff619c7ae80873842daa277096dd1f3da4b4182525d06c1041a9c8627be145c3435a79dba4dfe52f63fa51486762d1b55d449fb55f07d304fcc726b9cc1e42484bc6d72704e0192f4bb7e6c6438dbd9aecf8d10626bc2ce1904f96d7594b9fc0e8231e7d0281c050a322f519a92ceb8e8a5479567aa032130f3e21441d5bb62c3137ed877aa3fb8f185ec33747f3e91f49c17f2f4ec42f40ef3ce3417db710d9958a479d6b25d4b52bc541d1a7998f4289e72dc5d8d625253d7b1baf6a7d13b843498dba0fbec3d084e9e12e7a79253d5957e4885b4c602dc5ddf1620d581cd21b4765987029057f059dd6c5d38acd3432f614a90104b99bd30700a2583c175a41085794a21290b0e7c44b122d4c58dc99d77703d4ecade24b607c22343748974156c45dad30070281c100a8d548d2c91bd2b0a1d4bf9c08a214040ad0cefc0440b6aed55454819bfcd5411e2136ea7e8600691f4292202932e38e90e79397d50da0ac4dab5c45f5d21517e4262d9edf75d0ba22c05ed7233dc6b61a06500715cfcd878c307e6545ed729eaeaaf92e7d6a04816ef8b1040f361a213028d10319720f49be106b6b188d9fe26ef12367d476291233eafcbd70497a368851c073540a2c132838f7d79390e295dade23e477413c833d9343cb9c75eee0d8188424ab02af0c82a1ca7212525c690281c012315e04ad315f39c8c0657c93ed32ec2501c9cf2b3c3f6c07fc78ad95d9c5827201ef95d7f549ddba41a75ed1b2a5261037490d235c4f097a123dd454da7f2954a1ea4e612662e5086d6e532adef15506b2adf1aeb5237257df7193d85e2a4d61bbef24122deee62b41b73cf277486da9dee39311904e49fcf7e5cf7c1ba5da0b8e25853da1389b79753a894643ff8299d1c599130c9d0f25db07eafde4a1ee1e66f839c3b775000a5d8576f3b158d2cfe8cbec94376cb81d54c6066a4fc97f @@ -16,16 +16,18 @@ Input = 308206ff020100300d06092a864886f70d0101010500048206e9308206e5020100028201 # RSA 2048-bit key with invalid e == 3. Input = 308204bc020100300d06092a864886f70d0101010500048204a6308204a20201000282010100bbb126534d64a3a60711730846af3c2f4cf4fee28f48e7c9ca2b0b5a6c8ad51e2509d38b87ea6b039ad3a33ac57e78c444d86438b1938eed73219cba33b06454e429330ce0fcd356624630c58f32a7ab3bef8c7cb3f9843b09c3e61dc92329df330f002451b5910cac3e7b513d3ca1cd58e39a5cc277e64da395058805f69f19bb62571f28525c5a7f781055ea82bddcc8d10c7163060e2040aeb390e6cd8cbba216726875d3b2975fae77d9d092a9e65f545f8a1e282c7096b3e8293d47d15e656717b0ea2d9a170d489c2f3b596cb745fd3f587e10cf3ff043a1f42ed9d030d900c02676761e70e2b5a8fd9387dc658cc95b1ac3ca83ce3b352d6e71514025020103028201007d20c43788edc26eaf60f75ad9ca281f88a354970a309a8686c75ce6f30738bec35be25d0546f2026737c2272e545082d89042d076625f48f76bbdd177caed8ded70ccb340a88ce441842083b4cc6fc77d4a5da877fbad7cb12d4413db6cc694ccb4aac2e123b60872d4523628d31688e5ed11932c4feede6d0e03b003f9bf6556ac08bfb5829df7f6baf74772fd3e22ca571851c5a0efe8330ffb45863a804f870f4535da65761c81c22decb3bd8fe55fb250b09fce0f12f1c1a073ab5349a8b9d8e3fb826a04f441479cf3b4ecb7d61aca60f44a3f1ffbe2bd615cb65c29b11beeff13074c06356ece01581b45fcfc2cab27a8777a631b3361763d59b7f26b02818100f6434d514907d7c9f522c84633148b5e2fccf3bc0fc4e6a5c5f7b88b15a062433dae12c53d5e54e2a47427da06b6ff4e2c4635b90aa8887b365b96e6485584f64138c39ef0d9108dc8f569f5cdac877c5d4bfad3a89dbda8f5e88d05277462b2636618c6f21f845ebbca664f1c7d5534c15ff90f5faffd712ecd2f130b8f808d02818100c31cfcae4f06979c983cd5248af2554a6981743aaacfbf9e2e1f021d87d56a0119d177d270dd2c89f8970b1cbc3f52c02382b0c823ca8d58f5b5e09573f55deb0d68fe18b5b5821ae267c6cbde49d179c081b3166614619d263f02e3f5db2ef4cbb428c2f96490c200b640aa4e218bb68868a68eb0e2f1b43f55ccff5f2dd3f902818100a42cde3630afe5314e17302eccb85ce975334d280a83446e83fa7b0763c04182291eb72e28e98dec6da2c53c0479ff8972d97926071b05a779926499858e58a42b7b2d14a090b5b3db4e46a3de7304fd9387fc8d1b13d3c5f945b358c4f841cc42441084a16a583f27dc4434bda8e37880eaa60a3fcaa8f61f3374b75d0a55b3028181008213531edf59ba68657de36db1f6e386f100f8271c8a7fbec96a01690538f15611364fe1a0937306a5ba07687d7f8c8017ac75dac28708e5f923eb0e4d4e3e9cb39b54107923ac11ec452f32943136512b01220eeeb841136ed4ac97f93cc9f887cd7081fb98608155ced5c6dec107cf059b19b475eca122d4e3ddff94c937fb0281805d84a6c335b77a5d2468b81cab4ab69198906454bb499b00c8094456b855987dd20a95a5be48f21067554b86df117cc9abab45dca04265fb589606b2cbc074808f39862cbf39e3efd1df44e04fcd9f778574528fcb52f5b7e16066267cf08d29ce7a29deb3d9c3671634af765916a865646accbdec557d970de3b242e3450167 -Error = DECODE_ERROR +Error = TooSmall # A valid ECC P-256 key. Input = 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724 -Error = DECODE_ERROR +Error = WrongAlgorithm # An RSAPrivateKey, but with an AlgorithmIdentifier of ecPublicKey w/ P-256. Input = 308189020100301306072A8648CE3D020106082A8648CE3D0301070500046d306b0201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724 -Error = DECODE_ERROR +Error = WrongAlgorithm # The AlgorithmIdentifier is rsaEncryption, but it contains a P-256 ECPrivateKey. +# TODO: This should be "InvalidEncoding"; that would require delaying the version +# validation until more parsing is done. Input = 308181020100300d06092a864886f70d0101010500046d306b0201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724 -Error = DECODE_ERROR +Error = VersionNotSupported diff --git a/tests/rsa_tests.rs b/tests/rsa_tests.rs index f43c707b0..43baecb3e 100644 --- a/tests/rsa_tests.rs +++ b/tests/rsa_tests.rs @@ -43,6 +43,8 @@ use ring::rand; #[test] fn rsa_from_pkcs8_test() { test::from_file("tests/rsa_from_pkcs8_tests.txt", |section, test_case| { + use std::error::Error; + assert_eq!(section, ""); let input = test_case.consume_bytes("Input"); @@ -50,10 +52,12 @@ fn rsa_from_pkcs8_test() { let error = test_case.consume_optional_string("Error"); - assert_eq!( - signature::RSAKeyPair::from_pkcs8(input).is_ok(), - error.is_none() - ); + match (signature::RSAKeyPair::from_pkcs8(input), error) { + (Ok(_), None) => (), + (Err(e), None) => panic!("Failed with error \"{}\", but expected to succeed", e), + (Ok(_), Some(e)) => panic!("Succeeded, but expected error \"{}\"", e), + (Err(actual), Some(expected)) => assert_eq!(actual.description(), expected), + }; Ok(()) });