aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Vander Stoep <jeffv@google.com>2024-02-07 09:28:33 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2024-02-07 09:28:33 +0000
commit41891cf572f9bbfe8f59db1a423114863773c426 (patch)
treef9c07211b0ba2f5202bb23d3cc5d9ff80b3eb1db
parent29ed49d5abd1d17315c169a87d66947403701bce (diff)
parent49948d1655433282c52a9f3068b2a76371d0fd3a (diff)
downloadwebpki-main.tar.gz
Upgrade webpki to 0.22.4 am: 49948d1655HEADmastermain
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/webpki/+/2952526 Change-Id: I180f28d7e1b188f55609bee24042c2b3ab278ec2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json7
-rw-r--r--Android.bp14
-rw-r--r--Cargo.toml55
-rw-r--r--Cargo.toml.orig32
-rw-r--r--METADATA25
-rw-r--r--src/budget.rs60
-rw-r--r--src/cert.rs4
-rw-r--r--src/end_entity.rs54
-rw-r--r--src/error.rs36
-rw-r--r--src/lib.rs11
-rw-r--r--src/name/dns_name.rs2
-rw-r--r--src/name/verify.rs8
-rw-r--r--src/signed_data.rs20
-rw-r--r--src/verify_cert.rs120
-rw-r--r--tests/dns_name_tests.rs4
-rw-r--r--third-party/bettertls/LICENSE177
-rw-r--r--third-party/bettertls/README.md17
17 files changed, 536 insertions, 110 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index c33f2e1..7fc6bfd 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,6 @@
{
"git": {
- "sha1": "6c334a2cf5853fb0aa93b5eb0318c031fc2f6f98"
- }
-}
+ "sha1": "25c183db36d302b69fd9648432e2c679301cb18e"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 27efee5..935a7ab 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,7 +36,7 @@ rust_library {
host_supported: true,
crate_name: "webpki",
cargo_env_compat: true,
- cargo_pkg_version: "0.22.0",
+ cargo_pkg_version: "0.22.4",
srcs: ["src/lib.rs"],
edition: "2018",
features: [
@@ -58,7 +58,7 @@ rust_test {
host_supported: true,
crate_name: "webpki",
cargo_env_compat: true,
- cargo_pkg_version: "0.22.0",
+ cargo_pkg_version: "0.22.4",
srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -73,6 +73,8 @@ rust_test {
rustlibs: [
"libbase64_rust",
"libring",
+ "libserde",
+ "libserde_json",
"libuntrusted",
],
}
@@ -82,7 +84,7 @@ rust_test {
host_supported: true,
crate_name: "dns_name_tests",
cargo_env_compat: true,
- cargo_pkg_version: "0.22.0",
+ cargo_pkg_version: "0.22.4",
srcs: ["tests/dns_name_tests.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -97,6 +99,8 @@ rust_test {
rustlibs: [
"libbase64_rust",
"libring",
+ "libserde",
+ "libserde_json",
"libuntrusted",
"libwebpki",
],
@@ -107,7 +111,7 @@ rust_test {
host_supported: true,
crate_name: "integration",
cargo_env_compat: true,
- cargo_pkg_version: "0.22.0",
+ cargo_pkg_version: "0.22.4",
srcs: ["tests/integration.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -122,6 +126,8 @@ rust_test {
rustlibs: [
"libbase64_rust",
"libring",
+ "libserde",
+ "libserde_json",
"libuntrusted",
"libwebpki",
],
diff --git a/Cargo.toml b/Cargo.toml
index 4d687bb..5bec9cb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,32 +3,52 @@
# 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
+# 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)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
+rust-version = "1.61.0"
name = "webpki"
-version = "0.22.0"
+version = "0.22.4"
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/**/*"]
+include = [
+ "Cargo.toml",
+ "LICENSE",
+ "README.md",
+ "src/**/*.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"]
+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 = 0
debug-assertions = false
rpath = false
@@ -36,21 +56,30 @@ rpath = false
opt-level = 3
lto = true
codegen-units = 1
-debug = false
+debug = 0
debug-assertions = false
rpath = false
[lib]
name = "webpki"
+
[dependencies.ring]
-version = "0.16.19"
+version = "0.17.2"
default-features = false
[dependencies.untrusted]
-version = "0.7.1"
+version = "0.9"
+
[dev-dependencies.base64]
version = "0.9.1"
+[dev-dependencies.serde]
+version = "1.0"
+features = ["derive"]
+
+[dev-dependencies.serde_json]
+version = "1.0"
+
[features]
alloc = ["ring/alloc"]
std = ["alloc"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 28a5601..04618a0 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -16,13 +16,13 @@
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"
+rust-version = "1.61.0"
+version = "0.22.4"
include = [
"Cargo.toml",
@@ -30,21 +30,7 @@ include = [
"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/**/*.rs",
"src/data/**/*",
"tests/dns_name_tests.rs",
@@ -71,11 +57,13 @@ alloc = ["ring/alloc"]
std = ["alloc"]
[dependencies]
-ring = { version = "0.16.19", default-features = false }
-untrusted = "0.7.1"
+ring = { version = "0.17.2", default-features = false }
+untrusted = "0.9"
[dev-dependencies]
base64 = "0.9.1"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
[profile.bench]
opt-level = 3
@@ -92,3 +80,9 @@ rpath = false
lto = true
debug-assertions = false
codegen-units = 1
+
+[workspace]
+members = [
+ # Intentionally not a default member.
+ "rcgen-tests",
+]
diff --git a/METADATA b/METADATA
index a5376ed..9156ed1 100644
--- a/METADATA
+++ b/METADATA
@@ -1,19 +1,20 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update external/rust/crates/webpki
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
+
name: "webpki"
description: "Web PKI X.509 Certificate Verification."
third_party {
- url {
- type: HOMEPAGE
- value: "https://crates.io/crates/webpki"
- }
- url {
- type: ARCHIVE
- value: "https://static.crates.io/crates/webpki/webpki-0.22.0.crate"
- }
- version: "0.22.0"
license_type: NOTICE
last_upgrade_date {
- year: 2021
- month: 7
- day: 19
+ year: 2024
+ month: 2
+ day: 6
+ }
+ homepage: "https://crates.io/crates/webpki"
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/webpki/webpki-0.22.4.crate"
+ version: "0.22.4"
}
}
diff --git a/src/budget.rs b/src/budget.rs
new file mode 100644
index 0000000..ea73a7d
--- /dev/null
+++ b/src/budget.rs
@@ -0,0 +1,60 @@
+// Copyright 2015 Brian Smith.
+// Portions Copyright 2033 Daniel McCarney.
+//
+// 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::ErrorExt;
+
+pub(super) struct Budget {
+ signatures: usize,
+ build_chain_calls: usize,
+}
+
+impl Budget {
+ #[inline]
+ pub fn consume_signature(&mut self) -> Result<(), ErrorExt> {
+ checked_sub(
+ &mut self.signatures,
+ ErrorExt::MaximumSignatureChecksExceeded,
+ )
+ }
+
+ #[inline]
+ pub fn consume_build_chain_call(&mut self) -> Result<(), ErrorExt> {
+ checked_sub(
+ &mut self.build_chain_calls,
+ ErrorExt::MaximumPathBuildCallsExceeded,
+ )
+ }
+}
+
+fn checked_sub(value: &mut usize, underflow_error: ErrorExt) -> Result<(), ErrorExt> {
+ *value = value.checked_sub(1).ok_or(underflow_error)?;
+ Ok(())
+}
+
+impl Default for Budget {
+ fn default() -> Self {
+ Self {
+ // This limit is taken from the remediation for golang CVE-2018-16875. However,
+ // note that golang subsequently implemented AKID matching due to this limit
+ // being hit in real applications (see <https://github.com/spiffe/spire/issues/1004>).
+ // So this may actually be too aggressive.
+ signatures: 100,
+
+ // This limit is taken from mozilla::pkix, see:
+ // <https://github.com/nss-dev/nss/blob/bb4a1d38dd9e92923525ac6b5ed0288479f3f3fc/lib/mozpkix/lib/pkixbuild.cpp#L381-L393>
+ build_chain_calls: 200_000,
+ }
+ }
+}
diff --git a/src/cert.rs b/src/cert.rs
index 7c76f2e..792f49f 100644
--- a/src/cert.rs
+++ b/src/cert.rs
@@ -12,7 +12,7 @@
// 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};
+use crate::{der, equal, signed_data, Error};
pub enum EndEntityOrCa<'a> {
EndEntity,
@@ -66,7 +66,7 @@ pub(crate) fn parse_cert_internal<'a>(
// 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 {
+ if !equal(signature, signed_data.algorithm) {
return Err(Error::SignatureAlgorithmMismatch);
}
diff --git a/src/end_entity.rs b/src/end_entity.rs
index 8c0650a..cfe9ef1 100644
--- a/src/end_entity.rs
+++ b/src/end_entity.rs
@@ -13,7 +13,7 @@
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
use crate::{
- cert, name, signed_data, verify_cert, DnsNameRef, Error, SignatureAlgorithm, Time,
+ cert, name, signed_data, verify_cert, DnsNameRef, Error, ErrorExt, SignatureAlgorithm, Time,
TlsClientTrustAnchors, TlsServerTrustAnchors,
};
@@ -79,6 +79,25 @@ impl<'a> EndEntityCert<'a> {
&self.inner
}
+ /// Backward-SemVer-compatible wrapper around `verify_is_valid_tls_server_cert_ext`.
+ ///
+ /// Errors that aren't representable as an `Error` are mapped to `Error::UnknownIssuer`.
+ pub fn verify_is_valid_tls_server_cert(
+ &self,
+ supported_sig_algs: &[&SignatureAlgorithm],
+ trust_anchors: &TlsServerTrustAnchors,
+ intermediate_certs: &[&[u8]],
+ time: Time,
+ ) -> Result<(), Error> {
+ self.verify_is_valid_tls_server_cert_ext(
+ supported_sig_algs,
+ trust_anchors,
+ intermediate_certs,
+ time,
+ )
+ .map_err(ErrorExt::into_error_lossy)
+ }
+
/// Verifies that the end-entity certificate is valid for use by a TLS
/// server.
///
@@ -89,13 +108,13 @@ impl<'a> EndEntityCert<'a> {
/// 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(
+ pub fn verify_is_valid_tls_server_cert_ext(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
&TlsServerTrustAnchors(trust_anchors): &TlsServerTrustAnchors,
intermediate_certs: &[&[u8]],
time: Time,
- ) -> Result<(), Error> {
+ ) -> Result<(), ErrorExt> {
verify_cert::build_chain(
verify_cert::EKU_SERVER_AUTH,
supported_sig_algs,
@@ -103,10 +122,28 @@ impl<'a> EndEntityCert<'a> {
intermediate_certs,
&self.inner,
time,
- 0,
)
}
+ /// Backward-SemVer-compatible wrapper around `verify_is_valid_tls_client_cert_ext`.
+ ///
+ /// Errors that aren't representable as an `Error` are mapped to `Error::UnknownIssuer`.
+ pub fn verify_is_valid_tls_client_cert(
+ &self,
+ supported_sig_algs: &[&SignatureAlgorithm],
+ trust_anchors: &TlsClientTrustAnchors,
+ intermediate_certs: &[&[u8]],
+ time: Time,
+ ) -> Result<(), Error> {
+ self.verify_is_valid_tls_client_cert_ext(
+ supported_sig_algs,
+ trust_anchors,
+ intermediate_certs,
+ time,
+ )
+ .map_err(ErrorExt::into_error_lossy)
+ }
+
/// Verifies that the end-entity certificate is valid for use by a TLS
/// client.
///
@@ -121,13 +158,13 @@ impl<'a> EndEntityCert<'a> {
/// `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(
+ pub fn verify_is_valid_tls_client_cert_ext(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
&TlsClientTrustAnchors(trust_anchors): &TlsClientTrustAnchors,
intermediate_certs: &[&[u8]],
time: Time,
- ) -> Result<(), Error> {
+ ) -> Result<(), ErrorExt> {
verify_cert::build_chain(
verify_cert::EKU_CLIENT_AUTH,
supported_sig_algs,
@@ -135,13 +172,12 @@ impl<'a> EndEntityCert<'a> {
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)
+ name::verify_cert_dns_name(self, dns_name)
}
/// Verifies that the certificate is valid for at least one of the given DNS
@@ -182,7 +218,7 @@ impl<'a> EndEntityCert<'a> {
/// `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
+ /// `SignatureAndHashAlgorithm` 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.
///
diff --git a/src/error.rs b/src/error.rs
index deeb9a8..3cb7697 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -106,3 +106,39 @@ impl fmt::Display for Error {
/// Requires the `std` feature.
#[cfg(feature = "std")]
impl ::std::error::Error for Error {}
+
+/// An error that occurs during certificate validation or name validation.
+///
+/// `ErrorExt` effectively extends `Error` to support reporting new errors. Because `Error` is not
+/// declared `#[non_exhaustive]` it could not be directly extended in a backward-compatible way.
+#[non_exhaustive]
+pub enum ErrorExt {
+ Error(Error),
+ MaximumSignatureChecksExceeded,
+ /// The maximum number of internal path building calls has been reached. Path complexity is too great.
+ MaximumPathBuildCallsExceeded,
+}
+
+impl ErrorExt {
+ pub(crate) fn is_fatal(&self) -> bool {
+ match self {
+ Self::Error(_) => false,
+ Self::MaximumSignatureChecksExceeded | Self::MaximumPathBuildCallsExceeded => true,
+ }
+ }
+
+ pub(crate) fn into_error_lossy(self) -> Error {
+ match self {
+ Self::Error(e) => e,
+ Self::MaximumSignatureChecksExceeded | Self::MaximumPathBuildCallsExceeded => {
+ Error::UnknownIssuer
+ }
+ }
+ }
+}
+
+impl From<Error> for ErrorExt {
+ fn from(error: Error) -> Self {
+ Self::Error(error)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index ce9e71a..40bb959 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -24,7 +24,6 @@
//! | `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,
@@ -42,6 +41,8 @@
#[cfg_attr(test, macro_use)]
extern crate alloc;
+mod budget;
+
#[macro_use]
mod der;
@@ -58,7 +59,7 @@ mod verify_cert;
pub use {
end_entity::EndEntityCert,
- error::Error,
+ error::{Error, ErrorExt},
name::{DnsNameRef, InvalidDnsNameError},
signed_data::{
SignatureAlgorithm, ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256,
@@ -94,3 +95,9 @@ pub type TLSServerTrustAnchors<'a> = TlsServerTrustAnchors<'a>;
#[deprecated(note = "use TlsClientTrustAnchors")]
#[allow(unknown_lints, clippy::upper_case_acronyms)]
pub type TLSClientTrustAnchors<'a> = TlsClientTrustAnchors<'a>;
+
+// We don't operate on secret data so a convenient comparison function is warranted.
+#[must_use]
+fn equal(a: untrusted::Input, b: untrusted::Input) -> bool {
+ a.as_slice_less_safe() == b.as_slice_less_safe()
+}
diff --git a/src/name/dns_name.rs b/src/name/dns_name.rs
index e40e703..e4f18f2 100644
--- a/src/name/dns_name.rs
+++ b/src/name/dns_name.rs
@@ -125,7 +125,7 @@ impl<'a> DnsNameRef<'a> {
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();
+ let s: &str = (*self).into();
DnsName(s.to_ascii_lowercase())
}
}
diff --git a/src/name/verify.rs b/src/name/verify.rs
index 6082c19..699aea2 100644
--- a/src/name/verify.rs
+++ b/src/name/verify.rs
@@ -18,7 +18,7 @@ use super::{
};
use crate::{
cert::{Cert, EndEntityOrCa},
- der, Error,
+ der, equal, Error,
};
pub fn verify_cert_dns_name(
@@ -26,7 +26,7 @@ pub fn verify_cert_dns_name(
dns_name: DnsNameRef,
) -> Result<(), Error> {
let cert = cert.inner();
- let dns_name = untrusted::Input::from(dns_name.as_ref().as_ref());
+ let dns_name = untrusted::Input::from(dns_name.as_ref());
iterate_names(
cert.subject,
cert.subject_alt_name,
@@ -152,7 +152,7 @@ fn check_presented_id_conforms_to_constraints_in_subtree(
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))
+ general_subtree.read_all(Error::BadDer, general_name)
}
let base = match general_subtree(&mut constraints) {
@@ -234,7 +234,7 @@ fn presented_directory_name_matches_constraint(
subtrees: Subtrees,
) -> bool {
match subtrees {
- Subtrees::PermittedSubtrees => name == constraint,
+ Subtrees::PermittedSubtrees => equal(name, constraint),
Subtrees::ExcludedSubtrees => true,
}
}
diff --git a/src/signed_data.rs b/src/signed_data.rs
index 834f907..ccd834e 100644
--- a/src/signed_data.rs
+++ b/src/signed_data.rs
@@ -12,7 +12,7 @@
// 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 crate::{der, equal, Error};
use ring::signature;
/// X.509 certificates and related items that are signed are almost always
@@ -48,6 +48,7 @@ pub struct SignedData<'a> {
/// signatureAlgorithm AlgorithmIdentifier,
/// signatureValue BIT STRING
/// }
+/// ```
///
/// OCSP responses (RFC 6960) look like this:
/// ```ASN.1
@@ -312,7 +313,7 @@ struct AlgorithmIdentifier {
impl AlgorithmIdentifier {
fn matches_algorithm_id_value(&self, encoded: untrusted::Input) -> bool {
- encoded == self.asn1_id_value
+ equal(encoded, self.asn1_id_value)
}
}
@@ -460,10 +461,12 @@ mod tests {
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)
- })
+ expected_error,
+ signature
+ .read_all(Error::BadDer, |input| {
+ der::bit_string_with_no_unused_bits(input)
+ })
+ .unwrap_err()
);
}
@@ -481,10 +484,11 @@ mod tests {
let tsd = parse_test_signed_data(file_contents);
let spki = untrusted::Input::from(&tsd.spki);
assert_eq!(
- Err(expected_error),
+ expected_error,
spki.read_all(Error::BadDer, |input| {
der::expect_tag_and_get_value(input, der::Tag::Sequence)
})
+ .unwrap_err()
);
}
@@ -755,7 +759,7 @@ mod tests {
if line == end_section {
break;
}
- base64.push_str(&line);
+ base64.push_str(line);
}
base64::decode(&base64).unwrap()
diff --git a/src/verify_cert.rs b/src/verify_cert.rs
index c68e6cf..fe7ef9d 100644
--- a/src/verify_cert.rs
+++ b/src/verify_cert.rs
@@ -12,9 +12,14 @@
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+use core::default::Default;
+
use crate::{
+ budget::Budget,
cert::{self, Cert, EndEntityOrCa},
- der, name, signed_data, time, Error, SignatureAlgorithm, TrustAnchor,
+ der, equal,
+ error::ErrorExt,
+ name, signed_data, time, Error, SignatureAlgorithm, TrustAnchor,
};
pub fn build_chain(
@@ -24,8 +29,30 @@ pub fn build_chain(
intermediate_certs: &[&[u8]],
cert: &Cert,
time: time::Time,
+) -> Result<(), ErrorExt> {
+ build_chain_inner(
+ required_eku_if_present,
+ supported_sig_algs,
+ trust_anchors,
+ intermediate_certs,
+ cert,
+ time,
+ 0,
+ &mut Budget::default(),
+ )
+}
+
+#[allow(clippy::too_many_arguments)]
+fn build_chain_inner(
+ 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> {
+ budget: &mut Budget,
+) -> Result<(), ErrorExt> {
let used_as_ca = used_as_ca(&cert.ee_or_ca);
check_issuer_independent_properties(
@@ -43,7 +70,7 @@ pub fn build_chain(
const MAX_SUB_CA_COUNT: usize = 6;
if sub_ca_count >= MAX_SUB_CA_COUNT {
- return Err(Error::UnknownIssuer);
+ return Err(Error::UnknownIssuer.into());
}
}
UsedAsCa::No => {
@@ -55,47 +82,46 @@ pub fn build_chain(
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);
+ if !equal(cert.issuer, trust_anchor_subject) {
+ return Err(Error::UnknownIssuer.into());
}
- 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)?;
+ check_signatures(supported_sig_algs, cert, trust_anchor_spki, budget)?;
+
+ check_signed_chain_name_constraints(cert, trust_anchor)?;
Ok(())
}) {
Ok(()) => {
return Ok(());
}
- Err(..) => {
+ Err(e) => {
+ if e.is_fatal() {
+ return Err(e);
+ }
// 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))?;
+ cert::parse_cert(untrusted::Input::from(cert_der), EndEntityOrCa::Ca(cert))?;
- if potential_issuer.subject != cert.issuer {
- return Err(Error::UnknownIssuer);
+ if !equal(potential_issuer.subject, cert.issuer) {
+ return Err(Error::UnknownIssuer.into());
}
// 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
+ if equal(potential_issuer.spki.value(), prev.spki.value())
+ && equal(potential_issuer.subject, prev.subject)
{
- return Err(Error::UnknownIssuer);
+ return Err(Error::UnknownIssuer.into());
}
match &prev.ee_or_ca {
EndEntityOrCa::EndEntity => {
@@ -107,16 +133,13 @@ pub fn build_chain(
}
}
- 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(
+ budget.consume_build_chain_call()?;
+ build_chain_inner(
required_eku_if_present,
supported_sig_algs,
trust_anchors,
@@ -124,6 +147,7 @@ pub fn build_chain(
&potential_issuer,
time,
next_sub_ca_count,
+ budget,
)
})
}
@@ -132,10 +156,12 @@ fn check_signatures(
supported_sig_algs: &[&SignatureAlgorithm],
cert_chain: &Cert,
trust_anchor_key: untrusted::Input,
-) -> Result<(), Error> {
+ budget: &mut Budget,
+) -> Result<(), ErrorExt> {
let mut spki_value = trust_anchor_key;
let mut cert = cert_chain;
loop {
+ budget.consume_signature()?;
signed_data::verify_signed_data(supported_sig_algs, spki_value, &cert.signed_data)?;
// TODO: check revocation
@@ -154,6 +180,32 @@ fn check_signatures(
Ok(())
}
+fn check_signed_chain_name_constraints(
+ cert_chain: &Cert,
+ trust_anchor: &TrustAnchor,
+) -> Result<(), Error> {
+ let mut cert = cert_chain;
+ let mut name_constraints = trust_anchor.name_constraints.map(untrusted::Input::from);
+
+ loop {
+ untrusted::read_all_optional(name_constraints, Error::BadDer, |value| {
+ name::check_name_constraints(value, cert)
+ })?;
+
+ match &cert.ee_or_ca {
+ EndEntityOrCa::Ca(child_cert) => {
+ name_constraints = cert.name_constraints;
+ cert = child_cert;
+ }
+ EndEntityOrCa::EndEntity => {
+ break;
+ }
+ }
+ }
+
+ Ok(())
+}
+
fn check_issuer_independent_properties(
cert: &Cert,
time: time::Time,
@@ -302,7 +354,7 @@ fn check_eku(
Some(input) => {
loop {
let value = der::expect_tag_and_get_value(input, der::Tag::OID)?;
- if value == required_eku_if_present.oid_value {
+ if equal(value, required_eku_if_present.oid_value) {
input.skip_to_end();
break;
}
@@ -322,7 +374,10 @@ fn check_eku(
// 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 {
+ if equal(
+ required_eku_if_present.oid_value,
+ EKU_OCSP_SIGNING.oid_value,
+ ) {
return Err(Error::RequiredEkuNotFound);
}
@@ -333,8 +388,8 @@ fn check_eku(
fn loop_while_non_fatal_error<V>(
values: V,
- f: impl Fn(V::Item) -> Result<(), Error>,
-) -> Result<(), Error>
+ mut f: impl FnMut(V::Item) -> Result<(), ErrorExt>,
+) -> Result<(), ErrorExt>
where
V: IntoIterator,
{
@@ -343,10 +398,13 @@ where
Ok(()) => {
return Ok(());
}
- Err(..) => {
+ Err(e) => {
+ if e.is_fatal() {
+ return Err(e);
+ }
// If the error is not fatal, then keep going.
}
}
}
- Err(Error::UnknownIssuer)
+ Err(Error::UnknownIssuer.into())
}
diff --git a/tests/dns_name_tests.rs b/tests/dns_name_tests.rs
index b3a3adc..0e51999 100644
--- a/tests/dns_name_tests.rs
+++ b/tests/dns_name_tests.rs
@@ -250,7 +250,7 @@ static IP_ADDRESS_DNS_VALIDITY: &[(&[u8], bool)] = &[
(b"1.2.3.4\n", false),
// Nulls not allowed
(b"\0", false),
- (b"\01.2.3.4", false),
+ (b"\x001.2.3.4", false),
(b"1.2.3.4\0", false),
(b"1.2.3.4\0.5", false),
// Range
@@ -389,7 +389,7 @@ static IP_ADDRESS_DNS_VALIDITY: &[(&[u8], bool)] = &[
(b"::1\0:2", false),
(b"::1\0", false),
(b"::1.2.3.4\0", false),
- (b"::1.2\02.3.4", false),
+ (b"::1.2\x002.3.4", false),
];
#[test]
diff --git a/third-party/bettertls/LICENSE b/third-party/bettertls/LICENSE
new file mode 100644
index 0000000..4947287
--- /dev/null
+++ b/third-party/bettertls/LICENSE
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS \ No newline at end of file
diff --git a/third-party/bettertls/README.md b/third-party/bettertls/README.md
new file mode 100644
index 0000000..9c12dc8
--- /dev/null
+++ b/third-party/bettertls/README.md
@@ -0,0 +1,17 @@
+# BetterTLS Test Suite
+
+Generated using the Netflix [bettertls] project.
+
+[bettertls]: https://github.com/Netflix/bettertls
+
+## Pathbuilding
+
+To regenerate pathbuilding test data:
+
+1. Install Go
+2. Generate the JSON testdata export for the path building suite:
+
+```bash
+GOBIN=$PWD go install github.com/Netflix/bettertls/test-suites/cmd/bettertls@latest
+./bettertls export-tests --suite pathbuilding --out ./pathbuilding.tests.json
+```