Skip to content

Commit

Permalink
Implement IP address validation
Browse files Browse the repository at this point in the history
Introduce `IpAddressRef`, `DnsNameOrIpRef` and the owned type
`IpAddress`.

Introduce a new public function `verify_is_valid_for_dns_name_or_ip`
that validates a given host name or IP address against a
certificate. IP addresses are only compared against Subject
Alternative Names.

It's possible to convert the already existing types `DnsNameRef` and
`IpAddressRef` into a `DnsNameOrIpRef` for better ergonomics when
calling to `verify_cert_dns_name_or_ip`.

The behavior of `verify_cert_dns_name` has not been altered, and works
in the same way as it has done until now, so that if `webpki` gets
bumped as a dependency, it won't start accepting certificates that
would have been rejected until now without notice.

Neither `IpAddressRef`, `DnsNameOrIpRef` nor `IpAddress` can be
instantiated directly. They must be instantiated through the
`try_from_ascii` and `try_from_ascii_str` public functions. This
ensures that instances of these types are correct by construction.

IPv6 addresses are only validated and supported in their uncompressed
form.

Signed-off-by: Rafael Fernández López <ereslibre@ereslibre.es>
  • Loading branch information
ereslibre committed May 30, 2022
1 parent b481381 commit ec674f8
Show file tree
Hide file tree
Showing 8 changed files with 673 additions and 15 deletions.
13 changes: 12 additions & 1 deletion src/end_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
cert, name, signed_data, verify_cert, DnsNameOrIpRef, DnsNameRef, Error, SignatureAlgorithm,
TLSClientTrustAnchors, TLSServerTrustAnchors, Time,
};
use core::convert::TryFrom;
Expand All @@ -27,6 +27,9 @@ use core::convert::TryFrom;
/// certificate is currently valid *for use by a TLS server*.
/// * `EndEntityCert.verify_is_valid_for_dns_name`: Verify that the server's
/// certificate is valid for the host that is being connected to.
/// * `EndEntityCert.verify_is_valid_for_dns_name_or_ip`: Verify that the server's
/// certificate is valid for the host or IP address that is being connected to.
///
/// * `EndEntityCert.verify_signature`: Verify that the signature of server's
/// `ServerKeyExchange` message is valid for the server's certificate.
///
Expand Down Expand Up @@ -148,6 +151,14 @@ impl<'a> EndEntityCert<'a> {
name::verify_cert_dns_name(self, dns_name)
}

/// Verifies that the certificate is valid for the given DNS host name or IP address.
pub fn verify_is_valid_for_dns_name_or_ip(
&self,
dns_name_or_ip: DnsNameOrIpRef,
) -> Result<(), Error> {
name::verify_cert_dns_name_or_ip(self, dns_name_or_ip)
}

/// Verifies the signature `signature` of message `msg` using the
/// certificate's public key.
///
Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ mod verify_cert;
pub use {
end_entity::EndEntityCert,
error::Error,
name::{DnsNameRef, InvalidDnsNameError},
name::{
ip_address::InvalidIpAddressError, ip_address::IpAddressRef, DnsNameOrIpRef, DnsNameRef,
InvalidDnsNameError, InvalidDnsNameOrIpError,
},
signed_data::{
SignatureAlgorithm, ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256,
ECDSA_P384_SHA384, ED25519,
Expand All @@ -60,7 +63,7 @@ pub use {

#[cfg(feature = "alloc")]
pub use {
name::DnsName,
name::{ip_address::IpAddress, DnsName},
signed_data::{
RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_2048_8192_SHA384, RSA_PKCS1_2048_8192_SHA512,
RSA_PKCS1_3072_8192_SHA384, RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
Expand Down
8 changes: 6 additions & 2 deletions src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ pub use dns_name::{DnsNameRef, InvalidDnsNameError};
#[cfg(feature = "alloc")]
pub use dns_name::DnsName;

mod ip_address;
#[allow(clippy::module_inception)]
mod name;
pub use name::{DnsNameOrIpRef, InvalidDnsNameOrIpError};

pub mod ip_address;

mod verify;
pub(super) use verify::{check_name_constraints, verify_cert_dns_name};
pub(super) use verify::{check_name_constraints, verify_cert_dns_name, verify_cert_dns_name_or_ip};
9 changes: 8 additions & 1 deletion src/name/dns_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl From<DnsNameRef<'_>> for DnsName {
///
/// [RFC 5280 Section 7.2]: https://tools.ietf.org/html/rfc5280#section-7.2
#[derive(Clone, Copy)]
pub struct DnsNameRef<'a>(&'a [u8]);
pub struct DnsNameRef<'a>(pub(crate) &'a [u8]);

impl AsRef<[u8]> for DnsNameRef<'_> {
#[inline]
Expand Down Expand Up @@ -139,6 +139,13 @@ impl core::fmt::Debug for DnsNameRef<'_> {
}
}

#[cfg(not(feature = "alloc"))]
impl core::fmt::Debug for DnsNameRef<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.debug_tuple("DnsNameRef").field(&self.0).finish()
}
}

impl<'a> From<DnsNameRef<'a>> for &'a str {
fn from(DnsNameRef(d): DnsNameRef<'a>) -> Self {
// The unwrap won't fail because DnsNameRefs are guaranteed to be ASCII
Expand Down
Loading

0 comments on commit ec674f8

Please sign in to comment.