diff --git a/src/error.rs b/src/error.rs index 4ec8393e..f47e893b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,6 +33,9 @@ pub enum Error { /// The certificate is not valid for the name it is being validated for. CertNotValidForName, + /// The certificate is not valid for the ip it is being validated for. + CertNotValidForIp, + /// The certificate is not valid yet; i.e. the time it is being validated /// for is earlier than the certificate's notBefore time. CertNotValidYet, diff --git a/src/name.rs b/src/name.rs index 24cb69f0..203ca888 100644 --- a/src/name.rs +++ b/src/name.rs @@ -126,6 +126,43 @@ impl<'a> From> for &'a str { } } +/// An ip address +#[derive(Clone, Copy)] +pub enum IPAddress { + /// An ipv4 address + V4(Ipv4), +} + +impl From for IPAddress { + fn from(ipv4: Ipv4) -> IPAddress { + IPAddress::V4(ipv4) + } +} + +/// An Ipv4 address +#[derive(Clone, Copy)] +pub struct Ipv4 { + bytes: [u8;4], +} + +impl Ipv4 { + /// Create a new Ipv4 address from a byte array + pub fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4 { + Ipv4 { bytes: [a, b, c, d] } + } + + fn matches(&self, ip: untrusted::Input) -> bool { + let mut reader = untrusted::Reader::new(ip); + for i in 0..4 { + if reader.read_byte() != Ok(self.bytes[i]) { + return false + } + } + // TODO: Check at end + return true; + } +} + pub fn verify_cert_dns_name( cert: &super::EndEntityCert, DNSNameRef(dns_name): DNSNameRef, ) -> Result<(), Error> { @@ -154,6 +191,33 @@ pub fn verify_cert_dns_name( ) } +pub fn verify_cert_ip_san( + cert: &super::EndEntityCert, ip: IPAddress, +) -> Result<(), Error> { + let cert = &cert.inner; + iterate_names( + cert.subject, + cert.subject_alt_name, + Err(Error::CertNotValidForIp), + &|name| { + match name { + GeneralName::IPAddress(ip_addr) => { + match &ip { + IPAddress::V4(ipv4) => { + if ipv4.matches(ip_addr) { + return NameIteration::Stop(Ok(())) + } + } + } + } + _ => (), + } + NameIteration::KeepGoing + }, + ) +} + + // https://tools.ietf.org/html/rfc5280#section-4.2.1.10 pub fn check_name_constraints( input: Option<&mut untrusted::Reader>, subordinate_certs: &Cert, diff --git a/src/webpki.rs b/src/webpki.rs index adcded53..52b17592 100644 --- a/src/webpki.rs +++ b/src/webpki.rs @@ -60,7 +60,7 @@ pub mod trust_anchor_util; mod verify_cert; pub use error::Error; -pub use name::{DNSNameRef, InvalidDNSNameError}; +pub use name::{DNSNameRef, InvalidDNSNameError, IPAddress, Ipv4}; #[cfg(feature = "std")] pub use name::DNSName; @@ -186,6 +186,11 @@ impl<'a> EndEntityCert<'a> { name::verify_cert_dns_name(&self, dns_name) } + /// Verifies that the certificate is valid for the given ip address + pub fn verify_is_valid_for_ip(&self, ip: IPAddress) -> Result<(), Error> { + name::verify_cert_ip_san(&self, ip) + } + /// Verifies that the certificate is valid for at least one of the given DNS /// host names. ///