--- /dev/null
+{
+ "git": {
+ "sha1": "6c334a2cf5853fb0aa93b5eb0318c031fc2f6f98"
+ }
+}
--- /dev/null
+# 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"]
--- /dev/null
+# 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
--- /dev/null
+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.
--- /dev/null
+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.**
--- /dev/null
+// 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()
+ );
+ }
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+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
--- /dev/null
+\ 6\a*\86HÎ=\ 2\ 1\ 6\b*\86HÎ=\ 3\ 1\a
\ No newline at end of file
--- /dev/null
+\ 6\b*\86HÎ=\ 4\ 3\ 2
\ No newline at end of file
--- /dev/null
+\ 6\b*\86HÎ=\ 4\ 3\ 3
\ No newline at end of file
--- /dev/null
+\ 6\ 3+ep
\ No newline at end of file
--- /dev/null
+// 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 ),*]
+ )
+}
--- /dev/null
+// 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),
+ )
+ }
+}
--- /dev/null
+// 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 {}
--- /dev/null
+// 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>;
--- /dev/null
+// 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};
--- /dev/null
+// 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
+ );
+ }
+ }
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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.
+ ];
+}
--- /dev/null
+// 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()))
+ }
+}
--- /dev/null
+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(|_| ())
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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
+ );
+ }
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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.
--- /dev/null
+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).
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+# 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')
--- /dev/null
+-----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-----
--- /dev/null
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVDcg1BMqPneiNBL5+mj
+EB5mxjJzgkmClZR5z1jNht+As6+Mlgflni0bB8LjhWbIt+dZ6Bt4cSHOnAOnkMDO
+FwtrxJE6Eg1GQ2ux9nDVNvrQkoOLznXrxMh/af0pcSo8kItDmkqbV/fi3Q7agpbc
+Wc/4wTZOfO6lns4nb5s08oaUv3uF5Wc0sktNr6he6R3zSQ6YK5KZFzQdnEtGc4gw
+HWXZ9xt4JeANht3m4RNpMY89qZsZxmqoewYHuXQUAfl7W0DC3hoxOoLwSqL2bt2z
+MMeR8WAo51YY0cJnzAEETcnWIM6ealb5Osj3iSEknxRTley47SsODQ0maUUWj8wE
+jwIDAQAB
+-----END PUBLIC KEY-----
--- /dev/null
+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
--- /dev/null
+
+
+
+
+-----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
--- /dev/null
+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
--- /dev/null
+
+
+
+
+-----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
--- /dev/null
+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
--- /dev/null
+
+
+
+
+-----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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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