From 4a2a9218d4836190a14f2c4eb3400a7bfe45632f Mon Sep 17 00:00:00 2001
From: Brian Smith <brian@briansmith.org>
Date: Tue, 20 Apr 2021 17:29:16 -0700
Subject: [PATCH] Redesign name validation API to allow extensibility in the
 types of names.

In particular, prepare for allowing IP addresses in an API-compatible way.
---
 src/end_entity.rs | 24 +++++++++++++-----------
 src/lib.rs        |  2 +-
 src/name.rs       |  8 +++++++-
 src/name/name.rs  | 23 +++++++++++++++++++++++
 4 files changed, 44 insertions(+), 13 deletions(-)
 create mode 100644 src/name/name.rs

diff --git a/src/end_entity.rs b/src/end_entity.rs
index 0507b65e..b94486ea 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, Error, Name, SignatureAlgorithm, Time,
     TlsClientTrustAnchors, TlsServerTrustAnchors,
 };
 
@@ -27,7 +27,7 @@ use alloc::vec::Vec;
 ///
 /// * `EndEntityCert.verify_is_valid_tls_server_cert`: Verify that the server's
 ///   certificate is currently valid *for use by a TLS server*.
-/// * `EndEntityCert.verify_is_valid_for_dns_name`: Verify that the server's
+/// * `EndEntityCert.verify_name`: Verify that the server's
 ///   certificate is valid for the host that is being connected to.
 /// * `EndEntityCert.verify_signature`: Verify that the signature of server's
 ///   `ServerKeyExchange` message is valid for the server's certificate.
@@ -37,8 +37,8 @@ use alloc::vec::Vec;
 ///
 /// * `EndEntityCert.verify_is_valid_tls_client_cert`: Verify that the client's
 ///   certificate is currently valid *for use by a TLS client*.
-/// * `EndEntityCert.verify_is_valid_for_dns_name` or
-///   `EndEntityCert.verify_is_valid_for_at_least_one_dns_name`: Verify that the
+/// * `EndEntityCert.verify_name` or
+///   `EndEntityCert.verify_for_at_least_one_name`: Verify that the
 ///   client's certificate is valid for the identity or identities used to
 ///   identify the client. (Currently client authentication only works when the
 ///   client is identified by one or more DNS hostnames.)
@@ -140,8 +140,10 @@ impl<'a> EndEntityCert<'a> {
     }
 
     /// 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)
+    pub fn verify_for_name(&self, name: Name) -> Result<(), Error> {
+        match name {
+            Name::DnsName(dns_name) => name::verify_cert_dns_name(&self, dns_name),
+        }
     }
 
     /// Verifies that the certificate is valid for at least one of the given DNS
@@ -154,12 +156,12 @@ impl<'a> EndEntityCert<'a> {
     /// Requires the `alloc` default feature; i.e. this isn't available in
     /// `#![no_std]` configurations.
     #[cfg(feature = "alloc")]
-    pub fn verify_is_valid_for_at_least_one_dns_name<'names>(
+    pub fn verify_for_at_least_one_name<'names>(
         &self,
-        dns_names: impl Iterator<Item = DnsNameRef<'names>>,
-    ) -> Result<Vec<DnsNameRef<'names>>, Error> {
-        let result: Vec<DnsNameRef<'names>> = dns_names
-            .filter(|n| self.verify_is_valid_for_dns_name(*n).is_ok())
+        dns_names: impl Iterator<Item = Name<'names>>,
+    ) -> Result<Vec<Name<'names>>, Error> {
+        let result: Vec<Name<'names>> = dns_names
+            .filter(|n| self.verify_for_name(*n).is_ok())
             .collect();
         if result.is_empty() {
             return Err(Error::CertNotValidForName);
diff --git a/src/lib.rs b/src/lib.rs
index f8146102..1a22d5d2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -48,7 +48,7 @@ mod verify_cert;
 pub use {
     end_entity::EndEntityCert,
     error::Error,
-    name::{DnsNameRef, InvalidDnsNameError},
+    name::{DnsNameRef, InvalidDnsNameError, Name},
     signed_data::{
         SignatureAlgorithm, ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256,
         ECDSA_P384_SHA384, ED25519,
diff --git a/src/name.rs b/src/name.rs
index 040a8133..5658bbb5 100644
--- a/src/name.rs
+++ b/src/name.rs
@@ -13,7 +13,10 @@
 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 mod dns_name;
-pub use dns_name::{DnsNameRef, InvalidDnsNameError};
+pub use self::{
+    dns_name::{DnsNameRef, InvalidDnsNameError},
+    name::Name,
+};
 
 /// Requires the `alloc` feature.
 #[cfg(feature = "alloc")]
@@ -21,5 +24,8 @@ pub use dns_name::DnsName;
 
 mod ip_address;
 
+#[allow(clippy::module_inception)]
+mod name;
+
 mod verify;
 pub(super) use verify::{check_name_constraints, verify_cert_dns_name};
diff --git a/src/name/name.rs b/src/name/name.rs
new file mode 100644
index 00000000..e379f126
--- /dev/null
+++ b/src/name/name.rs
@@ -0,0 +1,23 @@
+// Copyright 2021 Brian Smith.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use super::DnsNameRef;
+
+/// A name that identifies a subject.
+#[derive(Clone, Copy)]
+#[non_exhaustive]
+pub enum Name<'a> {
+    /// A DNS name.
+    DnsName(DnsNameRef<'a>),
+}