From b5cb7880bd155739d65f1d7f19d45fb57a1922b7 Mon Sep 17 00:00:00 2001 From: Roy7Kim Date: Wed, 15 Mar 2023 17:15:07 +0900 Subject: [PATCH 1/1] Import webpki 0.22.0 --- .cargo_vcs_info.json | 5 + Cargo.toml | 56 ++ Cargo.toml.orig | 94 +++ LICENSE | 19 + README.md | 78 ++ src/calendar.rs | 162 +++++ src/cert.rs | 226 ++++++ src/data/README.md | 21 + src/data/alg-ecdsa-p256.der | 1 + src/data/alg-ecdsa-p384.der | Bin 0 -> 16 bytes src/data/alg-ecdsa-sha256.der | 1 + src/data/alg-ecdsa-sha384.der | 1 + src/data/alg-ed25519.der | 1 + src/data/alg-rsa-encryption.der | Bin 0 -> 13 bytes src/data/alg-rsa-pkcs1-sha256.der | Bin 0 -> 13 bytes src/data/alg-rsa-pkcs1-sha384.der | Bin 0 -> 13 bytes src/data/alg-rsa-pkcs1-sha512.der | Bin 0 -> 13 bytes src/data/alg-rsa-pss-sha256.der | Bin 0 -> 65 bytes src/data/alg-rsa-pss-sha384.der | Bin 0 -> 65 bytes src/data/alg-rsa-pss-sha512.der | Bin 0 -> 65 bytes src/der.rs | 173 +++++ src/end_entity.rs | 206 ++++++ src/error.rs | 108 +++ src/lib.rs | 96 +++ src/name.rs | 25 + src/name/dns_name.rs | 796 +++++++++++++++++++++ src/name/ip_address.rs | 64 ++ src/name/verify.rs | 328 +++++++++ src/signed_data.rs | 788 ++++++++++++++++++++ src/time.rs | 65 ++ src/trust_anchor.rs | 119 +++ src/verify_cert.rs | 352 +++++++++ tests/dns_name_tests.rs | 408 +++++++++++ tests/ed25519/ca.der | Bin 0 -> 459 bytes tests/ed25519/ee.der | Bin 0 -> 483 bytes tests/integration.rs | 91 +++ tests/misc/serial_neg.der | Bin 0 -> 1370 bytes tests/misc/serial_zero.der | Bin 0 -> 1028 bytes tests/netflix/ca.der | Bin 0 -> 1054 bytes tests/netflix/ee.der | Bin 0 -> 1772 bytes tests/netflix/inter.der | Bin 0 -> 1305 bytes third-party/chromium/LICENSE | 27 + .../chromium/data/verify_signed_data/README | 35 + .../ecdsa-prime256v1-sha512-spki-params-null.pem | 45 ++ ...dsa-prime256v1-sha512-unused-bits-signature.pem | 53 ++ .../ecdsa-prime256v1-sha512-using-ecdh-key.pem | 48 ++ .../ecdsa-prime256v1-sha512-using-ecmqv-key.pem | 48 ++ ...ecdsa-prime256v1-sha512-using-rsa-algorithm.pem | 48 ++ ...sa-prime256v1-sha512-wrong-signature-format.pem | 47 ++ .../verify_signed_data/ecdsa-prime256v1-sha512.pem | 49 ++ .../ecdsa-secp384r1-sha256-corrupted-data.pem | 53 ++ .../verify_signed_data/ecdsa-secp384r1-sha256.pem | 84 +++ .../verify_signed_data/ecdsa-using-rsa-key.pem | 51 ++ .../data/verify_signed_data/ours/make-pss.py | 59 ++ .../chromium/data/verify_signed_data/ours/priv.pem | 27 + .../chromium/data/verify_signed_data/ours/pub.pem | 9 + .../ours/rsa-pss-sha256-salt32-corrupted-data.pem | 63 ++ .../ours/rsa-pss-sha256-salt32.pem | 63 ++ .../ours/rsa-pss-sha384-salt48-corrupted-data.pem | 63 ++ .../ours/rsa-pss-sha384-salt48.pem | 63 ++ .../ours/rsa-pss-sha512-salt64-corrupted-data.pem | 63 ++ .../ours/rsa-pss-sha512-salt64.pem | 63 ++ .../rsa-pkcs1-sha1-bad-key-der-length.pem | 44 ++ .../rsa-pkcs1-sha1-bad-key-der-null.pem | 52 ++ .../rsa-pkcs1-sha1-key-params-absent.pem | 49 ++ .../rsa-pkcs1-sha1-using-pss-key-no-params.pem | 51 ++ .../rsa-pkcs1-sha1-wrong-algorithm.pem | 48 ++ .../data/verify_signed_data/rsa-pkcs1-sha1.pem | 53 ++ .../rsa-pkcs1-sha256-key-encoded-ber.pem | 62 ++ .../rsa-pkcs1-sha256-spki-non-null-params.pem | 59 ++ .../rsa-pkcs1-sha256-using-ecdsa-algorithm.pem | 55 ++ .../rsa-pkcs1-sha256-using-id-ea-rsa.pem | 54 ++ .../data/verify_signed_data/rsa-pkcs1-sha256.pem | 86 +++ ...rsa-pss-sha1-salt20-using-pss-key-no-params.pem | 48 ++ ...-sha1-salt20-using-pss-key-with-null-params.pem | 50 ++ .../verify_signed_data/rsa-pss-sha1-salt20.pem | 53 ++ .../verify_signed_data/rsa-pss-sha1-wrong-salt.pem | 51 ++ .../rsa-pss-sha256-mgf1-sha512-salt33.pem | 67 ++ ...pss-sha256-salt10-using-pss-key-with-params.pem | 74 ++ ...a256-salt10-using-pss-key-with-wrong-params.pem | 74 ++ .../verify_signed_data/rsa-pss-sha256-salt10.pem | 65 ++ .../data/verify_signed_data/rsa-using-ec-key.pem | 52 ++ .../verify_signed_data/rsa2048-pkcs1-sha512.pem | 93 +++ 83 files changed, 6585 insertions(+) create mode 100644 .cargo_vcs_info.json create mode 100644 Cargo.toml create mode 100644 Cargo.toml.orig create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/calendar.rs create mode 100644 src/cert.rs create mode 100644 src/data/README.md create mode 100644 src/data/alg-ecdsa-p256.der create mode 100644 src/data/alg-ecdsa-p384.der create mode 100644 src/data/alg-ecdsa-sha256.der create mode 100644 src/data/alg-ecdsa-sha384.der create mode 100644 src/data/alg-ed25519.der create mode 100644 src/data/alg-rsa-encryption.der create mode 100644 src/data/alg-rsa-pkcs1-sha256.der create mode 100644 src/data/alg-rsa-pkcs1-sha384.der create mode 100644 src/data/alg-rsa-pkcs1-sha512.der create mode 100644 src/data/alg-rsa-pss-sha256.der create mode 100644 src/data/alg-rsa-pss-sha384.der create mode 100644 src/data/alg-rsa-pss-sha512.der create mode 100644 src/der.rs create mode 100644 src/end_entity.rs create mode 100644 src/error.rs create mode 100644 src/lib.rs create mode 100644 src/name.rs create mode 100644 src/name/dns_name.rs create mode 100644 src/name/ip_address.rs create mode 100644 src/name/verify.rs create mode 100644 src/signed_data.rs create mode 100644 src/time.rs create mode 100644 src/trust_anchor.rs create mode 100644 src/verify_cert.rs create mode 100644 tests/dns_name_tests.rs create mode 100644 tests/ed25519/ca.der create mode 100644 tests/ed25519/ee.der create mode 100644 tests/integration.rs create mode 100644 tests/misc/serial_neg.der create mode 100644 tests/misc/serial_zero.der create mode 100644 tests/netflix/ca.der create mode 100644 tests/netflix/ee.der create mode 100644 tests/netflix/inter.der create mode 100644 third-party/chromium/LICENSE create mode 100644 third-party/chromium/data/verify_signed_data/README create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-spki-params-null.pem create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-unused-bits-signature.pem create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecdh-key.pem create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecmqv-key.pem create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-rsa-algorithm.pem create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-wrong-signature-format.pem create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512.pem create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256-corrupted-data.pem create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256.pem create mode 100644 third-party/chromium/data/verify_signed_data/ecdsa-using-rsa-key.pem create mode 100644 third-party/chromium/data/verify_signed_data/ours/make-pss.py create mode 100644 third-party/chromium/data/verify_signed_data/ours/priv.pem create mode 100644 third-party/chromium/data/verify_signed_data/ours/pub.pem create mode 100644 third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32-corrupted-data.pem create mode 100644 third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32.pem create mode 100644 third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48-corrupted-data.pem create mode 100644 third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48.pem create mode 100644 third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64-corrupted-data.pem create mode 100644 third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-length.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-null.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-key-params-absent.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-using-pss-key-no-params.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-wrong-algorithm.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-key-encoded-ber.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-spki-non-null-params.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-ecdsa-algorithm.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-id-ea-rsa.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-no-params.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-with-null-params.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pss-sha1-wrong-salt.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pss-sha256-mgf1-sha512-salt33.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-params.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-wrong-params.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa-using-ec-key.pem create mode 100644 third-party/chromium/data/verify_signed_data/rsa2048-pkcs1-sha512.pem diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..c33f2e1 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "6c334a2cf5853fb0aa93b5eb0318c031fc2f6f98" + } +} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4d687bb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,56 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "webpki" +version = "0.22.0" +authors = ["Brian Smith "] +include = ["Cargo.toml", "LICENSE", "README.md", "src/calendar.rs", "src/cert.rs", "src/der.rs", "src/end_entity.rs", "src/error.rs", "src/name.rs", "src/name/dns_name.rs", "src/name/ip_address.rs", "src/name/verify.rs", "src/signed_data.rs", "src/time.rs", "src/trust_anchor.rs", "src/verify_cert.rs", "src/lib.rs", "src/data/**/*", "tests/dns_name_tests.rs", "tests/integration.rs", "tests/misc/serial_neg.der", "tests/misc/serial_zero.der", "tests/netflix/ca.der", "tests/netflix/ee.der", "tests/netflix/inter.der", "tests/ed25519/ca.der", "tests/ed25519/ee.der", "third-party/chromium/**/*"] +description = "Web PKI X.509 Certificate Verification." +documentation = "https://briansmith.org/rustdoc/webpki/" +readme = "README.md" +categories = ["cryptography", "no-std"] +license-file = "LICENSE" +repository = "https://github.com/briansmith/webpki" +[package.metadata.docs.rs] +all-features = true +[profile.bench] +opt-level = 3 +lto = true +codegen-units = 1 +debug = false +debug-assertions = false +rpath = false + +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 +debug = false +debug-assertions = false +rpath = false + +[lib] +name = "webpki" +[dependencies.ring] +version = "0.16.19" +default-features = false + +[dependencies.untrusted] +version = "0.7.1" +[dev-dependencies.base64] +version = "0.9.1" + +[features] +alloc = ["ring/alloc"] +std = ["alloc"] diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..28a5601 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,94 @@ +# 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. + +[package] +authors = ["Brian Smith "] +categories = ["cryptography", "no-std"] +description = "Web PKI X.509 Certificate Verification." +documentation = "https://briansmith.org/rustdoc/webpki/" +edition = "2018" +license-file = "LICENSE" +name = "webpki" +readme = "README.md" +repository = "https://github.com/briansmith/webpki" +version = "0.22.0" + +include = [ + "Cargo.toml", + + "LICENSE", + "README.md", + + "src/calendar.rs", + "src/cert.rs", + "src/der.rs", + "src/end_entity.rs", + "src/error.rs", + "src/name.rs", + "src/name/dns_name.rs", + "src/name/ip_address.rs", + "src/name/verify.rs", + "src/signed_data.rs", + "src/time.rs", + "src/trust_anchor.rs", + "src/verify_cert.rs", + "src/lib.rs", + + "src/data/**/*", + + "tests/dns_name_tests.rs", + "tests/integration.rs", + "tests/misc/serial_neg.der", + "tests/misc/serial_zero.der", + "tests/netflix/ca.der", + "tests/netflix/ee.der", + "tests/netflix/inter.der", + "tests/ed25519/ca.der", + "tests/ed25519/ee.der", + + "third-party/chromium/**/*", +] + +[package.metadata.docs.rs] +all-features = true + +[lib] +name = "webpki" + +[features] +alloc = ["ring/alloc"] +std = ["alloc"] + +[dependencies] +ring = { version = "0.16.19", default-features = false } +untrusted = "0.7.1" + +[dev-dependencies] +base64 = "0.9.1" + +[profile.bench] +opt-level = 3 +debug = false +rpath = false +lto = true +debug-assertions = false +codegen-units = 1 + +[profile.release] +opt-level = 3 +debug = false +rpath = false +lto = true +debug-assertions = false +codegen-units = 1 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cd87be1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Except as otherwise noted, this project is licensed under the following +(ISC-style) terms: + +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. + +The files under third-party/chromium are licensed as described in +third-party/chromium/LICENSE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..29bb836 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +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. + + + +What is webpki? +================== + +webpki is a library that validates Web PKI (TLS/SSL) certificates. webpki +is designed to provide a **full** implementation of the client side of the +**Web PKI** to a diverse range of applications and devices, +including embedded (IoT) applications, mobile apps, desktop applications, and +server infrastructure. webpki is intended to not only be the best +implementation of the Web PKI, but to also *precisely define* what the Web PKI +is. + +webpki is written in [Rust](https://www.rust-lang.org/) and uses +[*ring*](https://github.com/briansmith/ring) for signature verification. + +webpki is strongly influenced by +[mozilla::pkix](https://github.com/briansmith/mozillapkix). You can read a +little about the ideas underlying both mozilla::pkix and webpki in +[insanity::pkix: A New Certificate Path Building & Validation +Library](https://briansmith.org/insanity-pkix). + +The Rust compiler statically guarantees there are no buffer overflows, +uses-after-free, double-frees, data races, etc. in webpki. webpki takes +advantage of Rust's borrow checker to ensure that its **zero-copy parsing** +strategy is safe and efficient. webpki *never* allocates memory on the heap, +and it maintains a tight bound on the amount of stack memory it uses. webpki +avoids all superfluous PKIX features in order to keep its object code size +small. Further reducing the code size of webpki is an important goal. + +This release is the very first prototype. Lots of improvements are planned, +including: + +* An extensive automated test suite. +* Key pinning. +* Certificate Transparency support. +* Short-lived certificate, OCSP stapling, and CRLSet support. +* Customization of the supported algorithms, key sizes, and elliptic curves + allowed during a validation. +* A C language wrapper interface to allow using webpki in non-Rust + applications. +* A specification of precisely what the Web PKI is. + + + +Demo +==== + +See https://github.com/ctz/rustls#example-code for an example of using +webpki. + + + +License +======= + +See [LICENSE](LICENSE). This project happily accepts pull requests without any +formal copyright/contributor license agreement. Pull requests must explicitly +indicate who owns the copyright to the code being contributed and that the code +is being licensed under the same terms as the existing webpki code. + + + +Bug Reporting +============= + +Please report bugs either as pull requests or as issues in [the issue +tracker](https://github.com/briansmith/webpki/issues). webpki has a +**full disclosure** vulnerability policy. **Please do NOT attempt to report +any security vulnerability in this code privately to anybody.** diff --git a/src/calendar.rs b/src/calendar.rs new file mode 100644 index 0000000..f1d2c44 --- /dev/null +++ b/src/calendar.rs @@ -0,0 +1,162 @@ +// Copyright 2015-2016 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 super::{time::Time, Error}; + +pub fn time_from_ymdhms_utc( + year: u64, + month: u64, + day_of_month: u64, + hours: u64, + minutes: u64, + seconds: u64, +) -> Result { + let days_before_year_since_unix_epoch = days_before_year_since_unix_epoch(year)?; + + const JAN: u64 = 31; + let feb = days_in_feb(year); + const MAR: u64 = 31; + const APR: u64 = 30; + const MAY: u64 = 31; + const JUN: u64 = 30; + const JUL: u64 = 31; + const AUG: u64 = 31; + const SEP: u64 = 30; + const OCT: u64 = 31; + const NOV: u64 = 30; + let days_before_month_in_year = match month { + 1 => 0, + 2 => JAN, + 3 => JAN + feb, + 4 => JAN + feb + MAR, + 5 => JAN + feb + MAR + APR, + 6 => JAN + feb + MAR + APR + MAY, + 7 => JAN + feb + MAR + APR + MAY + JUN, + 8 => JAN + feb + MAR + APR + MAY + JUN + JUL, + 9 => JAN + feb + MAR + APR + MAY + JUN + JUL + AUG, + 10 => JAN + feb + MAR + APR + MAY + JUN + JUL + AUG + SEP, + 11 => JAN + feb + MAR + APR + MAY + JUN + JUL + AUG + SEP + OCT, + 12 => JAN + feb + MAR + APR + MAY + JUN + JUL + AUG + SEP + OCT + NOV, + _ => unreachable!(), // `read_two_digits` already bounds-checked it. + }; + + let days_before = + days_before_year_since_unix_epoch + days_before_month_in_year + day_of_month - 1; + + let seconds_since_unix_epoch = + (days_before * 24 * 60 * 60) + (hours * 60 * 60) + (minutes * 60) + seconds; + + Ok(Time::from_seconds_since_unix_epoch( + seconds_since_unix_epoch, + )) +} + +fn days_before_year_since_unix_epoch(year: u64) -> Result { + // We don't support dates before January 1, 1970 because that is the + // Unix epoch. It is likely that other software won't deal well with + // certificates that have dates before the epoch. + if year < 1970 { + return Err(Error::BadDerTime); + } + let days_before_year_ad = days_before_year_ad(year); + debug_assert!(days_before_year_ad >= DAYS_BEFORE_UNIX_EPOCH_AD); + Ok(days_before_year_ad - DAYS_BEFORE_UNIX_EPOCH_AD) +} + +fn days_before_year_ad(year: u64) -> u64 { + ((year - 1) * 365) + + ((year - 1) / 4) // leap years are every 4 years, + - ((year - 1) / 100) // except years divisible by 100, + + ((year - 1) / 400) // except years divisible by 400. +} + +pub fn days_in_month(year: u64, month: u64) -> u64 { + match month { + 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, + 4 | 6 | 9 | 11 => 30, + 2 => days_in_feb(year), + _ => unreachable!(), // `read_two_digits` already bounds-checked it. + } +} + +fn days_in_feb(year: u64) -> u64 { + if (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) { + 29 + } else { + 28 + } +} + +#[allow(clippy::unreadable_literal)] // TODO: Make this clear. +const DAYS_BEFORE_UNIX_EPOCH_AD: u64 = 719162; + +#[cfg(test)] +mod tests { + #[test] + fn test_days_before_unix_epoch() { + use super::{days_before_year_ad, DAYS_BEFORE_UNIX_EPOCH_AD}; + assert_eq!(DAYS_BEFORE_UNIX_EPOCH_AD, days_before_year_ad(1970)); + } + + #[test] + fn test_days_in_month() { + use super::days_in_month; + assert_eq!(days_in_month(2017, 1), 31); + assert_eq!(days_in_month(2017, 2), 28); + assert_eq!(days_in_month(2017, 3), 31); + assert_eq!(days_in_month(2017, 4), 30); + assert_eq!(days_in_month(2017, 5), 31); + assert_eq!(days_in_month(2017, 6), 30); + assert_eq!(days_in_month(2017, 7), 31); + assert_eq!(days_in_month(2017, 8), 31); + assert_eq!(days_in_month(2017, 9), 30); + assert_eq!(days_in_month(2017, 10), 31); + assert_eq!(days_in_month(2017, 11), 30); + assert_eq!(days_in_month(2017, 12), 31); + + // leap cases + assert_eq!(days_in_month(2000, 2), 29); + assert_eq!(days_in_month(2004, 2), 29); + assert_eq!(days_in_month(2016, 2), 29); + assert_eq!(days_in_month(2100, 2), 28); + } + + #[allow(clippy::unreadable_literal)] // TODO: Make this clear. + #[test] + fn test_time_from_ymdhms_utc() { + use super::{time_from_ymdhms_utc, Time}; + + // year boundary + assert_eq!( + Time::from_seconds_since_unix_epoch(1483228799), + time_from_ymdhms_utc(2016, 12, 31, 23, 59, 59).unwrap() + ); + assert_eq!( + Time::from_seconds_since_unix_epoch(1483228800), + time_from_ymdhms_utc(2017, 1, 1, 0, 0, 0).unwrap() + ); + + // not a leap year + assert_eq!( + Time::from_seconds_since_unix_epoch(1492449162), + time_from_ymdhms_utc(2017, 4, 17, 17, 12, 42).unwrap() + ); + + // leap year, post-feb + assert_eq!( + Time::from_seconds_since_unix_epoch(1460913162), + time_from_ymdhms_utc(2016, 4, 17, 17, 12, 42).unwrap() + ); + } +} diff --git a/src/cert.rs b/src/cert.rs new file mode 100644 index 0000000..7c76f2e --- /dev/null +++ b/src/cert.rs @@ -0,0 +1,226 @@ +// 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, signed_data, Error}; + +pub enum EndEntityOrCa<'a> { + EndEntity, + Ca(&'a Cert<'a>), +} + +pub struct Cert<'a> { + pub ee_or_ca: EndEntityOrCa<'a>, + + pub signed_data: signed_data::SignedData<'a>, + pub issuer: untrusted::Input<'a>, + pub validity: untrusted::Input<'a>, + pub subject: untrusted::Input<'a>, + pub spki: der::Value<'a>, + + pub basic_constraints: Option>, + pub eku: Option>, + pub name_constraints: Option>, + pub subject_alt_name: Option>, +} + +pub fn parse_cert<'a>( + cert_der: untrusted::Input<'a>, + ee_or_ca: EndEntityOrCa<'a>, +) -> Result, Error> { + parse_cert_internal(cert_der, ee_or_ca, certificate_serial_number) +} + +/// Used by `parse_cert` for regular certificates (end-entity and intermediate) +/// and by `cert_der_as_trust_anchor` for trust anchors encoded as +/// certificates. +pub(crate) fn parse_cert_internal<'a>( + cert_der: untrusted::Input<'a>, + ee_or_ca: EndEntityOrCa<'a>, + serial_number: fn(input: &mut untrusted::Reader<'_>) -> Result<(), Error>, +) -> Result, Error> { + let (tbs, signed_data) = cert_der.read_all(Error::BadDer, |cert_der| { + der::nested( + cert_der, + der::Tag::Sequence, + Error::BadDer, + signed_data::parse_signed_data, + ) + })?; + + tbs.read_all(Error::BadDer, |tbs| { + version3(tbs)?; + serial_number(tbs)?; + + let signature = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; + // TODO: In mozilla::pkix, the comparison is done based on the + // normalized value (ignoring whether or not there is an optional NULL + // parameter for RSA-based algorithms), so this may be too strict. + if signature != signed_data.algorithm { + return Err(Error::SignatureAlgorithmMismatch); + } + + let issuer = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; + let validity = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; + let subject = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; + let spki = der::expect_tag(tbs, der::Tag::Sequence)?; + + // In theory there could be fields [1] issuerUniqueID and [2] + // subjectUniqueID, but in practice there never are, and to keep the + // code small and simple we don't accept any certificates that do + // contain them. + + let mut cert = Cert { + ee_or_ca, + + signed_data, + issuer, + validity, + subject, + spki, + + basic_constraints: None, + eku: None, + name_constraints: None, + subject_alt_name: None, + }; + + // mozilla::pkix allows the extensions to be omitted. However, since + // the subjectAltName extension is mandatory, the extensions are + // mandatory too, and we enforce that. Also, mozilla::pkix includes + // special logic for handling critical Netscape Cert Type extensions. + // That has been intentionally omitted. + + der::nested( + tbs, + der::Tag::ContextSpecificConstructed3, + Error::MissingOrMalformedExtensions, + |tagged| { + der::nested_of_mut( + tagged, + der::Tag::Sequence, + der::Tag::Sequence, + Error::BadDer, + |extension| { + let extn_id = der::expect_tag_and_get_value(extension, der::Tag::OID)?; + let critical = der::optional_boolean(extension)?; + let extn_value = + der::expect_tag_and_get_value(extension, der::Tag::OctetString)?; + match remember_extension(&mut cert, extn_id, extn_value)? { + Understood::No if critical => Err(Error::UnsupportedCriticalExtension), + _ => Ok(()), + } + }, + ) + }, + )?; + + Ok(cert) + }) +} + +// mozilla::pkix supports v1, v2, v3, and v4, including both the implicit +// (correct) and explicit (incorrect) encoding of v1. We allow only v3. +fn version3(input: &mut untrusted::Reader) -> Result<(), Error> { + der::nested( + input, + der::Tag::ContextSpecificConstructed0, + Error::UnsupportedCertVersion, + |input| { + let version = der::small_nonnegative_integer(input)?; + if version != 2 { + // v3 + return Err(Error::UnsupportedCertVersion); + } + Ok(()) + }, + ) +} + +pub fn certificate_serial_number(input: &mut untrusted::Reader) -> Result<(), Error> { + // https://tools.ietf.org/html/rfc5280#section-4.1.2.2: + // * Conforming CAs MUST NOT use serialNumber values longer than 20 octets." + // * "The serial number MUST be a positive integer [...]" + + let value = der::positive_integer(input)?; + if value.big_endian_without_leading_zero().len() > 20 { + return Err(Error::BadDer); + } + Ok(()) +} + +enum Understood { + Yes, + No, +} + +fn remember_extension<'a>( + cert: &mut Cert<'a>, + extn_id: untrusted::Input, + value: untrusted::Input<'a>, +) -> Result { + // We don't do anything with certificate policies so we can safely ignore + // all policy-related stuff. We assume that the policy-related extensions + // are not marked critical. + + // id-ce 2.5.29 + static ID_CE: [u8; 2] = oid![2, 5, 29]; + + if extn_id.len() != ID_CE.len() + 1 || !extn_id.as_slice_less_safe().starts_with(&ID_CE) { + return Ok(Understood::No); + } + + let out = match *extn_id.as_slice_less_safe().last().unwrap() { + // id-ce-keyUsage 2.5.29.15. We ignore the KeyUsage extension. For CA + // certificates, BasicConstraints.cA makes KeyUsage redundant. Firefox + // and other common browsers do not check KeyUsage for end-entities, + // though it would be kind of nice to ensure that a KeyUsage without + // the keyEncipherment bit could not be used for RSA key exchange. + 15 => { + return Ok(Understood::Yes); + } + + // id-ce-subjectAltName 2.5.29.17 + 17 => &mut cert.subject_alt_name, + + // id-ce-basicConstraints 2.5.29.19 + 19 => &mut cert.basic_constraints, + + // id-ce-nameConstraints 2.5.29.30 + 30 => &mut cert.name_constraints, + + // id-ce-extKeyUsage 2.5.29.37 + 37 => &mut cert.eku, + + _ => { + return Ok(Understood::No); + } + }; + + match *out { + Some(..) => { + // The certificate contains more than one instance of this + // extension. + return Err(Error::ExtensionValueInvalid); + } + None => { + // All the extensions that we care about are wrapped in a SEQUENCE. + let sequence_value = value.read_all(Error::BadDer, |value| { + der::expect_tag_and_get_value(value, der::Tag::Sequence) + })?; + *out = Some(sequence_value); + } + } + + Ok(Understood::Yes) +} diff --git a/src/data/README.md b/src/data/README.md new file mode 100644 index 0000000..78fc778 --- /dev/null +++ b/src/data/README.md @@ -0,0 +1,21 @@ +These files contain the binary DER encoding of the *values* of some +ASN.1 [`AlgorithmIdentifier`]s, without the outer `SEQUENCE` tag or the outer +length component. + +These files were encoded with the help of [der-ascii]. They can be decoded +using: + +```sh +go get github.com/google/der-ascii/cmd/der2ascii +der2ascii -i -o .ascii +``` + +New or modified der-ascii files can be encoded using: + +```sh +go get github.com/google/der-ascii/cmd/ascii2der +ascii2der i .ascii -o +``` + +[`AlgorithmIdentifier`]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2] +[der-ascii]: https://github.com/google/der-ascii diff --git a/src/data/alg-ecdsa-p256.der b/src/data/alg-ecdsa-p256.der new file mode 100644 index 0000000..d49c30d --- /dev/null +++ b/src/data/alg-ecdsa-p256.der @@ -0,0 +1 @@ +*†HÎ=*†HÎ= \ No newline at end of file diff --git a/src/data/alg-ecdsa-p384.der b/src/data/alg-ecdsa-p384.der new file mode 100644 index 0000000000000000000000000000000000000000..8b24916caf9bfaeaecb98407738772aa659fc65e GIT binary patch literal 16 XcmZQ$*J|@PXUoLM#;V=O!k`2I8~OtA literal 0 HcmV?d00001 diff --git a/src/data/alg-ecdsa-sha256.der b/src/data/alg-ecdsa-sha256.der new file mode 100644 index 0000000..b2ee128 --- /dev/null +++ b/src/data/alg-ecdsa-sha256.der @@ -0,0 +1 @@ +*†HÎ= \ No newline at end of file diff --git a/src/data/alg-ecdsa-sha384.der b/src/data/alg-ecdsa-sha384.der new file mode 100644 index 0000000..7c61d3a --- /dev/null +++ b/src/data/alg-ecdsa-sha384.der @@ -0,0 +1 @@ +*†HÎ= \ No newline at end of file diff --git a/src/data/alg-ed25519.der b/src/data/alg-ed25519.der new file mode 100644 index 0000000..7ca46fd --- /dev/null +++ b/src/data/alg-ed25519.der @@ -0,0 +1 @@ ++ep \ No newline at end of file diff --git a/src/data/alg-rsa-encryption.der b/src/data/alg-rsa-encryption.der new file mode 100644 index 0000000000000000000000000000000000000000..77d159a1c6fcc68fac95281029ab0c6ce52bb58f GIT binary patch literal 13 UcmZSM)N1o+`_9YA$jHh702QtRng9R* literal 0 HcmV?d00001 diff --git a/src/data/alg-rsa-pkcs1-sha256.der b/src/data/alg-rsa-pkcs1-sha256.der new file mode 100644 index 0000000000000000000000000000000000000000..ab52bcd80b62813edb30a9ab628a5530b2ada8eb GIT binary patch literal 13 UcmZSM)N1o+`_9YA$j!( + input: &mut untrusted::Reader<'a>, + tag: Tag, +) -> Result, Error> { + ring::io::der::expect_tag_and_get_value(input, tag).map_err(|_| Error::BadDer) +} + +pub struct Value<'a> { + value: untrusted::Input<'a>, +} + +impl<'a> Value<'a> { + pub fn value(&self) -> untrusted::Input<'a> { + self.value + } +} + +pub fn expect_tag<'a>(input: &mut untrusted::Reader<'a>, tag: Tag) -> Result, Error> { + let (actual_tag, value) = read_tag_and_get_value(input)?; + if usize::from(tag) != usize::from(actual_tag) { + return Err(Error::BadDer); + } + + Ok(Value { value }) +} + +#[inline(always)] +pub fn read_tag_and_get_value<'a>( + input: &mut untrusted::Reader<'a>, +) -> Result<(u8, untrusted::Input<'a>), Error> { + ring::io::der::read_tag_and_get_value(input).map_err(|_| Error::BadDer) +} + +// TODO: investigate taking decoder as a reference to reduce generated code +// size. +pub fn nested_of_mut<'a, E>( + input: &mut untrusted::Reader<'a>, + outer_tag: Tag, + inner_tag: Tag, + error: E, + mut decoder: impl FnMut(&mut untrusted::Reader<'a>) -> Result<(), E>, +) -> Result<(), E> +where + E: Copy, +{ + nested(input, outer_tag, error, |outer| { + loop { + nested(outer, inner_tag, error, |inner| decoder(inner))?; + if outer.at_end() { + break; + } + } + Ok(()) + }) +} + +pub fn bit_string_with_no_unused_bits<'a>( + input: &mut untrusted::Reader<'a>, +) -> Result, Error> { + nested(input, Tag::BitString, Error::BadDer, |value| { + let unused_bits_at_end = value.read_byte().map_err(|_| Error::BadDer)?; + if unused_bits_at_end != 0 { + return Err(Error::BadDer); + } + Ok(value.read_bytes_to_end()) + }) +} + +// Like mozilla::pkix, we accept the nonconformant explicit encoding of +// the default value (false) for compatibility with real-world certificates. +pub fn optional_boolean(input: &mut untrusted::Reader) -> Result { + if !input.peek(Tag::Boolean.into()) { + return Ok(false); + } + nested(input, Tag::Boolean, Error::BadDer, |input| { + match input.read_byte() { + Ok(0xff) => Ok(true), + Ok(0x00) => Ok(false), + _ => Err(Error::BadDer), + } + }) +} + +pub fn positive_integer<'a>(input: &'a mut untrusted::Reader) -> Result, Error> { + ring::io::der::positive_integer(input).map_err(|_| Error::BadDer) +} + +pub fn small_nonnegative_integer(input: &mut untrusted::Reader) -> Result { + ring::io::der::small_nonnegative_integer(input).map_err(|_| Error::BadDer) +} + +pub fn time_choice(input: &mut untrusted::Reader) -> Result { + let is_utc_time = input.peek(Tag::UTCTime.into()); + let expected_tag = if is_utc_time { + Tag::UTCTime + } else { + Tag::GeneralizedTime + }; + + fn read_digit(inner: &mut untrusted::Reader) -> Result { + const DIGIT: core::ops::RangeInclusive = b'0'..=b'9'; + let b = inner.read_byte().map_err(|_| Error::BadDerTime)?; + if DIGIT.contains(&b) { + return Ok(u64::from(b - DIGIT.start())); + } + Err(Error::BadDerTime) + } + + fn read_two_digits(inner: &mut untrusted::Reader, min: u64, max: u64) -> Result { + let hi = read_digit(inner)?; + let lo = read_digit(inner)?; + let value = (hi * 10) + lo; + if value < min || value > max { + return Err(Error::BadDerTime); + } + Ok(value) + } + + nested(input, expected_tag, Error::BadDer, |value| { + let (year_hi, year_lo) = if is_utc_time { + let lo = read_two_digits(value, 0, 99)?; + let hi = if lo >= 50 { 19 } else { 20 }; + (hi, lo) + } else { + let hi = read_two_digits(value, 0, 99)?; + let lo = read_two_digits(value, 0, 99)?; + (hi, lo) + }; + + let year = (year_hi * 100) + year_lo; + let month = read_two_digits(value, 1, 12)?; + let days_in_month = calendar::days_in_month(year, month); + let day_of_month = read_two_digits(value, 1, days_in_month)?; + let hours = read_two_digits(value, 0, 23)?; + let minutes = read_two_digits(value, 0, 59)?; + let seconds = read_two_digits(value, 0, 59)?; + + let time_zone = value.read_byte().map_err(|_| Error::BadDerTime)?; + if time_zone != b'Z' { + return Err(Error::BadDerTime); + } + + calendar::time_from_ymdhms_utc(year, month, day_of_month, hours, minutes, seconds) + }) +} + +macro_rules! oid { + ( $first:expr, $second:expr, $( $tail:expr ),* ) => + ( + [(40 * $first) + $second, $( $tail ),*] + ) +} diff --git a/src/end_entity.rs b/src/end_entity.rs new file mode 100644 index 0000000..8c0650a --- /dev/null +++ b/src/end_entity.rs @@ -0,0 +1,206 @@ +// Copyright 2015-2021 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::{ + cert, name, signed_data, verify_cert, DnsNameRef, Error, SignatureAlgorithm, Time, + TlsClientTrustAnchors, TlsServerTrustAnchors, +}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +/// An end-entity certificate. +/// +/// Server certificate processing in a TLS connection consists of several +/// steps. All of these steps are necessary: +/// +/// * `EndEntityCert.verify_is_valid_tls_server_cert`: Verify that the server's +/// certificate is currently valid *for use by a TLS server*. +/// * `EndEntityCert.verify_is_valid_for_dns_name`: Verify that the server's +/// certificate is valid for the host that is being connected to. +/// * `EndEntityCert.verify_signature`: Verify that the signature of server's +/// `ServerKeyExchange` message is valid for the server's certificate. +/// +/// Client certificate processing in a TLS connection consists of analogous +/// steps. All of these steps are necessary: +/// +/// * `EndEntityCert.verify_is_valid_tls_client_cert`: Verify that the client's +/// certificate is currently valid *for use by a TLS client*. +/// * `EndEntityCert.verify_is_valid_for_dns_name` or +/// `EndEntityCert.verify_is_valid_for_at_least_one_dns_name`: Verify that the +/// client's certificate is valid for the identity or identities used to +/// identify the client. (Currently client authentication only works when the +/// client is identified by one or more DNS hostnames.) +/// * `EndEntityCert.verify_signature`: Verify that the client's signature in +/// its `CertificateVerify` message is valid using the public key from the +/// client's certificate. +/// +/// Although it would be less error-prone to combine all these steps into a +/// single function call, some significant optimizations are possible if the +/// three steps are processed separately (in parallel). It does not matter much +/// which order the steps are done in, but **all of these steps must completed +/// before application data is sent and before received application data is +/// processed**. `EndEntityCert::from` is an inexpensive operation and is +/// deterministic, so if these tasks are done in multiple threads, it is +/// probably best to just call `EndEntityCert::from` multiple times (before each +/// operation) for the same DER-encoded ASN.1 certificate bytes. +pub struct EndEntityCert<'a> { + inner: cert::Cert<'a>, +} + +impl<'a> core::convert::TryFrom<&'a [u8]> for EndEntityCert<'a> { + type Error = Error; + + /// Parse the ASN.1 DER-encoded X.509 encoding of the certificate + /// `cert_der`. + fn try_from(cert_der: &'a [u8]) -> Result { + Ok(Self { + inner: cert::parse_cert( + untrusted::Input::from(cert_der), + cert::EndEntityOrCa::EndEntity, + )?, + }) + } +} + +impl<'a> EndEntityCert<'a> { + pub(super) fn inner(&self) -> &cert::Cert { + &self.inner + } + + /// Verifies that the end-entity certificate is valid for use by a TLS + /// server. + /// + /// `supported_sig_algs` is the list of signature algorithms that are + /// trusted for use in certificate signatures; the end-entity certificate's + /// public key is not validated against this list. `trust_anchors` is the + /// list of root CAs to trust. `intermediate_certs` is the sequence of + /// intermediate certificates that the server sent in the TLS handshake. + /// `time` is the time for which the validation is effective (usually the + /// current time). + pub fn verify_is_valid_tls_server_cert( + &self, + supported_sig_algs: &[&SignatureAlgorithm], + &TlsServerTrustAnchors(trust_anchors): &TlsServerTrustAnchors, + intermediate_certs: &[&[u8]], + time: Time, + ) -> Result<(), Error> { + verify_cert::build_chain( + verify_cert::EKU_SERVER_AUTH, + supported_sig_algs, + trust_anchors, + intermediate_certs, + &self.inner, + time, + 0, + ) + } + + /// Verifies that the end-entity certificate is valid for use by a TLS + /// client. + /// + /// If the certificate is not valid for any of the given names then this + /// fails with `Error::CertNotValidForName`. + /// + /// `supported_sig_algs` is the list of signature algorithms that are + /// trusted for use in certificate signatures; the end-entity certificate's + /// public key is not validated against this list. `trust_anchors` is the + /// list of root CAs to trust. `intermediate_certs` is the sequence of + /// intermediate certificates that the client sent in the TLS handshake. + /// `cert` is the purported end-entity certificate of the client. `time` is + /// the time for which the validation is effective (usually the current + /// time). + pub fn verify_is_valid_tls_client_cert( + &self, + supported_sig_algs: &[&SignatureAlgorithm], + &TlsClientTrustAnchors(trust_anchors): &TlsClientTrustAnchors, + intermediate_certs: &[&[u8]], + time: Time, + ) -> Result<(), Error> { + verify_cert::build_chain( + verify_cert::EKU_CLIENT_AUTH, + supported_sig_algs, + trust_anchors, + intermediate_certs, + &self.inner, + time, + 0, + ) + } + + /// Verifies that the certificate is valid for the given DNS host name. + pub fn verify_is_valid_for_dns_name(&self, dns_name: DnsNameRef) -> Result<(), Error> { + name::verify_cert_dns_name(&self, dns_name) + } + + /// Verifies that the certificate is valid for at least one of the given DNS + /// host names. + /// + /// If the certificate is not valid for any of the given names then this + /// fails with `Error::CertNotValidForName`. Otherwise the DNS names for + /// which the certificate is valid are returned. + /// + /// Requires the `alloc` default feature; i.e. this isn't available in + /// `#![no_std]` configurations. + #[cfg(feature = "alloc")] + pub fn verify_is_valid_for_at_least_one_dns_name<'names, Names>( + &self, + dns_names: Names, + ) -> Result>, Error> + where + Names: Iterator>, + { + let result: Vec> = dns_names + .filter(|n| self.verify_is_valid_for_dns_name(*n).is_ok()) + .collect(); + if result.is_empty() { + return Err(Error::CertNotValidForName); + } + Ok(result) + } + + /// Verifies the signature `signature` of message `msg` using the + /// certificate's public key. + /// + /// `signature_alg` is the algorithm to use to + /// verify the signature; the certificate's public key is verified to be + /// compatible with this algorithm. + /// + /// For TLS 1.2, `signature` corresponds to TLS's + /// `DigitallySigned.signature` and `signature_alg` corresponds to TLS's + /// `DigitallySigned.algorithm` of TLS type `SignatureAndHashAlgorithm`. In + /// TLS 1.2 a single `SignatureAndHashAlgorithm` may map to multiple + /// `SignatureAlgorithm`s. For example, a TLS 1.2 + /// `ignatureAndHashAlgorithm` of (ECDSA, SHA-256) may map to any or all + /// of {`ECDSA_P256_SHA256`, `ECDSA_P384_SHA256`}, depending on how the TLS + /// implementation is configured. + /// + /// For current TLS 1.3 drafts, `signature_alg` corresponds to TLS's + /// `algorithm` fields of type `SignatureScheme`. There is (currently) a + /// one-to-one correspondence between TLS 1.3's `SignatureScheme` and + /// `SignatureAlgorithm`. + pub fn verify_signature( + &self, + signature_alg: &SignatureAlgorithm, + msg: &[u8], + signature: &[u8], + ) -> Result<(), Error> { + signed_data::verify_signature( + signature_alg, + self.inner.spki.value(), + untrusted::Input::from(msg), + untrusted::Input::from(signature), + ) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..deeb9a8 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,108 @@ +// 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 core::fmt; + +/// An error that occurs during certificate validation or name validation. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Error { + /// The encoding of some ASN.1 DER-encoded item is invalid. + BadDer, + + /// The encoding of an ASN.1 DER-encoded time is invalid. + BadDerTime, + + /// A CA certificate is being used as an end-entity certificate. + CaUsedAsEndEntity, + + /// The certificate is expired; i.e. the time it is being validated for is + /// later than the certificate's notAfter time. + CertExpired, + + /// The certificate is not valid for the name it is being validated for. + CertNotValidForName, + + /// The certificate is not valid yet; i.e. the time it is being validated + /// for is earlier than the certificate's notBefore time. + CertNotValidYet, + + /// An end-entity certificate is being used as a CA certificate. + EndEntityUsedAsCa, + + /// An X.509 extension is invalid. + ExtensionValueInvalid, + + /// The certificate validity period (notBefore, notAfter) is invalid; e.g. + /// the notAfter time is earlier than the notBefore time. + InvalidCertValidity, + + /// The signature is invalid for the given public key. + InvalidSignatureForPublicKey, + + /// The certificate violates one or more name constraints. + NameConstraintViolation, + + /// The certificate violates one or more path length constraints. + PathLenConstraintViolated, + + /// The algorithm in the TBSCertificate "signature" field of a certificate + /// does not match the algorithm in the signature of the certificate. + SignatureAlgorithmMismatch, + + /// The certificate is not valid for the Extended Key Usage for which it is + /// being validated. + RequiredEkuNotFound, + + /// A valid issuer for the certificate could not be found. + UnknownIssuer, + + /// The certificate is not a v3 X.509 certificate. + /// + /// This error may be also reported if the certificate version field + /// is malformed. + UnsupportedCertVersion, + + /// The certificate extensions are missing or malformed. + /// + /// In particular, webpki requires the DNS name(s) be in the subjectAltName + /// extension as required by the CA/Browser Forum Baseline Requirements + /// and as recommended by RFC6125. + MissingOrMalformedExtensions, + + /// The certificate contains an unsupported critical extension. + UnsupportedCriticalExtension, + + /// The signature's algorithm does not match the algorithm of the public + /// key it is being validated for. This may be because the public key + /// algorithm's OID isn't recognized (e.g. DSA), or the public key + /// algorithm's parameters don't match the supported parameters for that + /// algorithm (e.g. ECC keys for unsupported curves), or the public key + /// algorithm and the signature algorithm simply don't match (e.g. + /// verifying an RSA signature with an ECC public key). + UnsupportedSignatureAlgorithmForPublicKey, + + /// The signature algorithm for a signature is not in the set of supported + /// signature algorithms given. + UnsupportedSignatureAlgorithm, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Requires the `std` feature. +#[cfg(feature = "std")] +impl ::std::error::Error for Error {} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ce9e71a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,96 @@ +// 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. + +//! webpki: Web PKI X.509 Certificate Validation. +//! +//! See `EndEntityCert`'s documentation for a description of the certificate +//! processing steps necessary for a TLS connection. +//! +//! # Features +//! +//! | Feature | Description | +//! | ------- | ----------- | +//! | `alloc` | Enable features that require use of the heap. Currently all RSA signature algorithms require this feature. | +//! | `std` | Enable features that require libstd. Implies `alloc`. | + +#![doc(html_root_url = "https://briansmith.org/rustdoc/")] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow( + clippy::doc_markdown, + clippy::if_not_else, + clippy::inline_always, + clippy::items_after_statements, + clippy::missing_errors_doc, + clippy::module_name_repetitions, + clippy::single_match, + clippy::single_match_else +)] +#![deny(clippy::as_conversions)] + +#[cfg(any(test, feature = "alloc"))] +#[cfg_attr(test, macro_use)] +extern crate alloc; + +#[macro_use] +mod der; + +mod calendar; +mod cert; +mod end_entity; +mod error; +mod name; +mod signed_data; +mod time; +mod trust_anchor; + +mod verify_cert; + +pub use { + end_entity::EndEntityCert, + error::Error, + name::{DnsNameRef, InvalidDnsNameError}, + signed_data::{ + SignatureAlgorithm, ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256, + ECDSA_P384_SHA384, ED25519, + }, + time::Time, + trust_anchor::{TlsClientTrustAnchors, TlsServerTrustAnchors, TrustAnchor}, +}; + +#[cfg(feature = "alloc")] +pub use { + name::DnsName, + signed_data::{ + RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_2048_8192_SHA384, RSA_PKCS1_2048_8192_SHA512, + RSA_PKCS1_3072_8192_SHA384, RSA_PSS_2048_8192_SHA256_LEGACY_KEY, + RSA_PSS_2048_8192_SHA384_LEGACY_KEY, RSA_PSS_2048_8192_SHA512_LEGACY_KEY, + }, +}; + +#[cfg(feature = "alloc")] +#[allow(unknown_lints, clippy::upper_case_acronyms)] +#[deprecated(note = "Use DnsName")] +pub type DNSName = DnsName; + +#[deprecated(note = "use DnsNameRef")] +#[allow(unknown_lints, clippy::upper_case_acronyms)] +pub type DNSNameRef<'a> = DnsNameRef<'a>; + +#[deprecated(note = "use TlsServerTrustAnchors")] +#[allow(unknown_lints, clippy::upper_case_acronyms)] +pub type TLSServerTrustAnchors<'a> = TlsServerTrustAnchors<'a>; + +#[deprecated(note = "use TlsClientTrustAnchors")] +#[allow(unknown_lints, clippy::upper_case_acronyms)] +pub type TLSClientTrustAnchors<'a> = TlsClientTrustAnchors<'a>; diff --git a/src/name.rs b/src/name.rs new file mode 100644 index 0000000..040a813 --- /dev/null +++ b/src/name.rs @@ -0,0 +1,25 @@ +// Copyright 2015-2020 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. + +mod dns_name; +pub use dns_name::{DnsNameRef, InvalidDnsNameError}; + +/// Requires the `alloc` feature. +#[cfg(feature = "alloc")] +pub use dns_name::DnsName; + +mod ip_address; + +mod verify; +pub(super) use verify::{check_name_constraints, verify_cert_dns_name}; diff --git a/src/name/dns_name.rs b/src/name/dns_name.rs new file mode 100644 index 0000000..e40e703 --- /dev/null +++ b/src/name/dns_name.rs @@ -0,0 +1,796 @@ +// Copyright 2015-2020 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. + +#[cfg(feature = "alloc")] +use alloc::string::String; + +/// A DNS Name suitable for use in the TLS Server Name Indication (SNI) +/// extension and/or for use as the reference hostname for which to verify a +/// certificate. +/// +/// A `DnsName` is guaranteed to be syntactically valid. The validity rules are +/// specified in [RFC 5280 Section 7.2], except that underscores are also +/// allowed. +/// +/// `DnsName` stores a copy of the input it was constructed from in a `String` +/// and so it is only available when the `std` default feature is enabled. +/// +/// `Eq`, `PartialEq`, etc. are not implemented because name comparison +/// frequently should be done case-insensitively and/or with other caveats that +/// depend on the specific circumstances in which the comparison is done. +/// +/// [RFC 5280 Section 7.2]: https://tools.ietf.org/html/rfc5280#section-7.2 +/// +/// Requires the `alloc` feature. +#[cfg(feature = "alloc")] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct DnsName(String); + +/// Requires the `alloc` feature. +#[cfg(feature = "alloc")] +impl DnsName { + /// Returns a `DnsNameRef` that refers to this `DnsName`. + pub fn as_ref(&self) -> DnsNameRef { + DnsNameRef(self.0.as_bytes()) + } +} + +/// Requires the `alloc` feature. +#[cfg(feature = "alloc")] +impl AsRef for DnsName { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +/// Requires the `alloc` feature. +// Deprecated +#[cfg(feature = "alloc")] +impl From> for DnsName { + fn from(dns_name: DnsNameRef) -> Self { + dns_name.to_owned() + } +} + +/// A reference to a DNS Name suitable for use in the TLS Server Name Indication +/// (SNI) extension and/or for use as the reference hostname for which to verify +/// a certificate. +/// +/// A `DnsNameRef` is guaranteed to be syntactically valid. The validity rules +/// are specified in [RFC 5280 Section 7.2], except that underscores are also +/// allowed. +/// +/// `Eq`, `PartialEq`, etc. are not implemented because name comparison +/// frequently should be done case-insensitively and/or with other caveats that +/// depend on the specific circumstances in which the comparison is done. +/// +/// [RFC 5280 Section 7.2]: https://tools.ietf.org/html/rfc5280#section-7.2 +#[derive(Clone, Copy)] +pub struct DnsNameRef<'a>(&'a [u8]); + +impl AsRef<[u8]> for DnsNameRef<'_> { + #[inline] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +/// An error indicating that a `DnsNameRef` could not built because the input +/// is not a syntactically-valid DNS Name. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct InvalidDnsNameError; + +impl core::fmt::Display for InvalidDnsNameError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Requires the `std` feature. +#[cfg(feature = "std")] +impl ::std::error::Error for InvalidDnsNameError {} + +impl<'a> DnsNameRef<'a> { + /// Constructs a `DnsNameRef` from the given input if the input is a + /// syntactically-valid DNS name. + pub fn try_from_ascii(dns_name: &'a [u8]) -> Result { + if !is_valid_reference_dns_id(untrusted::Input::from(dns_name)) { + return Err(InvalidDnsNameError); + } + + Ok(Self(dns_name)) + } + + /// Constructs a `DnsNameRef` from the given input if the input is a + /// syntactically-valid DNS name. + pub fn try_from_ascii_str(dns_name: &'a str) -> Result { + Self::try_from_ascii(dns_name.as_bytes()) + } + + /// Constructs a `DnsName` from this `DnsNameRef` + /// + /// Requires the `alloc` feature. + #[cfg(feature = "alloc")] + pub fn to_owned(&self) -> DnsName { + // DnsNameRef is already guaranteed to be valid ASCII, which is a + // subset of UTF-8. + let s: &str = self.clone().into(); + DnsName(s.to_ascii_lowercase()) + } +} + +/// Requires the `alloc` feature. +#[cfg(feature = "alloc")] +impl core::fmt::Debug for DnsNameRef<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + let lowercase = self.clone().to_owned(); + f.debug_tuple("DnsNameRef").field(&lowercase.0).finish() + } +} + +impl<'a> From> for &'a str { + fn from(DnsNameRef(d): DnsNameRef<'a>) -> Self { + // The unwrap won't fail because DnsNameRefs are guaranteed to be ASCII + // and ASCII is a subset of UTF-8. + core::str::from_utf8(d).unwrap() + } +} + +pub(super) fn presented_id_matches_reference_id( + presented_dns_id: untrusted::Input, + reference_dns_id: untrusted::Input, +) -> Option { + presented_id_matches_reference_id_internal( + presented_dns_id, + IdRole::Reference, + reference_dns_id, + ) +} + +pub(super) fn presented_id_matches_constraint( + presented_dns_id: untrusted::Input, + reference_dns_id: untrusted::Input, +) -> Option { + presented_id_matches_reference_id_internal( + presented_dns_id, + IdRole::NameConstraint, + reference_dns_id, + ) +} + +// We do not distinguish between a syntactically-invalid presented_dns_id and +// one that is syntactically valid but does not match reference_dns_id; in both +// cases, the result is false. +// +// We assume that both presented_dns_id and reference_dns_id are encoded in +// such a way that US-ASCII (7-bit) characters are encoded in one byte and no +// encoding of a non-US-ASCII character contains a code point in the range +// 0-127. For example, UTF-8 is OK but UTF-16 is not. +// +// RFC6125 says that a wildcard label may be of the form *., where +// and/or may be empty. However, NSS requires to be empty, and we +// follow NSS's stricter policy by accepting wildcards only of the form +// *., where may be empty. +// +// An relative presented DNS ID matches both an absolute reference ID and a +// relative reference ID. Absolute presented DNS IDs are not supported: +// +// Presented ID Reference ID Result +// ------------------------------------- +// example.com example.com Match +// example.com. example.com Mismatch +// example.com example.com. Match +// example.com. example.com. Mismatch +// +// There are more subtleties documented inline in the code. +// +// Name constraints /////////////////////////////////////////////////////////// +// +// This is all RFC 5280 has to say about dNSName constraints: +// +// DNS name restrictions are expressed as host.example.com. Any DNS +// name that can be constructed by simply adding zero or more labels to +// the left-hand side of the name satisfies the name constraint. For +// example, www.host.example.com would satisfy the constraint but +// host1.example.com would not. +// +// This lack of specificity has lead to a lot of uncertainty regarding +// subdomain matching. In particular, the following questions have been +// raised and answered: +// +// Q: Does a presented identifier equal (case insensitive) to the name +// constraint match the constraint? For example, does the presented +// ID "host.example.com" match a "host.example.com" constraint? +// A: Yes. RFC5280 says "by simply adding zero or more labels" and this +// is the case of adding zero labels. +// +// Q: When the name constraint does not start with ".", do subdomain +// presented identifiers match it? For example, does the presented +// ID "www.host.example.com" match a "host.example.com" constraint? +// A: Yes. RFC5280 says "by simply adding zero or more labels" and this +// is the case of adding more than zero labels. The example is the +// one from RFC 5280. +// +// Q: When the name constraint does not start with ".", does a +// non-subdomain prefix match it? For example, does "bigfoo.bar.com" +// match "foo.bar.com"? [4] +// A: No. We interpret RFC 5280's language of "adding zero or more labels" +// to mean that whole labels must be prefixed. +// +// (Note that the above three scenarios are the same as the RFC 6265 +// domain matching rules [0].) +// +// Q: Is a name constraint that starts with "." valid, and if so, what +// semantics does it have? For example, does a presented ID of +// "www.example.com" match a constraint of ".example.com"? Does a +// presented ID of "example.com" match a constraint of ".example.com"? +// A: This implementation, NSS[1], and SChannel[2] all support a +// leading ".", but OpenSSL[3] does not yet. Amongst the +// implementations that support it, a leading "." is legal and means +// the same thing as when the "." is omitted, EXCEPT that a +// presented identifier equal (case insensitive) to the name +// constraint is not matched; i.e. presented dNSName identifiers +// must be subdomains. Some CAs in Mozilla's CA program (e.g. HARICA) +// have name constraints with the leading "." in their root +// certificates. The name constraints imposed on DCISS by Mozilla also +// have the it, so supporting this is a requirement for backward +// compatibility, even if it is not yet standardized. So, for example, a +// presented ID of "www.example.com" matches a constraint of +// ".example.com" but a presented ID of "example.com" does not. +// +// Q: Is there a way to prevent subdomain matches? +// A: Yes. +// +// Some people have proposed that dNSName constraints that do not +// start with a "." should be restricted to exact (case insensitive) +// matches. However, such a change of semantics from what RFC5280 +// specifies would be a non-backward-compatible change in the case of +// permittedSubtrees constraints, and it would be a security issue for +// excludedSubtrees constraints. +// +// However, it can be done with a combination of permittedSubtrees and +// excludedSubtrees, e.g. "example.com" in permittedSubtrees and +// ".example.com" in excludedSubtrees. +// +// Q: Are name constraints allowed to be specified as absolute names? +// For example, does a presented ID of "example.com" match a name +// constraint of "example.com." and vice versa. +// A: Absolute names are not supported as presented IDs or name +// constraints. Only reference IDs may be absolute. +// +// Q: Is "" a valid dNSName constraint? If so, what does it mean? +// A: Yes. Any valid presented dNSName can be formed "by simply adding zero +// or more labels to the left-hand side" of "". In particular, an +// excludedSubtrees dNSName constraint of "" forbids all dNSNames. +// +// Q: Is "." a valid dNSName constraint? If so, what does it mean? +// A: No, because absolute names are not allowed (see above). +// +// [0] RFC 6265 (Cookies) Domain Matching rules: +// http://tools.ietf.org/html/rfc6265#section-5.1.3 +// [1] NSS source code: +// https://mxr.mozilla.org/nss/source/lib/certdb/genname.c?rev=2a7348f013cb#1209 +// [2] Description of SChannel's behavior from Microsoft: +// http://www.imc.org/ietf-pkix/mail-archive/msg04668.html +// [3] Proposal to add such support to OpenSSL: +// http://www.mail-archive.com/openssl-dev%40openssl.org/msg36204.html +// https://rt.openssl.org/Ticket/Display.html?id=3562 +// [4] Feedback on the lack of clarify in the definition that never got +// incorporated into the spec: +// https://www.ietf.org/mail-archive/web/pkix/current/msg21192.html +fn presented_id_matches_reference_id_internal( + presented_dns_id: untrusted::Input, + reference_dns_id_role: IdRole, + reference_dns_id: untrusted::Input, +) -> Option { + if !is_valid_dns_id(presented_dns_id, IdRole::Presented, AllowWildcards::Yes) { + return None; + } + + if !is_valid_dns_id(reference_dns_id, reference_dns_id_role, AllowWildcards::No) { + return None; + } + + let mut presented = untrusted::Reader::new(presented_dns_id); + let mut reference = untrusted::Reader::new(reference_dns_id); + + match reference_dns_id_role { + IdRole::Reference => (), + + IdRole::NameConstraint if presented_dns_id.len() > reference_dns_id.len() => { + if reference_dns_id.is_empty() { + // An empty constraint matches everything. + return Some(true); + } + + // If the reference ID starts with a dot then skip the prefix of + // the presented ID and start the comparison at the position of + // that dot. Examples: + // + // Matches Doesn't Match + // ----------------------------------------------------------- + // original presented ID: www.example.com badexample.com + // skipped: www ba + // presented ID w/o prefix: .example.com dexample.com + // reference ID: .example.com .example.com + // + // If the reference ID does not start with a dot then we skip + // the prefix of the presented ID but also verify that the + // prefix ends with a dot. Examples: + // + // Matches Doesn't Match + // ----------------------------------------------------------- + // original presented ID: www.example.com badexample.com + // skipped: www ba + // must be '.': . d + // presented ID w/o prefix: example.com example.com + // reference ID: example.com example.com + // + if reference.peek(b'.') { + if presented + .skip(presented_dns_id.len() - reference_dns_id.len()) + .is_err() + { + unreachable!(); + } + } else { + if presented + .skip(presented_dns_id.len() - reference_dns_id.len() - 1) + .is_err() + { + unreachable!(); + } + if presented.read_byte() != Ok(b'.') { + return Some(false); + } + } + } + + IdRole::NameConstraint => (), + + IdRole::Presented => unreachable!(), + } + + // Only allow wildcard labels that consist only of '*'. + if presented.peek(b'*') { + if presented.skip(1).is_err() { + unreachable!(); + } + + loop { + if reference.read_byte().is_err() { + return Some(false); + } + if reference.peek(b'.') { + break; + } + } + } + + loop { + let presented_byte = match (presented.read_byte(), reference.read_byte()) { + (Ok(p), Ok(r)) if ascii_lower(p) == ascii_lower(r) => p, + _ => { + return Some(false); + } + }; + + if presented.at_end() { + // Don't allow presented IDs to be absolute. + if presented_byte == b'.' { + return None; + } + break; + } + } + + // Allow a relative presented DNS ID to match an absolute reference DNS ID, + // unless we're matching a name constraint. + if !reference.at_end() { + if reference_dns_id_role != IdRole::NameConstraint { + match reference.read_byte() { + Ok(b'.') => (), + _ => { + return Some(false); + } + }; + } + if !reference.at_end() { + return Some(false); + } + } + + assert!(presented.at_end()); + assert!(reference.at_end()); + + Some(true) +} + +#[inline] +fn ascii_lower(b: u8) -> u8 { + match b { + b'A'..=b'Z' => b + b'a' - b'A', + _ => b, + } +} + +#[derive(Clone, Copy, PartialEq)] +enum AllowWildcards { + No, + Yes, +} + +#[derive(Clone, Copy, PartialEq)] +enum IdRole { + Reference, + Presented, + NameConstraint, +} + +fn is_valid_reference_dns_id(hostname: untrusted::Input) -> bool { + is_valid_dns_id(hostname, IdRole::Reference, AllowWildcards::No) +} + +// https://tools.ietf.org/html/rfc5280#section-4.2.1.6: +// +// When the subjectAltName extension contains a domain name system +// label, the domain name MUST be stored in the dNSName (an IA5String). +// The name MUST be in the "preferred name syntax", as specified by +// Section 3.5 of [RFC1034] and as modified by Section 2.1 of +// [RFC1123]. +// +// https://bugzilla.mozilla.org/show_bug.cgi?id=1136616: As an exception to the +// requirement above, underscores are also allowed in names for compatibility. +fn is_valid_dns_id( + hostname: untrusted::Input, + id_role: IdRole, + allow_wildcards: AllowWildcards, +) -> bool { + // https://blogs.msdn.microsoft.com/oldnewthing/20120412-00/?p=7873/ + if hostname.len() > 253 { + return false; + } + + let mut input = untrusted::Reader::new(hostname); + + if id_role == IdRole::NameConstraint && input.at_end() { + return true; + } + + let mut dot_count = 0; + let mut label_length = 0; + let mut label_is_all_numeric = false; + let mut label_ends_with_hyphen = false; + + // Only presented IDs are allowed to have wildcard labels. And, like + // Chromium, be stricter than RFC 6125 requires by insisting that a + // wildcard label consist only of '*'. + let is_wildcard = allow_wildcards == AllowWildcards::Yes && input.peek(b'*'); + let mut is_first_byte = !is_wildcard; + if is_wildcard { + if input.read_byte() != Ok(b'*') || input.read_byte() != Ok(b'.') { + return false; + } + dot_count += 1; + } + + loop { + const MAX_LABEL_LENGTH: usize = 63; + + match input.read_byte() { + Ok(b'-') => { + if label_length == 0 { + return false; // Labels must not start with a hyphen. + } + label_is_all_numeric = false; + label_ends_with_hyphen = true; + label_length += 1; + if label_length > MAX_LABEL_LENGTH { + return false; + } + } + + Ok(b'0'..=b'9') => { + if label_length == 0 { + label_is_all_numeric = true; + } + label_ends_with_hyphen = false; + label_length += 1; + if label_length > MAX_LABEL_LENGTH { + return false; + } + } + + Ok(b'a'..=b'z') | Ok(b'A'..=b'Z') | Ok(b'_') => { + label_is_all_numeric = false; + label_ends_with_hyphen = false; + label_length += 1; + if label_length > MAX_LABEL_LENGTH { + return false; + } + } + + Ok(b'.') => { + dot_count += 1; + if label_length == 0 && (id_role != IdRole::NameConstraint || !is_first_byte) { + return false; + } + if label_ends_with_hyphen { + return false; // Labels must not end with a hyphen. + } + label_length = 0; + } + + _ => { + return false; + } + } + is_first_byte = false; + + if input.at_end() { + break; + } + } + + // Only reference IDs, not presented IDs or name constraints, may be + // absolute. + if label_length == 0 && id_role != IdRole::Reference { + return false; + } + + if label_ends_with_hyphen { + return false; // Labels must not end with a hyphen. + } + + if label_is_all_numeric { + return false; // Last label must not be all numeric. + } + + if is_wildcard { + // If the DNS ID ends with a dot, the last dot signifies an absolute ID. + let label_count = if label_length == 0 { + dot_count + } else { + dot_count + 1 + }; + + // Like NSS, require at least two labels to follow the wildcard label. + // TODO: Allow the TrustDomain to control this on a per-eTLD+1 basis, + // similar to Chromium. Even then, it might be better to still enforce + // that there are at least two labels after the wildcard. + if label_count < 3 { + return false; + } + } + + true +} + +#[cfg(test)] +mod tests { + use super::*; + + const PRESENTED_MATCHES_REFERENCE: &[(&[u8], &[u8], Option)] = &[ + (b"", b"a", None), + (b"a", b"a", Some(true)), + (b"b", b"a", Some(false)), + (b"*.b.a", b"c.b.a", Some(true)), + (b"*.b.a", b"b.a", Some(false)), + (b"*.b.a", b"b.a.", Some(false)), + // Wildcard not in leftmost label + (b"d.c.b.a", b"d.c.b.a", Some(true)), + (b"d.*.b.a", b"d.c.b.a", None), + (b"d.c*.b.a", b"d.c.b.a", None), + (b"d.c*.b.a", b"d.cc.b.a", None), + // case sensitivity + ( + b"abcdefghijklmnopqrstuvwxyz", + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ", + Some(true), + ), + ( + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ", + b"abcdefghijklmnopqrstuvwxyz", + Some(true), + ), + (b"aBc", b"Abc", Some(true)), + // digits + (b"a1", b"a1", Some(true)), + // A trailing dot indicates an absolute name, and absolute names can match + // relative names, and vice-versa. + (b"example", b"example", Some(true)), + (b"example.", b"example.", None), + (b"example", b"example.", Some(true)), + (b"example.", b"example", None), + (b"example.com", b"example.com", Some(true)), + (b"example.com.", b"example.com.", None), + (b"example.com", b"example.com.", Some(true)), + (b"example.com.", b"example.com", None), + (b"example.com..", b"example.com.", None), + (b"example.com..", b"example.com", None), + (b"example.com...", b"example.com.", None), + // xn-- IDN prefix + (b"x*.b.a", b"xa.b.a", None), + (b"x*.b.a", b"xna.b.a", None), + (b"x*.b.a", b"xn-a.b.a", None), + (b"x*.b.a", b"xn--a.b.a", None), + (b"xn*.b.a", b"xn--a.b.a", None), + (b"xn-*.b.a", b"xn--a.b.a", None), + (b"xn--*.b.a", b"xn--a.b.a", None), + (b"xn*.b.a", b"xn--a.b.a", None), + (b"xn-*.b.a", b"xn--a.b.a", None), + (b"xn--*.b.a", b"xn--a.b.a", None), + (b"xn---*.b.a", b"xn--a.b.a", None), + // "*" cannot expand to nothing. + (b"c*.b.a", b"c.b.a", None), + // -------------------------------------------------------------------------- + // The rest of these are test cases adapted from Chromium's + // x509_certificate_unittest.cc. The parameter order is the opposite in + // Chromium's tests. Also, they some tests were modified to fit into this + // framework or due to intentional differences between mozilla::pkix and + // Chromium. + (b"foo.com", b"foo.com", Some(true)), + (b"f", b"f", Some(true)), + (b"i", b"h", Some(false)), + (b"*.foo.com", b"bar.foo.com", Some(true)), + (b"*.test.fr", b"www.test.fr", Some(true)), + (b"*.test.FR", b"wwW.tESt.fr", Some(true)), + (b".uk", b"f.uk", None), + (b"?.bar.foo.com", b"w.bar.foo.com", None), + (b"(www|ftp).foo.com", b"www.foo.com", None), // regex! + (b"www.foo.com\0", b"www.foo.com", None), + (b"www.foo.com\0*.foo.com", b"www.foo.com", None), + (b"ww.house.example", b"www.house.example", Some(false)), + (b"www.test.org", b"test.org", Some(false)), + (b"*.test.org", b"test.org", Some(false)), + (b"*.org", b"test.org", None), + // '*' must be the only character in the wildcard label + (b"w*.bar.foo.com", b"w.bar.foo.com", None), + (b"ww*ww.bar.foo.com", b"www.bar.foo.com", None), + (b"ww*ww.bar.foo.com", b"wwww.bar.foo.com", None), + (b"w*w.bar.foo.com", b"wwww.bar.foo.com", None), + (b"w*w.bar.foo.c0m", b"wwww.bar.foo.com", None), + (b"wa*.bar.foo.com", b"WALLY.bar.foo.com", None), + (b"*Ly.bar.foo.com", b"wally.bar.foo.com", None), + // Chromium does URL decoding of the reference ID, but we don't, and we also + // require that the reference ID is valid, so we can't test these two. + // (b"www.foo.com", b"ww%57.foo.com", Some(true)), + // (b"www&.foo.com", b"www%26.foo.com", Some(true)), + (b"*.test.de", b"www.test.co.jp", Some(false)), + (b"*.jp", b"www.test.co.jp", None), + (b"www.test.co.uk", b"www.test.co.jp", Some(false)), + (b"www.*.co.jp", b"www.test.co.jp", None), + (b"www.bar.foo.com", b"www.bar.foo.com", Some(true)), + (b"*.foo.com", b"www.bar.foo.com", Some(false)), + (b"*.*.foo.com", b"www.bar.foo.com", None), + // Our matcher requires the reference ID to be a valid DNS name, so we cannot + // test this case. + // (b"*.*.bar.foo.com", b"*..bar.foo.com", Some(false)), + (b"www.bath.org", b"www.bath.org", Some(true)), + // Our matcher requires the reference ID to be a valid DNS name, so we cannot + // test these cases. + // DNS_ID_MISMATCH("www.bath.org", ""), + // (b"www.bath.org", b"20.30.40.50", Some(false)), + // (b"www.bath.org", b"66.77.88.99", Some(false)), + + // IDN tests + ( + b"xn--poema-9qae5a.com.br", + b"xn--poema-9qae5a.com.br", + Some(true), + ), + ( + b"*.xn--poema-9qae5a.com.br", + b"www.xn--poema-9qae5a.com.br", + Some(true), + ), + ( + b"*.xn--poema-9qae5a.com.br", + b"xn--poema-9qae5a.com.br", + Some(false), + ), + (b"xn--poema-*.com.br", b"xn--poema-9qae5a.com.br", None), + (b"xn--*-9qae5a.com.br", b"xn--poema-9qae5a.com.br", None), + (b"*--poema-9qae5a.com.br", b"xn--poema-9qae5a.com.br", None), + // The following are adapted from the examples quoted from + // http://tools.ietf.org/html/rfc6125#section-6.4.3 + // (e.g., *.example.com would match foo.example.com but + // not bar.foo.example.com or example.com). + (b"*.example.com", b"foo.example.com", Some(true)), + (b"*.example.com", b"bar.foo.example.com", Some(false)), + (b"*.example.com", b"example.com", Some(false)), + (b"baz*.example.net", b"baz1.example.net", None), + (b"*baz.example.net", b"foobaz.example.net", None), + (b"b*z.example.net", b"buzz.example.net", None), + // Wildcards should not be valid for public registry controlled domains, + // and unknown/unrecognized domains, at least three domain components must + // be present. For mozilla::pkix and NSS, there must always be at least two + // labels after the wildcard label. + (b"*.test.example", b"www.test.example", Some(true)), + (b"*.example.co.uk", b"test.example.co.uk", Some(true)), + (b"*.example", b"test.example", None), + // The result is different than Chromium, because Chromium takes into account + // the additional knowledge it has that "co.uk" is a TLD. mozilla::pkix does + // not know that. + (b"*.co.uk", b"example.co.uk", Some(true)), + (b"*.com", b"foo.com", None), + (b"*.us", b"foo.us", None), + (b"*", b"foo", None), + // IDN variants of wildcards and registry controlled domains. + ( + b"*.xn--poema-9qae5a.com.br", + b"www.xn--poema-9qae5a.com.br", + Some(true), + ), + ( + b"*.example.xn--mgbaam7a8h", + b"test.example.xn--mgbaam7a8h", + Some(true), + ), + // RFC6126 allows this, and NSS accepts it, but Chromium disallows it. + // TODO: File bug against Chromium. + (b"*.com.br", b"xn--poema-9qae5a.com.br", Some(true)), + (b"*.xn--mgbaam7a8h", b"example.xn--mgbaam7a8h", None), + // Wildcards should be permissible for 'private' registry-controlled + // domains. (In mozilla::pkix, we do not know if it is a private registry- + // controlled domain or not.) + (b"*.appspot.com", b"www.appspot.com", Some(true)), + (b"*.s3.amazonaws.com", b"foo.s3.amazonaws.com", Some(true)), + // Multiple wildcards are not valid. + (b"*.*.com", b"foo.example.com", None), + (b"*.bar.*.com", b"foo.bar.example.com", None), + // Absolute vs relative DNS name tests. Although not explicitly specified + // in RFC 6125, absolute reference names (those ending in a .) should + // match either absolute or relative presented names. + // TODO: File errata against RFC 6125 about this. + (b"foo.com.", b"foo.com", None), + (b"foo.com", b"foo.com.", Some(true)), + (b"foo.com.", b"foo.com.", None), + (b"f.", b"f", None), + (b"f", b"f.", Some(true)), + (b"f.", b"f.", None), + (b"*.bar.foo.com.", b"www-3.bar.foo.com", None), + (b"*.bar.foo.com", b"www-3.bar.foo.com.", Some(true)), + (b"*.bar.foo.com.", b"www-3.bar.foo.com.", None), + // We require the reference ID to be a valid DNS name, so we cannot test this + // case. + // (b".", b".", Some(false)), + (b"*.com.", b"example.com", None), + (b"*.com", b"example.com.", None), + (b"*.com.", b"example.com.", None), + (b"*.", b"foo.", None), + (b"*.", b"foo", None), + // The result is different than Chromium because we don't know that co.uk is + // a TLD. + (b"*.co.uk.", b"foo.co.uk", None), + (b"*.co.uk.", b"foo.co.uk.", None), + ]; + + #[test] + fn presented_matches_reference_test() { + for &(presented, reference, expected_result) in PRESENTED_MATCHES_REFERENCE { + let actual_result = presented_id_matches_reference_id( + untrusted::Input::from(presented), + untrusted::Input::from(reference), + ); + assert_eq!( + actual_result, + expected_result, + "presented_dns_id_matches_reference_dns_id(\"{:?}\", IDRole::ReferenceID, \"{:?}\")", + presented, + reference + ); + } + } +} diff --git a/src/name/ip_address.rs b/src/name/ip_address.rs new file mode 100644 index 0000000..fac5362 --- /dev/null +++ b/src/name/ip_address.rs @@ -0,0 +1,64 @@ +// Copyright 2015-2020 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::Error; + +// https://tools.ietf.org/html/rfc5280#section-4.2.1.10 says: +// +// For IPv4 addresses, the iPAddress field of GeneralName MUST contain +// eight (8) octets, encoded in the style of RFC 4632 (CIDR) to represent +// an address range [RFC4632]. For IPv6 addresses, the iPAddress field +// MUST contain 32 octets similarly encoded. For example, a name +// constraint for "class C" subnet 192.0.2.0 is represented as the +// octets C0 00 02 00 FF FF FF 00, representing the CIDR notation +// 192.0.2.0/24 (mask 255.255.255.0). +pub(super) fn presented_id_matches_constraint( + name: untrusted::Input, + constraint: untrusted::Input, +) -> Result { + if name.len() != 4 && name.len() != 16 { + return Err(Error::BadDer); + } + if constraint.len() != 8 && constraint.len() != 32 { + return Err(Error::BadDer); + } + + // an IPv4 address never matches an IPv6 constraint, and vice versa. + if name.len() * 2 != constraint.len() { + return Ok(false); + } + + let (constraint_address, constraint_mask) = constraint.read_all(Error::BadDer, |value| { + let address = value.read_bytes(constraint.len() / 2).unwrap(); + let mask = value.read_bytes(constraint.len() / 2).unwrap(); + Ok((address, mask)) + })?; + + let mut name = untrusted::Reader::new(name); + let mut constraint_address = untrusted::Reader::new(constraint_address); + let mut constraint_mask = untrusted::Reader::new(constraint_mask); + loop { + let name_byte = name.read_byte().unwrap(); + let constraint_address_byte = constraint_address.read_byte().unwrap(); + let constraint_mask_byte = constraint_mask.read_byte().unwrap(); + if ((name_byte ^ constraint_address_byte) & constraint_mask_byte) != 0 { + return Ok(false); + } + if name.at_end() { + break; + } + } + + Ok(true) +} diff --git a/src/name/verify.rs b/src/name/verify.rs new file mode 100644 index 0000000..6082c19 --- /dev/null +++ b/src/name/verify.rs @@ -0,0 +1,328 @@ +// 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 super::{ + dns_name::{self, DnsNameRef}, + ip_address, +}; +use crate::{ + cert::{Cert, EndEntityOrCa}, + der, Error, +}; + +pub fn verify_cert_dns_name( + cert: &crate::EndEntityCert, + dns_name: DnsNameRef, +) -> Result<(), Error> { + let cert = cert.inner(); + let dns_name = untrusted::Input::from(dns_name.as_ref().as_ref()); + iterate_names( + cert.subject, + cert.subject_alt_name, + Err(Error::CertNotValidForName), + &|name| { + match name { + GeneralName::DnsName(presented_id) => { + match dns_name::presented_id_matches_reference_id(presented_id, dns_name) { + Some(true) => { + return NameIteration::Stop(Ok(())); + } + Some(false) => (), + None => { + return NameIteration::Stop(Err(Error::BadDer)); + } + } + } + _ => (), + } + NameIteration::KeepGoing + }, + ) +} + +// https://tools.ietf.org/html/rfc5280#section-4.2.1.10 +pub fn check_name_constraints( + input: Option<&mut untrusted::Reader>, + subordinate_certs: &Cert, +) -> Result<(), Error> { + let input = match input { + Some(input) => input, + None => { + return Ok(()); + } + }; + + fn parse_subtrees<'b>( + inner: &mut untrusted::Reader<'b>, + subtrees_tag: der::Tag, + ) -> Result>, Error> { + if !inner.peek(subtrees_tag.into()) { + return Ok(None); + } + let subtrees = der::nested(inner, subtrees_tag, Error::BadDer, |tagged| { + der::expect_tag_and_get_value(tagged, der::Tag::Sequence) + })?; + Ok(Some(subtrees)) + } + + let permitted_subtrees = parse_subtrees(input, der::Tag::ContextSpecificConstructed0)?; + let excluded_subtrees = parse_subtrees(input, der::Tag::ContextSpecificConstructed1)?; + + let mut child = subordinate_certs; + loop { + iterate_names(child.subject, child.subject_alt_name, Ok(()), &|name| { + check_presented_id_conforms_to_constraints(name, permitted_subtrees, excluded_subtrees) + })?; + + child = match child.ee_or_ca { + EndEntityOrCa::Ca(child_cert) => child_cert, + EndEntityOrCa::EndEntity => { + break; + } + }; + } + + Ok(()) +} + +fn check_presented_id_conforms_to_constraints( + name: GeneralName, + permitted_subtrees: Option, + excluded_subtrees: Option, +) -> NameIteration { + match check_presented_id_conforms_to_constraints_in_subtree( + name, + Subtrees::PermittedSubtrees, + permitted_subtrees, + ) { + stop @ NameIteration::Stop(..) => { + return stop; + } + NameIteration::KeepGoing => (), + }; + + check_presented_id_conforms_to_constraints_in_subtree( + name, + Subtrees::ExcludedSubtrees, + excluded_subtrees, + ) +} + +#[derive(Clone, Copy)] +enum Subtrees { + PermittedSubtrees, + ExcludedSubtrees, +} + +fn check_presented_id_conforms_to_constraints_in_subtree( + name: GeneralName, + subtrees: Subtrees, + constraints: Option, +) -> NameIteration { + let mut constraints = match constraints { + Some(constraints) => untrusted::Reader::new(constraints), + None => { + return NameIteration::KeepGoing; + } + }; + + let mut has_permitted_subtrees_match = false; + let mut has_permitted_subtrees_mismatch = false; + + loop { + // http://tools.ietf.org/html/rfc5280#section-4.2.1.10: "Within this + // profile, the minimum and maximum fields are not used with any name + // forms, thus, the minimum MUST be zero, and maximum MUST be absent." + // + // Since the default value isn't allowed to be encoded according to the + // DER encoding rules for DEFAULT, this is equivalent to saying that + // neither minimum or maximum must be encoded. + fn general_subtree<'b>( + input: &mut untrusted::Reader<'b>, + ) -> Result, Error> { + let general_subtree = der::expect_tag_and_get_value(input, der::Tag::Sequence)?; + general_subtree.read_all(Error::BadDer, |subtree| general_name(subtree)) + } + + let base = match general_subtree(&mut constraints) { + Ok(base) => base, + Err(err) => { + return NameIteration::Stop(Err(err)); + } + }; + + let matches = match (name, base) { + (GeneralName::DnsName(name), GeneralName::DnsName(base)) => { + dns_name::presented_id_matches_constraint(name, base).ok_or(Error::BadDer) + } + + (GeneralName::DirectoryName(name), GeneralName::DirectoryName(base)) => Ok( + presented_directory_name_matches_constraint(name, base, subtrees), + ), + + (GeneralName::IpAddress(name), GeneralName::IpAddress(base)) => { + ip_address::presented_id_matches_constraint(name, base) + } + + // RFC 4280 says "If a name constraints extension that is marked as + // critical imposes constraints on a particular name form, and an + // instance of that name form appears in the subject field or + // subjectAltName extension of a subsequent certificate, then the + // application MUST either process the constraint or reject the + // certificate." Later, the CABForum agreed to support non-critical + // constraints, so it is important to reject the cert without + // considering whether the name constraint it critical. + (GeneralName::Unsupported(name_tag), GeneralName::Unsupported(base_tag)) + if name_tag == base_tag => + { + Err(Error::NameConstraintViolation) + } + + _ => Ok(false), + }; + + match (subtrees, matches) { + (Subtrees::PermittedSubtrees, Ok(true)) => { + has_permitted_subtrees_match = true; + } + + (Subtrees::PermittedSubtrees, Ok(false)) => { + has_permitted_subtrees_mismatch = true; + } + + (Subtrees::ExcludedSubtrees, Ok(true)) => { + return NameIteration::Stop(Err(Error::NameConstraintViolation)); + } + + (Subtrees::ExcludedSubtrees, Ok(false)) => (), + + (_, Err(err)) => { + return NameIteration::Stop(Err(err)); + } + } + + if constraints.at_end() { + break; + } + } + + if has_permitted_subtrees_mismatch && !has_permitted_subtrees_match { + // If there was any entry of the given type in permittedSubtrees, then + // it required that at least one of them must match. Since none of them + // did, we have a failure. + NameIteration::Stop(Err(Error::NameConstraintViolation)) + } else { + NameIteration::KeepGoing + } +} + +// TODO: document this. +fn presented_directory_name_matches_constraint( + name: untrusted::Input, + constraint: untrusted::Input, + subtrees: Subtrees, +) -> bool { + match subtrees { + Subtrees::PermittedSubtrees => name == constraint, + Subtrees::ExcludedSubtrees => true, + } +} + +#[derive(Clone, Copy)] +enum NameIteration { + KeepGoing, + Stop(Result<(), Error>), +} + +fn iterate_names( + subject: untrusted::Input, + subject_alt_name: Option, + result_if_never_stopped_early: Result<(), Error>, + f: &dyn Fn(GeneralName) -> NameIteration, +) -> Result<(), Error> { + match subject_alt_name { + Some(subject_alt_name) => { + let mut subject_alt_name = untrusted::Reader::new(subject_alt_name); + // https://bugzilla.mozilla.org/show_bug.cgi?id=1143085: An empty + // subjectAltName is not legal, but some certificates have an empty + // subjectAltName. Since we don't support CN-IDs, the certificate + // will be rejected either way, but checking `at_end` before + // attempting to parse the first entry allows us to return a better + // error code. + while !subject_alt_name.at_end() { + let name = general_name(&mut subject_alt_name)?; + match f(name) { + NameIteration::Stop(result) => { + return result; + } + NameIteration::KeepGoing => (), + } + } + } + None => (), + } + + match f(GeneralName::DirectoryName(subject)) { + NameIteration::Stop(result) => result, + NameIteration::KeepGoing => result_if_never_stopped_early, + } +} + +// It is *not* valid to derive `Eq`, `PartialEq, etc. for this type. In +// particular, for the types of `GeneralName`s that we don't understand, we +// don't even store the value. Also, the meaning of a `GeneralName` in a name +// constraint is different than the meaning of the identically-represented +// `GeneralName` in other contexts. +#[derive(Clone, Copy)] +enum GeneralName<'a> { + DnsName(untrusted::Input<'a>), + DirectoryName(untrusted::Input<'a>), + IpAddress(untrusted::Input<'a>), + + // The value is the `tag & ~(der::CONTEXT_SPECIFIC | der::CONSTRUCTED)` so + // that the name constraint checking matches tags regardless of whether + // those bits are set. + Unsupported(u8), +} + +fn general_name<'a>(input: &mut untrusted::Reader<'a>) -> Result, Error> { + use ring::io::der::{CONSTRUCTED, CONTEXT_SPECIFIC}; + #[allow(clippy::identity_op)] + const OTHER_NAME_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED | 0; + const RFC822_NAME_TAG: u8 = CONTEXT_SPECIFIC | 1; + const DNS_NAME_TAG: u8 = CONTEXT_SPECIFIC | 2; + const X400_ADDRESS_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED | 3; + const DIRECTORY_NAME_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED | 4; + const EDI_PARTY_NAME_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED | 5; + const UNIFORM_RESOURCE_IDENTIFIER_TAG: u8 = CONTEXT_SPECIFIC | 6; + const IP_ADDRESS_TAG: u8 = CONTEXT_SPECIFIC | 7; + const REGISTERED_ID_TAG: u8 = CONTEXT_SPECIFIC | 8; + + let (tag, value) = der::read_tag_and_get_value(input)?; + let name = match tag { + DNS_NAME_TAG => GeneralName::DnsName(value), + DIRECTORY_NAME_TAG => GeneralName::DirectoryName(value), + IP_ADDRESS_TAG => GeneralName::IpAddress(value), + + OTHER_NAME_TAG + | RFC822_NAME_TAG + | X400_ADDRESS_TAG + | EDI_PARTY_NAME_TAG + | UNIFORM_RESOURCE_IDENTIFIER_TAG + | REGISTERED_ID_TAG => GeneralName::Unsupported(tag & !(CONTEXT_SPECIFIC | CONSTRUCTED)), + + _ => return Err(Error::BadDer), + }; + Ok(name) +} diff --git a/src/signed_data.rs b/src/signed_data.rs new file mode 100644 index 0000000..834f907 --- /dev/null +++ b/src/signed_data.rs @@ -0,0 +1,788 @@ +// 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 { + 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, + data: Vec, + algorithm: Vec, + signature: Vec, + } + + 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 { + // 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. + ]; +} diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..0457cae --- /dev/null +++ b/src/time.rs @@ -0,0 +1,65 @@ +// Copyright 2015-2016 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. + +//! Conversions into the library's time type. + +/// The time type. +/// +/// Internally this is merely a UNIX timestamp: a count of non-leap +/// seconds since the start of 1970. This type exists to assist +/// unit-of-measure correctness. +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub struct Time(u64); + +impl Time { + /// Create a `webpki::Time` from a unix timestamp. + /// + /// It is usually better to use the less error-prone + /// `webpki::Time::try_from(time: std::time::SystemTime)` instead when + /// `std::time::SystemTime` is available (when `#![no_std]` isn't being + /// used). + #[allow(clippy::must_use_candidate)] + pub fn from_seconds_since_unix_epoch(secs: u64) -> Self { + Self(secs) + } +} + +#[cfg(feature = "std")] +impl core::convert::TryFrom for Time { + type Error = std::time::SystemTimeError; + + /// Create a `webpki::Time` from a `std::time::SystemTime`. + /// + /// # Example: + /// + /// Construct a `webpki::Time` from the current system time: + /// + /// ``` + /// # extern crate ring; + /// # extern crate webpki; + /// # + /// #![cfg(feature = "std")] + /// use std::{convert::TryFrom, time::{SystemTime, SystemTimeError}}; + /// + /// # fn foo() -> Result<(), SystemTimeError> { + /// let time = webpki::Time::try_from(SystemTime::now())?; + /// # Ok(()) + /// # } + /// ``` + fn try_from(value: std::time::SystemTime) -> Result { + value + .duration_since(std::time::UNIX_EPOCH) + .map(|d| Self::from_seconds_since_unix_epoch(d.as_secs())) + } +} diff --git a/src/trust_anchor.rs b/src/trust_anchor.rs new file mode 100644 index 0000000..0ac2806 --- /dev/null +++ b/src/trust_anchor.rs @@ -0,0 +1,119 @@ +use crate::cert::{certificate_serial_number, Cert}; +use crate::{ + cert::{parse_cert_internal, EndEntityOrCa}, + der, Error, +}; + +/// A trust anchor (a.k.a. root CA). +/// +/// Traditionally, certificate verification libraries have represented trust +/// anchors as full X.509 root certificates. However, those certificates +/// contain a lot more data than is needed for verifying certificates. The +/// `TrustAnchor` representation allows an application to store just the +/// essential elements of trust anchors. The `webpki::trust_anchor_util` module +/// provides functions for converting X.509 certificates to to the minimized +/// `TrustAnchor` representation, either at runtime or in a build script. +#[derive(Debug)] +pub struct TrustAnchor<'a> { + /// The value of the `subject` field of the trust anchor. + pub subject: &'a [u8], + + /// The value of the `subjectPublicKeyInfo` field of the trust anchor. + pub spki: &'a [u8], + + /// The value of a DER-encoded NameConstraints, containing name + /// constraints to apply to the trust anchor, if any. + pub name_constraints: Option<&'a [u8]>, +} + +/// Trust anchors which may be used for authenticating servers. +#[derive(Debug)] +pub struct TlsServerTrustAnchors<'a>(pub &'a [TrustAnchor<'a>]); + +/// Trust anchors which may be used for authenticating clients. +#[derive(Debug)] +pub struct TlsClientTrustAnchors<'a>(pub &'a [TrustAnchor<'a>]); + +impl<'a> TrustAnchor<'a> { + /// Interprets the given DER-encoded certificate as a `TrustAnchor`. The + /// certificate is not validated. In particular, there is no check that the + /// certificate is self-signed or even that the certificate has the cA basic + /// constraint. + pub fn try_from_cert_der(cert_der: &'a [u8]) -> Result { + let cert_der = untrusted::Input::from(cert_der); + + // XXX: `EndEntityOrCA::EndEntity` is used instead of `EndEntityOrCA::CA` + // because we don't have a reference to a child cert, which is needed for + // `EndEntityOrCA::CA`. For this purpose, it doesn't matter. + // + // v1 certificates will result in `Error::BadDER` because `parse_cert` will + // expect a version field that isn't there. In that case, try to parse the + // certificate using a special parser for v1 certificates. Notably, that + // parser doesn't allow extensions, so there's no need to worry about + // embedded name constraints in a v1 certificate. + match parse_cert_internal( + cert_der, + EndEntityOrCa::EndEntity, + possibly_invalid_certificate_serial_number, + ) { + Ok(cert) => Ok(Self::from(cert)), + Err(Error::UnsupportedCertVersion) => parse_cert_v1(cert_der).or(Err(Error::BadDer)), + Err(err) => Err(err), + } + } +} + +fn possibly_invalid_certificate_serial_number(input: &mut untrusted::Reader) -> Result<(), Error> { + // https://tools.ietf.org/html/rfc5280#section-4.1.2.2: + // * Conforming CAs MUST NOT use serialNumber values longer than 20 octets." + // * "The serial number MUST be a positive integer [...]" + // + // However, we don't enforce these constraints on trust anchors, as there + // are widely-deployed trust anchors that violate these constraints. + skip(input, der::Tag::Integer) +} + +impl<'a> From> for TrustAnchor<'a> { + fn from(cert: Cert<'a>) -> Self { + Self { + subject: cert.subject.as_slice_less_safe(), + spki: cert.spki.value().as_slice_less_safe(), + name_constraints: cert.name_constraints.map(|nc| nc.as_slice_less_safe()), + } + } +} + +/// Parses a v1 certificate directly into a TrustAnchor. +fn parse_cert_v1(cert_der: untrusted::Input) -> Result { + // X.509 Certificate: https://tools.ietf.org/html/rfc5280#section-4.1. + cert_der.read_all(Error::BadDer, |cert_der| { + der::nested(cert_der, der::Tag::Sequence, Error::BadDer, |cert_der| { + let anchor = der::nested(cert_der, der::Tag::Sequence, Error::BadDer, |tbs| { + // The version number field does not appear in v1 certificates. + certificate_serial_number(tbs)?; + + skip(tbs, der::Tag::Sequence)?; // signature. + skip(tbs, der::Tag::Sequence)?; // issuer. + skip(tbs, der::Tag::Sequence)?; // validity. + let subject = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; + let spki = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; + + Ok(TrustAnchor { + subject: subject.as_slice_less_safe(), + spki: spki.as_slice_less_safe(), + name_constraints: None, + }) + }); + + // read and discard signatureAlgorithm + signature + skip(cert_der, der::Tag::Sequence)?; + skip(cert_der, der::Tag::BitString)?; + + anchor + }) + }) +} + +fn skip(input: &mut untrusted::Reader, tag: der::Tag) -> Result<(), Error> { + der::expect_tag_and_get_value(input, tag).map(|_| ()) +} diff --git a/src/verify_cert.rs b/src/verify_cert.rs new file mode 100644 index 0000000..c68e6cf --- /dev/null +++ b/src/verify_cert.rs @@ -0,0 +1,352 @@ +// 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::{ + cert::{self, Cert, EndEntityOrCa}, + der, name, signed_data, time, Error, SignatureAlgorithm, TrustAnchor, +}; + +pub fn build_chain( + required_eku_if_present: KeyPurposeId, + supported_sig_algs: &[&SignatureAlgorithm], + trust_anchors: &[TrustAnchor], + intermediate_certs: &[&[u8]], + cert: &Cert, + time: time::Time, + sub_ca_count: usize, +) -> Result<(), Error> { + let used_as_ca = used_as_ca(&cert.ee_or_ca); + + check_issuer_independent_properties( + cert, + time, + used_as_ca, + sub_ca_count, + required_eku_if_present, + )?; + + // TODO: HPKP checks. + + match used_as_ca { + UsedAsCa::Yes => { + const MAX_SUB_CA_COUNT: usize = 6; + + if sub_ca_count >= MAX_SUB_CA_COUNT { + return Err(Error::UnknownIssuer); + } + } + UsedAsCa::No => { + assert_eq!(0, sub_ca_count); + } + } + + // TODO: revocation. + + match loop_while_non_fatal_error(trust_anchors, |trust_anchor: &TrustAnchor| { + let trust_anchor_subject = untrusted::Input::from(trust_anchor.subject); + if cert.issuer != trust_anchor_subject { + return Err(Error::UnknownIssuer); + } + + let name_constraints = trust_anchor.name_constraints.map(untrusted::Input::from); + + untrusted::read_all_optional(name_constraints, Error::BadDer, |value| { + name::check_name_constraints(value, &cert) + })?; + + let trust_anchor_spki = untrusted::Input::from(trust_anchor.spki); + + // TODO: check_distrust(trust_anchor_subject, trust_anchor_spki)?; + + check_signatures(supported_sig_algs, cert, trust_anchor_spki)?; + + Ok(()) + }) { + Ok(()) => { + return Ok(()); + } + Err(..) => { + // If the error is not fatal, then keep going. + } + } + + loop_while_non_fatal_error(intermediate_certs, |cert_der| { + let potential_issuer = + cert::parse_cert(untrusted::Input::from(*cert_der), EndEntityOrCa::Ca(&cert))?; + + if potential_issuer.subject != cert.issuer { + return Err(Error::UnknownIssuer); + } + + // Prevent loops; see RFC 4158 section 5.2. + let mut prev = cert; + loop { + if potential_issuer.spki.value() == prev.spki.value() + && potential_issuer.subject == prev.subject + { + return Err(Error::UnknownIssuer); + } + match &prev.ee_or_ca { + EndEntityOrCa::EndEntity => { + break; + } + EndEntityOrCa::Ca(child_cert) => { + prev = child_cert; + } + } + } + + untrusted::read_all_optional(potential_issuer.name_constraints, Error::BadDer, |value| { + name::check_name_constraints(value, &cert) + })?; + + let next_sub_ca_count = match used_as_ca { + UsedAsCa::No => sub_ca_count, + UsedAsCa::Yes => sub_ca_count + 1, + }; + + build_chain( + required_eku_if_present, + supported_sig_algs, + trust_anchors, + intermediate_certs, + &potential_issuer, + time, + next_sub_ca_count, + ) + }) +} + +fn check_signatures( + supported_sig_algs: &[&SignatureAlgorithm], + cert_chain: &Cert, + trust_anchor_key: untrusted::Input, +) -> Result<(), Error> { + let mut spki_value = trust_anchor_key; + let mut cert = cert_chain; + loop { + signed_data::verify_signed_data(supported_sig_algs, spki_value, &cert.signed_data)?; + + // TODO: check revocation + + match &cert.ee_or_ca { + EndEntityOrCa::Ca(child_cert) => { + spki_value = cert.spki.value(); + cert = child_cert; + } + EndEntityOrCa::EndEntity => { + break; + } + } + } + + Ok(()) +} + +fn check_issuer_independent_properties( + cert: &Cert, + time: time::Time, + used_as_ca: UsedAsCa, + sub_ca_count: usize, + required_eku_if_present: KeyPurposeId, +) -> Result<(), Error> { + // TODO: check_distrust(trust_anchor_subject, trust_anchor_spki)?; + // TODO: Check signature algorithm like mozilla::pkix. + // TODO: Check SPKI like mozilla::pkix. + // TODO: check for active distrust like mozilla::pkix. + + // See the comment in `remember_extension` for why we don't check the + // KeyUsage extension. + + cert.validity + .read_all(Error::BadDer, |value| check_validity(value, time))?; + untrusted::read_all_optional(cert.basic_constraints, Error::BadDer, |value| { + check_basic_constraints(value, used_as_ca, sub_ca_count) + })?; + untrusted::read_all_optional(cert.eku, Error::BadDer, |value| { + check_eku(value, required_eku_if_present) + })?; + + Ok(()) +} + +// https://tools.ietf.org/html/rfc5280#section-4.1.2.5 +fn check_validity(input: &mut untrusted::Reader, time: time::Time) -> Result<(), Error> { + let not_before = der::time_choice(input)?; + let not_after = der::time_choice(input)?; + + if not_before > not_after { + return Err(Error::InvalidCertValidity); + } + if time < not_before { + return Err(Error::CertNotValidYet); + } + if time > not_after { + return Err(Error::CertExpired); + } + + // TODO: mozilla::pkix allows the TrustDomain to check not_before and + // not_after, to enforce things like a maximum validity period. We should + // do something similar. + + Ok(()) +} + +#[derive(Clone, Copy)] +enum UsedAsCa { + Yes, + No, +} + +fn used_as_ca(ee_or_ca: &EndEntityOrCa) -> UsedAsCa { + match ee_or_ca { + EndEntityOrCa::EndEntity => UsedAsCa::No, + EndEntityOrCa::Ca(..) => UsedAsCa::Yes, + } +} + +// https://tools.ietf.org/html/rfc5280#section-4.2.1.9 +fn check_basic_constraints( + input: Option<&mut untrusted::Reader>, + used_as_ca: UsedAsCa, + sub_ca_count: usize, +) -> Result<(), Error> { + let (is_ca, path_len_constraint) = match input { + Some(input) => { + let is_ca = der::optional_boolean(input)?; + + // https://bugzilla.mozilla.org/show_bug.cgi?id=985025: RFC 5280 + // says that a certificate must not have pathLenConstraint unless + // it is a CA certificate, but some real-world end-entity + // certificates have pathLenConstraint. + let path_len_constraint = if !input.at_end() { + let value = der::small_nonnegative_integer(input)?; + Some(usize::from(value)) + } else { + None + }; + + (is_ca, path_len_constraint) + } + None => (false, None), + }; + + match (used_as_ca, is_ca, path_len_constraint) { + (UsedAsCa::No, true, _) => Err(Error::CaUsedAsEndEntity), + (UsedAsCa::Yes, false, _) => Err(Error::EndEntityUsedAsCa), + (UsedAsCa::Yes, true, Some(len)) if sub_ca_count > len => { + Err(Error::PathLenConstraintViolated) + } + _ => Ok(()), + } +} + +#[derive(Clone, Copy)] +pub struct KeyPurposeId { + oid_value: untrusted::Input<'static>, +} + +// id-pkix OBJECT IDENTIFIER ::= { 1 3 6 1 5 5 7 } +// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } + +// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } +#[allow(clippy::identity_op)] // TODO: Make this clearer +pub static EKU_SERVER_AUTH: KeyPurposeId = KeyPurposeId { + oid_value: untrusted::Input::from(&[(40 * 1) + 3, 6, 1, 5, 5, 7, 3, 1]), +}; + +// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } +#[allow(clippy::identity_op)] // TODO: Make this clearer +pub static EKU_CLIENT_AUTH: KeyPurposeId = KeyPurposeId { + oid_value: untrusted::Input::from(&[(40 * 1) + 3, 6, 1, 5, 5, 7, 3, 2]), +}; + +// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } +#[allow(clippy::identity_op)] // TODO: Make this clearer +pub static EKU_OCSP_SIGNING: KeyPurposeId = KeyPurposeId { + oid_value: untrusted::Input::from(&[(40 * 1) + 3, 6, 1, 5, 5, 7, 3, 9]), +}; + +// https://tools.ietf.org/html/rfc5280#section-4.2.1.12 +// +// Notable Differences from RFC 5280: +// +// * We follow the convention established by Microsoft's implementation and +// mozilla::pkix of treating the EKU extension in a CA certificate as a +// restriction on the allowable EKUs for certificates issued by that CA. RFC +// 5280 doesn't prescribe any meaning to the EKU extension when a certificate +// is being used as a CA certificate. +// +// * We do not recognize anyExtendedKeyUsage. NSS and mozilla::pkix do not +// recognize it either. +// +// * We treat id-Netscape-stepUp as being equivalent to id-kp-serverAuth in CA +// certificates (only). Comodo has issued certificates that require this +// behavior that don't expire until June 2020. See https://bugzilla.mozilla.org/show_bug.cgi?id=982292. +fn check_eku( + input: Option<&mut untrusted::Reader>, + required_eku_if_present: KeyPurposeId, +) -> Result<(), Error> { + match input { + Some(input) => { + loop { + let value = der::expect_tag_and_get_value(input, der::Tag::OID)?; + if value == required_eku_if_present.oid_value { + input.skip_to_end(); + break; + } + if input.at_end() { + return Err(Error::RequiredEkuNotFound); + } + } + Ok(()) + } + None => { + // http://tools.ietf.org/html/rfc6960#section-4.2.2.2: + // "OCSP signing delegation SHALL be designated by the inclusion of + // id-kp-OCSPSigning in an extended key usage certificate extension + // included in the OCSP response signer's certificate." + // + // A missing EKU extension generally means "any EKU", but it is + // important that id-kp-OCSPSigning is explicit so that a normal + // end-entity certificate isn't able to sign trusted OCSP responses + // for itself or for other certificates issued by its issuing CA. + if required_eku_if_present.oid_value == EKU_OCSP_SIGNING.oid_value { + return Err(Error::RequiredEkuNotFound); + } + + Ok(()) + } + } +} + +fn loop_while_non_fatal_error( + values: V, + f: impl Fn(V::Item) -> Result<(), Error>, +) -> Result<(), Error> +where + V: IntoIterator, +{ + for v in values { + match f(v) { + Ok(()) => { + return Ok(()); + } + Err(..) => { + // If the error is not fatal, then keep going. + } + } + } + Err(Error::UnknownIssuer) +} diff --git a/tests/dns_name_tests.rs b/tests/dns_name_tests.rs new file mode 100644 index 0000000..b3a3adc --- /dev/null +++ b/tests/dns_name_tests.rs @@ -0,0 +1,408 @@ +// Copyright 2014-2017 Brian Smith. + +// (name, is_valid) +static DNS_NAME_VALIDITY: &[(&[u8], bool)] = &[ + (b"a", true), + (b"a.b", true), + (b"a.b.c", true), + (b"a.b.c.d", true), + + // Hyphens, one component. + (b"-", false), + (b"-a", false), + (b"a-", false), + (b"a-b", true), + + // Hyphens, last component. + (b"a.-", false), + (b"a.-a", false), + (b"a.a-", false), + (b"a.a-b", true), + + // Hyphens, not last component. + (b"-.a", false), + (b"-a.a", false), + (b"a-.a", false), + (b"a-b.a", true), + + // Underscores, one component. + (b"_", true), // TODO: Perhaps this should be rejected for '_' being sole character?. + (b"_a", true), // TODO: Perhaps this should be rejected for '_' being 1st? + (b"a_", true), + (b"a_b", true), + + // Underscores, last component. + (b"a._", true), // TODO: Perhaps this should be rejected for '_' being sole character?. + (b"a._a", true), // TODO: Perhaps this should be rejected for '_' being 1st? + (b"a.a_", true), + (b"a.a_b", true), + + // Underscores, not last component. + (b"_.a", true), // TODO: Perhaps this should be rejected for '_' being sole character?. + (b"_a.a", true), + (b"a_.a", true), + (b"a_b.a", true), + + // empty labels + (b"", false), + (b".", false), + (b"a", true), + (b".a", false), + (b".a.b", false), + (b"..a", false), + (b"a..b", false), + (b"a...b", false), + (b"a..b.c", false), + (b"a.b..c", false), + (b".a.b.c.", false), + + // absolute names + (b"a.", true), + (b"a.b.", true), + (b"a.b.c.", true), + + // absolute names with empty label at end + (b"a..", false), + (b"a.b..", false), + (b"a.b.c..", false), + (b"a...", false), + + // Punycode + (b"xn--", false), + (b"xn--.", false), + (b"xn--.a", false), + (b"a.xn--", false), + (b"a.xn--.", false), + (b"a.xn--.b", false), + (b"a.xn--.b", false), + (b"a.xn--\0.b", false), + (b"a.xn--a.b", true), + (b"xn--a", true), + (b"a.xn--a", true), + (b"a.xn--a.a", true), + (b"\xc4\x95.com", false), // UTF-8 ĕ + (b"xn--jea.com", true), // punycode ĕ + (b"xn--\xc4\x95.com", false), // UTF-8 ĕ, malformed punycode + UTF-8 mashup + + // Surprising punycode + (b"xn--google.com", true), // 䕮䕵䕶䕱.com + (b"xn--citibank.com", true), // 岍岊岊岅岉岎.com + (b"xn--cnn.com", true), // 䁾.com + (b"a.xn--cnn", true), // a.䁾 + (b"a.xn--cnn.com", true), // a.䁾.com + + (b"1.2.3.4", false), // IPv4 address + (b"1::2", false), // IPV6 address + + // whitespace not allowed anywhere. + (b" ", false), + (b" a", false), + (b"a ", false), + (b"a b", false), + (b"a.b 1", false), + (b"a\t", false), + + // Nulls not allowed + (b"\0", false), + (b"a\0", false), + (b"example.org\0.example.com", false), // Hi Moxie! + (b"\0a", false), + (b"xn--\0", false), + + // Allowed character set + (b"a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z", true), + (b"A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z", true), + (b"0.1.2.3.4.5.6.7.8.9.a", true), // "a" needed to avoid numeric last label + (b"a-b", true), // hyphen (a label cannot start or end with a hyphen) + + // An invalid character in various positions + (b"!", false), + (b"!a", false), + (b"a!", false), + (b"a!b", false), + (b"a.!", false), + (b"a.a!", false), + (b"a.!a", false), + (b"a.a!a", false), + (b"a.!a.a", false), + (b"a.a!.a", false), + (b"a.a!a.a", false), + + // Various other invalid characters + (b"a!", false), + (b"a@", false), + (b"a#", false), + (b"a$", false), + (b"a%", false), + (b"a^", false), + (b"a&", false), + (b"a*", false), + (b"a(", false), + (b"a)", false), + + // last label can't be fully numeric + (b"1", false), + (b"a.1", false), + + // other labels can be fully numeric + (b"1.a", true), + (b"1.2.a", true), + (b"1.2.3.a", true), + + // last label can be *partly* numeric + (b"1a", true), + (b"1.1a", true), + (b"1-1", true), + (b"a.1-1", true), + (b"a.1-a", true), + + // labels cannot start with a hyphen + (b"-", false), + (b"-1", false), + + // labels cannot end with a hyphen + (b"1-", false), + (b"1-.a", false), + (b"a-", false), + (b"a-.a", false), + (b"a.1-.a", false), + (b"a.a-.a", false), + + // labels can contain a hyphen in the middle + (b"a-b", true), + (b"1-2", true), + (b"a.a-1", true), + + // multiple consecutive hyphens allowed + (b"a--1", true), + (b"1---a", true), + (b"a-----------------b", true), + + // Wildcard specifications are not valid reference names. + (b"*.a", false), + (b"a*", false), + (b"a*.", false), + (b"a*.a", false), + (b"a*.a.", false), + (b"*.a.b", false), + (b"*.a.b.", false), + (b"a*.b.c", false), + (b"*.a.b.c", false), + (b"a*.b.c.d", false), + + // Multiple wildcards. + (b"a**.b.c", false), + (b"a*b*.c.d", false), + (b"a*.b*.c", false), + + // Wildcards not in the first label. + (b"a.*", false), + (b"a.*.b", false), + (b"a.b.*", false), + (b"a.b*.c", false), + (b"*.b*.c", false), + (b".*.a.b", false), + (b".a*.b.c", false), + + // Wildcards not at the end of the first label. + (b"*a.b.c", false), + (b"a*b.c.d", false), + + // Wildcards and IDNA prefix. + (b"x*.a.b", false), + (b"xn*.a.b", false), + (b"xn-*.a.b", false), + (b"xn--*.a.b", false), + (b"xn--w*.a.b", false), + + // Redacted labels from RFC6962bis draft 4 + // https://tools.ietf.org/html/draft-ietf-trans-rfc6962-bis-04#section-3.2.2 + (b"(PRIVATE).foo", false), + + // maximum label length is 63 characters + (b"123456789012345678901234567890123456789012345678901234567890abc", true), + (b"123456789012345678901234567890123456789012345678901234567890abcd", false), + + // maximum total length is 253 characters + (b"12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.123456789012345678901234567890123456789012345678a", + true), + (b"12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.1234567890123456789012345678901234567890123456789a", + false), +]; + +// (IP address, is valid DNS name). The comments here refer to the validity of +// the string as an IP address, not as a DNS name validity. +static IP_ADDRESS_DNS_VALIDITY: &[(&[u8], bool)] = &[ + (b"", false), + (b"1", false), + (b"1.2", false), + (b"1.2.3", false), + (b"1.2.3.4", false), + (b"1.2.3.4.5", false), + (b"1.2.3.4a", true), // a DNS name! + (b"a.2.3.4", false), // not even a DNS name! + (b"1::2", false), // IPv6 address + // Whitespace not allowed + (b" 1.2.3.4", false), + (b"1.2.3.4 ", false), + (b"1 .2.3.4", false), + (b"\n1.2.3.4", false), + (b"1.2.3.4\n", false), + // Nulls not allowed + (b"\0", false), + (b"\01.2.3.4", false), + (b"1.2.3.4\0", false), + (b"1.2.3.4\0.5", false), + // Range + (b"0.0.0.0", false), + (b"255.255.255.255", false), + (b"256.0.0.0", false), + (b"0.256.0.0", false), + (b"0.0.256.0", false), + (b"0.0.0.256", false), + (b"999.0.0.0", false), + (b"9999999999999999999.0.0.0", false), + // All digits allowed + (b"0.1.2.3", false), + (b"4.5.6.7", false), + (b"8.9.0.1", false), + // Leading zeros not allowed + (b"01.2.3.4", false), + (b"001.2.3.4", false), + (b"00000000001.2.3.4", false), + (b"010.2.3.4", false), + (b"1.02.3.4", false), + (b"1.2.03.4", false), + (b"1.2.3.04", false), + // Empty components + (b".2.3.4", false), + (b"1..3.4", false), + (b"1.2..4", false), + (b"1.2.3.", false), + // Too many components + (b"1.2.3.4.5", false), + (b"1.2.3.4.5.6", false), + (b"0.1.2.3.4", false), + (b"1.2.3.4.0", false), + // Leading/trailing dot + (b".1.2.3.4", false), + (b"1.2.3.4.", false), + // Other common forms of IPv4 address + // http://en.wikipedia.org/wiki/IPv4#Address_representations + (b"192.0.2.235", false), // dotted decimal (control value) + (b"0xC0.0x00.0x02.0xEB", true), // dotted hex - actually a DNS name! + (b"0301.0000.0002.0353", false), // dotted octal + (b"0xC00002EB", true), // non-dotted hex, actually a DNS name! + (b"3221226219", false), // non-dotted decimal + (b"030000001353", false), // non-dotted octal + (b"192.0.0002.0xEB", true), // mixed, actually a DNS name! + (b"1234", false), + (b"1234:5678", false), + (b"1234:5678:9abc", false), + (b"1234:5678:9abc:def0", false), + (b"1234:5678:9abc:def0:1234:", false), + (b"1234:5678:9abc:def0:1234:5678:", false), + (b"1234:5678:9abc:def0:1234:5678:9abc:", false), + (b"1234:5678:9abc:def0:1234:5678:9abc:def0", false), + (b"1234:5678:9abc:def0:1234:5678:9abc:def0:", false), + (b":1234:5678:9abc:def0:1234:5678:9abc:def0", false), + (b"1234:5678:9abc:def0:1234:5678:9abc:def0:0000", false), + // Valid contractions + (b"::1", false), + (b"::1234", false), + (b"1234::", false), + (b"1234::5678", false), + (b"1234:5678::abcd", false), + (b"1234:5678:9abc:def0:1234:5678:9abc::", false), + // Contraction in full IPv6 addresses not allowed + (b"::1234:5678:9abc:def0:1234:5678:9abc:def0", false), // start + (b"1234:5678:9abc:def0:1234:5678:9abc:def0::", false), // end + (b"1234:5678::9abc:def0:1234:5678:9abc:def0", false), // interior + // Multiple contractions not allowed + (b"::1::", false), + (b"::1::2", false), + (b"1::2::", false), + // Colon madness! + (b":", false), + (b"::", false), + (b":::", false), + (b"::::", false), + (b":::1", false), + (b"::::1", false), + (b"1:::2", false), + (b"1::::2", false), + (b"1:2:::", false), + (b"1:2::::", false), + (b"::1234:", false), + (b":1234::", false), + (b"01234::", false), // too many digits, even if zero + (b"12345678::", false), // too many digits or missing colon + // uppercase + (b"ABCD:EFAB::", false), + // miXeD CAse + (b"aBcd:eFAb::", false), + // IPv4-style + (b"::2.3.4.5", false), + (b"1234::2.3.4.5", false), + (b"::abcd:2.3.4.5", false), + (b"1234:5678:9abc:def0:1234:5678:252.253.254.255", false), + (b"1234:5678:9abc:def0:1234::252.253.254.255", false), + (b"1234::252.253.254", false), + (b"::252.253.254", false), + (b"::252.253.254.300", false), + (b"1234::252.253.254.255:", false), + (b"1234::252.253.254.255:5678", false), + // Contractions that don't contract + (b"::1234:5678:9abc:def0:1234:5678:9abc:def0", false), + (b"1234:5678:9abc:def0:1234:5678:9abc:def0::", false), + (b"1234:5678:9abc:def0::1234:5678:9abc:def0", false), + (b"1234:5678:9abc:def0:1234:5678::252.253.254.255", false), + // With and without leading zeros + (b"::123", false), + (b"::0123", false), + (b"::012", false), + (b"::0012", false), + (b"::01", false), + (b"::001", false), + (b"::0001", false), + (b"::0", false), + (b"::00", false), + (b"::000", false), + (b"::0000", false), + (b"::01234", false), + (b"::00123", false), + (b"::000123", false), + // Trailing zero + (b"::12340", false), + // Whitespace + (b" 1234:5678:9abc:def0:1234:5678:9abc:def0", false), + (b"\t1234:5678:9abc:def0:1234:5678:9abc:def0", false), + (b"\t1234:5678:9abc:def0:1234:5678:9abc:def0\n", false), + (b"1234 :5678:9abc:def0:1234:5678:9abc:def0", false), + (b"1234: 5678:9abc:def0:1234:5678:9abc:def0", false), + (b":: 2.3.4.5", false), + (b"1234::252.253.254.255 ", false), + (b"1234::252.253.254.255\n", false), + (b"1234::252.253. 254.255", false), + // Nulls + (b"\0", false), + (b"::1\0:2", false), + (b"::1\0", false), + (b"::1.2.3.4\0", false), + (b"::1.2\02.3.4", false), +]; + +#[test] +fn dns_name_ref_try_from_ascii_test() { + for &(s, is_valid) in DNS_NAME_VALIDITY + .iter() + .chain(IP_ADDRESS_DNS_VALIDITY.iter()) + { + assert_eq!( + webpki::DnsNameRef::try_from_ascii(s).is_ok(), + is_valid, + "DnsNameRef::try_from_ascii_str failed for \"{:?}\"", + s + ); + } +} diff --git a/tests/ed25519/ca.der b/tests/ed25519/ca.der new file mode 100644 index 0000000000000000000000000000000000000000..9994daf19130227211ac586e2a0ad8f9cb04583b GIT binary patch literal 459 zcmXqLVmxlp#8|n2nTe5!Nkp^k?gF_7D#CZ)Z4Gay94a|&94b7v(c}>g=OpOho zTv}SK1@#`YB11%2{|C{Tm(sdscs<#^tjO+C`!nAw_Li&@U%lxuOTRKm&6?9RW?#{8gs@}x|Ap33vunzQ;n%CC#~nl>KV W&YG3^KjH#oqh1$7cIk@K=NSMWIf%0W literal 0 HcmV?d00001 diff --git a/tests/ed25519/ee.der b/tests/ed25519/ee.der new file mode 100644 index 0000000000000000000000000000000000000000..5181f7bc2cf9c5118295fa4c855e3c07e8403176 GIT binary patch literal 483 zcmXqLV!UtA#5i#QGZP~d6O#ag0V^A`c4~n^q#?HfCmVAp3!5;LW2m99fgp&(!NV1t zpPQ;1T#{IlYN%)+4-(|!k?_nbNiE7tEl~*1Oi9lyDOLz5sZ{VONih^M5CAD*=HU$T z_YYABaSaYJkQ3)Mv@|d@G&V3dFg7)c66ZBCG&C@fe^%MZf74)S3jub zT2K!%D>D3xk~7N@SC9S}A!o(+EKIX^p0xdC`)O|P_p@Xlw{?$MTy0PZ4g*NKtT3Eh@=O%S;Ax3}k_p%kr^^ zv55H2JsI2ZzbW*Zg{%SNZl{|KhXo#iLrzwiMZ!R=K_o`yZG<1^(pigAw@>?&tUlQ& z^BynEnT`y*muSgFR>`pV&AGmzf~nzF1%Iw$0B>)!J6I b=_j02Ihp4E@T&2q15r+Y+L?Z-uV4TGTLg=l literal 0 HcmV?d00001 diff --git a/tests/integration.rs b/tests/integration.rs new file mode 100644 index 0000000..598641d --- /dev/null +++ b/tests/integration.rs @@ -0,0 +1,91 @@ +// Copyright 2016 Joseph Birr-Pixton. +// +// 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 core::convert::TryFrom; +extern crate webpki; + +static ALL_SIGALGS: &[&webpki::SignatureAlgorithm] = &[ + &webpki::ECDSA_P256_SHA256, + &webpki::ECDSA_P256_SHA384, + &webpki::ECDSA_P384_SHA256, + &webpki::ECDSA_P384_SHA384, + &webpki::ED25519, + #[cfg(feature = "alloc")] + &webpki::RSA_PKCS1_2048_8192_SHA256, + #[cfg(feature = "alloc")] + &webpki::RSA_PKCS1_2048_8192_SHA384, + #[cfg(feature = "alloc")] + &webpki::RSA_PKCS1_2048_8192_SHA512, + #[cfg(feature = "alloc")] + &webpki::RSA_PKCS1_3072_8192_SHA384, +]; + +/* Checks we can verify netflix's cert chain. This is notable + * because they're rooted at a Verisign v1 root. */ +#[cfg(feature = "alloc")] +#[test] +pub fn netflix() { + let ee: &[u8] = include_bytes!("netflix/ee.der"); + let inter = include_bytes!("netflix/inter.der"); + let ca = include_bytes!("netflix/ca.der"); + + let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()]; + let anchors = webpki::TlsServerTrustAnchors(&anchors); + + #[allow(clippy::unreadable_literal)] // TODO: Make this clear. + let time = webpki::Time::from_seconds_since_unix_epoch(1492441716); + + let cert = webpki::EndEntityCert::try_from(ee).unwrap(); + assert_eq!( + Ok(()), + cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[inter], time) + ); +} + +#[test] +pub fn ed25519() { + let ee: &[u8] = include_bytes!("ed25519/ee.der"); + let ca = include_bytes!("ed25519/ca.der"); + + let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()]; + let anchors = webpki::TlsServerTrustAnchors(&anchors); + + #[allow(clippy::unreadable_literal)] // TODO: Make this clear. + let time = webpki::Time::from_seconds_since_unix_epoch(1547363522); + + let cert = webpki::EndEntityCert::try_from(ee).unwrap(); + assert_eq!( + Ok(()), + cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[], time) + ); +} + +#[test] +fn read_root_with_zero_serial() { + let ca = include_bytes!("misc/serial_zero.der"); + let _ = + webpki::TrustAnchor::try_from_cert_der(ca).expect("godaddy cert should parse as anchor"); +} + +#[test] +fn read_root_with_neg_serial() { + let ca = include_bytes!("misc/serial_neg.der"); + let _ = webpki::TrustAnchor::try_from_cert_der(ca).expect("idcat cert should parse as anchor"); +} + +#[cfg(feature = "std")] +#[test] +fn time_constructor() { + let _ = webpki::Time::try_from(std::time::SystemTime::now()).unwrap(); +} diff --git a/tests/misc/serial_neg.der b/tests/misc/serial_neg.der new file mode 100644 index 0000000000000000000000000000000000000000..f1317bf3b2bdd38c9d3e97f65379e76cbcc0ef37 GIT binary patch literal 1370 zcmXqLVhuBBVzFDm%*4pVB=An#_VpFTdm<~6*06lOdxX(|myJ`a&7D}zDf zXG3lSPB!LH7B*of*I+|y14|HxOW4RUJvA>mGf}}gu_Q4kF)vXeB~`&WwWuUBEi*YW zIWu2D!_U)AAyC)A!obkb+)UR~(@?`e4WyY{SUxzls4O+JSRtS^DJL_z7+tTSsev&> zrA}CCdTOacMoCFQv6a4ld3m{Ba$-p`&>p?K)Dr!&)S|?qqSPD(jbu#&Vtgq%DWSs9p{82K3tni#p5niv@wHY*+Xe>B}`YKMEX!#7#+&&|au za)F;`bRC|woRj7x?vVTZ)Tws;+s|DdDX~o=lW!b&@_%zc(WkO+n`4~< zDlbg2%jRL&cO#q6PwS1{r87;7BvOrq%jeCCl@h*j__pnd=~eFv-Y$PubGz`o?v$ka zx!1m0?6Y6m$|bpD#SP9VX{I@;VIj^-+Z_*;kh~!Gb01z;>O1Y zjSmcDfeA`hkVVWuq){j}IX*Et*#Rlt8t{Xpg&7(Dv#=U411SSOkN`hOfCZQ_*&zD) zSj1RF798$&S!KS|b^r8+sEZ#f7R`Pc+iOq{Qm(*KVNhmJ!p5!5#>m3>wStkEk;@>} zK!=S3D8S0f&crB2Fyk1Q!c{XFXiBLQnca}n0kD_=rUOQX1s>5&jNg1cc36g*&S|I$ zo_RIu|HeY=GnSlF;;!9#zmG9G_OjfKzqS5n7dSpJnY{n>qqaHblO}}8+~DGcjoS~pF8JZ`;Mwaj+f=!^#!Y|?%tfP zlOor;e^1G@g@>zz-tb7}HS^fKC~}+s+v957^k*9v{&@1YWvvES*cw(ZlOyXNU_KJ}$|5)=16pS0Hs|8}08t833E#U+Ll+H+Fp%w3u=)hej|>&4X^d7s}!|Lm}>+mWBg{KN?WtyS)D literal 0 HcmV?d00001 diff --git a/tests/misc/serial_zero.der b/tests/misc/serial_zero.der new file mode 100644 index 0000000000000000000000000000000000000000..3cd289bdff9d0a2185b4271ebd7d1e278714a542 GIT binary patch literal 1028 zcmXqLVqq|7VtTQFnTe5!iIKs8myJ`a&7D}zC@A-4f18*?ZNn=n&ou%V)X zJcz?3ED@5Cs^Ffl;F6eIwpbt{QEvx~TcFsvGE>kPAtjH&r@(LEy>6)$}FigkQ3)MFflMQvNSXYk_J)YyvE2}`Z%JAQ32WWjI0dI zO^m$2z+vQIYGPz$xHtFut8fX=h0(9C$9uHB+IRQ=llSQ|Us5wRhZio0E8qQgw&%-& z2HP-+WRCn>pUzycuV)HT3K6)msqxBzQd{r(6#DppG|Z&k?F&(hv_w4?6GZn+Hy zZ@8-GuQHGL-mZ52hVHkO;@jnm%>0&#+o#T$bXzMgcG16%2i*7SpZ1DAe0|#L>LZ~= z!n!y5*2fFkF5qD0*cP)i)q=%!rrhF$gp#9rVyq zO5gg1!@pKX{aNU+xmt?Ed~^>0rNBbXPRIhS;Mifi%K{jMifwq9*wTB}@{*}G$#@453n zr;L?f$aJil+wGVqc2ePK=e)Dlw+g=3%~Vqm2~l00SKRt+w&Gl_oF={%KMwKy5|GQ8 z_P(9>2!CSt+IP*`Q-AbLYALdOt?;V(ZS&dbc^rFQ`>Aa!)_!vNo6kSB!bgdMC0_r- zrP}#RJ@n28W{NLh5=xwU^`gTj0iSnA)(AK+TRM;H>eQrJyyrWbjvDbi(Vp-6jCa1q UoQ*rRqQcjj^RJh!zrA-p0Q?z-P5=M^ literal 0 HcmV?d00001 diff --git a/tests/netflix/ca.der b/tests/netflix/ca.der new file mode 100644 index 0000000000000000000000000000000000000000..6a15da32d617a7fcc84635993dea453a6b1fcde8 GIT binary patch literal 1054 zcmXqLVv#avVrF6zWSCvY=DFA|Y3J2<6Fdqv--jFUvTc;+SR8Oj^Tf+VJN*9;*q4EOMO|+I-5O^TnMT zi{ENB*w<<>k9e1zS#j7WGPLvA zi{KcUS93okiE%GK8PD_K-l4K{>%V7x`sKK6k+I0hr&Y<9=Ovk6ZB3oW_bgbYL^@7Z zC^-7K&hutZYteOG89T$LGchwVFd~N#Fu4Omh><~1WZJ>ilQ_QH>z~~8?@84BlzX=o z{o7`ml$nL!y&$T8bZxz;ym?Zw-IeF@=fs(BoBqUM{{rZ#TE?t0-YPo`n3Z z2ev9J>9KR4URiS5eYfnv>r1oG=wEXB>v?y1=lS4Ww-}-{_BY-Cu}fGspsnd|pFzK- z+XgvX(_0ovViI0!pKS}wxwk&)HRA<2m48+JLQ+B*JAS9l6cKTaS@(Qhrg)=vl=;ax ztuMzOEsP17y)1;`{D+SRkIyiWvNjXDy*TgH-?f_#-FnaDF5GqC<7UIP{xT2!=d9=F tnftwh_r@2~u!dXPbH)4T$~C4u{CCuK*V^ozTvdMl-YxIEKi1wA1OR@tik$!e literal 0 HcmV?d00001 diff --git a/tests/netflix/ee.der b/tests/netflix/ee.der new file mode 100644 index 0000000000000000000000000000000000000000..c06fbe51e79f36a1cfcd7645d77f1361a1bcfbde GIT binary patch literal 1772 zcmZux3s6jZ7(eIi+1=XR+G@KMX0?POw*P5`OxJ5kZcRl(E9uzXwq;*;AHBn#+vIv= zLRTfL!s>ykyc4bvvr{RFk++5s-5YsE$~~KIqv6b)Ip5>=|9;>9JKy&Kg4O{DYI6|< zff2+Zd$_&cW3 z;5bJlD{+-xD&`5*T8&zZ>*Z<{<_@@4HzZ2yulxPOVkRam)!gSdDa+twOEi$#A_|hq(Y}tBt|3BU6i0$de}X7OBL1 zY%lrY>2co5CVpwzZt3N zmv4*0YqLD;3!B1@M(qi=eD3LOjALJm!4tmrd#&D_mrzd>hPEq(=Bk+N;yGX1B3Rk^ zXKtNQy{I*J9#W+!%d;n3FAzwdIhG5ST@XDO`Nl9Z#w>N+UQjvR*w+3b_y+0~^C{{X z^{I14R_j6AigK50W1HKs?$iS62_Y*~`)*ID@Wo#_lLJnsJQ0@N9+yC*u(L;*0ynA+ zjUVzF9`gGVbIY6#9E%D*x6Tw?wS1dbSIu!|%Nit`mR4SEp3D6F>e=3!hUng_j-eM< zoSf3PKl2U0s^hZX%DfW~BX^YqZKCIl@4S>%(^bOxw|E?d!LIV&Wag&_J8euwC@=&c zASgH>D64?+C0RzUBWi31#zzDP7mL*fmHzvJ682(zn9dlgHcl{yWVMoT(#d5igJu}m ze%SB92?tWgh9RsL-ybZ3VU`gRX@XRt)=bgqahY_uODk1Kah>!ht{+;%IyGDliU0`M zTSYbue??IWLM<8?67G1^+JiRUG?+@IQ{W*90k{>;Lq%W(SVp7Cj4X0cK?v~pAA-Ox zuJL-kMmN*PN5a=7E5&Q9&FCZ6=)m|P5CTT9ehSlSaNusG@IcAN-srqKwh7OUe3T({ z-V!_M*!z@sLbq4I=l?h`jCz1EQyYCLL;*R1DnH^H}}|IGT;@k-;Ve=amY5WHf+qV_ZhLMc!n zn2+#cvsnS1AFc;RxS3nARiZf^y=jFDs?E6>C!Q}kn48H!cxz_`QC;1jhZCc!PSx-4 zwshF6oiYBIFNZ3r8C^GFM+1}q#ec}_ahDJmW^U`M_Tq+o`F-&!*{;_4HyE-bHDO`B zR|aX%4$v+H3y?9;7rNTvj_5u8=Fay%{bEkRD7rMZz20rNK!?J zU*{2Kb5FzlwuFYd^A@yYhb0h=t;&l`BYC@m&!0A2k3IWjp4|QLlYMaq*y~qUYVZ63 zT}j=XQrcHP$a{5&mnZ2?uRL0DobCQ{XGKT+>!7%v?)P&};!l$-kw{tFjTSPquT4GY z=I|rxwlP|YF1^jkG0#Z$mmNvHW@@TESwHz8_Wc?n7dN?vVCb+QdZbFV+eo`8Keo@N!Sr&6Wm%Tr#Y^VhwX?34uD;2#5TgG88T6S~ literal 0 HcmV?d00001 diff --git a/tests/netflix/inter.der b/tests/netflix/inter.der new file mode 100644 index 0000000000000000000000000000000000000000..ff25d78fc51b870d8aca2397110fcb545f0d8294 GIT binary patch literal 1305 zcmXqLVih%LV*a~;nTe5!NkBtSePX5Yq$e7m6qoy*G+$@H%f_kI=F#?@mywa1mBFC# zlp(hPCmVAp3!5-gXt1HUfhdT>CCnF=T9g@_nVzSk;F*`KXDDwV3zFa#7K2GBgcOw) zmnit9mXzlgWgA)3aLX@JNGvVM$S=yQN=;EHElyR) z&&#PabTx1W>17tSfoV~2&PgmTRxnlwC{4=AOjZae%FInHs#I`JEh@=O%S=uz$;{7F zaDTL-nhyfi%RgBEglpiFqZd z$qLT-MFsgqV8fAo43|drvA%&W#MNqW1u$0!rzV#cr78rc7L}zIDL6ZV+-G9Y#HfTE z{*0^)%uS5^3_x)%rY1&4hD|qEPsx<%ZWRx+Jn9w1Iqik_4W5q~^Jh6}BpqIA5L_23 z*c8oN#jF3YlI7%UwX*9`Jb4p`D0&Y z*thS0Z(3#C>h^9jb`&~%!sg7iX;m$XQc5h$>f+rp8Htnm`2rh^Ww&fS_qy&v(wcXh zs~Jn)XPUk~b=7v|yXiAd^{#T^Q=RE0I&I0hT&56ttt_=iY*xu0Yqt1YES((iUs(Uz zt%t`XzRo|X8vB{S>YkeK7ER8x{I<&ZVqT46zdtn#9Nuy5MyQeP$G!Y}dxEBViA}Ex zQcNz@7yEf6ZQs|eoHzcfGn{p)$@jM3@STa7k%4h>6Qct#L~Ow6O_q;Ej720q;e6tR z(EBaUB3%huo4!_kah7;*AOsA1Sz$)T|12B^Y(R>Mk-;DvB&NWUVvuBzz{Zu(=E2zZ z!jg3Q_jggg=orzIQDx;*Nz)D}gyu4hm7#Q`yRHT=jpR1o-P;8(9SI1-^ zBZW(4Q9+`CKFBh877YV61C<3z3l!SqpvD%13{6e~8(EN?XsnxVte0GrW55T}zz=dU z3ovuC8EAs|f-Fh~3QOb+WDKPYB!B|IOdtqLP66JYx`w)@#wG@OFjp}$g47$x!T1J@ zZ9*v4rhu$PPG!Jc4oqc?4AJ62`>usmfBA00={@b#@o$P3G8^7MpC$Q19cnQB@`bK{l5EPz)6m!yTZM!qy>s)9#(Ig!tA{+ za6^-WRg}t{cf1j?>vm~Yoid)csqv70-$C{B4j)(++J7}abn4HV;M|qLnf?#UQg~Dw zSDG__vMe^abg-sELS{v_*z@NP=4!6L@g&o-a%$$z>X=x@Iaj`a_HY$WnR=ZoujrPu el1jc^RKcAClOE_k4gc{(Tm9YF|I(Woo2vjOoV&9C literal 0 HcmV?d00001 diff --git a/third-party/chromium/LICENSE b/third-party/chromium/LICENSE new file mode 100644 index 0000000..a32e00c --- /dev/null +++ b/third-party/chromium/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third-party/chromium/data/verify_signed_data/README b/third-party/chromium/data/verify_signed_data/README new file mode 100644 index 0000000..2acf6e6 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/README @@ -0,0 +1,35 @@ +This directory contains test data for testing net::VerifySignedData(). + +When adding or changing test data, run the script + $ python annotate_test_data.py + +This script will apply a uniform formatting. For instance it will add a +comment showing what the parsed ASN.1 looks like, and reformat the base64 to +have consistent line breaks. + +The general format for the test files is as follows: + + + + + -----BEGIN PUBLIC KEY----- + + -----END PUBLIC KEY----- + + -----BEGIN ALGORITHM----- + + -----END ALGORITHM----- + + -----BEGIN DATA----- + + -----END DATA----- + + -----BEGIN SIGNATURE----- + + -----END SIGNATURE----- + + +Comments for a PEM block should be placed immediately below that block. +The script will also insert a comment after the block describing its parsed +ASN.1 structure (your extra comments need to be above the script-generated +comments or they will be stripped). diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-spki-params-null.pem b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-spki-params-null.pem new file mode 100644 index 0000000..6f65be1 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-spki-params-null.pem @@ -0,0 +1,45 @@ +This is the same test as ecdsa-prime256v1-sha512.pem except the public key's +algorithm has been tampered with. The parameters for ecPublicKey should be a +namedCurve, but here they have been replaced by NULL. + + + +-----BEGIN PUBLIC KEY----- +MFEwCwYHKoZIzj0CAQUAA0IABJywz2kwPa/HYdTkaHtOzwOebTSrlkr4CBDY1VikqNb3LVEjOhe +IkgqG7gihlix576MX+3h54pfa0hRtuZX6HHg= +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 81 cons: SEQUENCE + 2:d=1 hl=2 l= 11 cons: SEQUENCE + 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey + 13:d=2 hl=2 l= 0 prim: NULL + 15:d=1 hl=2 l= 66 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAME +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA512 + + + +-----BEGIN DATA----- +bikyFTMBpO72gOZCiSmtrpiMEI1mijH/VdBImUfXX/gaRr+J6E1kAfAjvm6HaI+814TXhcqEZzV +SSstS0ARSyEBApHnnzDMJNkQdk7vnIqlDKm4dsRK1yUA7ECcssTR/1hnUY/ep0iOtdv3gbYpog1 +APuEMjWr/5jiQb37VTjD4= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A0cAMEQCIEufkeQoUocmGh0ckjz2Gc1SwXXP5/G+YKUljGEDSLo9AiAoxF+QHXHEGymGOOwNaoX +X/LDDO7/sWpyBCEa2OSiahA== +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 71 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-unused-bits-signature.pem b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-unused-bits-signature.pem new file mode 100644 index 0000000..24efda0 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-unused-bits-signature.pem @@ -0,0 +1,53 @@ +This is the same test as ecdsa-prime256v1-sha512.pem, however the SIGNATURE has +been changed to a (valid) BIT STRING containing two unused bits. + +Note that the last two bits of the original signature were 0, so the +DER-encoded bytes portion of the mutated BIT STRING remains the same. All that +changes is the octet at the start which indicates the number of unused bits. + +In other words SIGNATURE changes from: + 03 47 00 30 ... 84 +To: + 03 47 02 30 ... 84 + + + +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnLDPaTA9r8dh1ORoe07PA55tNKuWSvgIENjVWKS +o1vctUSM6F4iSCobuCKGWLHnvoxf7eHnil9rSFG25lfoceA== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 89 cons: SEQUENCE + 2:d=1 hl=2 l= 19 cons: SEQUENCE + 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey + 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 + 23:d=1 hl=2 l= 66 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAME +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA512 + + + +-----BEGIN DATA----- +bikyFTMBpO72gOZCiSmtrpiMEI1mijH/VdBImUfXX/gaRr+J6E1kAfAjvm6HaI+814TXhcqEZzV +SSstS0ARSyEBApHnnzDMJNkQdk7vnIqlDKm4dsRK1yUA7ECcssTR/1hnUY/ep0iOtdv3gbYpog1 +APuEMjWr/5jiQb37VTjD4= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A0cCMEQCIEufkeQoUocmGh0ckjz2Gc1SwXXP5/G+YKUljGEDSLo9AiAoxF+QHXHEGymGOOwNaoX +X/LDDO7/sWpyBCEa2OSiahA== +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 71 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecdh-key.pem b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecdh-key.pem new file mode 100644 index 0000000..4f3e26c --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecdh-key.pem @@ -0,0 +1,48 @@ +This uses the same test data as ecdsa-prime256v1-sha512.pem, HOWEVER the +algorithm OID for the public key has been changed from id-ecPublicKey +(1.2.840.10045.2.1) to id-ecDH (1.3.132.1.12) + +This test should fail because the public key's algorithm does not match that of +the signature algorithm. + + + +-----BEGIN PUBLIC KEY----- +MFcwEQYFK4EEAQwGCCqGSM49AwEHA0IABJywz2kwPa/HYdTkaHtOzwOebTSrlkr4CBDY1VikqNb +3LVEjOheIkgqG7gihlix576MX+3h54pfa0hRtuZX6HHg= +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 87 cons: SEQUENCE + 2:d=1 hl=2 l= 17 cons: SEQUENCE + 4:d=2 hl=2 l= 5 prim: OBJECT :1.3.132.1.12 + 11:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 + 21:d=1 hl=2 l= 66 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAME +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA512 + + + +-----BEGIN DATA----- +bikyFTMBpO72gOZCiSmtrpiMEI1mijH/VdBImUfXX/gaRr+J6E1kAfAjvm6HaI+814TXhcqEZzV +SSstS0ARSyEBApHnnzDMJNkQdk7vnIqlDKm4dsRK1yUA7ECcssTR/1hnUY/ep0iOtdv3gbYpog1 +APuEMjWr/5jiQb37VTjD4= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A0cAMEQCIEufkeQoUocmGh0ckjz2Gc1SwXXP5/G+YKUljGEDSLo9AiAoxF+QHXHEGymGOOwNaoX +X/LDDO7/sWpyBCEa2OSiahA== +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 71 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecmqv-key.pem b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecmqv-key.pem new file mode 100644 index 0000000..d0b906f --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecmqv-key.pem @@ -0,0 +1,48 @@ +This uses the same test data as ecdsa-prime256v1-sha512.pem, HOWEVER the +algorithm OID for the public key has been changed from id-ecPublicKey +(1.2.840.10045.2.1) to id-ecMQV (1.3.132.1.13) + +This test should fail because the public key's algorithm does not match that of +the signature algorithm. + + + +-----BEGIN PUBLIC KEY----- +MFcwEQYFK4EEAQ0GCCqGSM49AwEHA0IABJywz2kwPa/HYdTkaHtOzwOebTSrlkr4CBDY1VikqNb +3LVEjOheIkgqG7gihlix576MX+3h54pfa0hRtuZX6HHg= +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 87 cons: SEQUENCE + 2:d=1 hl=2 l= 17 cons: SEQUENCE + 4:d=2 hl=2 l= 5 prim: OBJECT :1.3.132.1.13 + 11:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 + 21:d=1 hl=2 l= 66 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAME +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA512 + + + +-----BEGIN DATA----- +bikyFTMBpO72gOZCiSmtrpiMEI1mijH/VdBImUfXX/gaRr+J6E1kAfAjvm6HaI+814TXhcqEZzV +SSstS0ARSyEBApHnnzDMJNkQdk7vnIqlDKm4dsRK1yUA7ECcssTR/1hnUY/ep0iOtdv3gbYpog1 +APuEMjWr/5jiQb37VTjD4= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A0cAMEQCIEufkeQoUocmGh0ckjz2Gc1SwXXP5/G+YKUljGEDSLo9AiAoxF+QHXHEGymGOOwNaoX +X/LDDO7/sWpyBCEa2OSiahA== +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 71 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-rsa-algorithm.pem b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-rsa-algorithm.pem new file mode 100644 index 0000000..8085486 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-rsa-algorithm.pem @@ -0,0 +1,48 @@ +This test specified a valid ECDSA signature and EC key (the same as ecdsa-prime256v1-sha512.pem) + +The problem however is the signature algorithm is indicated as being RSA PKCS#1 v1.5. + +Signature verification consequently should fail. + + + +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnLDPaTA9r8dh1ORoe07PA55tNKuWSvgIENjVWKS +o1vctUSM6F4iSCobuCKGWLHnvoxf7eHnil9rSFG25lfoceA== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 89 cons: SEQUENCE + 2:d=1 hl=2 l= 19 cons: SEQUENCE + 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey + 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 + 23:d=1 hl=2 l= 66 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBDQUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha512WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +bikyFTMBpO72gOZCiSmtrpiMEI1mijH/VdBImUfXX/gaRr+J6E1kAfAjvm6HaI+814TXhcqEZzV +SSstS0ARSyEBApHnnzDMJNkQdk7vnIqlDKm4dsRK1yUA7ECcssTR/1hnUY/ep0iOtdv3gbYpog1 +APuEMjWr/5jiQb37VTjD4= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A0cAMEQCIEufkeQoUocmGh0ckjz2Gc1SwXXP5/G+YKUljGEDSLo9AiAoxF+QHXHEGymGOOwNaoX +X/LDDO7/sWpyBCEa2OSiahA== +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 71 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-wrong-signature-format.pem b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-wrong-signature-format.pem new file mode 100644 index 0000000..d51317c --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-wrong-signature-format.pem @@ -0,0 +1,47 @@ +This is the same as ecdsa-prime256v1-sha512.pem, except the signature is wrong. + +Rather than encoding the signature into a DER-encoded Ecdsa-Sig-Value, it is a +concatenation of the r and s values. This is the format that WebCrypto uses for +ECDSA signature, but not what is used for digital signatures. + + + +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnLDPaTA9r8dh1ORoe07PA55tNKuWSvgIENjVWKS +o1vctUSM6F4iSCobuCKGWLHnvoxf7eHnil9rSFG25lfoceA== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 89 cons: SEQUENCE + 2:d=1 hl=2 l= 19 cons: SEQUENCE + 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey + 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 + 23:d=1 hl=2 l= 66 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAME +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA512 + + + +-----BEGIN DATA----- +bikyFTMBpO72gOZCiSmtrpiMEI1mijH/VdBImUfXX/gaRr+J6E1kAfAjvm6HaI+814TXhcqEZzV +SSstS0ARSyEBApHnnzDMJNkQdk7vnIqlDKm4dsRK1yUA7ECcssTR/1hnUY/ep0iOtdv3gbYpog1 +APuEMjWr/5jiQb37VTjD4= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A0EAS5+R5ChShyYaHRySPPYZzVLBdc/n8b5gpSWMYQNIuj0oxF+QHXHEGymGOOwNaoXX/LDDO7/ +sWpyBCEa2OSiahA== +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 65 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512.pem b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512.pem new file mode 100644 index 0000000..54f490c --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512.pem @@ -0,0 +1,49 @@ +The key, message, and signature come from: +http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip (SigVer.rsp) + +The algorithm DER was synthesized to match, and the signature (provided as an r +and s tuple) was encoded into a Ecdsa-Sig-Value and then a BIT STRING. + +It uses ECDSA verification, using curve prime256v1 and SHA-512 + + + +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnLDPaTA9r8dh1ORoe07PA55tNKuWSvgIENjVWKS +o1vctUSM6F4iSCobuCKGWLHnvoxf7eHnil9rSFG25lfoceA== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 89 cons: SEQUENCE + 2:d=1 hl=2 l= 19 cons: SEQUENCE + 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey + 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 + 23:d=1 hl=2 l= 66 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAME +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA512 + + + +-----BEGIN DATA----- +bikyFTMBpO72gOZCiSmtrpiMEI1mijH/VdBImUfXX/gaRr+J6E1kAfAjvm6HaI+814TXhcqEZzV +SSstS0ARSyEBApHnnzDMJNkQdk7vnIqlDKm4dsRK1yUA7ECcssTR/1hnUY/ep0iOtdv3gbYpog1 +APuEMjWr/5jiQb37VTjD4= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A0cAMEQCIEufkeQoUocmGh0ckjz2Gc1SwXXP5/G+YKUljGEDSLo9AiAoxF+QHXHEGymGOOwNaoX +X/LDDO7/sWpyBCEa2OSiahA== +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 71 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256-corrupted-data.pem b/third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256-corrupted-data.pem new file mode 100644 index 0000000..5f5380b --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256-corrupted-data.pem @@ -0,0 +1,53 @@ +This is the same test as ecdsa-secp384r1-sha256.pem, except the DATA section +has been corrupted. The third byte has been set to 0. + +This signature should NOT verify successfully. + + + +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEXFhBi+WKXuxeK9yCbC9jqEchwjCNsQ4RXAsJ07oHZ+Q +qz55cNIY5BAhcQ0QYY5jv9BimGL9SuhUuOSuS3Pn9rrsIFGcFsihy4kDr8rq5+7RbN8bV057gW5 +emYjLtvDsQ +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 118 cons: SEQUENCE + 2:d=1 hl=2 l= 16 cons: SEQUENCE + 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey + 13:d=2 hl=2 l= 5 prim: OBJECT :secp384r1 + 20:d=1 hl=2 l= 98 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAMC +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA256 + + + +-----BEGIN DATA----- +MIIA6KADAgECAgkAtUVxft6/Vc0wCgYIKoZIzj0EAwIwbzELMAkGA1UEBhMCQVUxEzARBgNVBAg +MClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAw +wDYWhhMRowGAYJKoZIhvcNAQkBFgtzdXBAZm9vLmNvbTAeFw0xNTA3MDIwMDM4MTRaFw0xNjA3M +DEwMDM4MTRaMG8xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ +bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDDAKBgNVBAMMA2FoYTEaMBgGCSqGSIb3DQEJARYLc3V +wQGZvby5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARcWEGL5Ype7F4r3IJsL2OoRyHCMI2xDh +FcCwnTugdn5CrPnlw0hjkECFxDRBhjmO/0GKYYv1K6FS45K5Lc+f2uuwgUZwWyKHLiQOvyurn7t +Fs3xtXTnuBbl6ZiMu28OxCjUDBOMB0GA1UdDgQWBBR6nDgtPalG2JvSlWzdGRCi/Cu7NjAfBgNV +HSMEGDAWgBR6nDgtPalG2JvSlWzdGRCi/Cu7NjAMBgNVHRMEBTADAQH/ +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A2kAMGYCMQCDwMSZVJZ8qThiNTV7VX57m8fdNnNS6cXIjRYoh4SozlYWmWGh87nhmJJCeD16jVM +CMQDkroAY8oNi8RxLUor+LozhhVgu24tu6lcp4MP8H3Zy8bBea5HerAd1AqJp3Ox7KDU= +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 105 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256.pem b/third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256.pem new file mode 100644 index 0000000..d5c3798 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256.pem @@ -0,0 +1,84 @@ +This test data was produced by creating a self-signed EC cert using OpenSSL, +and then extracting the relevant fields. + +It uses ECDSA with curve secp384r1 an SHA-256. + +(1) Generate self-signed certificate + + openssl ecparam -out ec_key.pem -name secp384r1 -genkey + openssl req -new -key ec_key.pem -x509 -nodes -days 365 -out cert.pem + +(2) Extract public key + + openssl x509 -in cert.pem -pubkey -noout > pubkey.pem + cat pubkey.pem + +(3) Extract signed data (tbsCertificate) + + openssl asn1parse -in cert.pem -out tbs -noout -strparse 4 + base64 tbs + +(4) Extract signature algorithm + + # Find the offset of the signature algorithm near the end (496 in this case) + openssl asn1parse -in cert.pem + + openssl asn1parse -in cert.pem -out alg -noout -strparse 496 + base64 alg + +(5) Extract the signature + + # Find the final offset of BIT STRING (508 in this case) + openssl asn1parse -in cert.pem + + openssl asn1parse -in cert.pem -out sig -noout -strparse 508 + base64 sig + + + +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEXFhBi+WKXuxeK9yCbC9jqEchwjCNsQ4RXAsJ07oHZ+Q +qz55cNIY5BAhcQ0QYY5jv9BimGL9SuhUuOSuS3Pn9rrsIFGcFsihy4kDr8rq5+7RbN8bV057gW5 +emYjLtvDsQ +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 118 cons: SEQUENCE + 2:d=1 hl=2 l= 16 cons: SEQUENCE + 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey + 13:d=2 hl=2 l= 5 prim: OBJECT :secp384r1 + 20:d=1 hl=2 l= 98 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAMC +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA256 + + + +-----BEGIN DATA----- +MIIB6KADAgECAgkAtUVxft6/Vc0wCgYIKoZIzj0EAwIwbzELMAkGA1UEBhMCQVUxEzARBgNVBAg +MClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAw +wDYWhhMRowGAYJKoZIhvcNAQkBFgtzdXBAZm9vLmNvbTAeFw0xNTA3MDIwMDM4MTRaFw0xNjA3M +DEwMDM4MTRaMG8xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ +bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDDAKBgNVBAMMA2FoYTEaMBgGCSqGSIb3DQEJARYLc3V +wQGZvby5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARcWEGL5Ype7F4r3IJsL2OoRyHCMI2xDh +FcCwnTugdn5CrPnlw0hjkECFxDRBhjmO/0GKYYv1K6FS45K5Lc+f2uuwgUZwWyKHLiQOvyurn7t +Fs3xtXTnuBbl6ZiMu28OxCjUDBOMB0GA1UdDgQWBBR6nDgtPalG2JvSlWzdGRCi/Cu7NjAfBgNV +HSMEGDAWgBR6nDgtPalG2JvSlWzdGRCi/Cu7NjAMBgNVHRMEBTADAQH/ +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A2kAMGYCMQCDwMSZVJZ8qThiNTV7VX57m8fdNnNS6cXIjRYoh4SozlYWmWGh87nhmJJCeD16jVM +CMQDkroAY8oNi8RxLUor+LozhhVgu24tu6lcp4MP8H3Zy8bBea5HerAd1AqJp3Ox7KDU= +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 105 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ecdsa-using-rsa-key.pem b/third-party/chromium/data/verify_signed_data/ecdsa-using-rsa-key.pem new file mode 100644 index 0000000..653bcd7 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ecdsa-using-rsa-key.pem @@ -0,0 +1,51 @@ +This test specifies an ECDSA signature algorithm (and a valid ecdsa signature), +HOWEVER it provides an RSA key. Verification should fail. + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLsDjatUqRN/rH +mH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2VCAltWyuLbfXWce9jd8CSHL +I8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh4fINDOjP+yJJvZohNwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAMC +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA256 + + + +-----BEGIN DATA----- +MIIB6KADAgECAgkAtUVxft6/Vc0wCgYIKoZIzj0EAwIwbzELMAkGA1UEBhMCQVUxEzARBgNVBAg +MClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAw +wDYWhhMRowGAYJKoZIhvcNAQkBFgtzdXBAZm9vLmNvbTAeFw0xNTA3MDIwMDM4MTRaFw0xNjA3M +DEwMDM4MTRaMG8xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ +bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDDAKBgNVBAMMA2FoYTEaMBgGCSqGSIb3DQEJARYLc3V +wQGZvby5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARcWEGL5Ype7F4r3IJsL2OoRyHCMI2xDh +FcCwnTugdn5CrPnlw0hjkECFxDRBhjmO/0GKYYv1K6FS45K5Lc+f2uuwgUZwWyKHLiQOvyurn7t +Fs3xtXTnuBbl6ZiMu28OxCjUDBOMB0GA1UdDgQWBBR6nDgtPalG2JvSlWzdGRCi/Cu7NjAfBgNV +HSMEGDAWgBR6nDgtPalG2JvSlWzdGRCi/Cu7NjAMBgNVHRMEBTADAQH/ +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A2kAMGYCMQCDwMSZVJZ8qThiNTV7VX57m8fdNnNS6cXIjRYoh4SozlYWmWGh87nhmJJCeD16jVM +CMQDkroAY8oNi8RxLUor+LozhhVgu24tu6lcp4MP8H3Zy8bBea5HerAd1AqJp3Ox7KDU= +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=2 l= 105 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ours/make-pss.py b/third-party/chromium/data/verify_signed_data/ours/make-pss.py new file mode 100644 index 0000000..7708bc8 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ours/make-pss.py @@ -0,0 +1,59 @@ +# Copyright 2016 Joseph Birr-Pixton. +# +# 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. + +import subprocess +import glob +import hashlib +import os + +TOP = '../../../../../' + +def dump(bin, type): + return '-----BEGIN %s-----\n%s-----END %s-----\n' % \ + (type, bin.encode('base64'), type) + +def gen(outfile, paramfile, hashfn): + param = open(paramfile).read() + + rand = os.urandom(64) + hash = getattr(hashlib, hashfn)(rand).digest() + + proc = subprocess.Popen(['openssl', 'pkeyutl', + '-inkey', 'priv.pem', + '-sign', + '-pkeyopt', 'rsa_padding_mode:pss', + '-pkeyopt', 'rsa_pss_saltlen:-1', + '-pkeyopt', 'digest:%s' % hashfn + ], + stdout = subprocess.PIPE, + stdin = subprocess.PIPE) + + sig, _ = proc.communicate(hash) + + with open(outfile, 'w') as f: + print >>f, dump(open('pub.der').read(), 'PUBLIC KEY') + print >>f, dump(param, 'ALGORITHM') + print >>f, dump(rand, 'DATA') + + assert len(sig) == 256 # only works with 2048-bit keys + # turn it into a DER bitstring + print >>f, dump('\x03\x82\x01\x01\x00' + sig, 'SIGNATURE') + +if __name__ == '__main__': + subprocess.check_call('openssl genrsa -out priv.pem 2048', shell = True) + subprocess.check_call('openssl rsa -pubout -out pub.pem -in priv.pem', shell = True) + subprocess.check_call('openssl asn1parse -inform pem -in pub.pem -out pub.der', shell = True) + gen('rsa-pss-sha256-salt32.pem', TOP + 'src/data/alg-pss-sha256.der', 'sha256') + gen('rsa-pss-sha384-salt48.pem', TOP + 'src/data/alg-pss-sha384.der', 'sha384') + gen('rsa-pss-sha512-salt64.pem', TOP + 'src/data/alg-pss-sha512.der', 'sha512') diff --git a/third-party/chromium/data/verify_signed_data/ours/priv.pem b/third-party/chromium/data/verify_signed_data/ours/priv.pem new file mode 100644 index 0000000..dea5059 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ours/priv.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAtVDcg1BMqPneiNBL5+mjEB5mxjJzgkmClZR5z1jNht+As6+M +lgflni0bB8LjhWbIt+dZ6Bt4cSHOnAOnkMDOFwtrxJE6Eg1GQ2ux9nDVNvrQkoOL +znXrxMh/af0pcSo8kItDmkqbV/fi3Q7agpbcWc/4wTZOfO6lns4nb5s08oaUv3uF +5Wc0sktNr6he6R3zSQ6YK5KZFzQdnEtGc4gwHWXZ9xt4JeANht3m4RNpMY89qZsZ +xmqoewYHuXQUAfl7W0DC3hoxOoLwSqL2bt2zMMeR8WAo51YY0cJnzAEETcnWIM6e +alb5Osj3iSEknxRTley47SsODQ0maUUWj8wEjwIDAQABAoIBAFBAVQmt7fBQgAWQ +JDimeWz198k7pVKCVND94Zg7luReYmmlhpUVM7V6A1/BC9EMuERlmq6YEgwIyZjW +KUFxhQZAINfk64334CSRMzh/om9uPgjLPoMIQG1dzL9NtR0Ic7wKV8afxPf/mKL9 +Iwvv1+HMoi5qafzz58xNNLk6OgopdZ6H2aX0QtHy/jkMfpE3od7W6Xc+lSVUO7HG +zmN3pHaM5K5n59cX9jpg/K+a0loceY5vmqUfXyh6IP6h1XyAb2NTpU04klDEFEnU +tyaYtxL4ZMZzefoeVXVbCl7qeOE7KGIz7gcvsqL7T8dkK+uYf6mLENeyUvkCKiTG +QAqon0kCgYEA6pLLAYpRo9JbLYlYNt9iTFwIU+R8RcxzZrltm7OUqztmaVq4pOek +cPw/2sCMvqeEWuGhv+bbeIsDUWADU9NFkWySlVRKEFKGb3ECv3A07yfP2K22exOZ +/SglNZKB/ycvpOMcNKnJD5folRrh0actdVG8vOf/sN+887vE77u0f6sCgYEAxeC0 +/6831k/EWYzQf+OfeLqx4ABgkgqxvMXO3YCp44+DD4l8TVWfP4Ahfj5p0zCbXIv3 +5OcxdVwjNljZw4Y5vDNwcDK7vKwp9ne+H9xJB7Yedfr7oyJbqIvJ1nd6jmXoL7GA +dX9xSxJ1CucD7RAY99MS77y8xm0sTbFoI6SvOq0CgYEApeQihXhYvE6sBMw5ArWA +QxhjG1FfQc2RX3iLt62M2nwrtR5frt/TP8RlNEMwRjUaOVuQlLKjS+Cj/Ay2zbWA +YZQzJkByEJEMWgvGMbUc+nVhXI+dmfUG1i5qAjUWkmgYHbgv3l6kvs5jwe88/JQK +ZgnkPISmI2RXsNd+MzzALfkCgYB/56HXa/ERzHnE0KqtH/si1LrJajUB8Xu14761 +msc12rwCvQHmEyRerXn42msZIeAq0CaqhW6Ix8fTB1erdQW4yx8wxvpnGHn/YKM6 +gO+L1oKWDGe/qSPKLKGIya4kgWa1/Wxlhr06o3GYXH9DKxaYio1A/aSgNk1e4v/H +mlnR+QKBgQDd2cdhBTXIo5FZSONip5GG2Ku9m60qGSyPTCqxLNWBfYE/fu0aFCUU +GemqA2ygxFnyCG1Af0SDWwQFH8W7BJ6H1geJVcwVKLrZokKOul8kdwXCxz1J2XGe +gskT4Dsd9K8TSU3J09XVKhC5SrF0vDjdXOE6rtFSqa/bs7B2JcfNwQ== +-----END RSA PRIVATE KEY----- diff --git a/third-party/chromium/data/verify_signed_data/ours/pub.pem b/third-party/chromium/data/verify_signed_data/ours/pub.pem new file mode 100644 index 0000000..7ece334 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ours/pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVDcg1BMqPneiNBL5+mj +EB5mxjJzgkmClZR5z1jNht+As6+Mlgflni0bB8LjhWbIt+dZ6Bt4cSHOnAOnkMDO +FwtrxJE6Eg1GQ2ux9nDVNvrQkoOLznXrxMh/af0pcSo8kItDmkqbV/fi3Q7agpbc +Wc/4wTZOfO6lns4nb5s08oaUv3uF5Wc0sktNr6he6R3zSQ6YK5KZFzQdnEtGc4gw +HWXZ9xt4JeANht3m4RNpMY89qZsZxmqoewYHuXQUAfl7W0DC3hoxOoLwSqL2bt2z +MMeR8WAo51YY0cJnzAEETcnWIM6ealb5Osj3iSEknxRTley47SsODQ0maUUWj8wE +jwIDAQAB +-----END PUBLIC KEY----- diff --git a/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32-corrupted-data.pem b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32-corrupted-data.pem new file mode 100644 index 0000000..153189b --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32-corrupted-data.pem @@ -0,0 +1,63 @@ +This has had DATA corrupted, so the signature is not valid. + + + +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVDcg1BMqPneiNBL5+mjEB5mxjJzgkm +ClZR5z1jNht+As6+Mlgflni0bB8LjhWbIt+dZ6Bt4cSHOnAOnkMDOFwtrxJE6Eg1GQ2ux9nDVNv +rQkoOLznXrxMh/af0pcSo8kItDmkqbV/fi3Q7agpbcWc/4wTZOfO6lns4nb5s08oaUv3uF5Wc0s +ktNr6he6R3zSQ6YK5KZFzQdnEtGc4gwHWXZ9xt4JeANht3m4RNpMY89qZsZxmqoewYHuXQUAfl7 +W0DC3hoxOoLwSqL2bt2zMMeR8WAo51YY0cJnzAEETcnWIM6ealb5Osj3iSEknxRTley47SsODQ0 +maUUWj8wEjwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAIBBQCiAwIBIA== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha256 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha256 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :20 + + + +-----BEGIN DATA----- +K6BCjy4hCNAZBmRT+wS4h5wDg7pO67oHFabDt5cXNp8X6sLNH2vjICLtO2niPwZ/Yk2ySxC8MgO +/+U9sdSXxqA== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4IBAQCZqlXJhviFKOKHe1ssbm0ThtAnAbcuP3ACBZyfpBjfYlxAgltNzBnmEtxjsbZQPMXHDHy +Y+fdEXwK2vboCz7BzIRXcrcJGzjsBc2zPeNZlmhaadIoa5d8jy3kxnT+f3YVjKGZBqwDaqE5Kie +jhV0laTK+cNGFXo9a3ylICok+s4jVN2Y7qE+ImgyANbZyn1d6W6VnFf4GVvin2hFwTCcZnKA6Db +NYnArbbNmHmMB2S+1Kw9dAklnzZmwWgNSRirtTpUHTBIWYq3B0hPL8IzwKk89/iKDaY2fpV/Wnt +oL2mgM7oa/7+oQWa27BGYftYZmDpIQtNbUeO4VBnaeqGgA5f +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=4 l= 257 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32.pem b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32.pem new file mode 100644 index 0000000..85d4f61 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32.pem @@ -0,0 +1,63 @@ + + + + +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVDcg1BMqPneiNBL5+mjEB5mxjJzgkm +ClZR5z1jNht+As6+Mlgflni0bB8LjhWbIt+dZ6Bt4cSHOnAOnkMDOFwtrxJE6Eg1GQ2ux9nDVNv +rQkoOLznXrxMh/af0pcSo8kItDmkqbV/fi3Q7agpbcWc/4wTZOfO6lns4nb5s08oaUv3uF5Wc0s +ktNr6he6R3zSQ6YK5KZFzQdnEtGc4gwHWXZ9xt4JeANht3m4RNpMY89qZsZxmqoewYHuXQUAfl7 +W0DC3hoxOoLwSqL2bt2zMMeR8WAo51YY0cJnzAEETcnWIM6ealb5Osj3iSEknxRTley47SsODQ0 +maUUWj8wEjwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAIBBQCiAwIBIA== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha256 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha256 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :20 + + + +-----BEGIN DATA----- +K5BCjy4hCNAZBmRT+wS4h5wDg7pO67oHFabDt5cXNp8X6sLNH2vjICLtO2niPwZ/Yk2ySxC8MgO +/+U9sdSXxqA== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4IBAQCZqlXJhviFKOKHe1ssbm0ThtAnAbcuP3ACBZyfpBjfYlxAgltNzBnmEtxjsbZQPMXHDHy +Y+fdEXwK2vboCz7BzIRXcrcJGzjsBc2zPeNZlmhaadIoa5d8jy3kxnT+f3YVjKGZBqwDaqE5Kie +jhV0laTK+cNGFXo9a3ylICok+s4jVN2Y7qE+ImgyANbZyn1d6W6VnFf4GVvin2hFwTCcZnKA6Db +NYnArbbNmHmMB2S+1Kw9dAklnzZmwWgNSRirtTpUHTBIWYq3B0hPL8IzwKk89/iKDaY2fpV/Wnt +oL2mgM7oa/7+oQWa27BGYftYZmDpIQtNbUeO4VBnaeqGgA5f +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=4 l= 257 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48-corrupted-data.pem b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48-corrupted-data.pem new file mode 100644 index 0000000..d3e7a0b --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48-corrupted-data.pem @@ -0,0 +1,63 @@ +This has had DATA corrupted, so the signature is not valid. + + + +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVDcg1BMqPneiNBL5+mjEB5mxjJzgkm +ClZR5z1jNht+As6+Mlgflni0bB8LjhWbIt+dZ6Bt4cSHOnAOnkMDOFwtrxJE6Eg1GQ2ux9nDVNv +rQkoOLznXrxMh/af0pcSo8kItDmkqbV/fi3Q7agpbcWc/4wTZOfO6lns4nb5s08oaUv3uF5Wc0s +ktNr6he6R3zSQ6YK5KZFzQdnEtGc4gwHWXZ9xt4JeANht3m4RNpMY89qZsZxmqoewYHuXQUAfl7 +W0DC3hoxOoLwSqL2bt2zMMeR8WAo51YY0cJnzAEETcnWIM6ealb5Osj3iSEknxRTley47SsODQ0 +maUUWj8wEjwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAICBQCiAwIBMA== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha384 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha384 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :30 + + + +-----BEGIN DATA----- +TDrlz5dKOqfOQhirwHj00bsVlf+0WEe2qMe9l6SVr9SHB4Eow26r+aU7+pGZFp774O041xIeU2g +ZHYzNWBjGZQ== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4IBAQBvlL4AuwL3hEobMolBzR/0gzuJ9u4ATWEPO5uTiBtdJ5Nx9O6gFCrtZMwfEU9q4bzazKV +yWRSpn23GZjlmNYhFCNlfY3l6IlhxGEVz/YeOglrBR8hFbA17835jTmcCR09G6SZ7Wwm8NV7riw +woW15A1N2axuaAAcCxf9T48uehAmXrfApJygl2PWeKzzATUAuGzLLmQ0hNGVvUraxCJfiehtnMl +kWUiSZgjvmXKv6N2JtN8dHMHVEzPTBou4a25ozQIRAIGFvZYcDm5DW4CNJqFM1mTv2BEeOCW5hw +Bt60xm8kXOX4OGwgEyB/aHttWHPdAiFUoODo5j4MtcvajuWt +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=4 l= 257 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48.pem b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48.pem new file mode 100644 index 0000000..ac65a9c --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48.pem @@ -0,0 +1,63 @@ + + + + +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVDcg1BMqPneiNBL5+mjEB5mxjJzgkm +ClZR5z1jNht+As6+Mlgflni0bB8LjhWbIt+dZ6Bt4cSHOnAOnkMDOFwtrxJE6Eg1GQ2ux9nDVNv +rQkoOLznXrxMh/af0pcSo8kItDmkqbV/fi3Q7agpbcWc/4wTZOfO6lns4nb5s08oaUv3uF5Wc0s +ktNr6he6R3zSQ6YK5KZFzQdnEtGc4gwHWXZ9xt4JeANht3m4RNpMY89qZsZxmqoewYHuXQUAfl7 +W0DC3hoxOoLwSqL2bt2zMMeR8WAo51YY0cJnzAEETcnWIM6ealb5Osj3iSEknxRTley47SsODQ0 +maUUWj8wEjwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAICBQCiAwIBMA== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha384 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha384 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :30 + + + +-----BEGIN DATA----- +TDRlz5dKOqfOQhirwHj00bsVlf+0WEe2qMe9l6SVr9SHB4Eow26r+aU7+pGZFp774O041xIeU2g +ZHYzNWBjGZQ== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4IBAQBvlL4AuwL3hEobMolBzR/0gzuJ9u4ATWEPO5uTiBtdJ5Nx9O6gFCrtZMwfEU9q4bzazKV +yWRSpn23GZjlmNYhFCNlfY3l6IlhxGEVz/YeOglrBR8hFbA17835jTmcCR09G6SZ7Wwm8NV7riw +woW15A1N2axuaAAcCxf9T48uehAmXrfApJygl2PWeKzzATUAuGzLLmQ0hNGVvUraxCJfiehtnMl +kWUiSZgjvmXKv6N2JtN8dHMHVEzPTBou4a25ozQIRAIGFvZYcDm5DW4CNJqFM1mTv2BEeOCW5hw +Bt60xm8kXOX4OGwgEyB/aHttWHPdAiFUoODo5j4MtcvajuWt +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=4 l= 257 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64-corrupted-data.pem b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64-corrupted-data.pem new file mode 100644 index 0000000..7696270 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64-corrupted-data.pem @@ -0,0 +1,63 @@ +This has had DATA corrupted, so the signature is not valid. + + + +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVDcg1BMqPneiNBL5+mjEB5mxjJzgkm +ClZR5z1jNht+As6+Mlgflni0bB8LjhWbIt+dZ6Bt4cSHOnAOnkMDOFwtrxJE6Eg1GQ2ux9nDVNv +rQkoOLznXrxMh/af0pcSo8kItDmkqbV/fi3Q7agpbcWc/4wTZOfO6lns4nb5s08oaUv3uF5Wc0s +ktNr6he6R3zSQ6YK5KZFzQdnEtGc4gwHWXZ9xt4JeANht3m4RNpMY89qZsZxmqoewYHuXQUAfl7 +W0DC3hoxOoLwSqL2bt2zMMeR8WAo51YY0cJnzAEETcnWIM6ealb5Osj3iSEknxRTley47SsODQ0 +maUUWj8wEjwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIDBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAIDBQCiAwIBQA== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha512 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha512 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :40 + + + +-----BEGIN DATA----- +BENGYY+GrDsvawb08kP/OZ0iWbG5yBlJpCIJ1YLPfTCjEouvBzwAkWpUEsI3zk0N8+xcMyJ3qOi +pIsX4YnFfPw== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4IBAQBPA1K787NaEycdAHDfil1/F2imI9PcVD5ZuloBz9Qj1q4ZfdZ9PMagunDBVRQoBr1VDhI +6VkDfyQvhKebIbSsfk/qJoNZGCZtsKhXcGm5ZI2+fUbbMW7EwlKle8SqXCHRAIICND/qwundcqp +kLNHOqOK8GRUYHnJcMmQbMCBUx9aw3IRu2LRp6FtBwA16stpSat/NlX+aH79f1B/uoFpDVzG7Kw +oqmAuv81vOVQSCNTn4MrCyxmJTLqbk6frXN7nRF+SQOPksUwXXYgpzGyFhrwgUHwkc3skNx/jOT +fpWnvjOUVbi80Sa9i7EIOcmt4IP4a3BRPWT/MTYDDPADIgVf +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=4 l= 257 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64.pem b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64.pem new file mode 100644 index 0000000..3504cc8 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64.pem @@ -0,0 +1,63 @@ + + + + +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVDcg1BMqPneiNBL5+mjEB5mxjJzgkm +ClZR5z1jNht+As6+Mlgflni0bB8LjhWbIt+dZ6Bt4cSHOnAOnkMDOFwtrxJE6Eg1GQ2ux9nDVNv +rQkoOLznXrxMh/af0pcSo8kItDmkqbV/fi3Q7agpbcWc/4wTZOfO6lns4nb5s08oaUv3uF5Wc0s +ktNr6he6R3zSQ6YK5KZFzQdnEtGc4gwHWXZ9xt4JeANht3m4RNpMY89qZsZxmqoewYHuXQUAfl7 +W0DC3hoxOoLwSqL2bt2zMMeR8WAo51YY0cJnzAEETcnWIM6ealb5Osj3iSEknxRTley47SsODQ0 +maUUWj8wEjwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIDBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAIDBQCiAwIBQA== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha512 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha512 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :40 + + + +-----BEGIN DATA----- +BEnGYY+GrDsvawb08kP/OZ0iWbG5yBlJpCIJ1YLPfTCjEouvBzwAkWpUEsI3zk0N8+xcMyJ3qOi +pIsX4YnFfPw== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4IBAQBPA1K787NaEycdAHDfil1/F2imI9PcVD5ZuloBz9Qj1q4ZfdZ9PMagunDBVRQoBr1VDhI +6VkDfyQvhKebIbSsfk/qJoNZGCZtsKhXcGm5ZI2+fUbbMW7EwlKle8SqXCHRAIICND/qwundcqp +kLNHOqOK8GRUYHnJcMmQbMCBUx9aw3IRu2LRp6FtBwA16stpSat/NlX+aH79f1B/uoFpDVzG7Kw +oqmAuv81vOVQSCNTn4MrCyxmJTLqbk6frXN7nRF+SQOPksUwXXYgpzGyFhrwgUHwkc3skNx/jOT +fpWnvjOUVbi80Sa9i7EIOcmt4IP4a3BRPWT/MTYDDPADIgVf +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=4 l= 257 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-length.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-length.pem new file mode 100644 index 0000000..ef7967d --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-length.pem @@ -0,0 +1,44 @@ +Same test as rsa-pkcs1-sha1.pem except the length of the first SEQUENCE has +been increased by 2 (which makes it invalid). + + + +-----BEGIN PUBLIC KEY----- +MIOfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLsDjatUqRN/rH +mH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2VCAltWyuLbfXWce9jd8CSHL +I8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh4fINDOjP+yJJvZohNwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] +Error in encoding + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBBQUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAGvDoGZWhCkwokfjDVhktNgZI2unxollhirX28TiSvKOhrtTHwM1i+X7dHd8YIb4UMrviT8 +Nb8wtDJHsATaTtOoAuAzUmqxOy1+JEa/lOa2kqPOCPR0T5HLRSQVHxlnHYX89JAh9228rcglhZ/ +wJfKsY6aRY/LY0zc6O41iUxITX +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-null.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-null.pem new file mode 100644 index 0000000..59559f4 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-null.pem @@ -0,0 +1,52 @@ +Same test as rsa-pkcs1-sha1.pem except an extra NULL (0x05, 0x00) has been +appended to the SPKI. + +The DER can still be parsed, however it should fail due to the unconsumed data +at the end. + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLsDjatUqRN/rH +mH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2VCAltWyuLbfXWce9jd8CSHL +I8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh4fINDOjP+yJJvZohNwIDAQABBQA= +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + 162:d=0 hl=2 l= 0 prim: NULL + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBBQUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAGvDoGZWhCkwokfjDVhktNgZI2unxollhirX28TiSvKOhrtTHwM1i+X7dHd8YIb4UMrviT8 +Nb8wtDJHsATaTtOoAuAzUmqxOy1+JEa/lOa2kqPOCPR0T5HLRSQVHxlnHYX89JAh9228rcglhZ/ +wJfKsY6aRY/LY0zc6O41iUxITX +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-key-params-absent.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-key-params-absent.pem new file mode 100644 index 0000000..10cc3d0 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-key-params-absent.pem @@ -0,0 +1,49 @@ +Same test as rsa-pkcs1-sha1.pem, except the SPKI has been modified so the +algorithm parameters are absent rather than NULL. + +This should fail because RFC 3279 says the parameters MUST be NULL. + + + +-----BEGIN PUBLIC KEY----- +MIGdMAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEApW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h9 +62ZHYxRBW/+2xYrTA8oOhKoijlN/1JqtykcuzB86r/OCx39XNlQgJbVsri2311nHvY3fAkhyyPC +cKcOJZjm/4nRnxBazC0/DLNfKSgOE4a29kxO8i4eHyDQzoz/siSb2aITcCAwEAAQ== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 157 cons: SEQUENCE + 3:d=1 hl=2 l= 11 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBBQUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAGvDoGZWhCkwokfjDVhktNgZI2unxollhirX28TiSvKOhrtTHwM1i+X7dHd8YIb4UMrviT8 +Nb8wtDJHsATaTtOoAuAzUmqxOy1+JEa/lOa2kqPOCPR0T5HLRSQVHxlnHYX89JAh9228rcglhZ/ +wJfKsY6aRY/LY0zc6O41iUxITX +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-using-pss-key-no-params.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-using-pss-key-no-params.pem new file mode 100644 index 0000000..0dfff97 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-using-pss-key-no-params.pem @@ -0,0 +1,51 @@ +This is the same test as rsa-pkcs1-sha1.pem, except the SPKI has been modified +so that the key algorithm is rsaPss (1.2.840.113549.1.1.10) with absent +parameters. + +Subsequently this should fail, as a PSS key should not be used with a signature +algorithm for PKCS#1 v1.5. + + + +-----BEGIN PUBLIC KEY----- +MIGdMAsGCSqGSIb3DQEBCgOBjQAwgYkCgYEApW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h9 +62ZHYxRBW/+2xYrTA8oOhKoijlN/1JqtykcuzB86r/OCx39XNlQgJbVsri2311nHvY3fAkhyyPC +cKcOJZjm/4nRnxBazC0/DLNfKSgOE4a29kxO8i4eHyDQzoz/siSb2aITcCAwEAAQ== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 157 cons: SEQUENCE + 3:d=1 hl=2 l= 11 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsassaPss + 16:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBBQUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAGvDoGZWhCkwokfjDVhktNgZI2unxollhirX28TiSvKOhrtTHwM1i+X7dHd8YIb4UMrviT8 +Nb8wtDJHsATaTtOoAuAzUmqxOy1+JEa/lOa2kqPOCPR0T5HLRSQVHxlnHYX89JAh9228rcglhZ/ +wJfKsY6aRY/LY0zc6O41iUxITX +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-wrong-algorithm.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-wrong-algorithm.pem new file mode 100644 index 0000000..9aaedba --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-wrong-algorithm.pem @@ -0,0 +1,48 @@ +This is the same as rsa-pkcs1-sha1.pem, however the ALGORITHM has been change +to have SHA256 instead of SHA1. Using this algorithm verification should fail. + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLsDjatUqRN/rH +mH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2VCAltWyuLbfXWce9jd8CSHL +I8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh4fINDOjP+yJJvZohNwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBCwUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAGvDoGZWhCkwokfjDVhktNgZI2unxollhirX28TiSvKOhrtTHwM1i+X7dHd8YIb4UMrviT8 +Nb8wtDJHsATaTtOoAuAzUmqxOy1+JEa/lOa2kqPOCPR0T5HLRSQVHxlnHYX89JAh9228rcglhZ/ +wJfKsY6aRY/LY0zc6O41iUxITX +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1.pem new file mode 100644 index 0000000..0972aca --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1.pem @@ -0,0 +1,53 @@ +The key, message, and signature come from Example 1 of: +ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt + +(The algorithm DER was synthesized to match, and the signature enclosed in a BIT STRING). + +It uses an RSA key with modulus length of 1024 bits, PKCS#1 v1.5 padding, and +SHA-1 as the digest. + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLsDjatUqRN/rH +mH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2VCAltWyuLbfXWce9jd8CSHL +I8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh4fINDOjP+yJJvZohNwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBBQUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAGvDoGZWhCkwokfjDVhktNgZI2unxollhirX28TiSvKOhrtTHwM1i+X7dHd8YIb4UMrviT8 +Nb8wtDJHsATaTtOoAuAzUmqxOy1+JEa/lOa2kqPOCPR0T5HLRSQVHxlnHYX89JAh9228rcglhZ/ +wJfKsY6aRY/LY0zc6O41iUxITX +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-key-encoded-ber.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-key-encoded-ber.pem new file mode 100644 index 0000000..2a8db4a --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-key-encoded-ber.pem @@ -0,0 +1,62 @@ +This is the same test as rsa-pkcs1-sha256.pem except the SPKI has been encoded +using a non-minimal length for the outtermost SEQUENCE. + +Under DER, the tag-length-value encodings should be minimal and hence this should fail. + +Specifically the SPKI start was changed from: + 30 81 9f +To: + 30 82 00 9f + +(the length of 0x9F is being expressed using 2 bytes instead of 1) + + + +-----BEGIN PUBLIC KEY----- +MIIAnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqkfgdjI9YqzadSZ2Ns0CEEUD8+8m7OplIx0 +94X+QD8mooNrunwT04asbLIINGL4qiI/+9IVSvyV3Kj9c4EeQIbANGoJ8AI3wf6MOBB/txxGFed +qqcTffKVMQvtZdoYFbZ/MQkvyRsoyvunb/pWcN4sSaF9kY1bXSeP3J99fBIYUCAwEAAQ== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=4 l= 159 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBCwUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +MIIB46ADAgECAgkA3l4tFOVii0UwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCQVUxEzARBgN +VBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1 +UEAwwGTXkga2V5MB4XDTE1MDcwMjE3MDYzOVoXDTE2MDcwMTE3MDYzOVowVjELMAkGA1UEBhMCQ +VUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGTXkga2V5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqR+B2Mj1irNp +1JnY2zQIQRQPz7ybs6mUjHT3hf5APyaig2u6fBPThqxssgg0YviqIj/70hVK/JXcqP1zgR5AhsA +0agnwAjfB/ow4EH+3HEYV52qpxN98pUxC+1l2hgVtn8xCS/JGyjK+6dv+lZw3ixJoX2RjVtdJ4/ +cn318EhhQIDAQABo1AwTjAdBgNVHQ4EFgQUzQBVKTEknyLndWd2HTsBdTKvyikwHwYDVR0jBBgw +FoAUzQBVKTEknyLndWd2HTsBdTKvyikwDAYDVR0TBAUwAwEB/w== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBADrHSmFSJw/Gv7hs5PNzpaJwAri/sitarIZfzN/SjR+n8L8yeTEoiDb1+BkxlFvXvPHTaOK +oO3WlslNNOxh1W5/JkYYGOUkCcyIjnln6qS560imcr3VNjomT/M8M2Iss+rJiKau1TRuaP7H8i6 ++Gqf3saGdr8/LnvFAdNQvkalQt +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-spki-non-null-params.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-spki-non-null-params.pem new file mode 100644 index 0000000..4e7fc96 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-spki-non-null-params.pem @@ -0,0 +1,59 @@ +This is the same test as rsa-pkcs1-sha256.pem except the SPKI has been tampered +with. The parameters have been changed from NULL to an INTEGER. + +This was done by changing: + + 05 00 (NULL) +To: + 02 00 (INTEGER) + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQIAA4GNADCBiQKBgQCqR+B2Mj1irNp1JnY2zQIQRQPz7ybs6mUjHT3 +hf5APyaig2u6fBPThqxssgg0YviqIj/70hVK/JXcqP1zgR5AhsA0agnwAjfB/ow4EH+3HEYV52q +pxN98pUxC+1l2hgVtn8xCS/JGyjK+6dv+lZw3ixJoX2RjVtdJ4/cn318EhhQIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: INTEGER :00 + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBCwUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +MIIB46ADAgECAgkA3l4tFOVii0UwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCQVUxEzARBgN +VBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1 +UEAwwGTXkga2V5MB4XDTE1MDcwMjE3MDYzOVoXDTE2MDcwMTE3MDYzOVowVjELMAkGA1UEBhMCQ +VUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGTXkga2V5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqR+B2Mj1irNp +1JnY2zQIQRQPz7ybs6mUjHT3hf5APyaig2u6fBPThqxssgg0YviqIj/70hVK/JXcqP1zgR5AhsA +0agnwAjfB/ow4EH+3HEYV52qpxN98pUxC+1l2hgVtn8xCS/JGyjK+6dv+lZw3ixJoX2RjVtdJ4/ +cn318EhhQIDAQABo1AwTjAdBgNVHQ4EFgQUzQBVKTEknyLndWd2HTsBdTKvyikwHwYDVR0jBBgw +FoAUzQBVKTEknyLndWd2HTsBdTKvyikwDAYDVR0TBAUwAwEB/w== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBADrHSmFSJw/Gv7hs5PNzpaJwAri/sitarIZfzN/SjR+n8L8yeTEoiDb1+BkxlFvXvPHTaOK +oO3WlslNNOxh1W5/JkYYGOUkCcyIjnln6qS560imcr3VNjomT/M8M2Iss+rJiKau1TRuaP7H8i6 ++Gqf3saGdr8/LnvFAdNQvkalQt +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-ecdsa-algorithm.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-ecdsa-algorithm.pem new file mode 100644 index 0000000..a9b9eb9 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-ecdsa-algorithm.pem @@ -0,0 +1,55 @@ +This test specified a valid RSA PKCS#1 v.1.5 signature and RSA key (the same as rsa-pkcs1-sha256.pem). + +The problem however is the signature algorithm is indicated as being ECDSA. + +Signature verification consequently should fail. + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqR+B2Mj1irNp1JnY2zQIQRQPz7ybs6mUjHT3 +hf5APyaig2u6fBPThqxssgg0YviqIj/70hVK/JXcqP1zgR5AhsA0agnwAjfB/ow4EH+3HEYV52q +pxN98pUxC+1l2hgVtn8xCS/JGyjK+6dv+lZw3ixJoX2RjVtdJ4/cn318EhhQIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MAoGCCqGSM49BAMC +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 10 cons: SEQUENCE + 2:d=1 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA256 + + + +-----BEGIN DATA----- +MIIB46ADAgECAgkA3l4tFOVii0UwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCQVUxEzARBgN +VBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1 +UEAwwGTXkga2V5MB4XDTE1MDcwMjE3MDYzOVoXDTE2MDcwMTE3MDYzOVowVjELMAkGA1UEBhMCQ +VUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGTXkga2V5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqR+B2Mj1irNp +1JnY2zQIQRQPz7ybs6mUjHT3hf5APyaig2u6fBPThqxssgg0YviqIj/70hVK/JXcqP1zgR5AhsA +0agnwAjfB/ow4EH+3HEYV52qpxN98pUxC+1l2hgVtn8xCS/JGyjK+6dv+lZw3ixJoX2RjVtdJ4/ +cn318EhhQIDAQABo1AwTjAdBgNVHQ4EFgQUzQBVKTEknyLndWd2HTsBdTKvyikwHwYDVR0jBBgw +FoAUzQBVKTEknyLndWd2HTsBdTKvyikwDAYDVR0TBAUwAwEB/w== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBADrHSmFSJw/Gv7hs5PNzpaJwAri/sitarIZfzN/SjR+n8L8yeTEoiDb1+BkxlFvXvPHTaOK +oO3WlslNNOxh1W5/JkYYGOUkCcyIjnln6qS560imcr3VNjomT/M8M2Iss+rJiKau1TRuaP7H8i6 ++Gqf3saGdr8/LnvFAdNQvkalQt +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-id-ea-rsa.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-id-ea-rsa.pem new file mode 100644 index 0000000..dd5d39c --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-id-ea-rsa.pem @@ -0,0 +1,54 @@ +This is the same test as rsa-pkcs1-sha256.pem except the SPKI has been tampered +with. Rather than using an rsaEncryption OID for the key's algorithm, it uses +id-ea-rsa (2.5.8.1.1). + + + +-----BEGIN PUBLIC KEY----- +MIGaMAgGBFUIAQEFAAOBjQAwgYkCgYEAqkfgdjI9YqzadSZ2Ns0CEEUD8+8m7OplIx094X+QD8m +ooNrunwT04asbLIINGL4qiI/+9IVSvyV3Kj9c4EeQIbANGoJ8AI3wf6MOBB/txxGFedqqcTffKV +MQvtZdoYFbZ/MQkvyRsoyvunb/pWcN4sSaF9kY1bXSeP3J99fBIYUCAwEAAQ== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 154 cons: SEQUENCE + 3:d=1 hl=2 l= 8 cons: SEQUENCE + 5:d=2 hl=2 l= 4 prim: OBJECT :rsa + 11:d=2 hl=2 l= 0 prim: NULL + 13:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBCwUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +MIIB46ADAgECAgkA3l4tFOVii0UwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCQVUxEzARBgN +VBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1 +UEAwwGTXkga2V5MB4XDTE1MDcwMjE3MDYzOVoXDTE2MDcwMTE3MDYzOVowVjELMAkGA1UEBhMCQ +VUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGTXkga2V5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqR+B2Mj1irNp +1JnY2zQIQRQPz7ybs6mUjHT3hf5APyaig2u6fBPThqxssgg0YviqIj/70hVK/JXcqP1zgR5AhsA +0agnwAjfB/ow4EH+3HEYV52qpxN98pUxC+1l2hgVtn8xCS/JGyjK+6dv+lZw3ixJoX2RjVtdJ4/ +cn318EhhQIDAQABo1AwTjAdBgNVHQ4EFgQUzQBVKTEknyLndWd2HTsBdTKvyikwHwYDVR0jBBgw +FoAUzQBVKTEknyLndWd2HTsBdTKvyikwDAYDVR0TBAUwAwEB/w== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBADrHSmFSJw/Gv7hs5PNzpaJwAri/sitarIZfzN/SjR+n8L8yeTEoiDb1+BkxlFvXvPHTaOK +oO3WlslNNOxh1W5/JkYYGOUkCcyIjnln6qS560imcr3VNjomT/M8M2Iss+rJiKau1TRuaP7H8i6 ++Gqf3saGdr8/LnvFAdNQvkalQt +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256.pem b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256.pem new file mode 100644 index 0000000..8509111 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256.pem @@ -0,0 +1,86 @@ +This test data was produced by creating a self-signed EC cert using OpenSSL, +and then extracting the relevant fields. + +It uses RSA PKCS#1 v1.5 with SHA-256 and a 1024-bit key. + +(1) Generate self-signed certificate + + openssl genrsa -out rsa_key.pem 1024 + openssl req -new -key rsa_key.pem -x509 -nodes -days 365 -out cert.pem + +(2) Extract public key + + openssl x509 -in cert.pem -pubkey -noout > pubkey.pem + cat pubkey.pem + +(3) Extract signed data (tbsCertificate) + + openssl asn1parse -in cert.pem -out tbs -noout -strparse 4 + base64 tbs + +(4) Extract signature algorithm + + # Find the offset of the signature algorithm near the end (491 in this case) + openssl asn1parse -in cert.pem + + openssl asn1parse -in cert.pem -out alg -noout -strparse 491 + base64 alg + +(5) Extract the signature + + # Find the final offset of BIT STRING (506 in this case) + openssl asn1parse -in cert.pem + + openssl asn1parse -in cert.pem -out sig -noout -strparse 506 + base64 sig + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqR+B2Mj1irNp1JnY2zQIQRQPz7ybs6mUjHT3 +hf5APyaig2u6fBPThqxssgg0YviqIj/70hVK/JXcqP1zgR5AhsA0agnwAjfB/ow4EH+3HEYV52q +pxN98pUxC+1l2hgVtn8xCS/JGyjK+6dv+lZw3ixJoX2RjVtdJ4/cn318EhhQIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBCwUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +MIIB46ADAgECAgkA3l4tFOVii0UwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCQVUxEzARBgN +VBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1 +UEAwwGTXkga2V5MB4XDTE1MDcwMjE3MDYzOVoXDTE2MDcwMTE3MDYzOVowVjELMAkGA1UEBhMCQ +VUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGTXkga2V5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqR+B2Mj1irNp +1JnY2zQIQRQPz7ybs6mUjHT3hf5APyaig2u6fBPThqxssgg0YviqIj/70hVK/JXcqP1zgR5AhsA +0agnwAjfB/ow4EH+3HEYV52qpxN98pUxC+1l2hgVtn8xCS/JGyjK+6dv+lZw3ixJoX2RjVtdJ4/ +cn318EhhQIDAQABo1AwTjAdBgNVHQ4EFgQUzQBVKTEknyLndWd2HTsBdTKvyikwHwYDVR0jBBgw +FoAUzQBVKTEknyLndWd2HTsBdTKvyikwDAYDVR0TBAUwAwEB/w== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBADrHSmFSJw/Gv7hs5PNzpaJwAri/sitarIZfzN/SjR+n8L8yeTEoiDb1+BkxlFvXvPHTaOK +oO3WlslNNOxh1W5/JkYYGOUkCcyIjnln6qS560imcr3VNjomT/M8M2Iss+rJiKau1TRuaP7H8i6 ++Gqf3saGdr8/LnvFAdNQvkalQt +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-no-params.pem b/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-no-params.pem new file mode 100644 index 0000000..503cc2e --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-no-params.pem @@ -0,0 +1,48 @@ +This is the same test as rsa-pss-sha1-salt20.pem, except the public key's +algorithm identifier has been changed from rsaEncryption (1.2.840.113549.1.1.1) +to rsaPss (1.2.840.113549.1.1.10). + + + +-----BEGIN PUBLIC KEY----- +MIGdMAsGCSqGSIb3DQEBCgOBjQAwgYkCgYEApW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h9 +62ZHYxRBW/+2xYrTA8oOhKoijlN/1JqtykcuzB86r/OCx39XNlQgJbVsri2311nHvY3fAkhyyPC +cKcOJZjm/4nRnxBazC0/DLNfKSgOE4a29kxO8i4eHyDQzoz/siSb2aITcCAwEAAQ== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 157 cons: SEQUENCE + 3:d=1 hl=2 l= 11 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsassaPss + 16:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBCjAA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 0 cons: SEQUENCE + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAJB0MI+1mOlwGyKUOI5S+XH6rCtgpRRa8YXfUoe17SiH5Xzn/UTchjTkB8jg5DYLwibz7CJ +/nZ5UY46NMfUFEhXfbrucL5V5qndZijj5FLW5wb2DxOL584Kg0Ko1Qv/uZZhKYBvGnrKN6yfcoS +yCwtTD9mzVAPH/K5lNik4wy7M8 +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-with-null-params.pem b/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-with-null-params.pem new file mode 100644 index 0000000..222614b --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-with-null-params.pem @@ -0,0 +1,50 @@ +This is the same test as rsa-pss-sha1-salt20.pem, except the public key's +algorithm identifier has been changed from rsaEncryption (1.2.840.113549.1.1.1) +to rsaPss (1.2.840.113549.1.1.10). Note that the PSS parameters have been +encoded as NULL which is incorrect. + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBCgUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLsDjatUqRN/rH +mH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2VCAltWyuLbfXWce9jd8CSHL +I8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh4fINDOjP+yJJvZohNwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsassaPss + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBCjAA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 0 cons: SEQUENCE + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAJB0MI+1mOlwGyKUOI5S+XH6rCtgpRRa8YXfUoe17SiH5Xzn/UTchjTkB8jg5DYLwibz7CJ +/nZ5UY46NMfUFEhXfbrucL5V5qndZijj5FLW5wb2DxOL584Kg0Ko1Qv/uZZhKYBvGnrKN6yfcoS +yCwtTD9mzVAPH/K5lNik4wy7M8 +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20.pem b/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20.pem new file mode 100644 index 0000000..e56f0fe --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20.pem @@ -0,0 +1,53 @@ +The key, message, and signature come from Example 1.1 of: +ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip (pss-vect.txt) + +(The algorithm DER was synthesized to match, and the signature enclosed in a BIT STRING). + +It uses an RSA key with modulus length of 1024 bits, PSS padding, +SHA-1 as the digest, MGF1 with SHA-1, and salt length of 20. + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLsDjatUqRN/rH +mH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2VCAltWyuLbfXWce9jd8CSHL +I8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh4fINDOjP+yJJvZohNwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBCjAA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 0 cons: SEQUENCE + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAJB0MI+1mOlwGyKUOI5S+XH6rCtgpRRa8YXfUoe17SiH5Xzn/UTchjTkB8jg5DYLwibz7CJ +/nZ5UY46NMfUFEhXfbrucL5V5qndZijj5FLW5wb2DxOL584Kg0Ko1Qv/uZZhKYBvGnrKN6yfcoS +yCwtTD9mzVAPH/K5lNik4wy7M8 +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-wrong-salt.pem b/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-wrong-salt.pem new file mode 100644 index 0000000..57ec775 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pss-sha1-wrong-salt.pem @@ -0,0 +1,51 @@ +Same as rsa-pss-sha1-wrong-salt.pem except the ALGORITHM has been changed to +have a salt of 23. When verified using this algorithm it will fail, however if +the default salt of 20 were used it would succeed. + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLsDjatUqRN/rH +mH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2VCAltWyuLbfXWce9jd8CSHL +I8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh4fINDOjP+yJJvZohNwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MBIGCSqGSIb3DQEBCjAFogMCARc= +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 18 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 5 cons: SEQUENCE + 15:d=2 hl=2 l= 3 cons: cont [ 2 ] + 17:d=3 hl=2 l= 1 prim: INTEGER :17 + + + +-----BEGIN DATA----- +zch9oiPXht87ReC7vHITJtHuKvgGzDFUdcxvDZxm4bYjcdRc4jkuGskoRMMQEC8Vag2NUsH0xAu +jqmUJV4bLdpdXplY7qVj+0LzJhOi1F6PV9RWyO4pB50qoZ2k/kN+wYabobfqu5kRywA5fIJRXKc +vr538Gznjgj0CY+6QfnWGTwDF+i2DUtghKy0LSnjgIo7w3LYXjMRcPy/fMctC3HClmSLOk0Q9BY +pXQgHqmJcqydE/Z6o/SI8QlNwKYKL0WvgJUbxMP0uM7k20mduCK7RtzMYt1CgFn0A== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAJB0MI+1mOlwGyKUOI5S+XH6rCtgpRRa8YXfUoe17SiH5Xzn/UTchjTkB8jg5DYLwibz7CJ +/nZ5UY46NMfUFEhXfbrucL5V5qndZijj5FLW5wb2DxOL584Kg0Ko1Qv/uZZhKYBvGnrKN6yfcoS +yCwtTD9mzVAPH/K5lNik4wy7M8 +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-mgf1-sha512-salt33.pem b/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-mgf1-sha512-salt33.pem new file mode 100644 index 0000000..f3b9dcb --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-mgf1-sha512-salt33.pem @@ -0,0 +1,67 @@ +This test exercises using a different hash function parameter to the mask gen +function (SHA-256 for the hash, but SHA-512 for the MGF1 hash). + +This test data was constructed manually by calling signing functions from +OpenSSL code. + +It constructs an RSASSA-PSS signature using: + * Key with modulus 1024 bit + * Salt length 33 bytes + * Digest function of SHA-256 + * Mask gen function of MGF1 with SHA-512 + + + +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLsDjatUqRN/rH +mH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2VCAltWyuLbfXWce9jd8CSHL +I8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh4fINDOjP+yJJvZohNwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAIDBQCiAwIBIQ== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha256 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha512 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :21 + + + +-----BEGIN DATA----- +VGVzdCBtZXNzYWdlIHRvIGJlIHNpZ25lZC4uLg== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBAFob0HSC5uuTqKu4J/lj+5bDa+Hhij4H3klWnvt6Yc+wwPza7/UC4lgGGyvZqD32RUEdt7v +Z14qqYNk53b5aj4C2gBMvLzV7Pay4mmQM4DSWa5JHMxTILqE3DDqihrbMcBw2q3XAsLcjeqLWQ9 +yp8tfnV21h98qsCLtErrxZWHRr +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-params.pem b/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-params.pem new file mode 100644 index 0000000..e0140b3 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-params.pem @@ -0,0 +1,74 @@ +This is the same test as rsa-pss-sha256-salt10.pem except instead of specifying +the SPKI using rsaEncryption it is specified using rsaPss along with +parameters that match those of the signature algorithm. + + + +-----BEGIN PUBLIC KEY----- +MIHRMEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZ +IAWUDBAIBBQCiAwIBCgOBiwAwgYcCgYEAvkmbXn8GyD+gKT4xRlyOtrWK+SC65Sp7W5v+t6py2x +JkES6z/UMdMaKn5QlBVmkpSUoOiR7VYTkYtLUbDR+5d4Oyas99DzhM+zX00oJPXdOAYjomvxgLY +5YcYZ3NsgyuQG8i9uJ2yAo3JZSQz+tywacahPGEbTMId7o+MQHsnHsCARE= +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 209 cons: SEQUENCE + 3:d=1 hl=2 l= 65 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsassaPss + 16:d=2 hl=2 l= 52 cons: SEQUENCE + 18:d=3 hl=2 l= 15 cons: cont [ 0 ] + 20:d=4 hl=2 l= 13 cons: SEQUENCE + 22:d=5 hl=2 l= 9 prim: OBJECT :sha256 + 33:d=5 hl=2 l= 0 prim: NULL + 35:d=3 hl=2 l= 28 cons: cont [ 1 ] + 37:d=4 hl=2 l= 26 cons: SEQUENCE + 39:d=5 hl=2 l= 9 prim: OBJECT :mgf1 + 50:d=5 hl=2 l= 13 cons: SEQUENCE + 52:d=6 hl=2 l= 9 prim: OBJECT :sha256 + 63:d=6 hl=2 l= 0 prim: NULL + 65:d=3 hl=2 l= 3 cons: cont [ 2 ] + 67:d=4 hl=2 l= 1 prim: INTEGER :0A + 70:d=1 hl=3 l= 139 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAIBBQCiAwIBCg== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha256 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha256 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :0A + + + +-----BEGIN DATA----- +x/UnD8pyX5vRn1GajXzKPMXAeQJAKfO65RD5sCFA/iOJCOT2wY8HqJxofIaEZpsfHbK6+SUaPIK +frMtJMIThbsnijViGgHSl1iIWZ91uUo0W/iyfPbTPr2xNzoyEOa84zqqqnOLsrnvI9KWlXjv5bf +nNV1xPnLMnlRuM3+QIcWg= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBABHhafL9QLB2Qbl2iiqxmWX7bCfxD88DI/zG0S608cBrMw3aoepQRAevop3p6+A3T+nR59D +/vV/Bzzo0RuQUVBXSqyT3ibNGTFxDola7wdaSz38EgB2sW7QBpKA6t9VyioYMGeGk3Hl8pULIID +zsLmAesMUfVn8u2gIrC5693u76 +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-wrong-params.pem b/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-wrong-params.pem new file mode 100644 index 0000000..646ac1f --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-wrong-params.pem @@ -0,0 +1,74 @@ +This is the same test as rsa-pss-sha256-salt10-using-pss-key-with-params.pem +except the hash in the PSS key's parameters has been changed from SHA-256 to +SHA-384. + + + +-----BEGIN PUBLIC KEY----- +MIHRMEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZ +IAWUDBAIBBQCiAwIBCgOBiwAwgYcCgYEAvkmbXn8GyD+gKT4xRlyOtrWK+SC65Sp7W5v+t6py2x +JkES6z/UMdMaKn5QlBVmkpSUoOiR7VYTkYtLUbDR+5d4Oyas99DzhM+zX00oJPXdOAYjomvxgLY +5YcYZ3NsgyuQG8i9uJ2yAo3JZSQz+tywacahPGEbTMId7o+MQHsnHsCARE= +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 209 cons: SEQUENCE + 3:d=1 hl=2 l= 65 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsassaPss + 16:d=2 hl=2 l= 52 cons: SEQUENCE + 18:d=3 hl=2 l= 15 cons: cont [ 0 ] + 20:d=4 hl=2 l= 13 cons: SEQUENCE + 22:d=5 hl=2 l= 9 prim: OBJECT :sha384 + 33:d=5 hl=2 l= 0 prim: NULL + 35:d=3 hl=2 l= 28 cons: cont [ 1 ] + 37:d=4 hl=2 l= 26 cons: SEQUENCE + 39:d=5 hl=2 l= 9 prim: OBJECT :mgf1 + 50:d=5 hl=2 l= 13 cons: SEQUENCE + 52:d=6 hl=2 l= 9 prim: OBJECT :sha256 + 63:d=6 hl=2 l= 0 prim: NULL + 65:d=3 hl=2 l= 3 cons: cont [ 2 ] + 67:d=4 hl=2 l= 1 prim: INTEGER :0A + 70:d=1 hl=3 l= 139 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAIBBQCiAwIBCg== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha256 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha256 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :0A + + + +-----BEGIN DATA----- +x/UnD8pyX5vRn1GajXzKPMXAeQJAKfO65RD5sCFA/iOJCOT2wY8HqJxofIaEZpsfHbK6+SUaPIK +frMtJMIThbsnijViGgHSl1iIWZ91uUo0W/iyfPbTPr2xNzoyEOa84zqqqnOLsrnvI9KWlXjv5bf +nNV1xPnLMnlRuM3+QIcWg= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBABHhafL9QLB2Qbl2iiqxmWX7bCfxD88DI/zG0S608cBrMw3aoepQRAevop3p6+A3T+nR59D +/vV/Bzzo0RuQUVBXSqyT3ibNGTFxDola7wdaSz38EgB2sW7QBpKA6t9VyioYMGeGk3Hl8pULIID +zsLmAesMUfVn8u2gIrC5693u76 +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10.pem b/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10.pem new file mode 100644 index 0000000..fc37f41 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10.pem @@ -0,0 +1,65 @@ +The key, message, and signature come from: +http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-2rsatestvectors.zip (SigVerPSS_186-3.rsp) + +(The algorithm DER was synthesized to match, and the signature wrapped in a BIT STRING). + +It uses an RSA key with modulus length of 1024 bits, PSS padding, +SHA-256 as the digest, MGF1 with SHA-256, and salt length of 10. + + + +-----BEGIN PUBLIC KEY----- +MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC+SZtefwbIP6ApPjFGXI62tYr5ILrlKntbm/6 +3qnLbEmQRLrP9Qx0xoqflCUFWaSlJSg6JHtVhORi0tRsNH7l3g7Jqz30POEz7NfTSgk9d04BiOi +a/GAtjlhxhnc2yDK5AbyL24nbICjcllJDP63LBpxqE8YRtMwh3uj4xAeycewIBEQ== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=3 l= 157 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 139 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWU +DBAIBBQCiAwIBCg== +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 65 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :rsassaPss + 13:d=1 hl=2 l= 52 cons: SEQUENCE + 15:d=2 hl=2 l= 15 cons: cont [ 0 ] + 17:d=3 hl=2 l= 13 cons: SEQUENCE + 19:d=4 hl=2 l= 9 prim: OBJECT :sha256 + 30:d=4 hl=2 l= 0 prim: NULL + 32:d=2 hl=2 l= 28 cons: cont [ 1 ] + 34:d=3 hl=2 l= 26 cons: SEQUENCE + 36:d=4 hl=2 l= 9 prim: OBJECT :mgf1 + 47:d=4 hl=2 l= 13 cons: SEQUENCE + 49:d=5 hl=2 l= 9 prim: OBJECT :sha256 + 60:d=5 hl=2 l= 0 prim: NULL + 62:d=2 hl=2 l= 3 cons: cont [ 2 ] + 64:d=3 hl=2 l= 1 prim: INTEGER :0A + + + +-----BEGIN DATA----- +x/UnD8pyX5vRn1GajXzKPMXAeQJAKfO65RD5sCFA/iOJCOT2wY8HqJxofIaEZpsfHbK6+SUaPIK +frMtJMIThbsnijViGgHSl1iIWZ91uUo0W/iyfPbTPr2xNzoyEOa84zqqqnOLsrnvI9KWlXjv5bf +nNV1xPnLMnlRuM3+QIcWg= +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBABHhafL9QLB2Qbl2iiqxmWX7bCfxD88DI/zG0S608cBrMw3aoepQRAevop3p6+A3T+nR59D +/vV/Bzzo0RuQUVBXSqyT3ibNGTFxDola7wdaSz38EgB2sW7QBpKA6t9VyioYMGeGk3Hl8pULIID +zsLmAesMUfVn8u2gIrC5693u76 +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa-using-ec-key.pem b/third-party/chromium/data/verify_signed_data/rsa-using-ec-key.pem new file mode 100644 index 0000000..b9a3777 --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa-using-ec-key.pem @@ -0,0 +1,52 @@ +This test specifies an RSA PKCS#1 v1.5 signature algorithm (and a valid RSA +signature), HOWEVER it provides an EC key. Verification should fail. + + + +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnLDPaTA9r8dh1ORoe07PA55tNKuWSvgIENjVWKS +o1vctUSM6F4iSCobuCKGWLHnvoxf7eHnil9rSFG25lfoceA== +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=2 l= 89 cons: SEQUENCE + 2:d=1 hl=2 l= 19 cons: SEQUENCE + 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey + 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 + 23:d=1 hl=2 l= 66 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBCwUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +MIIB46ADAgECAgkA3l4tFOVii0UwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCQVUxEzARBgN +VBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1 +UEAwwGTXkga2V5MB4XDTE1MDcwMjE3MDYzOVoXDTE2MDcwMTE3MDYzOVowVjELMAkGA1UEBhMCQ +VUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGTXkga2V5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqR+B2Mj1irNp +1JnY2zQIQRQPz7ybs6mUjHT3hf5APyaig2u6fBPThqxssgg0YviqIj/70hVK/JXcqP1zgR5AhsA +0agnwAjfB/ow4EH+3HEYV52qpxN98pUxC+1l2hgVtn8xCS/JGyjK+6dv+lZw3ixJoX2RjVtdJ4/ +cn318EhhQIDAQABo1AwTjAdBgNVHQ4EFgQUzQBVKTEknyLndWd2HTsBdTKvyikwHwYDVR0jBBgw +FoAUzQBVKTEknyLndWd2HTsBdTKvyikwDAYDVR0TBAUwAwEB/w== +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4GBADrHSmFSJw/Gv7hs5PNzpaJwAri/sitarIZfzN/SjR+n8L8yeTEoiDb1+BkxlFvXvPHTaOK +oO3WlslNNOxh1W5/JkYYGOUkCcyIjnln6qS560imcr3VNjomT/M8M2Iss+rJiKau1TRuaP7H8i6 ++Gqf3saGdr8/LnvFAdNQvkalQt +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=3 l= 129 prim: BIT STRING diff --git a/third-party/chromium/data/verify_signed_data/rsa2048-pkcs1-sha512.pem b/third-party/chromium/data/verify_signed_data/rsa2048-pkcs1-sha512.pem new file mode 100644 index 0000000..342a31b --- /dev/null +++ b/third-party/chromium/data/verify_signed_data/rsa2048-pkcs1-sha512.pem @@ -0,0 +1,93 @@ +This test data was produced by creating a self-signed RSA cert using OpenSSL, +and then extracting the relevant fields. + +It uses RSA PKCS#1 v1.5 with SHA-512 and a 2048-bit key. + +(1) Generate self-signed certificate + + openssl genrsa -out rsa_key.pem 2048 + openssl req -new -key rsa_key.pem -x509 -nodes -days 365 -sha512 -out cert.pem + +(2) Extract public key + + openssl x509 -in cert.pem -pubkey -noout > pubkey.pem + cat pubkey.pem + +(3) Extract signed data (tbsCertificate) + + openssl asn1parse -in cert.pem -out tbs -noout -strparse 4 + base64 tbs + +(4) Extract signature algorithm + + # Find the offset of the signature algorithm near the end (589 in this case) + openssl asn1parse -in cert.pem + + openssl asn1parse -in cert.pem -out alg -noout -strparse 589 + base64 alg + +(5) Extract the signature + + # Find the final offset of BIT STRING (506 in this case) + openssl asn1parse -in cert.pem + + openssl asn1parse -in cert.pem -out sig -noout -strparse 506 + base64 sig + + + +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzcu2shJRrXFAwMkf30y2AY1zIg9VF/h +egYcejzdR2AzUb8vU2TXld2i8pp44l+DrvtqmzS7G+yxx3uOx+zsoqBaUT0c9HfkbE+IRmcLkQF +vYpSpm6Eu8OS14CSmEtiR91Et8LR0+bd0Gn3pgmb+epFJBaBPeDSiI/smqKCs7yP04+tS4Q4r47 +G04LhSp4/hmqH32b4Gcm9nsihHV9FfPfVdxDQUEJp3AgyBPwhPZEAyhoQS73TjjxXHqJRSz37Sl +ueMVPuNncqbT4nAMKz25J1CtRlQh21uZzfY2QRP3m6rAZquQUos1febC6A7qmhQljWKKmXtfVY+ +fAamstdHrWwIDAQAB +-----END PUBLIC KEY----- + +$ openssl asn1parse -i < [PUBLIC KEY] + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING + + + +-----BEGIN ALGORITHM----- +MA0GCSqGSIb3DQEBDQUA +-----END ALGORITHM----- + +$ openssl asn1parse -i < [ALGORITHM] + 0:d=0 hl=2 l= 13 cons: SEQUENCE + 2:d=1 hl=2 l= 9 prim: OBJECT :sha512WithRSAEncryption + 13:d=1 hl=2 l= 0 prim: NULL + + + +-----BEGIN DATA----- +MIICRaADAgECAgkA7jWRLkwHvHswDQYJKoZIhvcNAQENBQAwRTELMAkGA1UEBhMCQVUxEzARBgN +VBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xNT +A3MjgwMjIyMzFaFw0xNjA3MjcwMjIyMzFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lL +VN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDNy7ayElGtcUDAyR/fTLYBjXMiD1UX+F6Bhx6PN1HYDNRvy9TZNeV +3aLymnjiX4Ou+2qbNLsb7LHHe47H7OyioFpRPRz0d+RsT4hGZwuRAW9ilKmboS7w5LXgJKYS2JH +3US3wtHT5t3QafemCZv56kUkFoE94NKIj+yaooKzvI/Tj61LhDivjsbTguFKnj+GaoffZvgZyb2 +eyKEdX0V899V3ENBQQmncCDIE/CE9kQDKGhBLvdOOPFceolFLPftKW54xU+42dyptPicAwrPbkn +UK1GVCHbW5nN9jZBE/ebqsBmq5BSizV95sLoDuqaFCWNYoqZe19Vj58Bqay10etbAgMBAAGjUDB +OMB0GA1UdDgQWBBRsCPajkEscZM6SpLbNTa/7dY5azzAfBgNVHSMEGDAWgBRsCPajkEscZM6SpL +bNTa/7dY5azzAMBgNVHRMEBTADAQH/ +-----END DATA----- + + + +-----BEGIN SIGNATURE----- +A4IBAQAhKSNq+X/CfzhtNsMo6MJpTBjJBV5fhHerIZr6e3ozCTBCR29vYsVnJ4/6i5lL1pNeOhM +ldthnuSlMzTS1Zme1OqRWB3U8QmwCFwhDxW/i4fdT8kxDAmELNp4z0GcXbe27V895PE0R/m8P47 +B6xbra+SQlEMW12K1EndUqrO6vgLbobV14mveWdgc0KIOnDKgsTHV8NTV1w3qtp1ujfvizYfBZu +yyMOA1yZPDpREZtClro7lufwDQ7+LgSdtNLMDAMzapfIjAEPVNVLmJzMgzaHqMsZM8gP8vWAdfc +R4mCmWXVotrM6d1rjJGdRADAONYCC4/+d1IMkVGoVfpaej6I +-----END SIGNATURE----- + +$ openssl asn1parse -i < [SIGNATURE] + 0:d=0 hl=4 l= 257 prim: BIT STRING -- 2.7.4