Import webpki 0.22.0 upstream upstream/0.22.0
authorRoy7Kim <myoungwoon.kim@samsung.com>
Wed, 15 Mar 2023 08:15:07 +0000 (17:15 +0900)
committerRoy7Kim <myoungwoon.kim@samsung.com>
Wed, 15 Mar 2023 08:15:07 +0000 (17:15 +0900)
83 files changed:
.cargo_vcs_info.json [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
Cargo.toml.orig [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
src/calendar.rs [new file with mode: 0644]
src/cert.rs [new file with mode: 0644]
src/data/README.md [new file with mode: 0644]
src/data/alg-ecdsa-p256.der [new file with mode: 0644]
src/data/alg-ecdsa-p384.der [new file with mode: 0644]
src/data/alg-ecdsa-sha256.der [new file with mode: 0644]
src/data/alg-ecdsa-sha384.der [new file with mode: 0644]
src/data/alg-ed25519.der [new file with mode: 0644]
src/data/alg-rsa-encryption.der [new file with mode: 0644]
src/data/alg-rsa-pkcs1-sha256.der [new file with mode: 0644]
src/data/alg-rsa-pkcs1-sha384.der [new file with mode: 0644]
src/data/alg-rsa-pkcs1-sha512.der [new file with mode: 0644]
src/data/alg-rsa-pss-sha256.der [new file with mode: 0644]
src/data/alg-rsa-pss-sha384.der [new file with mode: 0644]
src/data/alg-rsa-pss-sha512.der [new file with mode: 0644]
src/der.rs [new file with mode: 0644]
src/end_entity.rs [new file with mode: 0644]
src/error.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]
src/name.rs [new file with mode: 0644]
src/name/dns_name.rs [new file with mode: 0644]
src/name/ip_address.rs [new file with mode: 0644]
src/name/verify.rs [new file with mode: 0644]
src/signed_data.rs [new file with mode: 0644]
src/time.rs [new file with mode: 0644]
src/trust_anchor.rs [new file with mode: 0644]
src/verify_cert.rs [new file with mode: 0644]
tests/dns_name_tests.rs [new file with mode: 0644]
tests/ed25519/ca.der [new file with mode: 0644]
tests/ed25519/ee.der [new file with mode: 0644]
tests/integration.rs [new file with mode: 0644]
tests/misc/serial_neg.der [new file with mode: 0644]
tests/misc/serial_zero.der [new file with mode: 0644]
tests/netflix/ca.der [new file with mode: 0644]
tests/netflix/ee.der [new file with mode: 0644]
tests/netflix/inter.der [new file with mode: 0644]
third-party/chromium/LICENSE [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/README [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-spki-params-null.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-unused-bits-signature.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecdh-key.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-ecmqv-key.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-using-rsa-algorithm.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512-wrong-signature-format.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-prime256v1-sha512.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256-corrupted-data.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-secp384r1-sha256.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ecdsa-using-rsa-key.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ours/make-pss.py [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ours/priv.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ours/pub.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32-corrupted-data.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha256-salt32.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48-corrupted-data.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha384-salt48.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64-corrupted-data.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/ours/rsa-pss-sha512-salt64.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-length.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-bad-key-der-null.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-key-params-absent.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-using-pss-key-no-params.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1-wrong-algorithm.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha1.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-key-encoded-ber.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-spki-non-null-params.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-ecdsa-algorithm.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256-using-id-ea-rsa.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pkcs1-sha256.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-no-params.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20-using-pss-key-with-null-params.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pss-sha1-salt20.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pss-sha1-wrong-salt.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pss-sha256-mgf1-sha512-salt33.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-params.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10-using-pss-key-with-wrong-params.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-pss-sha256-salt10.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa-using-ec-key.pem [new file with mode: 0644]
third-party/chromium/data/verify_signed_data/rsa2048-pkcs1-sha512.pem [new file with mode: 0644]

diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644 (file)
index 0000000..c33f2e1
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "6c334a2cf5853fb0aa93b5eb0318c031fc2f6f98"
+  }
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..4d687bb
--- /dev/null
@@ -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 <brian@briansmith.org>"]
+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 (file)
index 0000000..28a5601
--- /dev/null
@@ -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 <brian@briansmith.org>"]
+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 (file)
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 (file)
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 (file)
index 0000000..f1d2c44
--- /dev/null
@@ -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<Time, Error> {
+    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<u64, Error> {
+    // 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 (file)
index 0000000..7c76f2e
--- /dev/null
@@ -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<untrusted::Input<'a>>,
+    pub eku: Option<untrusted::Input<'a>>,
+    pub name_constraints: Option<untrusted::Input<'a>>,
+    pub subject_alt_name: Option<untrusted::Input<'a>>,
+}
+
+pub fn parse_cert<'a>(
+    cert_der: untrusted::Input<'a>,
+    ee_or_ca: EndEntityOrCa<'a>,
+) -> Result<Cert<'a>, 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<Cert<'a>, 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<Understood, Error> {
+    // 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 (file)
index 0000000..78fc778
--- /dev/null
@@ -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 <filename> -o <filename>.ascii
+```
+
+New or modified der-ascii files can be encoded using:
+
+```sh
+go get github.com/google/der-ascii/cmd/ascii2der
+ascii2der i <filename>.ascii -o <filename>
+```
+
+[`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 (file)
index 0000000..d49c30d
--- /dev/null
@@ -0,0 +1 @@
+\ 6\a*\86HÎ=\ 2\ 1\ 6\b*\86HÎ=\ 3\ 1\a
\ 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 (file)
index 0000000..8b24916
Binary files /dev/null and b/src/data/alg-ecdsa-p384.der differ
diff --git a/src/data/alg-ecdsa-sha256.der b/src/data/alg-ecdsa-sha256.der
new file mode 100644 (file)
index 0000000..b2ee128
--- /dev/null
@@ -0,0 +1 @@
+\ 6\b*\86HÎ=\ 4\ 3\ 2
\ 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 (file)
index 0000000..7c61d3a
--- /dev/null
@@ -0,0 +1 @@
+\ 6\b*\86HÎ=\ 4\ 3\ 3
\ No newline at end of file
diff --git a/src/data/alg-ed25519.der b/src/data/alg-ed25519.der
new file mode 100644 (file)
index 0000000..7ca46fd
--- /dev/null
@@ -0,0 +1 @@
+\ 6\ 3+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 (file)
index 0000000..77d159a
Binary files /dev/null and b/src/data/alg-rsa-encryption.der differ
diff --git a/src/data/alg-rsa-pkcs1-sha256.der b/src/data/alg-rsa-pkcs1-sha256.der
new file mode 100644 (file)
index 0000000..ab52bcd
Binary files /dev/null and b/src/data/alg-rsa-pkcs1-sha256.der differ
diff --git a/src/data/alg-rsa-pkcs1-sha384.der b/src/data/alg-rsa-pkcs1-sha384.der
new file mode 100644 (file)
index 0000000..c0d43b1
Binary files /dev/null and b/src/data/alg-rsa-pkcs1-sha384.der differ
diff --git a/src/data/alg-rsa-pkcs1-sha512.der b/src/data/alg-rsa-pkcs1-sha512.der
new file mode 100644 (file)
index 0000000..71aded5
Binary files /dev/null and b/src/data/alg-rsa-pkcs1-sha512.der differ
diff --git a/src/data/alg-rsa-pss-sha256.der b/src/data/alg-rsa-pss-sha256.der
new file mode 100644 (file)
index 0000000..87328f7
Binary files /dev/null and b/src/data/alg-rsa-pss-sha256.der differ
diff --git a/src/data/alg-rsa-pss-sha384.der b/src/data/alg-rsa-pss-sha384.der
new file mode 100644 (file)
index 0000000..9c3b170
Binary files /dev/null and b/src/data/alg-rsa-pss-sha384.der differ
diff --git a/src/data/alg-rsa-pss-sha512.der b/src/data/alg-rsa-pss-sha512.der
new file mode 100644 (file)
index 0000000..c0ad57d
Binary files /dev/null and b/src/data/alg-rsa-pss-sha512.der differ
diff --git a/src/der.rs b/src/der.rs
new file mode 100644 (file)
index 0000000..43d0847
--- /dev/null
@@ -0,0 +1,173 @@
+// 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::{calendar, time, Error};
+pub use ring::io::{
+    der::{nested, Tag, CONSTRUCTED},
+    Positive,
+};
+
+#[inline(always)]
+pub fn expect_tag_and_get_value<'a>(
+    input: &mut untrusted::Reader<'a>,
+    tag: Tag,
+) -> Result<untrusted::Input<'a>, 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<Value<'a>, 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<untrusted::Input<'a>, 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<bool, Error> {
+    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<Positive<'a>, Error> {
+    ring::io::der::positive_integer(input).map_err(|_| Error::BadDer)
+}
+
+pub fn small_nonnegative_integer(input: &mut untrusted::Reader) -> Result<u8, Error> {
+    ring::io::der::small_nonnegative_integer(input).map_err(|_| Error::BadDer)
+}
+
+pub fn time_choice(input: &mut untrusted::Reader) -> Result<time::Time, Error> {
+    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<u64, Error> {
+        const DIGIT: core::ops::RangeInclusive<u8> = 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<u64, Error> {
+        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 (file)
index 0000000..8c0650a
--- /dev/null
@@ -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<Self, Self::Error> {
+        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<Vec<DnsNameRef<'names>>, Error>
+    where
+        Names: Iterator<Item = DnsNameRef<'names>>,
+    {
+        let result: Vec<DnsNameRef<'names>> = 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 (file)
index 0000000..deeb9a8
--- /dev/null
@@ -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 (file)
index 0000000..ce9e71a
--- /dev/null
@@ -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 (file)
index 0000000..040a813
--- /dev/null
@@ -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 (file)
index 0000000..e40e703
--- /dev/null
@@ -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<str> for DnsName {
+    fn as_ref(&self) -> &str {
+        self.0.as_ref()
+    }
+}
+
+/// Requires the `alloc` feature.
+// Deprecated
+#[cfg(feature = "alloc")]
+impl From<DnsNameRef<'_>> 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<Self, InvalidDnsNameError> {
+        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, InvalidDnsNameError> {
+        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<DnsNameRef<'a>> 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<bool> {
+    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<bool> {
+    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 <x>*<y>.<DNSID>, where
+// <x> and/or <y> may be empty. However, NSS requires <y> to be empty, and we
+// follow NSS's stricter policy by accepting wildcards only of the form
+// <x>*.<DNSID>, where <x> 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<bool> {
+    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<bool>)] = &[
+        (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 (file)
index 0000000..fac5362
--- /dev/null
@@ -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<bool, Error> {
+    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 (file)
index 0000000..6082c19
--- /dev/null
@@ -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<Option<untrusted::Input<'b>>, 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<untrusted::Input>,
+    excluded_subtrees: Option<untrusted::Input>,
+) -> 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<untrusted::Input>,
+) -> 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<GeneralName<'b>, 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<untrusted::Input>,
+    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<GeneralName<'a>, 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 (file)
index 0000000..834f907
--- /dev/null
@@ -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<SubjectPublicKeyInfo, Error> {
+    input.read_all(Error::BadDer, |input| {
+        let algorithm_id_value = der::expect_tag_and_get_value(input, der::Tag::Sequence)?;
+        let key_value = der::bit_string_with_no_unused_bits(input)?;
+        Ok(SubjectPublicKeyInfo {
+            algorithm_id_value,
+            key_value,
+        })
+    })
+}
+
+/// A signature algorithm.
+pub struct SignatureAlgorithm {
+    public_key_alg_id: AlgorithmIdentifier,
+    signature_alg_id: AlgorithmIdentifier,
+    verification_alg: &'static dyn signature::VerificationAlgorithm,
+}
+
+/// ECDSA signatures using the P-256 curve and SHA-256.
+pub static ECDSA_P256_SHA256: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: ECDSA_P256,
+    signature_alg_id: ECDSA_SHA256,
+    verification_alg: &signature::ECDSA_P256_SHA256_ASN1,
+};
+
+/// ECDSA signatures using the P-256 curve and SHA-384. Deprecated.
+pub static ECDSA_P256_SHA384: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: ECDSA_P256,
+    signature_alg_id: ECDSA_SHA384,
+    verification_alg: &signature::ECDSA_P256_SHA384_ASN1,
+};
+
+/// ECDSA signatures using the P-384 curve and SHA-256. Deprecated.
+pub static ECDSA_P384_SHA256: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: ECDSA_P384,
+    signature_alg_id: ECDSA_SHA256,
+    verification_alg: &signature::ECDSA_P384_SHA256_ASN1,
+};
+
+/// ECDSA signatures using the P-384 curve and SHA-384.
+pub static ECDSA_P384_SHA384: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: ECDSA_P384,
+    signature_alg_id: ECDSA_SHA384,
+    verification_alg: &signature::ECDSA_P384_SHA384_ASN1,
+};
+
+/// RSA PKCS#1 1.5 signatures using SHA-256 for keys of 2048-8192 bits.
+///
+/// Requires the `alloc` feature.
+#[cfg(feature = "alloc")]
+pub static RSA_PKCS1_2048_8192_SHA256: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: RSA_ENCRYPTION,
+    signature_alg_id: RSA_PKCS1_SHA256,
+    verification_alg: &signature::RSA_PKCS1_2048_8192_SHA256,
+};
+
+/// RSA PKCS#1 1.5 signatures using SHA-384 for keys of 2048-8192 bits.
+///
+/// Requires the `alloc` feature.
+#[cfg(feature = "alloc")]
+pub static RSA_PKCS1_2048_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: RSA_ENCRYPTION,
+    signature_alg_id: RSA_PKCS1_SHA384,
+    verification_alg: &signature::RSA_PKCS1_2048_8192_SHA384,
+};
+
+/// RSA PKCS#1 1.5 signatures using SHA-512 for keys of 2048-8192 bits.
+///
+/// Requires the `alloc` feature.
+#[cfg(feature = "alloc")]
+pub static RSA_PKCS1_2048_8192_SHA512: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: RSA_ENCRYPTION,
+    signature_alg_id: RSA_PKCS1_SHA512,
+    verification_alg: &signature::RSA_PKCS1_2048_8192_SHA512,
+};
+
+/// RSA PKCS#1 1.5 signatures using SHA-384 for keys of 3072-8192 bits.
+///
+/// Requires the `alloc` feature.
+#[cfg(feature = "alloc")]
+pub static RSA_PKCS1_3072_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: RSA_ENCRYPTION,
+    signature_alg_id: RSA_PKCS1_SHA384,
+    verification_alg: &signature::RSA_PKCS1_3072_8192_SHA384,
+};
+
+/// RSA PSS signatures using SHA-256 for keys of 2048-8192 bits and of
+/// type rsaEncryption; see [RFC 4055 Section 1.2].
+///
+/// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2
+///
+/// Requires the `alloc` feature.
+#[cfg(feature = "alloc")]
+pub static RSA_PSS_2048_8192_SHA256_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: RSA_ENCRYPTION,
+    signature_alg_id: RSA_PSS_SHA256,
+    verification_alg: &signature::RSA_PSS_2048_8192_SHA256,
+};
+
+/// RSA PSS signatures using SHA-384 for keys of 2048-8192 bits and of
+/// type rsaEncryption; see [RFC 4055 Section 1.2].
+///
+/// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2
+///
+/// Requires the `alloc` feature.
+#[cfg(feature = "alloc")]
+pub static RSA_PSS_2048_8192_SHA384_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: RSA_ENCRYPTION,
+    signature_alg_id: RSA_PSS_SHA384,
+    verification_alg: &signature::RSA_PSS_2048_8192_SHA384,
+};
+
+/// RSA PSS signatures using SHA-512 for keys of 2048-8192 bits and of
+/// type rsaEncryption; see [RFC 4055 Section 1.2].
+///
+/// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2
+///
+/// Requires the `alloc` feature.
+#[cfg(feature = "alloc")]
+pub static RSA_PSS_2048_8192_SHA512_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: RSA_ENCRYPTION,
+    signature_alg_id: RSA_PSS_SHA512,
+    verification_alg: &signature::RSA_PSS_2048_8192_SHA512,
+};
+
+/// ED25519 signatures according to RFC 8410
+pub static ED25519: SignatureAlgorithm = SignatureAlgorithm {
+    public_key_alg_id: ED_25519,
+    signature_alg_id: ED_25519,
+    verification_alg: &signature::ED25519,
+};
+
+struct AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input<'static>,
+}
+
+impl AlgorithmIdentifier {
+    fn matches_algorithm_id_value(&self, encoded: untrusted::Input) -> bool {
+        encoded == self.asn1_id_value
+    }
+}
+
+// See src/data/README.md.
+
+const ECDSA_P256: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-p256.der")),
+};
+
+const ECDSA_P384: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-p384.der")),
+};
+
+const ECDSA_SHA256: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-sha256.der")),
+};
+
+const ECDSA_SHA384: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-sha384.der")),
+};
+
+#[cfg(feature = "alloc")]
+const RSA_ENCRYPTION: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-encryption.der")),
+};
+
+#[cfg(feature = "alloc")]
+const RSA_PKCS1_SHA256: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pkcs1-sha256.der")),
+};
+
+#[cfg(feature = "alloc")]
+const RSA_PKCS1_SHA384: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pkcs1-sha384.der")),
+};
+
+#[cfg(feature = "alloc")]
+const RSA_PKCS1_SHA512: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pkcs1-sha512.der")),
+};
+
+#[cfg(feature = "alloc")]
+const RSA_PSS_SHA256: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pss-sha256.der")),
+};
+
+#[cfg(feature = "alloc")]
+const RSA_PSS_SHA384: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pss-sha384.der")),
+};
+
+#[cfg(feature = "alloc")]
+const RSA_PSS_SHA512: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pss-sha512.der")),
+};
+
+const ED_25519: AlgorithmIdentifier = AlgorithmIdentifier {
+    asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ed25519.der")),
+};
+
+#[cfg(test)]
+mod tests {
+    use crate::{der, signed_data, Error};
+    use alloc::{string::String, vec::Vec};
+
+    macro_rules! test_file_bytes {
+        ( $file_name:expr ) => {
+            include_bytes!(concat!(
+                "../third-party/chromium/data/verify_signed_data/",
+                $file_name
+            ))
+        };
+    }
+
+    // TODO: The expected results need to be modified for SHA-1 deprecation.
+
+    macro_rules! test_verify_signed_data {
+        ($fn_name:ident, $file_name:expr, $expected_result:expr) => {
+            #[test]
+            fn $fn_name() {
+                test_verify_signed_data(test_file_bytes!($file_name), $expected_result);
+            }
+        };
+    }
+
+    fn test_verify_signed_data(file_contents: &[u8], expected_result: Result<(), Error>) {
+        let tsd = parse_test_signed_data(file_contents);
+        let spki_value = untrusted::Input::from(&tsd.spki);
+        let spki_value = spki_value
+            .read_all(Error::BadDer, |input| {
+                der::expect_tag_and_get_value(input, der::Tag::Sequence)
+            })
+            .unwrap();
+
+        // we can't use `parse_signed_data` because it requires `data`
+        // to be an ASN.1 SEQUENCE, and that isn't the case with
+        // Chromium's test data. TODO: The test data set should be
+        // expanded with SEQUENCE-wrapped data so that we can actually
+        // test `parse_signed_data`.
+
+        let algorithm = untrusted::Input::from(&tsd.algorithm);
+        let algorithm = algorithm
+            .read_all(Error::BadDer, |input| {
+                der::expect_tag_and_get_value(input, der::Tag::Sequence)
+            })
+            .unwrap();
+
+        let signature = untrusted::Input::from(&tsd.signature);
+        let signature = signature
+            .read_all(Error::BadDer, |input| {
+                der::bit_string_with_no_unused_bits(input)
+            })
+            .unwrap();
+
+        let signed_data = signed_data::SignedData {
+            data: untrusted::Input::from(&tsd.data),
+            algorithm,
+            signature,
+        };
+
+        assert_eq!(
+            expected_result,
+            signed_data::verify_signed_data(
+                SUPPORTED_ALGORITHMS_IN_TESTS,
+                spki_value,
+                &signed_data
+            )
+        );
+    }
+
+    // XXX: This is testing code that isn't even in this module.
+    macro_rules! test_verify_signed_data_signature_outer {
+        ($fn_name:ident, $file_name:expr, $expected_result:expr) => {
+            #[test]
+            fn $fn_name() {
+                test_verify_signed_data_signature_outer(
+                    test_file_bytes!($file_name),
+                    $expected_result,
+                );
+            }
+        };
+    }
+
+    fn test_verify_signed_data_signature_outer(file_contents: &[u8], expected_error: Error) {
+        let tsd = parse_test_signed_data(file_contents);
+        let signature = untrusted::Input::from(&tsd.signature);
+        assert_eq!(
+            Err(expected_error),
+            signature.read_all(Error::BadDer, |input| {
+                der::bit_string_with_no_unused_bits(input)
+            })
+        );
+    }
+
+    // XXX: This is testing code that is not even in this module.
+    macro_rules! test_parse_spki_bad_outer {
+        ($fn_name:ident, $file_name:expr, $error:expr) => {
+            #[test]
+            fn $fn_name() {
+                test_parse_spki_bad_outer(test_file_bytes!($file_name), $error)
+            }
+        };
+    }
+
+    fn test_parse_spki_bad_outer(file_contents: &[u8], expected_error: Error) {
+        let tsd = parse_test_signed_data(file_contents);
+        let spki = untrusted::Input::from(&tsd.spki);
+        assert_eq!(
+            Err(expected_error),
+            spki.read_all(Error::BadDer, |input| {
+                der::expect_tag_and_get_value(input, der::Tag::Sequence)
+            })
+        );
+    }
+
+    const UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY: Error = if cfg!(feature = "alloc") {
+        Error::UnsupportedSignatureAlgorithmForPublicKey
+    } else {
+        Error::UnsupportedSignatureAlgorithm
+    };
+
+    const INVALID_SIGNATURE_FOR_RSA_KEY: Error = if cfg!(feature = "alloc") {
+        Error::InvalidSignatureForPublicKey
+    } else {
+        Error::UnsupportedSignatureAlgorithm
+    };
+
+    const OK_IF_RSA_AVAILABLE: Result<(), Error> = if cfg!(feature = "alloc") {
+        Ok(())
+    } else {
+        Err(Error::UnsupportedSignatureAlgorithm)
+    };
+
+    // XXX: Some of the BadDER tests should have better error codes, maybe?
+
+    // XXX: We should have a variant of this test with a SHA-256 digest that gives
+    // `Error::UnsupportedSignatureAlgorithmForPublicKey`.
+    test_verify_signed_data!(
+        test_ecdsa_prime256v1_sha512_spki_params_null,
+        "ecdsa-prime256v1-sha512-spki-params-null.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data_signature_outer!(
+        test_ecdsa_prime256v1_sha512_unused_bits_signature,
+        "ecdsa-prime256v1-sha512-unused-bits-signature.pem",
+        Error::BadDer
+    );
+    // XXX: We should have a variant of this test with a SHA-256 digest that gives
+    // `Error::UnsupportedSignatureAlgorithmForPublicKey`.
+    test_verify_signed_data!(
+        test_ecdsa_prime256v1_sha512_using_ecdh_key,
+        "ecdsa-prime256v1-sha512-using-ecdh-key.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    // XXX: We should have a variant of this test with a SHA-256 digest that gives
+    // `Error::UnsupportedSignatureAlgorithmForPublicKey`.
+    test_verify_signed_data!(
+        test_ecdsa_prime256v1_sha512_using_ecmqv_key,
+        "ecdsa-prime256v1-sha512-using-ecmqv-key.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_ecdsa_prime256v1_sha512_using_rsa_algorithm,
+        "ecdsa-prime256v1-sha512-using-rsa-algorithm.pem",
+        Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY)
+    );
+    // XXX: We should have a variant of this test with a SHA-256 digest that gives
+    // `Error::InvalidSignatureForPublicKey`.
+    test_verify_signed_data!(
+        test_ecdsa_prime256v1_sha512_wrong_signature_format,
+        "ecdsa-prime256v1-sha512-wrong-signature-format.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    // Differs from Chromium because we don't support P-256 with SHA-512.
+    test_verify_signed_data!(
+        test_ecdsa_prime256v1_sha512,
+        "ecdsa-prime256v1-sha512.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_ecdsa_secp384r1_sha256_corrupted_data,
+        "ecdsa-secp384r1-sha256-corrupted-data.pem",
+        Err(Error::InvalidSignatureForPublicKey)
+    );
+    test_verify_signed_data!(
+        test_ecdsa_secp384r1_sha256,
+        "ecdsa-secp384r1-sha256.pem",
+        Ok(())
+    );
+    test_verify_signed_data!(
+        test_ecdsa_using_rsa_key,
+        "ecdsa-using-rsa-key.pem",
+        Err(Error::UnsupportedSignatureAlgorithmForPublicKey)
+    );
+
+    test_parse_spki_bad_outer!(
+        test_rsa_pkcs1_sha1_bad_key_der_length,
+        "rsa-pkcs1-sha1-bad-key-der-length.pem",
+        Error::BadDer
+    );
+    test_parse_spki_bad_outer!(
+        test_rsa_pkcs1_sha1_bad_key_der_null,
+        "rsa-pkcs1-sha1-bad-key-der-null.pem",
+        Error::BadDer
+    );
+    test_verify_signed_data!(
+        test_rsa_pkcs1_sha1_key_params_absent,
+        "rsa-pkcs1-sha1-key-params-absent.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_rsa_pkcs1_sha1_using_pss_key_no_params,
+        "rsa-pkcs1-sha1-using-pss-key-no-params.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_rsa_pkcs1_sha1_wrong_algorithm,
+        "rsa-pkcs1-sha1-wrong-algorithm.pem",
+        Err(INVALID_SIGNATURE_FOR_RSA_KEY)
+    );
+    test_verify_signed_data!(
+        test_rsa_pkcs1_sha1,
+        "rsa-pkcs1-sha1.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    // XXX: RSA PKCS#1 with SHA-1 is a supported algorithm, but we only accept
+    // 2048-8192 bit keys, and this test file is using a 1024 bit key. Thus,
+    // our results differ from Chromium's. TODO: this means we need a 2048+ bit
+    // version of this test.
+    test_verify_signed_data!(
+        test_rsa_pkcs1_sha256,
+        "rsa-pkcs1-sha256.pem",
+        Err(INVALID_SIGNATURE_FOR_RSA_KEY)
+    );
+    test_parse_spki_bad_outer!(
+        test_rsa_pkcs1_sha256_key_encoded_ber,
+        "rsa-pkcs1-sha256-key-encoded-ber.pem",
+        Error::BadDer
+    );
+    test_verify_signed_data!(
+        test_rsa_pkcs1_sha256_spki_non_null_params,
+        "rsa-pkcs1-sha256-spki-non-null-params.pem",
+        Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY)
+    );
+    test_verify_signed_data!(
+        test_rsa_pkcs1_sha256_using_ecdsa_algorithm,
+        "rsa-pkcs1-sha256-using-ecdsa-algorithm.pem",
+        Err(Error::UnsupportedSignatureAlgorithmForPublicKey)
+    );
+    test_verify_signed_data!(
+        test_rsa_pkcs1_sha256_using_id_ea_rsa,
+        "rsa-pkcs1-sha256-using-id-ea-rsa.pem",
+        Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY)
+    );
+
+    // Chromium's PSS test are for parameter combinations we don't support.
+    test_verify_signed_data!(
+        test_rsa_pss_sha1_salt20_using_pss_key_no_params,
+        "rsa-pss-sha1-salt20-using-pss-key-no-params.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha1_salt20_using_pss_key_with_null_params,
+        "rsa-pss-sha1-salt20-using-pss-key-with-null-params.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha1_salt20,
+        "rsa-pss-sha1-salt20.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha1_wrong_salt,
+        "rsa-pss-sha1-wrong-salt.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha256_mgf1_sha512_salt33,
+        "rsa-pss-sha256-mgf1-sha512-salt33.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha256_salt10_using_pss_key_with_params,
+        "rsa-pss-sha256-salt10-using-pss-key-with-params.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha256_salt10_using_pss_key_with_wrong_params,
+        "rsa-pss-sha256-salt10-using-pss-key-with-wrong-params.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha256_salt10,
+        "rsa-pss-sha256-salt10.pem",
+        Err(Error::UnsupportedSignatureAlgorithm)
+    );
+
+    // Our PSS tests that should work.
+    test_verify_signed_data!(
+        test_rsa_pss_sha256_salt32,
+        "ours/rsa-pss-sha256-salt32.pem",
+        OK_IF_RSA_AVAILABLE
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha384_salt48,
+        "ours/rsa-pss-sha384-salt48.pem",
+        OK_IF_RSA_AVAILABLE
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha512_salt64,
+        "ours/rsa-pss-sha512-salt64.pem",
+        OK_IF_RSA_AVAILABLE
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha256_salt32_corrupted_data,
+        "ours/rsa-pss-sha256-salt32-corrupted-data.pem",
+        Err(INVALID_SIGNATURE_FOR_RSA_KEY)
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha384_salt48_corrupted_data,
+        "ours/rsa-pss-sha384-salt48-corrupted-data.pem",
+        Err(INVALID_SIGNATURE_FOR_RSA_KEY)
+    );
+    test_verify_signed_data!(
+        test_rsa_pss_sha512_salt64_corrupted_data,
+        "ours/rsa-pss-sha512-salt64-corrupted-data.pem",
+        Err(INVALID_SIGNATURE_FOR_RSA_KEY)
+    );
+
+    test_verify_signed_data!(
+        test_rsa_using_ec_key,
+        "rsa-using-ec-key.pem",
+        Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY)
+    );
+    test_verify_signed_data!(
+        test_rsa2048_pkcs1_sha512,
+        "rsa2048-pkcs1-sha512.pem",
+        OK_IF_RSA_AVAILABLE
+    );
+
+    struct TestSignedData {
+        spki: Vec<u8>,
+        data: Vec<u8>,
+        algorithm: Vec<u8>,
+        signature: Vec<u8>,
+    }
+
+    fn parse_test_signed_data(file_contents: &[u8]) -> TestSignedData {
+        let mut lines = core::str::from_utf8(file_contents).unwrap().lines();
+        let spki = read_pem_section(&mut lines, "PUBLIC KEY");
+        let algorithm = read_pem_section(&mut lines, "ALGORITHM");
+        let data = read_pem_section(&mut lines, "DATA");
+        let signature = read_pem_section(&mut lines, "SIGNATURE");
+
+        TestSignedData {
+            spki,
+            data,
+            algorithm,
+            signature,
+        }
+    }
+
+    use alloc::str::Lines;
+
+    fn read_pem_section(lines: &mut Lines, section_name: &str) -> Vec<u8> {
+        // Skip comments and header
+        let begin_section = format!("-----BEGIN {}-----", section_name);
+        loop {
+            let line = lines.next().unwrap();
+            if line == begin_section {
+                break;
+            }
+        }
+
+        let mut base64 = String::new();
+
+        let end_section = format!("-----END {}-----", section_name);
+        loop {
+            let line = lines.next().unwrap();
+            if line == end_section {
+                break;
+            }
+            base64.push_str(&line);
+        }
+
+        base64::decode(&base64).unwrap()
+    }
+
+    static SUPPORTED_ALGORITHMS_IN_TESTS: &[&signed_data::SignatureAlgorithm] = &[
+        // Reasonable algorithms.
+        &signed_data::ECDSA_P256_SHA256,
+        &signed_data::ECDSA_P384_SHA384,
+        &signed_data::ED25519,
+        #[cfg(feature = "alloc")]
+        &signed_data::RSA_PKCS1_2048_8192_SHA256,
+        #[cfg(feature = "alloc")]
+        &signed_data::RSA_PKCS1_2048_8192_SHA384,
+        #[cfg(feature = "alloc")]
+        &signed_data::RSA_PKCS1_2048_8192_SHA512,
+        #[cfg(feature = "alloc")]
+        &signed_data::RSA_PKCS1_3072_8192_SHA384,
+        #[cfg(feature = "alloc")]
+        &signed_data::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
+        #[cfg(feature = "alloc")]
+        &signed_data::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
+        #[cfg(feature = "alloc")]
+        &signed_data::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
+        // Algorithms deprecated because they are annoying (P-521) or because
+        // they are nonsensical combinations.
+        &signed_data::ECDSA_P256_SHA384, // Truncates digest.
+        &signed_data::ECDSA_P384_SHA256, // Digest is unnecessarily short.
+    ];
+}
diff --git a/src/time.rs b/src/time.rs
new file mode 100644 (file)
index 0000000..0457cae
--- /dev/null
@@ -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<std::time::SystemTime> 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<Self, Self::Error> {
+        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 (file)
index 0000000..0ac2806
--- /dev/null
@@ -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<Self, Error> {
+        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<Cert<'a>> 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<TrustAnchor, Error> {
+    // 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 (file)
index 0000000..c68e6cf
--- /dev/null
@@ -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<V>(
+    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 (file)
index 0000000..b3a3adc
--- /dev/null
@@ -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 (file)
index 0000000..9994daf
Binary files /dev/null and b/tests/ed25519/ca.der differ
diff --git a/tests/ed25519/ee.der b/tests/ed25519/ee.der
new file mode 100644 (file)
index 0000000..5181f7b
Binary files /dev/null and b/tests/ed25519/ee.der differ
diff --git a/tests/integration.rs b/tests/integration.rs
new file mode 100644 (file)
index 0000000..598641d
--- /dev/null
@@ -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 (file)
index 0000000..f1317bf
Binary files /dev/null and b/tests/misc/serial_neg.der differ
diff --git a/tests/misc/serial_zero.der b/tests/misc/serial_zero.der
new file mode 100644 (file)
index 0000000..3cd289b
Binary files /dev/null and b/tests/misc/serial_zero.der differ
diff --git a/tests/netflix/ca.der b/tests/netflix/ca.der
new file mode 100644 (file)
index 0000000..6a15da3
Binary files /dev/null and b/tests/netflix/ca.der differ
diff --git a/tests/netflix/ee.der b/tests/netflix/ee.der
new file mode 100644 (file)
index 0000000..c06fbe5
Binary files /dev/null and b/tests/netflix/ee.der differ
diff --git a/tests/netflix/inter.der b/tests/netflix/inter.der
new file mode 100644 (file)
index 0000000..ff25d78
Binary files /dev/null and b/tests/netflix/inter.der differ
diff --git a/third-party/chromium/LICENSE b/third-party/chromium/LICENSE
new file mode 100644 (file)
index 0000000..a32e00c
--- /dev/null
@@ -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 (file)
index 0000000..2acf6e6
--- /dev/null
@@ -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:
+
+
+  <A description of the test>
+
+  -----BEGIN PUBLIC KEY-----
+  <base64-encoded, DER-encoded, SPKI>
+  -----END PUBLIC KEY-----
+
+  -----BEGIN ALGORITHM-----
+  <base64-encoded, DER-encoded, AlgorithmIdentifier for the signature.>
+  -----END ALGORITHM-----
+
+  -----BEGIN DATA-----
+  <base64-encoded data that is being verified>
+  -----END DATA-----
+
+  -----BEGIN SIGNATURE-----
+  <base64-encoded, DER-encoded, BIT STRING of the 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 (file)
index 0000000..6f65be1
--- /dev/null
@@ -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 (file)
index 0000000..24efda0
--- /dev/null
@@ -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 (file)
index 0000000..4f3e26c
--- /dev/null
@@ -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 (file)
index 0000000..d0b906f
--- /dev/null
@@ -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 (file)
index 0000000..8085486
--- /dev/null
@@ -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 (file)
index 0000000..d51317c
--- /dev/null
@@ -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 (file)
index 0000000..54f490c
--- /dev/null
@@ -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 (file)
index 0000000..5f5380b
--- /dev/null
@@ -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 (file)
index 0000000..d5c3798
--- /dev/null
@@ -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 (file)
index 0000000..653bcd7
--- /dev/null
@@ -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 (file)
index 0000000..7708bc8
--- /dev/null
@@ -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 (file)
index 0000000..dea5059
--- /dev/null
@@ -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 (file)
index 0000000..7ece334
--- /dev/null
@@ -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 (file)
index 0000000..153189b
--- /dev/null
@@ -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 (file)
index 0000000..85d4f61
--- /dev/null
@@ -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 (file)
index 0000000..d3e7a0b
--- /dev/null
@@ -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 (file)
index 0000000..ac65a9c
--- /dev/null
@@ -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 (file)
index 0000000..7696270
--- /dev/null
@@ -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 (file)
index 0000000..3504cc8
--- /dev/null
@@ -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 (file)
index 0000000..ef7967d
--- /dev/null
@@ -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 (file)
index 0000000..59559f4
--- /dev/null
@@ -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 (file)
index 0000000..10cc3d0
--- /dev/null
@@ -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 (file)
index 0000000..0dfff97
--- /dev/null
@@ -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 (file)
index 0000000..9aaedba
--- /dev/null
@@ -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 (file)
index 0000000..0972aca
--- /dev/null
@@ -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 (file)
index 0000000..2a8db4a
--- /dev/null
@@ -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 (file)
index 0000000..4e7fc96
--- /dev/null
@@ -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 (file)
index 0000000..a9b9eb9
--- /dev/null
@@ -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 (file)
index 0000000..dd5d39c
--- /dev/null
@@ -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 (file)
index 0000000..8509111
--- /dev/null
@@ -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 (file)
index 0000000..503cc2e
--- /dev/null
@@ -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 (file)
index 0000000..222614b
--- /dev/null
@@ -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 (file)
index 0000000..e56f0fe
--- /dev/null
@@ -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 (file)
index 0000000..57ec775
--- /dev/null
@@ -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 (file)
index 0000000..f3b9dcb
--- /dev/null
@@ -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 (file)
index 0000000..e0140b3
--- /dev/null
@@ -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 (file)
index 0000000..646ac1f
--- /dev/null
@@ -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 (file)
index 0000000..fc37f41
--- /dev/null
@@ -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 (file)
index 0000000..b9a3777
--- /dev/null
@@ -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 (file)
index 0000000..342a31b
--- /dev/null
@@ -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