| // Copyright 2015 Brian Smith. |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR |
| // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| use crate::{der, Error}; |
| use ring::signature; |
| |
| /// X.509 certificates and related items that are signed are almost always |
| /// encoded in the format "tbs||signatureAlgorithm||signature". This structure |
| /// captures this pattern. |
| pub struct SignedData<'a> { |
| /// The signed data. This would be `tbsCertificate` in the case of an X.509 |
| /// certificate, `tbsResponseData` in the case of an OCSP response, and the |
| /// data nested in the `digitally-signed` construct for TLS 1.2 signed |
| /// data. |
| data: untrusted::Input<'a>, |
| |
| /// The value of the `AlgorithmIdentifier`. This would be |
| /// `signatureAlgorithm` in the case of an X.509 certificate or OCSP |
| /// response. This would have to be synthesized in the case of TLS 1.2 |
| /// signed data, since TLS does not identify algorithms by ASN.1 OIDs. |
| pub(crate) algorithm: untrusted::Input<'a>, |
| |
| /// The value of the signature. This would be `signature` in an X.509 |
| /// certificate or OCSP response. This would be the value of |
| /// `DigitallySigned.signature` for TLS 1.2 signed data. |
| signature: untrusted::Input<'a>, |
| } |
| |
| /// Parses the concatenation of "tbs||signatureAlgorithm||signature" that |
| /// is common in the X.509 certificate and OCSP response syntaxes. |
| /// |
| /// X.509 Certificates (RFC 5280) look like this: |
| /// |
| /// ```ASN.1 |
| /// Certificate (SEQUENCE) { |
| /// tbsCertificate TBSCertificate, |
| /// signatureAlgorithm AlgorithmIdentifier, |
| /// signatureValue BIT STRING |
| /// } |
| /// |
| /// OCSP responses (RFC 6960) look like this: |
| /// ```ASN.1 |
| /// BasicOCSPResponse { |
| /// tbsResponseData ResponseData, |
| /// signatureAlgorithm AlgorithmIdentifier, |
| /// signature BIT STRING, |
| /// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL |
| /// } |
| /// ``` |
| /// |
| /// Note that this function does NOT parse the outermost `SEQUENCE` or the |
| /// `certs` value. |
| /// |
| /// The return value's first component is the contents of |
| /// `tbsCertificate`/`tbsResponseData`; the second component is a `SignedData` |
| /// structure that can be passed to `verify_signed_data`. |
| pub(crate) fn parse_signed_data<'a>( |
| der: &mut untrusted::Reader<'a>, |
| ) -> Result<(untrusted::Input<'a>, SignedData<'a>), Error> { |
| let (data, tbs) = |
| der.read_partial(|input| der::expect_tag_and_get_value(input, der::Tag::Sequence))?; |
| let algorithm = der::expect_tag_and_get_value(der, der::Tag::Sequence)?; |
| let signature = der::bit_string_with_no_unused_bits(der)?; |
| |
| Ok(( |
| tbs, |
| SignedData { |
| data, |
| algorithm, |
| signature, |
| }, |
| )) |
| } |
| |
| /// Verify `signed_data` using the public key in the DER-encoded |
| /// SubjectPublicKeyInfo `spki` using one of the algorithms in |
| /// `supported_algorithms`. |
| /// |
| /// The algorithm is chosen based on the algorithm information encoded in the |
| /// algorithm identifiers in `public_key` and `signed_data.algorithm`. The |
| /// ordering of the algorithms in `supported_algorithms` does not really matter, |
| /// but generally more common algorithms should go first, as it is scanned |
| /// linearly for matches. |
| pub(crate) fn verify_signed_data( |
| supported_algorithms: &[&SignatureAlgorithm], |
| spki_value: untrusted::Input, |
| signed_data: &SignedData, |
| ) -> Result<(), Error> { |
| // We need to verify the signature in `signed_data` using the public key |
| // in `public_key`. In order to know which *ring* signature verification |
| // algorithm to use, we need to know the public key algorithm (ECDSA, |
| // RSA PKCS#1, etc.), the curve (if applicable), and the digest algorithm. |
| // `signed_data` identifies only the public key algorithm and the digest |
| // algorithm, and `public_key` identifies only the public key algorithm and |
| // the curve (if any). Thus, we have to combine information from both |
| // inputs to figure out which `ring::signature::VerificationAlgorithm` to |
| // use to verify the signature. |
| // |
| // This is all further complicated by the fact that we don't have any |
| // implicit knowledge about any algorithms or identifiers, since all of |
| // that information is encoded in `supported_algorithms.` In particular, we |
| // avoid hard-coding any of that information so that (link-time) dead code |
| // elimination will work effectively in eliminating code for unused |
| // algorithms. |
| |
| // Parse the signature. |
| // |
| let mut found_signature_alg_match = false; |
| for supported_alg in supported_algorithms.iter().filter(|alg| { |
| alg.signature_alg_id |
| .matches_algorithm_id_value(signed_data.algorithm) |
| }) { |
| match verify_signature( |
| supported_alg, |
| spki_value, |
| signed_data.data, |
| signed_data.signature, |
| ) { |
| Err(Error::UnsupportedSignatureAlgorithmForPublicKey) => { |
| found_signature_alg_match = true; |
| continue; |
| } |
| result => { |
| return result; |
| } |
| } |
| } |
| |
| if found_signature_alg_match { |
| Err(Error::UnsupportedSignatureAlgorithmForPublicKey) |
| } else { |
| Err(Error::UnsupportedSignatureAlgorithm) |
| } |
| } |
| |
| pub(crate) fn verify_signature( |
| signature_alg: &SignatureAlgorithm, |
| spki_value: untrusted::Input, |
| msg: untrusted::Input, |
| signature: untrusted::Input, |
| ) -> Result<(), Error> { |
| let spki = parse_spki_value(spki_value)?; |
| if !signature_alg |
| .public_key_alg_id |
| .matches_algorithm_id_value(spki.algorithm_id_value) |
| { |
| return Err(Error::UnsupportedSignatureAlgorithmForPublicKey); |
| } |
| signature::UnparsedPublicKey::new( |
| signature_alg.verification_alg, |
| spki.key_value.as_slice_less_safe(), |
| ) |
| .verify(msg.as_slice_less_safe(), signature.as_slice_less_safe()) |
| .map_err(|_| Error::InvalidSignatureForPublicKey) |
| } |
| |
| struct SubjectPublicKeyInfo<'a> { |
| algorithm_id_value: untrusted::Input<'a>, |
| key_value: untrusted::Input<'a>, |
| } |
| |
| // Parse the public key into an algorithm OID, an optional curve OID, and the |
| // key value. The caller needs to check whether these match the |
| // `PublicKeyAlgorithm` for the `SignatureAlgorithm` that is matched when |
| // parsing the signature. |
| fn parse_spki_value(input: untrusted::Input) -> Result<SubjectPublicKeyInfo, Error> { |
| input.read_all(Error::BadDer, |input| { |
| let algorithm_id_value = der::expect_tag_and_get_value(input, der::Tag::Sequence)?; |
| let key_value = der::bit_string_with_no_unused_bits(input)?; |
| Ok(SubjectPublicKeyInfo { |
| algorithm_id_value, |
| key_value, |
| }) |
| }) |
| } |
| |
| /// A signature algorithm. |
| pub struct SignatureAlgorithm { |
| public_key_alg_id: AlgorithmIdentifier, |
| signature_alg_id: AlgorithmIdentifier, |
| verification_alg: &'static dyn signature::VerificationAlgorithm, |
| } |
| |
| /// ECDSA signatures using the P-256 curve and SHA-256. |
| pub static ECDSA_P256_SHA256: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: ECDSA_P256, |
| signature_alg_id: ECDSA_SHA256, |
| verification_alg: &signature::ECDSA_P256_SHA256_ASN1, |
| }; |
| |
| /// ECDSA signatures using the P-256 curve and SHA-384. Deprecated. |
| pub static ECDSA_P256_SHA384: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: ECDSA_P256, |
| signature_alg_id: ECDSA_SHA384, |
| verification_alg: &signature::ECDSA_P256_SHA384_ASN1, |
| }; |
| |
| /// ECDSA signatures using the P-384 curve and SHA-256. Deprecated. |
| pub static ECDSA_P384_SHA256: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: ECDSA_P384, |
| signature_alg_id: ECDSA_SHA256, |
| verification_alg: &signature::ECDSA_P384_SHA256_ASN1, |
| }; |
| |
| /// ECDSA signatures using the P-384 curve and SHA-384. |
| pub static ECDSA_P384_SHA384: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: ECDSA_P384, |
| signature_alg_id: ECDSA_SHA384, |
| verification_alg: &signature::ECDSA_P384_SHA384_ASN1, |
| }; |
| |
| /// RSA PKCS#1 1.5 signatures using SHA-256 for keys of 2048-8192 bits. |
| /// |
| /// Requires the `alloc` feature. |
| #[cfg(feature = "alloc")] |
| pub static RSA_PKCS1_2048_8192_SHA256: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: RSA_ENCRYPTION, |
| signature_alg_id: RSA_PKCS1_SHA256, |
| verification_alg: &signature::RSA_PKCS1_2048_8192_SHA256, |
| }; |
| |
| /// RSA PKCS#1 1.5 signatures using SHA-384 for keys of 2048-8192 bits. |
| /// |
| /// Requires the `alloc` feature. |
| #[cfg(feature = "alloc")] |
| pub static RSA_PKCS1_2048_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: RSA_ENCRYPTION, |
| signature_alg_id: RSA_PKCS1_SHA384, |
| verification_alg: &signature::RSA_PKCS1_2048_8192_SHA384, |
| }; |
| |
| /// RSA PKCS#1 1.5 signatures using SHA-512 for keys of 2048-8192 bits. |
| /// |
| /// Requires the `alloc` feature. |
| #[cfg(feature = "alloc")] |
| pub static RSA_PKCS1_2048_8192_SHA512: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: RSA_ENCRYPTION, |
| signature_alg_id: RSA_PKCS1_SHA512, |
| verification_alg: &signature::RSA_PKCS1_2048_8192_SHA512, |
| }; |
| |
| /// RSA PKCS#1 1.5 signatures using SHA-384 for keys of 3072-8192 bits. |
| /// |
| /// Requires the `alloc` feature. |
| #[cfg(feature = "alloc")] |
| pub static RSA_PKCS1_3072_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: RSA_ENCRYPTION, |
| signature_alg_id: RSA_PKCS1_SHA384, |
| verification_alg: &signature::RSA_PKCS1_3072_8192_SHA384, |
| }; |
| |
| /// RSA PSS signatures using SHA-256 for keys of 2048-8192 bits and of |
| /// type rsaEncryption; see [RFC 4055 Section 1.2]. |
| /// |
| /// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 |
| /// |
| /// Requires the `alloc` feature. |
| #[cfg(feature = "alloc")] |
| pub static RSA_PSS_2048_8192_SHA256_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: RSA_ENCRYPTION, |
| signature_alg_id: RSA_PSS_SHA256, |
| verification_alg: &signature::RSA_PSS_2048_8192_SHA256, |
| }; |
| |
| /// RSA PSS signatures using SHA-384 for keys of 2048-8192 bits and of |
| /// type rsaEncryption; see [RFC 4055 Section 1.2]. |
| /// |
| /// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 |
| /// |
| /// Requires the `alloc` feature. |
| #[cfg(feature = "alloc")] |
| pub static RSA_PSS_2048_8192_SHA384_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: RSA_ENCRYPTION, |
| signature_alg_id: RSA_PSS_SHA384, |
| verification_alg: &signature::RSA_PSS_2048_8192_SHA384, |
| }; |
| |
| /// RSA PSS signatures using SHA-512 for keys of 2048-8192 bits and of |
| /// type rsaEncryption; see [RFC 4055 Section 1.2]. |
| /// |
| /// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 |
| /// |
| /// Requires the `alloc` feature. |
| #[cfg(feature = "alloc")] |
| pub static RSA_PSS_2048_8192_SHA512_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: RSA_ENCRYPTION, |
| signature_alg_id: RSA_PSS_SHA512, |
| verification_alg: &signature::RSA_PSS_2048_8192_SHA512, |
| }; |
| |
| /// ED25519 signatures according to RFC 8410 |
| pub static ED25519: SignatureAlgorithm = SignatureAlgorithm { |
| public_key_alg_id: ED_25519, |
| signature_alg_id: ED_25519, |
| verification_alg: &signature::ED25519, |
| }; |
| |
| struct AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input<'static>, |
| } |
| |
| impl AlgorithmIdentifier { |
| fn matches_algorithm_id_value(&self, encoded: untrusted::Input) -> bool { |
| encoded == self.asn1_id_value |
| } |
| } |
| |
| // See src/data/README.md. |
| |
| const ECDSA_P256: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-p256.der")), |
| }; |
| |
| const ECDSA_P384: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-p384.der")), |
| }; |
| |
| const ECDSA_SHA256: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-sha256.der")), |
| }; |
| |
| const ECDSA_SHA384: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-sha384.der")), |
| }; |
| |
| #[cfg(feature = "alloc")] |
| const RSA_ENCRYPTION: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-encryption.der")), |
| }; |
| |
| #[cfg(feature = "alloc")] |
| const RSA_PKCS1_SHA256: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pkcs1-sha256.der")), |
| }; |
| |
| #[cfg(feature = "alloc")] |
| const RSA_PKCS1_SHA384: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pkcs1-sha384.der")), |
| }; |
| |
| #[cfg(feature = "alloc")] |
| const RSA_PKCS1_SHA512: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pkcs1-sha512.der")), |
| }; |
| |
| #[cfg(feature = "alloc")] |
| const RSA_PSS_SHA256: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pss-sha256.der")), |
| }; |
| |
| #[cfg(feature = "alloc")] |
| const RSA_PSS_SHA384: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pss-sha384.der")), |
| }; |
| |
| #[cfg(feature = "alloc")] |
| const RSA_PSS_SHA512: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pss-sha512.der")), |
| }; |
| |
| const ED_25519: AlgorithmIdentifier = AlgorithmIdentifier { |
| asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ed25519.der")), |
| }; |
| |
| #[cfg(test)] |
| mod tests { |
| use crate::{der, signed_data, Error}; |
| use alloc::{string::String, vec::Vec}; |
| |
| macro_rules! test_file_bytes { |
| ( $file_name:expr ) => { |
| include_bytes!(concat!( |
| "../third-party/chromium/data/verify_signed_data/", |
| $file_name |
| )) |
| }; |
| } |
| |
| // TODO: The expected results need to be modified for SHA-1 deprecation. |
| |
| macro_rules! test_verify_signed_data { |
| ($fn_name:ident, $file_name:expr, $expected_result:expr) => { |
| #[test] |
| fn $fn_name() { |
| test_verify_signed_data(test_file_bytes!($file_name), $expected_result); |
| } |
| }; |
| } |
| |
| fn test_verify_signed_data(file_contents: &[u8], expected_result: Result<(), Error>) { |
| let tsd = parse_test_signed_data(file_contents); |
| let spki_value = untrusted::Input::from(&tsd.spki); |
| let spki_value = spki_value |
| .read_all(Error::BadDer, |input| { |
| der::expect_tag_and_get_value(input, der::Tag::Sequence) |
| }) |
| .unwrap(); |
| |
| // we can't use `parse_signed_data` because it requires `data` |
| // to be an ASN.1 SEQUENCE, and that isn't the case with |
| // Chromium's test data. TODO: The test data set should be |
| // expanded with SEQUENCE-wrapped data so that we can actually |
| // test `parse_signed_data`. |
| |
| let algorithm = untrusted::Input::from(&tsd.algorithm); |
| let algorithm = algorithm |
| .read_all(Error::BadDer, |input| { |
| der::expect_tag_and_get_value(input, der::Tag::Sequence) |
| }) |
| .unwrap(); |
| |
| let signature = untrusted::Input::from(&tsd.signature); |
| let signature = signature |
| .read_all(Error::BadDer, |input| { |
| der::bit_string_with_no_unused_bits(input) |
| }) |
| .unwrap(); |
| |
| let signed_data = signed_data::SignedData { |
| data: untrusted::Input::from(&tsd.data), |
| algorithm, |
| signature, |
| }; |
| |
| assert_eq!( |
| expected_result, |
| signed_data::verify_signed_data( |
| SUPPORTED_ALGORITHMS_IN_TESTS, |
| spki_value, |
| &signed_data |
| ) |
| ); |
| } |
| |
| // XXX: This is testing code that isn't even in this module. |
| macro_rules! test_verify_signed_data_signature_outer { |
| ($fn_name:ident, $file_name:expr, $expected_result:expr) => { |
| #[test] |
| fn $fn_name() { |
| test_verify_signed_data_signature_outer( |
| test_file_bytes!($file_name), |
| $expected_result, |
| ); |
| } |
| }; |
| } |
| |
| fn test_verify_signed_data_signature_outer(file_contents: &[u8], expected_error: Error) { |
| let tsd = parse_test_signed_data(file_contents); |
| let signature = untrusted::Input::from(&tsd.signature); |
| assert_eq!( |
| Err(expected_error), |
| signature.read_all(Error::BadDer, |input| { |
| der::bit_string_with_no_unused_bits(input) |
| }) |
| ); |
| } |
| |
| // XXX: This is testing code that is not even in this module. |
| macro_rules! test_parse_spki_bad_outer { |
| ($fn_name:ident, $file_name:expr, $error:expr) => { |
| #[test] |
| fn $fn_name() { |
| test_parse_spki_bad_outer(test_file_bytes!($file_name), $error) |
| } |
| }; |
| } |
| |
| fn test_parse_spki_bad_outer(file_contents: &[u8], expected_error: Error) { |
| let tsd = parse_test_signed_data(file_contents); |
| let spki = untrusted::Input::from(&tsd.spki); |
| assert_eq!( |
| Err(expected_error), |
| spki.read_all(Error::BadDer, |input| { |
| der::expect_tag_and_get_value(input, der::Tag::Sequence) |
| }) |
| ); |
| } |
| |
| const UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY: Error = if cfg!(feature = "alloc") { |
| Error::UnsupportedSignatureAlgorithmForPublicKey |
| } else { |
| Error::UnsupportedSignatureAlgorithm |
| }; |
| |
| const INVALID_SIGNATURE_FOR_RSA_KEY: Error = if cfg!(feature = "alloc") { |
| Error::InvalidSignatureForPublicKey |
| } else { |
| Error::UnsupportedSignatureAlgorithm |
| }; |
| |
| const OK_IF_RSA_AVAILABLE: Result<(), Error> = if cfg!(feature = "alloc") { |
| Ok(()) |
| } else { |
| Err(Error::UnsupportedSignatureAlgorithm) |
| }; |
| |
| // XXX: Some of the BadDER tests should have better error codes, maybe? |
| |
| // XXX: We should have a variant of this test with a SHA-256 digest that gives |
| // `Error::UnsupportedSignatureAlgorithmForPublicKey`. |
| test_verify_signed_data!( |
| test_ecdsa_prime256v1_sha512_spki_params_null, |
| "ecdsa-prime256v1-sha512-spki-params-null.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data_signature_outer!( |
| test_ecdsa_prime256v1_sha512_unused_bits_signature, |
| "ecdsa-prime256v1-sha512-unused-bits-signature.pem", |
| Error::BadDer |
| ); |
| // XXX: We should have a variant of this test with a SHA-256 digest that gives |
| // `Error::UnsupportedSignatureAlgorithmForPublicKey`. |
| test_verify_signed_data!( |
| test_ecdsa_prime256v1_sha512_using_ecdh_key, |
| "ecdsa-prime256v1-sha512-using-ecdh-key.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| // XXX: We should have a variant of this test with a SHA-256 digest that gives |
| // `Error::UnsupportedSignatureAlgorithmForPublicKey`. |
| test_verify_signed_data!( |
| test_ecdsa_prime256v1_sha512_using_ecmqv_key, |
| "ecdsa-prime256v1-sha512-using-ecmqv-key.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_ecdsa_prime256v1_sha512_using_rsa_algorithm, |
| "ecdsa-prime256v1-sha512-using-rsa-algorithm.pem", |
| Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY) |
| ); |
| // XXX: We should have a variant of this test with a SHA-256 digest that gives |
| // `Error::InvalidSignatureForPublicKey`. |
| test_verify_signed_data!( |
| test_ecdsa_prime256v1_sha512_wrong_signature_format, |
| "ecdsa-prime256v1-sha512-wrong-signature-format.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| // Differs from Chromium because we don't support P-256 with SHA-512. |
| test_verify_signed_data!( |
| test_ecdsa_prime256v1_sha512, |
| "ecdsa-prime256v1-sha512.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_ecdsa_secp384r1_sha256_corrupted_data, |
| "ecdsa-secp384r1-sha256-corrupted-data.pem", |
| Err(Error::InvalidSignatureForPublicKey) |
| ); |
| test_verify_signed_data!( |
| test_ecdsa_secp384r1_sha256, |
| "ecdsa-secp384r1-sha256.pem", |
| Ok(()) |
| ); |
| test_verify_signed_data!( |
| test_ecdsa_using_rsa_key, |
| "ecdsa-using-rsa-key.pem", |
| Err(Error::UnsupportedSignatureAlgorithmForPublicKey) |
| ); |
| |
| test_parse_spki_bad_outer!( |
| test_rsa_pkcs1_sha1_bad_key_der_length, |
| "rsa-pkcs1-sha1-bad-key-der-length.pem", |
| Error::BadDer |
| ); |
| test_parse_spki_bad_outer!( |
| test_rsa_pkcs1_sha1_bad_key_der_null, |
| "rsa-pkcs1-sha1-bad-key-der-null.pem", |
| Error::BadDer |
| ); |
| test_verify_signed_data!( |
| test_rsa_pkcs1_sha1_key_params_absent, |
| "rsa-pkcs1-sha1-key-params-absent.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pkcs1_sha1_using_pss_key_no_params, |
| "rsa-pkcs1-sha1-using-pss-key-no-params.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pkcs1_sha1_wrong_algorithm, |
| "rsa-pkcs1-sha1-wrong-algorithm.pem", |
| Err(INVALID_SIGNATURE_FOR_RSA_KEY) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pkcs1_sha1, |
| "rsa-pkcs1-sha1.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| // XXX: RSA PKCS#1 with SHA-1 is a supported algorithm, but we only accept |
| // 2048-8192 bit keys, and this test file is using a 1024 bit key. Thus, |
| // our results differ from Chromium's. TODO: this means we need a 2048+ bit |
| // version of this test. |
| test_verify_signed_data!( |
| test_rsa_pkcs1_sha256, |
| "rsa-pkcs1-sha256.pem", |
| Err(INVALID_SIGNATURE_FOR_RSA_KEY) |
| ); |
| test_parse_spki_bad_outer!( |
| test_rsa_pkcs1_sha256_key_encoded_ber, |
| "rsa-pkcs1-sha256-key-encoded-ber.pem", |
| Error::BadDer |
| ); |
| test_verify_signed_data!( |
| test_rsa_pkcs1_sha256_spki_non_null_params, |
| "rsa-pkcs1-sha256-spki-non-null-params.pem", |
| Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pkcs1_sha256_using_ecdsa_algorithm, |
| "rsa-pkcs1-sha256-using-ecdsa-algorithm.pem", |
| Err(Error::UnsupportedSignatureAlgorithmForPublicKey) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pkcs1_sha256_using_id_ea_rsa, |
| "rsa-pkcs1-sha256-using-id-ea-rsa.pem", |
| Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY) |
| ); |
| |
| // Chromium's PSS test are for parameter combinations we don't support. |
| test_verify_signed_data!( |
| test_rsa_pss_sha1_salt20_using_pss_key_no_params, |
| "rsa-pss-sha1-salt20-using-pss-key-no-params.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha1_salt20_using_pss_key_with_null_params, |
| "rsa-pss-sha1-salt20-using-pss-key-with-null-params.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha1_salt20, |
| "rsa-pss-sha1-salt20.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha1_wrong_salt, |
| "rsa-pss-sha1-wrong-salt.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha256_mgf1_sha512_salt33, |
| "rsa-pss-sha256-mgf1-sha512-salt33.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha256_salt10_using_pss_key_with_params, |
| "rsa-pss-sha256-salt10-using-pss-key-with-params.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha256_salt10_using_pss_key_with_wrong_params, |
| "rsa-pss-sha256-salt10-using-pss-key-with-wrong-params.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha256_salt10, |
| "rsa-pss-sha256-salt10.pem", |
| Err(Error::UnsupportedSignatureAlgorithm) |
| ); |
| |
| // Our PSS tests that should work. |
| test_verify_signed_data!( |
| test_rsa_pss_sha256_salt32, |
| "ours/rsa-pss-sha256-salt32.pem", |
| OK_IF_RSA_AVAILABLE |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha384_salt48, |
| "ours/rsa-pss-sha384-salt48.pem", |
| OK_IF_RSA_AVAILABLE |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha512_salt64, |
| "ours/rsa-pss-sha512-salt64.pem", |
| OK_IF_RSA_AVAILABLE |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha256_salt32_corrupted_data, |
| "ours/rsa-pss-sha256-salt32-corrupted-data.pem", |
| Err(INVALID_SIGNATURE_FOR_RSA_KEY) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha384_salt48_corrupted_data, |
| "ours/rsa-pss-sha384-salt48-corrupted-data.pem", |
| Err(INVALID_SIGNATURE_FOR_RSA_KEY) |
| ); |
| test_verify_signed_data!( |
| test_rsa_pss_sha512_salt64_corrupted_data, |
| "ours/rsa-pss-sha512-salt64-corrupted-data.pem", |
| Err(INVALID_SIGNATURE_FOR_RSA_KEY) |
| ); |
| |
| test_verify_signed_data!( |
| test_rsa_using_ec_key, |
| "rsa-using-ec-key.pem", |
| Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY) |
| ); |
| test_verify_signed_data!( |
| test_rsa2048_pkcs1_sha512, |
| "rsa2048-pkcs1-sha512.pem", |
| OK_IF_RSA_AVAILABLE |
| ); |
| |
| struct TestSignedData { |
| spki: Vec<u8>, |
| data: Vec<u8>, |
| algorithm: Vec<u8>, |
| signature: Vec<u8>, |
| } |
| |
| fn parse_test_signed_data(file_contents: &[u8]) -> TestSignedData { |
| let mut lines = core::str::from_utf8(file_contents).unwrap().lines(); |
| let spki = read_pem_section(&mut lines, "PUBLIC KEY"); |
| let algorithm = read_pem_section(&mut lines, "ALGORITHM"); |
| let data = read_pem_section(&mut lines, "DATA"); |
| let signature = read_pem_section(&mut lines, "SIGNATURE"); |
| |
| TestSignedData { |
| spki, |
| data, |
| algorithm, |
| signature, |
| } |
| } |
| |
| use alloc::str::Lines; |
| |
| fn read_pem_section(lines: &mut Lines, section_name: &str) -> Vec<u8> { |
| // Skip comments and header |
| let begin_section = format!("-----BEGIN {}-----", section_name); |
| loop { |
| let line = lines.next().unwrap(); |
| if line == begin_section { |
| break; |
| } |
| } |
| |
| let mut base64 = String::new(); |
| |
| let end_section = format!("-----END {}-----", section_name); |
| loop { |
| let line = lines.next().unwrap(); |
| if line == end_section { |
| break; |
| } |
| base64.push_str(&line); |
| } |
| |
| base64::decode(&base64).unwrap() |
| } |
| |
| static SUPPORTED_ALGORITHMS_IN_TESTS: &[&signed_data::SignatureAlgorithm] = &[ |
| // Reasonable algorithms. |
| &signed_data::ECDSA_P256_SHA256, |
| &signed_data::ECDSA_P384_SHA384, |
| &signed_data::ED25519, |
| #[cfg(feature = "alloc")] |
| &signed_data::RSA_PKCS1_2048_8192_SHA256, |
| #[cfg(feature = "alloc")] |
| &signed_data::RSA_PKCS1_2048_8192_SHA384, |
| #[cfg(feature = "alloc")] |
| &signed_data::RSA_PKCS1_2048_8192_SHA512, |
| #[cfg(feature = "alloc")] |
| &signed_data::RSA_PKCS1_3072_8192_SHA384, |
| #[cfg(feature = "alloc")] |
| &signed_data::RSA_PSS_2048_8192_SHA256_LEGACY_KEY, |
| #[cfg(feature = "alloc")] |
| &signed_data::RSA_PSS_2048_8192_SHA384_LEGACY_KEY, |
| #[cfg(feature = "alloc")] |
| &signed_data::RSA_PSS_2048_8192_SHA512_LEGACY_KEY, |
| // Algorithms deprecated because they are annoying (P-521) or because |
| // they are nonsensical combinations. |
| &signed_data::ECDSA_P256_SHA384, // Truncates digest. |
| &signed_data::ECDSA_P384_SHA256, // Digest is unnecessarily short. |
| ]; |
| } |