use pki_types::IpAddr;
use super::verify::{GeneralName, NameIterator};
use crate::Error;
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()),
};
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))
}
fn presented_id_matches_reference_id(
presented_id: untrusted::Input,
reference_id: untrusted::Input,
) -> bool {
match (presented_id.len(), reference_id.len()) {
(4, 4) => (),
(16, 16) => (),
_ => {
return false;
}
};
let mut presented_ip_address = untrusted::Reader::new(presented_id);
let mut reference_ip_address = untrusted::Reader::new(reference_id);
while !presented_ip_address.at_end() {
let presented_ip_address_byte = presented_ip_address.read_byte().unwrap();
let reference_ip_address_byte = reference_ip_address.read_byte().unwrap();
if presented_ip_address_byte != reference_ip_address_byte {
return false;
}
}
true
}
pub(super) fn presented_id_matches_constraint(
name: untrusted::Input,
constraint: untrusted::Input,
) -> Result<bool, Error> {
match (name.len(), constraint.len()) {
(4, 8) => (),
(16, 32) => (),
(4, 32) | (16, 8) => {
return Ok(false);
}
(4, _) | (16, _) => {
return Err(Error::InvalidNetworkMaskConstraint);
}
_ => {
return Err(Error::BadDer);
}
};
let (constraint_address, constraint_mask) = constraint.read_all(Error::BadDer, |value| {
let address = value.read_bytes(constraint.len() / 2).unwrap();
let mask = value.read_bytes(constraint.len() / 2).unwrap();
Ok((address, mask))
})?;
let mut name = untrusted::Reader::new(name);
let mut constraint_address = untrusted::Reader::new(constraint_address);
let mut constraint_mask = untrusted::Reader::new(constraint_mask);
let mut seen_zero_bit = false;
loop {
let name_byte = name.read_byte().unwrap();
let constraint_address_byte = constraint_address.read_byte().unwrap();
let constraint_mask_byte = constraint_mask.read_byte().unwrap();
let leading = constraint_mask_byte.leading_ones();
let trailing = constraint_mask_byte.trailing_zeros();
if leading + trailing != 8 {
return Err(Error::InvalidNetworkMaskConstraint);
}
if seen_zero_bit && constraint_mask_byte != 0x00 {
return Err(Error::InvalidNetworkMaskConstraint);
}
if constraint_mask_byte != 0xff {
seen_zero_bit = true;
}
if ((name_byte ^ constraint_address_byte) & constraint_mask_byte) != 0 {
return Ok(false);
}
if name.at_end() {
break;
}
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn presented_id_matches_constraint_ipv4_test() {
let names_and_constraints = vec![
(
[0xC0, 0x00, 0x02, 0x00],
[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
Ok(true),
),
(
[0xC0, 0x00, 0x02, 0x01],
[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
Ok(true),
),
(
[0xC0, 0x00, 0x02, 0xFF],
[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
Ok(true),
),
(
[0xC0, 0x00, 0x01, 0xFF],
[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
Ok(false),
),
(
[0xC0, 0x00, 0x03, 0x00],
[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
Ok(false),
),
];
for (name, constraint, match_result) in names_and_constraints {
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&name),
untrusted::Input::from(&constraint),
),
match_result
)
}
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[0xC0, 0x00, 0x02]),
untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00]),
),
Err(Error::BadDer),
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0x00]),
untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00]),
),
Err(Error::BadDer),
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]),
untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF]),
),
Err(Error::InvalidNetworkMaskConstraint),
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]),
untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00]),
),
Err(Error::InvalidNetworkMaskConstraint),
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]),
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]),
),
Ok(false),
);
}
#[test]
fn presented_id_matches_constraint_ipv6_test() {
let names_and_constraints = vec![
(
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
],
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
],
Ok(true),
),
(
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01,
],
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
],
Ok(true),
),
(
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
],
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
],
Ok(true),
),
(
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
],
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
],
Ok(false),
),
(
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
],
[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
],
Ok(false),
),
];
for (name, constraint, match_result) in names_and_constraints {
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&name),
untrusted::Input::from(&constraint),
),
match_result
)
}
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
]),
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]),
),
Err(Error::BadDer),
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
]),
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]),
),
Err(Error::BadDer),
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
]),
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
]),
),
Err(Error::InvalidNetworkMaskConstraint),
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
]),
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]),
),
Err(Error::InvalidNetworkMaskConstraint),
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(&[
0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
]),
untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00]),
),
Ok(false),
);
}
#[test]
fn test_presented_id_matches_reference_id() {
assert!(!presented_id_matches_reference_id(
untrusted::Input::from(&[]),
untrusted::Input::from(&[]),
));
assert!(!presented_id_matches_reference_id(
untrusted::Input::from(&[0x01]),
untrusted::Input::from(&[])
));
assert!(!presented_id_matches_reference_id(
untrusted::Input::from(&[]),
untrusted::Input::from(&[0x01])
));
assert!(presented_id_matches_reference_id(
untrusted::Input::from(&[1, 2, 3, 4]),
untrusted::Input::from(&[1, 2, 3, 4])
));
assert!(!presented_id_matches_reference_id(
untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
untrusted::Input::from(&[1, 2, 3, 4])
));
assert!(!presented_id_matches_reference_id(
untrusted::Input::from(&[1, 2, 3, 4]),
untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
));
assert!(presented_id_matches_reference_id(
untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
));
}
#[test]
fn presented_id_matches_constraint_rejects_incorrect_length_arguments() {
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(b"\x00\x00\x00"),
untrusted::Input::from(b"")
),
Err(Error::BadDer)
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(b"\x00\x00\x00\x00\x00"),
untrusted::Input::from(b"")
),
Err(Error::BadDer)
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
),
untrusted::Input::from(b"")
),
Err(Error::BadDer)
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
),
untrusted::Input::from(b"")
),
Err(Error::BadDer)
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(b"\x00\x00\x00\x00"),
untrusted::Input::from(b"\x00\x00\x00\x00\xff\xff\xff")
),
Err(Error::InvalidNetworkMaskConstraint)
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(b"\x00\x00\x00\x00"),
untrusted::Input::from(b"\x00\x00\x00\x00\xff\xff\xff\xff\x00")
),
Err(Error::InvalidNetworkMaskConstraint)
);
assert_eq!(
presented_id_matches_constraint(untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")),
Err(Error::InvalidNetworkMaskConstraint)
);
assert_eq!(
presented_id_matches_constraint(untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")),
Err(Error::InvalidNetworkMaskConstraint)
);
assert_eq!(
presented_id_matches_constraint(untrusted::Input::from(b"\x00\x00\x00\x00"),
untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")),
Ok(false)
);
assert_eq!(
presented_id_matches_constraint(
untrusted::Input::from(
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
),
untrusted::Input::from(b"\x00\x00\x00\x00\xff\xff\xff\xff")
),
Ok(false)
);
}
}
#[cfg(all(test, feature = "alloc"))]
mod alloc_tests {
use super::*;
const PRESENTED_MATCHES_CONSTRAINT: &[(&str, &str, &str, Result<bool, Error>)] = &[
("2001:db8::", "8.8.8.8", "255.255.255.255", Ok(false)),
("8.8.8.8", "2001:db8::", "ffff::", Ok(false)),
(
"8.8.8.8",
"8.8.8.8",
"255.255.255.1",
Err(Error::InvalidNetworkMaskConstraint),
),
(
"8.8.8.8",
"8.8.8.8",
"255.255.0.255",
Err(Error::InvalidNetworkMaskConstraint),
),
(
"8.8.8.8",
"8.8.8.8",
"255.0.255.255",
Err(Error::InvalidNetworkMaskConstraint),
),
(
"8.8.8.8",
"8.8.8.8",
"0.255.255.255",
Err(Error::InvalidNetworkMaskConstraint),
),
(
"8.8.8.8",
"8.8.8.8",
"1.255.255.255",
Err(Error::InvalidNetworkMaskConstraint),
),
(
"8.8.8.8",
"8.8.8.8",
"128.128.128.128",
Err(Error::InvalidNetworkMaskConstraint),
),
("8.8.8.8", "8.8.8.8", "255.255.255.255", Ok(true)),
("8.8.8.9", "8.8.8.8", "255.255.255.255", Ok(false)),
("8.8.8.9", "8.8.8.8", "255.255.255.254", Ok(true)),
("8.8.8.10", "8.8.8.8", "255.255.255.254", Ok(false)),
("8.8.8.10", "8.8.8.8", "255.255.255.0", Ok(true)),
("8.8.15.10", "8.8.8.8", "255.255.248.0", Ok(true)),
("8.8.16.10", "8.8.8.8", "255.255.248.0", Ok(false)),
("8.8.16.10", "8.8.8.8", "255.255.0.0", Ok(true)),
("8.31.16.10", "8.8.8.8", "255.224.0.0", Ok(true)),
("8.32.16.10", "8.8.8.8", "255.224.0.0", Ok(false)),
("8.32.16.10", "8.8.8.8", "255.0.0.0", Ok(true)),
("63.32.16.10", "8.8.8.8", "192.0.0.0", Ok(true)),
("64.32.16.10", "8.8.8.8", "192.0.0.0", Ok(false)),
("64.32.16.10", "8.8.8.8", "0.0.0.0", Ok(true)),
(
"2001:db8::",
"2001:db8::",
"fffe:ffff::",
Err(Error::InvalidNetworkMaskConstraint),
),
(
"2001:db8::",
"2001:db8::",
"ffff:fdff::",
Err(Error::InvalidNetworkMaskConstraint),
),
(
"2001:db8::",
"2001:db8::",
"ffff:feff::",
Err(Error::InvalidNetworkMaskConstraint),
),
(
"2001:db8::",
"2001:db8::",
"ffff:fcff::",
Err(Error::InvalidNetworkMaskConstraint),
),
(
"2001:db8::",
"2001:db8::",
"7fff:ffff::",
Err(Error::InvalidNetworkMaskConstraint),
),
("2001:db8::", "2001:db8::", "ffff:ffff::", Ok(true)),
("2001:db9::", "2001:db8::", "ffff:ffff::", Ok(false)),
("2001:db9::", "2001:db8::", "ffff:fffe::", Ok(true)),
("2001:dba::", "2001:db8::", "ffff:fffe::", Ok(false)),
("2001:dba::", "2001:db8::", "ffff:ff00::", Ok(true)),
("2001:dca::", "2001:db8::", "ffff:fe00::", Ok(true)),
("2001:fca::", "2001:db8::", "ffff:fe00::", Ok(false)),
("2001:fca::", "2001:db8::", "ffff:0000::", Ok(true)),
("2000:fca::", "2001:db8::", "fffe:0000::", Ok(true)),
("2003:fca::", "2001:db8::", "fffe:0000::", Ok(false)),
("2003:fca::", "2001:db8::", "ff00:0000::", Ok(true)),
("1003:fca::", "2001:db8::", "e000:0000::", Ok(false)),
("1003:fca::", "2001:db8::", "0000:0000::", Ok(true)),
];
#[cfg(feature = "std")]
#[test]
fn presented_matches_constraint_test() {
use std::boxed::Box;
use std::net::IpAddr;
for &(presented, constraint_address, constraint_mask, expected_result) in
PRESENTED_MATCHES_CONSTRAINT
{
let presented_bytes: Box<[u8]> = match presented.parse::<IpAddr>().unwrap() {
IpAddr::V4(p) => Box::new(p.octets()),
IpAddr::V6(p) => Box::new(p.octets()),
};
let ca_bytes: Box<[u8]> = match constraint_address.parse::<IpAddr>().unwrap() {
IpAddr::V4(ca) => Box::new(ca.octets()),
IpAddr::V6(ca) => Box::new(ca.octets()),
};
let cm_bytes: Box<[u8]> = match constraint_mask.parse::<IpAddr>().unwrap() {
IpAddr::V4(cm) => Box::new(cm.octets()),
IpAddr::V6(cm) => Box::new(cm.octets()),
};
let constraint_bytes = [ca_bytes, cm_bytes].concat();
let actual_result = presented_id_matches_constraint(
untrusted::Input::from(&presented_bytes),
untrusted::Input::from(&constraint_bytes),
);
assert_eq!(
actual_result, expected_result,
"presented_id_matches_constraint(\"{:?}\", \"{:?}\")",
presented_bytes, constraint_bytes
);
}
}
}