From a978de797b927fb71c6534438689989c54b074c1 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 21 Nov 2023 12:21:41 +0100 Subject: [PATCH] Migrate to pki-types ServerName --- Cargo.toml | 4 +- src/cert.rs | 9 +- src/end_entity.rs | 26 +- src/lib.rs | 9 +- src/subject_name/dns_name.rs | 155 +----- src/subject_name/ip_address.rs | 917 +-------------------------------- src/subject_name/mod.rs | 14 +- src/subject_name/name.rs | 95 ---- tests/better_tls.rs | 6 +- tests/dns_name_tests.rs | 408 --------------- tests/integration.rs | 10 +- tests/name_tests.rs | 73 --- tests/tls_server_certs.rs | 10 +- 13 files changed, 88 insertions(+), 1648 deletions(-) delete mode 100644 src/subject_name/name.rs delete mode 100644 tests/dns_name_tests.rs delete mode 100644 tests/name_tests.rs diff --git a/Cargo.toml b/Cargo.toml index c1d434fe..60f1a683 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,11 +76,11 @@ default = ["std", "ring"] alloc = ["ring?/alloc", "pki-types/alloc"] aws_lc_rs = ["dep:aws-lc-rs"] ring = ["dep:ring"] -std = ["alloc"] +std = ["alloc", "pki-types/std"] [dependencies] aws-lc-rs = { version = "1.0.0", optional = true } -pki-types = { package = "rustls-pki-types", version = "0.2.1", default-features = false } +pki-types = { package = "rustls-pki-types", version = "0.2.2", git = "https://github.com/rustls/pki-types", rev = "4e64f851484b9430d071b38bceeaa0cbe2141821", default-features = false } ring = { version = "0.17", default-features = false, optional = true } untrusted = "0.9" diff --git a/src/cert.rs b/src/cert.rs index f3682556..e9989e5a 100644 --- a/src/cert.rs +++ b/src/cert.rs @@ -12,14 +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 pki_types::CertificateDer; +use pki_types::{CertificateDer, DnsName}; use crate::der::{self, DerIterator, FromDer, Tag, CONSTRUCTED, CONTEXT_SPECIFIC}; use crate::error::{DerTypeId, Error}; +use crate::public_values_eq; use crate::signed_data::SignedData; use crate::subject_name::{GeneralName, NameIterator, WildcardDnsNameRef}; use crate::x509::{remember_extension, set_extension_once, DistributionPointName, Extension}; -use crate::{public_values_eq, DnsNameRef}; /// A parsed X509 certificate. pub struct Cert<'a> { @@ -145,8 +145,9 @@ impl<'a> Cert<'a> { // if the name could be converted to a DNS name, return it; otherwise, // keep going. - match DnsNameRef::try_from_ascii(presented_id.as_slice_less_safe()) { - Ok(dns_name) => Some(dns_name.as_str()), + let dns_str = core::str::from_utf8(presented_id.as_slice_less_safe()).ok()?; + match DnsName::try_from(dns_str) { + Ok(_) => Some(dns_str), Err(_) => { match WildcardDnsNameRef::try_from_ascii(presented_id.as_slice_less_safe()) { Ok(wildcard_dns_name) => Some(wildcard_dns_name.as_str()), diff --git a/src/end_entity.rs b/src/end_entity.rs index 19d6ed00..d61955ef 100644 --- a/src/end_entity.rs +++ b/src/end_entity.rs @@ -14,11 +14,13 @@ use core::ops::Deref; -use pki_types::{CertificateDer, SignatureVerificationAlgorithm, TrustAnchor, UnixTime}; +use pki_types::{ + CertificateDer, ServerName, SignatureVerificationAlgorithm, TrustAnchor, UnixTime, +}; use crate::crl::RevocationOptions; use crate::error::Error; -use crate::subject_name::{NameIterator, SubjectNameRef}; +use crate::subject_name::{verify_dns_names, NameIterator, verify_ip_address_names}; use crate::verify_cert::{self, KeyUsage, VerifiedPath}; use crate::{cert, signed_data}; @@ -119,17 +121,21 @@ impl<'a> EndEntityCert<'a> { /// Verifies that the certificate is valid for the given Subject Name. pub fn verify_is_valid_for_subject_name( &self, - subject_name: SubjectNameRef, + server_name: &ServerName<'_>, ) -> Result<(), Error> { - match subject_name { - SubjectNameRef::DnsName(dns_name) => dns_name.verify_dns_names(NameIterator::new( - Some(self.inner.subject), - self.inner.subject_alt_name, - )), + match server_name { + ServerName::DnsName(dns_name) => verify_dns_names( + dns_name, + NameIterator::new(Some(self.inner.subject), self.inner.subject_alt_name), + ), // IP addresses are not compared against the subject field; // only against Subject Alternative Names. - SubjectNameRef::IpAddress(ip_address) => ip_address - .verify_ip_address_names(NameIterator::new(None, self.inner.subject_alt_name)), + #[cfg(feature = "std")] + ServerName::IpAddress(ip_address) => verify_ip_address_names( + &ip_address, + NameIterator::new(None, self.inner.subject_alt_name), + ), + _ => unreachable!(), } } diff --git a/src/lib.rs b/src/lib.rs index 20b9c222..bdb7804d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,10 +75,6 @@ pub use { end_entity::EndEntityCert, error::{DerTypeId, Error}, signed_data::alg_id, - subject_name::{ - AddrParseError, DnsNameRef, InvalidDnsNameError, InvalidSubjectNameError, IpAddrRef, - SubjectNameRef, - }, trust_anchor::extract_trust_anchor, verify_cert::KeyUsage, }; @@ -86,10 +82,7 @@ pub use { pub use pki_types as types; #[cfg(feature = "alloc")] -pub use { - crl::{OwnedCertRevocationList, OwnedRevokedCert}, - subject_name::{DnsName, IpAddr}, -}; +pub use crl::{OwnedCertRevocationList, OwnedRevokedCert}; #[cfg(feature = "ring")] /// Signature verification algorithm implementations using the *ring* crypto library. diff --git a/src/subject_name/dns_name.rs b/src/subject_name/dns_name.rs index 4b57f956..5bf61274 100644 --- a/src/subject_name/dns_name.rs +++ b/src/subject_name/dns_name.rs @@ -12,130 +12,37 @@ // 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; use core::fmt::Write; +use pki_types::{DnsName, InvalidDnsNameError}; + use super::verify::{GeneralName, NameIterator}; use crate::Error; -/// 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`s do not include wildcard labels. -/// -/// `DnsName` stores a copy of the input it was constructed from in a `String` -/// and so it is only available when the `alloc` default feature is enabled. -/// -/// [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); - -#[cfg(feature = "alloc")] -impl DnsName { - /// Returns a `DnsNameRef` that refers to this `DnsName`. - pub fn as_ref(&self) -> DnsNameRef { - DnsNameRef(self.0.as_bytes()) - } -} - -#[cfg(feature = "alloc")] -impl AsRef for DnsName { - fn as_ref(&self) -> &str { - self.0.as_ref() - } -} - -/// 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. `DnsNameRef`s do not include wildcard labels. -/// -/// [RFC 5280 Section 7.2]: https://tools.ietf.org/html/rfc5280#section-7.2 -#[derive(Clone, Copy, Eq, PartialEq, Hash)] -pub struct DnsNameRef<'a>(pub(crate) &'a [u8]); - -impl<'a> DnsNameRef<'a> { - /// Constructs a `DnsNameRef` from the given input if the input is a - /// syntactically-valid DNS name. - pub fn try_from_ascii(dns_name: &'a [u8]) -> Result { - if !is_valid_dns_id( - untrusted::Input::from(dns_name), - IdRole::Reference, - Wildcards::Deny, - ) { - return Err(InvalidDnsNameError); - } - - Ok(Self(dns_name)) - } - - /// Constructs a `DnsNameRef` from the given input if the input is a - /// syntactically-valid DNS name. - pub fn try_from_ascii_str(dns_name: &'a str) -> Result { - Self::try_from_ascii(dns_name.as_bytes()) - } - - pub(crate) fn verify_dns_names(&self, mut names: NameIterator<'_>) -> Result<(), Error> { - let dns_name = untrusted::Input::from(self.as_str().as_bytes()); - names - .find_map(|result| { - let name = match result { - Ok(name) => name, - Err(err) => return Some(Err(err)), - }; - - let presented_id = match name { - GeneralName::DnsName(presented) => presented, - _ => return None, - }; - - match presented_id_matches_reference_id(presented_id, IdRole::Reference, dns_name) { - Ok(true) => Some(Ok(())), - Ok(false) | Err(Error::MalformedDnsIdentifier) => None, - Err(e) => Some(Err(e)), - } - }) - .unwrap_or(Err(Error::CertNotValidForName)) - } - - /// Constructs a `DnsName` from this `DnsNameRef` - #[cfg(feature = "alloc")] - pub fn to_owned(&self) -> DnsName { - // DnsNameRef is already guaranteed to be valid ASCII, which is subset of UTF-8. - DnsName(self.as_str().to_ascii_lowercase()) - } - - /// Yields a reference to the DNS name as a `&str`. - pub fn as_str(&self) -> &'a str { - // The unwrap won't fail because `DnsNameRef` values are guaranteed to be ASCII and ASCII - // is a subset of UTF-8. - core::str::from_utf8(self.0).unwrap() - } -} - -impl core::fmt::Debug for DnsNameRef<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { - f.write_str("DnsNameRef(\"")?; +pub(crate) fn verify_dns_names( + reference: &DnsName<'_>, + mut names: NameIterator<'_>, +) -> Result<(), Error> { + let dns_name = untrusted::Input::from(reference.as_ref().as_bytes()); + names + .find_map(|result| { + let name = match result { + Ok(name) => name, + Err(err) => return Some(Err(err)), + }; - // Convert each byte of the underlying ASCII string to a `char` and - // downcase it prior to formatting it. We avoid self.clone().to_owned() - // since it requires allocation. - for &ch in self.0 { - f.write_char(char::from(ch).to_ascii_lowercase())?; - } + let presented_id = match name { + GeneralName::DnsName(presented) => presented, + _ => return None, + }; - f.write_str("\")") - } + match presented_id_matches_reference_id(presented_id, IdRole::Reference, dns_name) { + Ok(true) => Some(Ok(())), + Ok(false) | Err(Error::MalformedDnsIdentifier) => None, + Err(e) => Some(Err(e)), + } + }) + .unwrap_or(Err(Error::CertNotValidForName)) } /// A reference to a DNS Name presented by a server that may include a wildcard. @@ -191,20 +98,6 @@ impl core::fmt::Debug for WildcardDnsNameRef<'_> { } } -/// 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) - } -} - -#[cfg(feature = "std")] -impl ::std::error::Error for InvalidDnsNameError {} - // 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 diff --git a/src/subject_name/ip_address.rs b/src/subject_name/ip_address.rs index af7e8670..194d3241 100644 --- a/src/subject_name/ip_address.rs +++ b/src/subject_name/ip_address.rs @@ -12,209 +12,39 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -#[cfg(feature = "std")] -use core::fmt::Write; +use pki_types::IpAddr; use super::verify::{GeneralName, NameIterator}; use crate::Error; -#[cfg(feature = "alloc")] -use alloc::string::String; - -const VALID_IP_BY_CONSTRUCTION: &str = "IP address is a valid string by construction"; - -/// Either a IPv4 or IPv6 address, plus its owned string representation -#[cfg(feature = "alloc")] -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub enum IpAddr { - /// An IPv4 address and its owned string representation - V4(String, [u8; 4]), - /// An IPv6 address and its owned string representation - V6(String, [u8; 16]), -} - -#[cfg(feature = "alloc")] -impl AsRef for IpAddr { - fn as_ref(&self) -> &str { - match self { - IpAddr::V4(ip_address, _) | IpAddr::V6(ip_address, _) => ip_address.as_str(), - } - } -} - -/// Either a IPv4 or IPv6 address, plus its borrowed string representation -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum IpAddrRef<'a> { - /// An IPv4 address and its borrowed string representation - V4(&'a [u8], [u8; 4]), - /// An IPv6 address and its borrowed string representation - V6(&'a [u8], [u8; 16]), -} - -impl<'a> IpAddrRef<'a> { - pub(crate) fn verify_ip_address_names(&self, mut names: NameIterator<'_>) -> Result<(), Error> { - let ip_address = match self { - IpAddrRef::V4(_, ref ip_address_octets) => untrusted::Input::from(ip_address_octets), - IpAddrRef::V6(_, ref ip_address_octets) => untrusted::Input::from(ip_address_octets), - }; - - names - .find_map(|result| { - let name = match result { - Ok(name) => name, - Err(err) => return Some(Err(err)), - }; - - let presented_id = match name { - GeneralName::IpAddress(presented) => presented, - _ => return None, - }; - - match presented_id_matches_reference_id(presented_id, ip_address) { - true => Some(Ok(())), - false => None, - } - }) - .unwrap_or(Err(Error::CertNotValidForName)) - } -} - -#[cfg(feature = "alloc")] -impl<'a> From> for IpAddr { - fn from(ip_address: IpAddrRef<'a>) -> IpAddr { - match ip_address { - IpAddrRef::V4(ip_address, ip_address_octets) => IpAddr::V4( - String::from_utf8(ip_address.to_vec()).expect(VALID_IP_BY_CONSTRUCTION), - ip_address_octets, - ), - IpAddrRef::V6(ip_address, ip_address_octets) => IpAddr::V6( - String::from_utf8(ip_address.to_vec()).expect(VALID_IP_BY_CONSTRUCTION), - ip_address_octets, - ), - } - } -} - -#[cfg(feature = "alloc")] -impl<'a> From<&'a IpAddr> for IpAddrRef<'a> { - fn from(ip_address: &'a IpAddr) -> IpAddrRef<'a> { - match ip_address { - IpAddr::V4(ip_address, ip_address_octets) => { - IpAddrRef::V4(ip_address.as_bytes(), *ip_address_octets) - } - IpAddr::V6(ip_address, ip_address_octets) => { - IpAddrRef::V6(ip_address.as_bytes(), *ip_address_octets) - } - } - } -} - -/// An error indicating that an `IpAddrRef` could not built because -/// the input could not be parsed as an IP address. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct AddrParseError; - -impl core::fmt::Display for AddrParseError { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "{:?}", self) - } -} - #[cfg(feature = "std")] -impl ::std::error::Error for AddrParseError {} - -impl<'a> IpAddrRef<'a> { - /// Constructs an `IpAddrRef` from the given input if the input is - /// a valid IPv4 or IPv6 address. - pub fn try_from_ascii(ip_address: &'a [u8]) -> Result { - if let Ok(ip_address) = parse_ipv4_address(ip_address) { - Ok(ip_address) - } else if let Ok(ip_address) = parse_ipv6_address(ip_address) { - Ok(ip_address) - } else { - Err(AddrParseError) - } - } - - /// Constructs an `IpAddrRef` from the given input if the input is a - /// valid IP address. - pub fn try_from_ascii_str(ip_address: &'a str) -> Result { - Self::try_from_ascii(ip_address.as_bytes()) - } - - /// Constructs an `IpAddr` from this `IpAddrRef` - #[cfg(feature = "alloc")] - pub fn to_owned(&self) -> IpAddr { - match self { - IpAddrRef::V4(ip_address, ip_address_octets) => IpAddr::V4( - String::from_utf8(ip_address.to_vec()).expect(VALID_IP_BY_CONSTRUCTION), - *ip_address_octets, - ), - IpAddrRef::V6(ip_address, ip_address_octets) => IpAddr::V6( - String::from_utf8(ip_address.to_vec()).expect(VALID_IP_BY_CONSTRUCTION), - *ip_address_octets, - ), - } - } -} - -#[cfg(feature = "std")] -fn ipv6_to_uncompressed_string(octets: [u8; 16]) -> String { - let mut result = String::with_capacity(39); - for i in 0..7 { - result - .write_fmt(format_args!( - "{:02x?}{:02x?}:", - octets[i * 2], - octets[(i * 2) + 1] - )) - .expect("unexpected error while formatting IPv6 address"); - } - result - .write_fmt(format_args!("{:02x?}{:02x?}", octets[14], octets[15])) - .expect("unexpected error while formatting IPv6 address"); +pub(crate) fn verify_ip_address_names( + reference: &IpAddr, + mut names: NameIterator<'_>, +) -> Result<(), Error> { + let ip_address = match reference { + IpAddr::V4(ip) => untrusted::Input::from(ip.as_ref()), + IpAddr::V6(ip) => untrusted::Input::from(ip.as_ref()), + }; - result -} + names + .find_map(|result| { + let name = match result { + Ok(name) => name, + Err(err) => return Some(Err(err)), + }; -#[cfg(feature = "std")] -impl From for IpAddr { - fn from(ip_address: std::net::IpAddr) -> IpAddr { - match ip_address { - std::net::IpAddr::V4(ip_address) => { - IpAddr::V4(ip_address.to_string(), ip_address.octets()) - } - std::net::IpAddr::V6(ip_address) => IpAddr::V6( - // We cannot rely on the Display implementation of - // std::net::Ipv6Addr given that it might return - // compressed IPv6 addresses if the address can be - // expressed in such form. However, given we don't - // support the IPv6 compressed form, we should not - // generate such format either when converting from a - // type that supports it. - ipv6_to_uncompressed_string(ip_address.octets()), - ip_address.octets(), - ), - } - } -} + let presented_id = match name { + GeneralName::IpAddress(presented) => presented, + _ => return None, + }; -impl<'a> From> for &'a str { - fn from(ip_address: IpAddrRef<'a>) -> &'a str { - match ip_address { - IpAddrRef::V4(ip_address, _) | IpAddrRef::V6(ip_address, _) => { - core::str::from_utf8(ip_address).expect(VALID_IP_BY_CONSTRUCTION) + match presented_id_matches_reference_id(presented_id, ip_address) { + true => Some(Ok(())), + false => None, } - } - } -} - -impl<'a> From> for &'a [u8] { - fn from(ip_address: IpAddrRef<'a>) -> &'a [u8] { - match ip_address { - IpAddrRef::V4(ip_address, _) | IpAddrRef::V6(ip_address, _) => ip_address, - } - } + }) + .unwrap_or(Err(Error::CertNotValidForName)) } // https://tools.ietf.org/html/rfc5280#section-4.2.1.6 says: @@ -335,577 +165,10 @@ pub(super) fn presented_id_matches_constraint( Ok(true) } -pub(crate) fn parse_ipv4_address(ip_address_: &[u8]) -> Result { - let mut ip_address = untrusted::Reader::new(untrusted::Input::from(ip_address_)); - let mut is_first_byte = true; - let mut current_octet: [u8; 3] = [0, 0, 0]; - let mut current_size = 0; - let mut dot_count = 0; - - let mut octet = 0; - let mut octets: [u8; 4] = [0, 0, 0, 0]; - - // Returns a u32 so it's possible to identify (and error) when - // provided textual octets > 255, not representable by u8. - fn radix10_to_octet(textual_octets: &[u8]) -> u32 { - let mut result: u32 = 0; - for digit in textual_octets.iter() { - result *= 10; - result += u32::from(*digit); - } - result - } - - loop { - match ip_address.read_byte() { - Ok(b'.') => { - if is_first_byte { - // IPv4 address cannot start with a dot. - return Err(AddrParseError); - } - if ip_address.at_end() { - // IPv4 address cannot end with a dot. - return Err(AddrParseError); - } - if dot_count == 3 { - // IPv4 address cannot have more than three dots. - return Err(AddrParseError); - } - dot_count += 1; - if current_size == 0 { - // IPv4 address cannot contain two dots in a row. - return Err(AddrParseError); - } - let current_raw_octet = radix10_to_octet(¤t_octet[..current_size]); - if current_raw_octet > 255 { - // No octet can be greater than 255. - return Err(AddrParseError); - } - octets[octet] = - TryInto::::try_into(current_raw_octet).expect("invalid character"); - octet += 1; - // We move on to the next textual octet. - current_octet = [0, 0, 0]; - current_size = 0; - } - Ok(number @ b'0'..=b'9') => { - if number == b'0' - && current_size == 0 - && !ip_address.peek(b'.') - && !ip_address.at_end() - { - // No octet can start with 0 if a dot does not follow and if we are not at the end. - return Err(AddrParseError); - } - if current_size >= current_octet.len() { - // More than 3 octets in a triple - return Err(AddrParseError); - } - current_octet[current_size] = number - b'0'; - current_size += 1; - } - _ => { - return Err(AddrParseError); - } - } - is_first_byte = false; - - if ip_address.at_end() { - let last_octet = radix10_to_octet(¤t_octet[..current_size]); - if current_size > 0 && last_octet > 255 { - // No octet can be greater than 255. - return Err(AddrParseError); - } - octets[octet] = TryInto::::try_into(last_octet).expect("invalid character"); - break; - } - } - if dot_count != 3 { - return Err(AddrParseError); - } - Ok(IpAddrRef::V4(ip_address_, octets)) -} - -pub(crate) fn parse_ipv6_address(ip_address_: &[u8]) -> Result { - // Compressed addresses are not supported. Also, IPv4-mapped IPv6 - // addresses are not supported. This makes 8 groups of 4 - // hexadecimal characters + 7 colons. - if ip_address_.len() != 39 { - return Err(AddrParseError); - } - - let mut ip_address = untrusted::Reader::new(untrusted::Input::from(ip_address_)); - let mut is_first_byte = true; - let mut current_textual_block_size = 0; - let mut colon_count = 0; - - let mut octet = 0; - let mut previous_character = None; - let mut octets: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - loop { - match ip_address.read_byte() { - Ok(b':') => { - if is_first_byte { - // Uncompressed IPv6 address cannot start with a colon. - return Err(AddrParseError); - } - if ip_address.at_end() { - // Uncompressed IPv6 address cannot end with a colon. - return Err(AddrParseError); - } - if colon_count == 7 { - // IPv6 address cannot have more than seven colons. - return Err(AddrParseError); - } - colon_count += 1; - if current_textual_block_size == 0 { - // Uncompressed IPv6 address cannot contain two colons in a row. - return Err(AddrParseError); - } - if current_textual_block_size != 4 { - // Compressed IPv6 addresses are not supported. - return Err(AddrParseError); - } - // We move on to the next textual block. - current_textual_block_size = 0; - previous_character = None; - } - Ok(character @ b'0'..=b'9') - | Ok(character @ b'a'..=b'f') - | Ok(character @ b'A'..=b'F') => { - if current_textual_block_size == 4 { - // Blocks cannot contain more than 4 hexadecimal characters. - return Err(AddrParseError); - } - if let Some(previous_character_) = previous_character { - octets[octet] = (TryInto::::try_into( - TryInto::::try_into( - (TryInto::::try_into(previous_character_) - .expect("invalid character")) - .to_digit(16) - // Safe to unwrap because we know character is within hexadecimal bounds ([0-9a-f]) - .unwrap(), - ) - .expect("invalid character"), - ) - .expect("invalid character") - << 4) - | (TryInto::::try_into( - TryInto::::try_into(character) - .expect("invalid character") - .to_digit(16) - // Safe to unwrap because we know character is within hexadecimal bounds ([0-9a-f]) - .unwrap(), - ) - .expect("invalid character")); - previous_character = None; - octet += 1; - } else { - previous_character = Some(character); - } - current_textual_block_size += 1; - } - _ => { - return Err(AddrParseError); - } - } - is_first_byte = false; - - if ip_address.at_end() { - break; - } - } - if colon_count != 7 { - return Err(AddrParseError); - } - Ok(IpAddrRef::V6(ip_address_, octets)) -} - #[cfg(test)] mod tests { use super::*; - const fn ipv4_address( - ip_address: &[u8], - octets: [u8; 4], - ) -> (&[u8], Result) { - (ip_address, Ok(IpAddrRef::V4(ip_address, octets))) - } - - const IPV4_ADDRESSES: &[(&[u8], Result)] = &[ - // Valid IPv4 addresses - ipv4_address(b"0.0.0.0", [0, 0, 0, 0]), - ipv4_address(b"1.1.1.1", [1, 1, 1, 1]), - ipv4_address(b"205.0.0.0", [205, 0, 0, 0]), - ipv4_address(b"0.205.0.0", [0, 205, 0, 0]), - ipv4_address(b"0.0.205.0", [0, 0, 205, 0]), - ipv4_address(b"0.0.0.205", [0, 0, 0, 205]), - ipv4_address(b"0.0.0.20", [0, 0, 0, 20]), - // Invalid IPv4 addresses - (b"", Err(AddrParseError)), - (b"...", Err(AddrParseError)), - (b".0.0.0.0", Err(AddrParseError)), - (b"0.0.0.0.", Err(AddrParseError)), - (b"0.0.0", Err(AddrParseError)), - (b"0.0.0.", Err(AddrParseError)), - (b"256.0.0.0", Err(AddrParseError)), - (b"0.256.0.0", Err(AddrParseError)), - (b"0.0.256.0", Err(AddrParseError)), - (b"0.0.0.256", Err(AddrParseError)), - (b"1..1.1.1", Err(AddrParseError)), - (b"1.1..1.1", Err(AddrParseError)), - (b"1.1.1..1", Err(AddrParseError)), - (b"025.0.0.0", Err(AddrParseError)), - (b"0.025.0.0", Err(AddrParseError)), - (b"0.0.025.0", Err(AddrParseError)), - (b"0.0.0.025", Err(AddrParseError)), - (b"1234.0.0.0", Err(AddrParseError)), - (b"0.1234.0.0", Err(AddrParseError)), - (b"0.0.1234.0", Err(AddrParseError)), - (b"0.0.0.1234", Err(AddrParseError)), - ]; - - #[test] - fn parse_ipv4_address_test() { - for &(ip_address, expected_result) in IPV4_ADDRESSES { - assert_eq!(parse_ipv4_address(ip_address), expected_result,); - } - } - - const fn ipv6_address( - ip_address: &[u8], - octets: [u8; 16], - ) -> (&[u8], Result) { - (ip_address, Ok(IpAddrRef::V6(ip_address, octets))) - } - - const IPV6_ADDRESSES: &[(&[u8], Result)] = &[ - // Valid IPv6 addresses - ipv6_address( - b"2a05:d018:076c:b685:e8ab:afd3:af51:3aed", - [ - 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51, - 0x3a, 0xed, - ], - ), - ipv6_address( - b"2A05:D018:076C:B685:E8AB:AFD3:AF51:3AED", - [ - 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51, - 0x3a, 0xed, - ], - ), - ipv6_address( - b"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - [ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, - ], - ), - ipv6_address( - b"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", - [ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, - ], - ), - ipv6_address( - b"FFFF:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - [ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, - ], - ), - // Invalid IPv6 addresses - // Missing octets on uncompressed addresses. The unmatching letter has the violation - ( - b"aaa:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:aaa:ffff:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:aaa:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:aaa:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:aaa:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff:aaa:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff:ffff:aaa:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff:ffff:ffff:aaa", - Err(AddrParseError), - ), - // Wrong hexadecimal characters on different positions - ( - b"ffgf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:gfff:ffff:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:fffg:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffgf:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:gfff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff:fgff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff:ffff:ffgf:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff:ffff:ffgf:fffg", - Err(AddrParseError), - ), - // Wrong colons on uncompressed addresses - ( - b":ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff::ffff:ffff:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff::ffff:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff::ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff::ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff::ffff:ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff:ffff::ffff:ffff", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff:ffff:ffff::ffff", - Err(AddrParseError), - ), - // More colons than allowed - ( - b"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:", - Err(AddrParseError), - ), - ( - b"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - Err(AddrParseError), - ), - // v Invalid UTF-8 encoding - ( - b"\xc3\x28a05:d018:076c:b685:e8ab:afd3:af51:3aed", - Err(AddrParseError), - ), - // v Invalid hexadecimal - ( - b"ga05:d018:076c:b685:e8ab:afd3:af51:3aed", - Err(AddrParseError), - ), - // Cannot start with colon - ( - b":a05:d018:076c:b685:e8ab:afd3:af51:3aed", - Err(AddrParseError), - ), - // Cannot end with colon - ( - b"2a05:d018:076c:b685:e8ab:afd3:af51:3ae:", - Err(AddrParseError), - ), - // Cannot have more than seven colons - ( - b"2a05:d018:076c:b685:e8ab:afd3:af51:3a::", - Err(AddrParseError), - ), - // Cannot contain two colons in a row - ( - b"2a05::018:076c:b685:e8ab:afd3:af51:3aed", - Err(AddrParseError), - ), - // v Textual block size is longer - ( - b"2a056:d018:076c:b685:e8ab:afd3:af51:3ae", - Err(AddrParseError), - ), - // v Textual block size is shorter - ( - b"2a0:d018:076c:b685:e8ab:afd3:af51:3aed ", - Err(AddrParseError), - ), - // Shorter IPv6 address - (b"d018:076c:b685:e8ab:afd3:af51:3aed", Err(AddrParseError)), - // Longer IPv6 address - ( - b"2a05:d018:076c:b685:e8ab:afd3:af51:3aed3aed", - Err(AddrParseError), - ), - // These are valid IPv6 addresses, but we don't support compressed addresses - (b"0:0:0:0:0:0:0:1", Err(AddrParseError)), - ( - b"2a05:d018:76c:b685:e8ab:afd3:af51:3aed", - Err(AddrParseError), - ), - ]; - - #[test] - fn parse_ipv6_address_test() { - for &(ip_address, expected_result) in IPV6_ADDRESSES { - assert_eq!(parse_ipv6_address(ip_address), expected_result,); - } - } - - #[test] - fn try_from_ascii_ip_address_test() { - const IP_ADDRESSES: &[(&[u8], Result)] = &[ - // Valid IPv4 addresses - ( - b"127.0.0.1", - Ok(IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1])), - ), - // Invalid IPv4 addresses - ( - // Ends with a dot; misses one octet - b"127.0.0.", - Err(AddrParseError), - ), - // Valid IPv6 addresses - ( - b"0000:0000:0000:0000:0000:0000:0000:0001", - Ok(IpAddrRef::V6( - b"0000:0000:0000:0000:0000:0000:0000:0001", - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - )), - ), - // Invalid IPv6 addresses - ( - // IPv6 addresses in compressed form are not supported - b"0:0:0:0:0:0:0:1", - Err(AddrParseError), - ), - // Something else - ( - // A hostname - b"example.com", - Err(AddrParseError), - ), - ]; - for &(ip_address, expected_result) in IP_ADDRESSES { - assert_eq!(IpAddrRef::try_from_ascii(ip_address), expected_result) - } - } - - #[test] - fn try_from_ascii_str_ip_address_test() { - const IP_ADDRESSES: &[(&str, Result)] = &[ - // Valid IPv4 addresses - ("127.0.0.1", Ok(IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]))), - // Invalid IPv4 addresses - ( - // Ends with a dot; misses one octet - "127.0.0.", - Err(AddrParseError), - ), - // Valid IPv6 addresses - ( - "0000:0000:0000:0000:0000:0000:0000:0001", - Ok(IpAddrRef::V6( - b"0000:0000:0000:0000:0000:0000:0000:0001", - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - )), - ), - // Invalid IPv6 addresses - ( - // IPv6 addresses in compressed form are not supported - "0:0:0:0:0:0:0:1", - Err(AddrParseError), - ), - // Something else - ( - // A hostname - "example.com", - Err(AddrParseError), - ), - ]; - for &(ip_address, expected_result) in IP_ADDRESSES { - assert_eq!(IpAddrRef::try_from_ascii_str(ip_address), expected_result) - } - } - - #[test] - fn str_from_ip_address_ref_test() { - let ip_addresses = vec![ - // IPv4 addresses - (IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]), "127.0.0.1"), - // IPv6 addresses - ( - IpAddrRef::V6( - b"0000:0000:0000:0000:0000:0000:0000:0001", - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - ), - "0000:0000:0000:0000:0000:0000:0000:0001", - ), - ]; - for (ip_address, expected_ip_address) in ip_addresses { - assert_eq!(Into::<&str>::into(ip_address), expected_ip_address,) - } - } - - #[test] - fn u8_array_from_ip_address_ref_test() { - let ip_addresses = vec![ - // IPv4 addresses - (IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]), "127.0.0.1"), - // IPv6 addresses - ( - IpAddrRef::V6( - b"0000:0000:0000:0000:0000:0000:0000:0001", - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - ), - "0000:0000:0000:0000:0000:0000:0000:0001", - ), - ]; - for (ip_address, expected_ip_address) in ip_addresses { - assert_eq!( - Into::<&[u8]>::into(ip_address), - expected_ip_address.as_bytes() - ) - } - } - #[test] fn presented_id_matches_constraint_ipv4_test() { let names_and_constraints = vec![ @@ -1287,138 +550,6 @@ mod tests { mod alloc_tests { use super::*; - #[test] - fn as_ref_ip_address_test() { - assert_eq!( - IpAddr::V4(String::from("127.0.0.1"), [127, 0, 0, 1]).as_ref(), - "127.0.0.1", - ); - assert_eq!( - IpAddr::V6( - String::from("0000:0000:0000:0000:0000:0000:0000:0001"), - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] - ) - .as_ref(), - "0000:0000:0000:0000:0000:0000:0000:0001", - ); - } - - #[test] - fn from_ip_address_ref_for_ip_address_test() { - { - let (ip_address, ip_address_octets) = ("127.0.0.1", [127, 0, 0, 1]); - assert_eq!( - IpAddr::from(IpAddrRef::V4(ip_address.as_bytes(), ip_address_octets)), - IpAddr::V4(String::from(ip_address), ip_address_octets), - ) - } - { - let (ip_address, ip_address_octets) = ( - "0000:0000:0000:0000:0000:0000:0000:0001", - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - ); - assert_eq!( - IpAddr::from(IpAddrRef::V6(ip_address.as_bytes(), ip_address_octets)), - IpAddr::V6(String::from(ip_address), ip_address_octets), - ) - } - } - - #[test] - fn from_ip_address_for_ip_address_ref_test() { - { - let ip_address = IpAddr::V4(String::from("127.0.0.1"), [127, 0, 0, 1]); - assert_eq!( - IpAddrRef::from(&ip_address), - IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]), - ) - } - { - let ip_address = IpAddr::V6( - String::from("0000:0000:0000:0000:0000:0000:0000:0001"), - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - ); - assert_eq!( - IpAddrRef::from(&ip_address), - IpAddrRef::V6( - b"0000:0000:0000:0000:0000:0000:0000:0001", - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] - ), - ) - } - } - - #[test] - fn display_invalid_ip_address_error_test() { - assert_eq!(AddrParseError.to_string(), String::from("AddrParseError"),) - } - - #[test] - fn ip_address_ref_to_owned_test() { - { - assert_eq!( - IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]).to_owned(), - IpAddr::V4(String::from("127.0.0.1"), [127, 0, 0, 1]), - ) - } - { - assert_eq!( - IpAddrRef::V6( - b"0000:0000:0000:0000:0000:0000:0000:0001", - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - ) - .to_owned(), - IpAddr::V6( - String::from("0000:0000:0000:0000:0000:0000:0000:0001"), - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - ), - ) - } - } - - #[test] - fn ip_address_from_std_net_ipaddr_test() { - let ip_addresses = vec![ - ( - std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)), - IpAddr::V4(String::from("127.0.0.1"), [127, 0, 0, 1]), - ), - ( - std::net::IpAddr::V6(std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), - IpAddr::V6( - String::from("0000:0000:0000:0000:0000:0000:0000:0001"), - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - ), - ), - ]; - for (ip_address, expected_ip_address) in ip_addresses { - assert_eq!(IpAddr::from(ip_address), expected_ip_address,) - } - } - - #[test] - fn ipv6_to_uncompressed_string_test() { - let ip_addresses = vec![ - ( - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - String::from("0000:0000:0000:0000:0000:0000:0000:0001"), - ), - ( - [ - 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x84, 0x8e, 0x48, 0x47, 0xc9, 0x84, - 0xaa, 0xb3, 0x4d, - ], - String::from("2a05:d018:076c:b684:8e48:47c9:84aa:b34d"), - ), - ]; - for (ip_address_octets, expected_result) in ip_addresses { - assert_eq!( - ipv6_to_uncompressed_string(ip_address_octets), - expected_result, - ) - } - } - // (presented_address, constraint_address, constraint_mask, expected_result) const PRESENTED_MATCHES_CONSTRAINT: &[(&str, &str, &str, Result)] = &[ // Cannot mix IpV4 with IpV6 and viceversa diff --git a/src/subject_name/mod.rs b/src/subject_name/mod.rs index 4a5f5973..fc64b94c 100644 --- a/src/subject_name/mod.rs +++ b/src/subject_name/mod.rs @@ -13,20 +13,10 @@ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. mod dns_name; -pub(super) use dns_name::WildcardDnsNameRef; -pub use dns_name::{DnsNameRef, InvalidDnsNameError}; - -#[cfg(feature = "alloc")] -pub use dns_name::DnsName; - -mod name; -pub use name::{InvalidSubjectNameError, SubjectNameRef}; +pub(crate) use dns_name::{verify_dns_names, WildcardDnsNameRef}; mod ip_address; -pub use ip_address::{AddrParseError, IpAddrRef}; - -#[cfg(feature = "alloc")] -pub use ip_address::IpAddr; +pub(crate) use ip_address::verify_ip_address_names; mod verify; pub(super) use verify::{check_name_constraints, GeneralName, NameIterator}; diff --git a/src/subject_name/name.rs b/src/subject_name/name.rs deleted file mode 100644 index 3f34edfa..00000000 --- a/src/subject_name/name.rs +++ /dev/null @@ -1,95 +0,0 @@ -// 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::DnsNameRef; - -use super::ip_address::{self, IpAddrRef}; - -/// A DNS name or IP address, which borrows its text representation. -#[derive(Debug, Clone, Copy)] -pub enum SubjectNameRef<'a> { - /// A valid DNS name - DnsName(DnsNameRef<'a>), - - /// A valid IP address - IpAddress(IpAddrRef<'a>), -} - -/// An error indicating that a `SubjectNameRef` could not built -/// because the input is not a syntactically-valid DNS Name or IP -/// address. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct InvalidSubjectNameError; - -impl<'a> SubjectNameRef<'a> { - /// Attempts to decode an encodingless string as either an IPv4 address, IPv6 address or - /// DNS name; in that order. In practice this space is non-overlapping because - /// DNS name components are separated by periods but cannot be wholly numeric (so cannot - /// overlap with a valid IPv4 address), and IPv6 addresses are separated by colons but - /// cannot contain periods. - /// - /// The IPv6 address encoding supported here is extremely simplified; it does not support - /// compression, all leading zeroes must be present in each 16-bit word, etc. Generally - /// this is not suitable as a parse for human-provided addresses for this reason. Instead: - /// consider parsing these with `std::net::IpAddr` and then using - /// `IpAddr::from`. - pub fn try_from_ascii(subject_name: &'a [u8]) -> Result { - if let Ok(ip_address) = ip_address::parse_ipv4_address(subject_name) { - return Ok(SubjectNameRef::IpAddress(ip_address)); - } else if let Ok(ip_address) = ip_address::parse_ipv6_address(subject_name) { - return Ok(SubjectNameRef::IpAddress(ip_address)); - } else { - Ok(SubjectNameRef::DnsName( - DnsNameRef::try_from_ascii(subject_name).map_err(|_| InvalidSubjectNameError)?, - )) - } - } - - /// Constructs a `SubjectNameRef` from the given input if the - /// input is a syntactically-valid DNS name or IP address. - pub fn try_from_ascii_str(subject_name: &'a str) -> Result { - Self::try_from_ascii(subject_name.as_bytes()) - } -} - -impl<'a> From> for SubjectNameRef<'a> { - fn from(dns_name: DnsNameRef<'a>) -> SubjectNameRef { - SubjectNameRef::DnsName(DnsNameRef(dns_name.0)) - } -} - -impl<'a> From> for SubjectNameRef<'a> { - fn from(dns_name: IpAddrRef<'a>) -> SubjectNameRef { - match dns_name { - IpAddrRef::V4(ip_address, ip_address_octets) => { - SubjectNameRef::IpAddress(IpAddrRef::V4(ip_address, ip_address_octets)) - } - IpAddrRef::V6(ip_address, ip_address_octets) => { - SubjectNameRef::IpAddress(IpAddrRef::V6(ip_address, ip_address_octets)) - } - } - } -} - -impl AsRef<[u8]> for SubjectNameRef<'_> { - #[inline] - fn as_ref(&self) -> &[u8] { - match self { - SubjectNameRef::DnsName(dns_name) => dns_name.0, - SubjectNameRef::IpAddress(ip_address) => match ip_address { - IpAddrRef::V4(ip_address, _) | IpAddrRef::V6(ip_address, _) => ip_address, - }, - } - } -} diff --git a/tests/better_tls.rs b/tests/better_tls.rs index b7396816..4fb8bd36 100644 --- a/tests/better_tls.rs +++ b/tests/better_tls.rs @@ -6,11 +6,11 @@ use std::fs::File; use base64::{engine::general_purpose, Engine as _}; use bzip2::read::BzDecoder; -use pki_types::UnixTime; +use pki_types::{ServerName, UnixTime}; use serde::Deserialize; use webpki::types::{CertificateDer, SignatureVerificationAlgorithm, TrustAnchor}; -use webpki::{extract_trust_anchor, KeyUsage, SubjectNameRef}; +use webpki::{extract_trust_anchor, KeyUsage}; // All of the BetterTLS testcases use P256 keys. static ALGS: &[&dyn SignatureVerificationAlgorithm] = &[ @@ -87,7 +87,7 @@ fn run_testsuite(suite_name: &str, suite: &BetterTlsSuite, roots: &[TrustAnchor] ) .and_then(|_| { ee_cert.verify_is_valid_for_subject_name( - SubjectNameRef::try_from_ascii_str(&testcase.hostname) + &ServerName::try_from(testcase.hostname.as_str()) .expect("invalid testcase hostname"), ) }); diff --git a/tests/dns_name_tests.rs b/tests/dns_name_tests.rs deleted file mode 100644 index 7c916ed9..00000000 --- a/tests/dns_name_tests.rs +++ /dev/null @@ -1,408 +0,0 @@ -// 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"\x00", false), - (b"\x001.2.3.4", false), - (b"1.2.3.4\x00", false), - (b"1.2.3.4\x00.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"\x00", false), - (b"::1\x00:2", false), - (b"::1\x00", false), - (b"::1.2.3.4\x00", false), - (b"::1.2\x002.3.4", false), -]; - -#[test] -fn dns_name_ref_try_from_ascii_test() { - for &(s, is_valid) in DNS_NAME_VALIDITY - .iter() - .chain(IP_ADDRESS_DNS_VALIDITY.iter()) - { - assert_eq!( - webpki::DnsNameRef::try_from_ascii(s).is_ok(), - is_valid, - "DnsNameRef::try_from_ascii_str failed for \"{:?}\"", - s - ); - } -} diff --git a/tests/integration.rs b/tests/integration.rs index 42433a0e..c3f25da7 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -51,6 +51,8 @@ fn netflix() { #[cfg(feature = "alloc")] #[test] fn cloudflare_dns() { + use pki_types::ServerName; + let ee: &[u8] = include_bytes!("cloudflare_dns/ee.der"); let inter = CertificateDer::from(&include_bytes!("cloudflare_dns/inter.der")[..]); let ca = CertificateDer::from(&include_bytes!("cloudflare_dns/ca.der")[..]); @@ -75,19 +77,19 @@ fn cloudflare_dns() { .is_ok()); let check_name = |name: &str| { - let subject_name_ref = webpki::SubjectNameRef::try_from_ascii_str(name).unwrap(); + let subject_name_ref = ServerName::try_from(name).unwrap(); assert_eq!( Ok(()), - cert.verify_is_valid_for_subject_name(subject_name_ref) + cert.verify_is_valid_for_subject_name(&subject_name_ref) ); println!("{:?} ok as name", name); }; let check_addr = |addr: &str| { - let subject_name_ref = webpki::SubjectNameRef::try_from_ascii(addr.as_bytes()).unwrap(); + let subject_name_ref = ServerName::try_from(addr.as_bytes()).unwrap(); assert_eq!( Ok(()), - cert.verify_is_valid_for_subject_name(subject_name_ref) + cert.verify_is_valid_for_subject_name(&subject_name_ref) ); println!("{:?} ok as address", addr); }; diff --git a/tests/name_tests.rs b/tests/name_tests.rs deleted file mode 100644 index acca118f..00000000 --- a/tests/name_tests.rs +++ /dev/null @@ -1,73 +0,0 @@ -use webpki::DnsNameRef; - -fn compile_time_assert_send() {} -fn compile_time_assert_sync() {} - -#[test] -fn test_dns_name_ref_traits() { - compile_time_assert_send::(); - compile_time_assert_sync::(); - - let a = DnsNameRef::try_from_ascii(b"example.com").unwrap(); - - // `Copy` - { - let _b = a; - let _c = a; - } - - // `Clone` - #[allow(clippy::clone_on_copy)] - let _ = a.clone(); - // TODO: verify the clone is the same as `a`. - - // TODO: Don't require `alloc` for these. - #[cfg(feature = "alloc")] - { - // `Debug`. - assert_eq!(format!("{:?}", &a), "DnsNameRef(\"example.com\")"); - } -} - -#[cfg(feature = "alloc")] -#[test] -fn test_dns_name_traits() { - use webpki::DnsName; - - fn compile_time_assert_hash() {} - - compile_time_assert_hash::(); - compile_time_assert_send::(); - compile_time_assert_sync::(); - - let a_ref = DnsNameRef::try_from_ascii(b"example.com").unwrap(); - let a = a_ref.to_owned(); - - // `Clone`, `Debug`, `PartialEq`. - assert_eq!(&a, &a.clone()); - - // `Debug`. - assert_eq!(format!("{:?}", &a), "DnsName(\"example.com\")"); - - // PartialEq is case-insensitive - assert_eq!( - a, - DnsNameRef::try_from_ascii(b"Example.Com") - .unwrap() - .to_owned() - ); - - // PartialEq isn't completely wrong. - assert_ne!( - a, - DnsNameRef::try_from_ascii(b"fxample.com") - .unwrap() - .to_owned() - ); - assert_ne!( - a, - DnsNameRef::try_from_ascii(b"example.co") - .unwrap() - .to_owned() - ); -} diff --git a/tests/tls_server_certs.rs b/tests/tls_server_certs.rs index ba5eeaa8..d504df9f 100644 --- a/tests/tls_server_certs.rs +++ b/tests/tls_server_certs.rs @@ -15,7 +15,7 @@ use core::time::Duration; -use pki_types::{CertificateDer, UnixTime}; +use pki_types::{CertificateDer, UnixTime, ServerName}; use webpki::{extract_trust_anchor, KeyUsage}; fn check_cert( @@ -41,14 +41,14 @@ fn check_cert( )?; for valid in valid_names { - let name = webpki::SubjectNameRef::try_from_ascii_str(valid).unwrap(); - assert_eq!(cert.verify_is_valid_for_subject_name(name), Ok(())); + let name = ServerName::try_from(*valid).unwrap(); + assert_eq!(cert.verify_is_valid_for_subject_name(&name), Ok(())); } for invalid in invalid_names { - let name = webpki::SubjectNameRef::try_from_ascii_str(invalid).unwrap(); + let name = ServerName::try_from(*invalid).unwrap(); assert_eq!( - cert.verify_is_valid_for_subject_name(name), + cert.verify_is_valid_for_subject_name(&name), Err(webpki::Error::CertNotValidForName) ); }