From 29a112defaa9b33644eff88d8134a744941ed016 Mon Sep 17 00:00:00 2001 From: Alessandro Bono Date: Mon, 9 Aug 2021 16:28:54 +0200 Subject: [PATCH 01/12] dns_name: Update assert string to new API We don't call anymore `presented_dns_id_matches_reference_dns_id`. --- src/subject_name/dns_name.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/subject_name/dns_name.rs b/src/subject_name/dns_name.rs index 348d9a0f..2dbdeeda 100644 --- a/src/subject_name/dns_name.rs +++ b/src/subject_name/dns_name.rs @@ -792,11 +792,9 @@ mod tests { untrusted::Input::from(reference), ); assert_eq!( - actual_result, - expected_result, - "presented_dns_id_matches_reference_dns_id(\"{:?}\", IDRole::ReferenceID, \"{:?}\")", - presented, - reference + actual_result, expected_result, + "presented_id_matches_reference_id(\"{:?}\", \"{:?}\")", + presented, reference ); } } From 89569afa739c272cb4cd6b1ec3610ecdd789ce09 Mon Sep 17 00:00:00 2001 From: Alessandro Bono Date: Mon, 9 Aug 2021 16:30:39 +0200 Subject: [PATCH 02/12] dns_name: Add tests for `presented_id_matches_constraint` --- src/subject_name/dns_name.rs | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/subject_name/dns_name.rs b/src/subject_name/dns_name.rs index 2dbdeeda..54e3c979 100644 --- a/src/subject_name/dns_name.rs +++ b/src/subject_name/dns_name.rs @@ -798,4 +798,63 @@ mod tests { ); } } + + // (presented_name, constraint, expected_matches) + const PRESENTED_MATCHES_CONSTRAINT: &[(&[u8], &[u8], Option)] = &[ + // No absolute presented IDs allowed + (b".", b"", None), + (b"www.example.com.", b"", None), + (b"www.example.com.", b"www.example.com.", None), + // No absolute constraints allowed + (b"www.example.com", b".", None), + (b"www.example.com", b"www.example.com.", None), + // No wildcard in constraints allowed + (b"www.example.com", b"*.example.com", None), + // No empty presented IDs allowed + (b"", b"", None), + // Empty constraints match everything allowed + (b"example.com", b"", Some(true)), + (b"*.example.com", b"", Some(true)), + // Constraints that start with a dot + (b"www.example.com", b".example.com", Some(true)), + (b"www.example.com", b".EXAMPLE.COM", Some(true)), + (b"www.example.com", b".axample.com", Some(false)), + (b"www.example.com", b".xample.com", Some(false)), + (b"www.example.com", b".exampl.com", Some(false)), + (b"badexample.com", b".example.com", Some(false)), + // Constraints that do not start with a dot + (b"www.example.com", b"example.com", Some(true)), + (b"www.example.com", b"EXAMPLE.COM", Some(true)), + (b"www.example.com", b"axample.com", Some(false)), + (b"www.example.com", b"xample.com", Some(false)), + (b"www.example.com", b"exampl.com", Some(false)), + (b"badexample.com", b"example.com", Some(false)), + // Presented IDs with wildcard + (b"*.example.com", b".example.com", Some(true)), + (b"*.example.com", b"example.com", Some(true)), + (b"*.example.com", b"www.example.com", Some(true)), + (b"*.example.com", b"www.EXAMPLE.COM", Some(true)), + (b"*.example.com", b"www.axample.com", Some(false)), + (b"*.example.com", b".xample.com", Some(false)), + (b"*.example.com", b"xample.com", Some(false)), + (b"*.example.com", b".exampl.com", Some(false)), + (b"*.example.com", b"exampl.com", Some(false)), + // Matching IDs + (b"www.example.com", b"www.example.com", Some(true)), + ]; + + #[test] + fn presented_matches_constraint_test() { + for &(presented, constraint, expected_result) in PRESENTED_MATCHES_CONSTRAINT { + let actual_result = presented_id_matches_constraint( + untrusted::Input::from(presented), + untrusted::Input::from(constraint), + ); + assert_eq!( + actual_result, expected_result, + "presented_id_matches_constraint(\"{:?}\", \"{:?}\")", + presented, constraint + ); + } + } } From 1536dba477cb8e29505c24e9b8b2458beafd113d Mon Sep 17 00:00:00 2001 From: Alessandro Bono Date: Tue, 10 Aug 2021 14:33:09 +0200 Subject: [PATCH 03/12] ip_address: Add tests for `presented_id_matches_constraint` --- src/subject_name/ip_address.rs | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/subject_name/ip_address.rs b/src/subject_name/ip_address.rs index f5ac5ced..cecadd39 100644 --- a/src/subject_name/ip_address.rs +++ b/src/subject_name/ip_address.rs @@ -1293,4 +1293,74 @@ mod alloc_tests { ) } } + + // (presented_address, constraint_address, constraint_mask, expected_result) + const PRESENTED_MATCHES_CONSTRAINT: &[(&str, &str, &str, Result)] = &[ + // Cannot mix IpV4 with IpV6 and viceversa + ("2001:db8::", "8.8.8.8", "255.255.255.255", Ok(false)), + ("8.8.8.8", "2001:db8::", "ffff::", Ok(false)), + // IpV4 + ("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)), + // IpV6 + ("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::().unwrap() { + IpAddr::V4(p) => Box::new(p.octets()), + IpAddr::V6(p) => Box::new(p.octets()), + }; + let ca_bytes: Box<[u8]> = match constraint_address.parse::().unwrap() { + IpAddr::V4(ca) => Box::new(ca.octets()), + IpAddr::V6(ca) => Box::new(ca.octets()), + }; + let cm_bytes: Box<[u8]> = match constraint_mask.parse::().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 + ); + } + } } From 53d7dc059d4594e4290ffe543c7f0ec1a082bfff Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 27 Nov 2022 13:55:08 +0100 Subject: [PATCH 04/12] Fix name constraints check There were two bugs. webpki didn't: 1. read the X.509 Name Constraints field in its entirety, nor 2. check the certificate subject against the constraints correctly (1) is a simple fix, (2) requires reading the Common Name from the certificate. Requires lifting some DER parsing logic from ring to parse UTF8String and Set fields. Ring doesn't support those and isn't likely to in the near future, see https://github.com/briansmith/ring/pull/1265. Closes #3. --- src/der.rs | 59 +++++++++++++++++++++++++++++++++++-- src/subject_name/verify.rs | 24 ++++++++++++--- tests/integration.rs | 19 ++++++++++++ tests/wpt/ca.der | Bin 0 -> 16523 bytes tests/wpt/ee.der | Bin 0 -> 8384 bytes 5 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 tests/wpt/ca.der create mode 100644 tests/wpt/ee.der diff --git a/src/der.rs b/src/der.rs index adcb371d..bb8213c7 100644 --- a/src/der.rs +++ b/src/der.rs @@ -14,16 +14,71 @@ use crate::{calendar, time, Error}; pub(crate) use ring::io::{ - der::{nested, Tag}, + der::{CONSTRUCTED, CONTEXT_SPECIFIC}, Positive, }; +// Copied (and extended) from ring's src/der.rs +#[allow(clippy::upper_case_acronyms)] +#[derive(Clone, Copy, Eq, PartialEq)] +#[repr(u8)] +pub(crate) enum Tag { + Boolean = 0x01, + Integer = 0x02, + BitString = 0x03, + OctetString = 0x04, + OID = 0x06, + UTF8String = 0x0C, + Sequence = CONSTRUCTED | 0x10, // 0x30 + Set = CONSTRUCTED | 0x11, // 0x31 + UTCTime = 0x17, + GeneralizedTime = 0x18, + + #[allow(clippy::identity_op)] + ContextSpecificConstructed0 = CONTEXT_SPECIFIC | CONSTRUCTED | 0, + ContextSpecificConstructed1 = CONTEXT_SPECIFIC | CONSTRUCTED | 1, + ContextSpecificConstructed3 = CONTEXT_SPECIFIC | CONSTRUCTED | 3, +} + +impl From for usize { + #[allow(clippy::as_conversions)] + fn from(tag: Tag) -> Self { + tag as Self + } +} + +impl From for u8 { + #[allow(clippy::as_conversions)] + fn from(tag: Tag) -> Self { + tag as Self + } // XXX: narrowing conversion. +} + #[inline(always)] pub(crate) fn expect_tag_and_get_value<'a>( input: &mut untrusted::Reader<'a>, tag: Tag, ) -> Result, Error> { - ring::io::der::expect_tag_and_get_value(input, tag).map_err(|_| Error::BadDER) + let (actual_tag, inner) = read_tag_and_get_value(input)?; + if usize::from(tag) != usize::from(actual_tag) { + return Err(Error::BadDER); + } + Ok(inner) +} + +// TODO: investigate taking decoder as a reference to reduce generated code +// size. +pub(crate) fn nested<'a, F, R, E: Copy>( + input: &mut untrusted::Reader<'a>, + tag: Tag, + error: E, + decoder: F, +) -> Result +where + F: FnOnce(&mut untrusted::Reader<'a>) -> Result, +{ + let inner = expect_tag_and_get_value(input, tag).map_err(|_| error)?; + inner.read_all(error, decoder) } pub struct Value<'a> { diff --git a/src/subject_name/verify.rs b/src/subject_name/verify.rs index fabebeed..f09bd926 100644 --- a/src/subject_name/verify.rs +++ b/src/subject_name/verify.rs @@ -99,10 +99,7 @@ pub(crate) fn check_name_constraints( if !inner.peek(subtrees_tag.into()) { return Ok(None); } - let subtrees = der::nested(inner, subtrees_tag, Error::BadDER, |tagged| { - der::expect_tag_and_get_value(tagged, der::Tag::Sequence) - })?; - Ok(Some(subtrees)) + der::expect_tag_and_get_value(inner, subtrees_tag).map(Some) } let permitted_subtrees = parse_subtrees(input, der::Tag::ContextSpecificConstructed0)?; @@ -205,6 +202,10 @@ fn check_presented_id_conforms_to_constraints_in_subtree( dns_name::presented_id_matches_constraint(name, base).ok_or(Error::BadDER) } + (GeneralName::DirectoryName(name), GeneralName::DnsName(base)) => { + common_name(name).map(|cn| cn == base) + } + (GeneralName::DirectoryName(name), GeneralName::DirectoryName(base)) => Ok( presented_directory_name_matches_constraint(name, base, subtrees), ), @@ -365,3 +366,18 @@ fn general_name<'a>(input: &mut untrusted::Reader<'a>) -> Result }; Ok(name) } + +static COMMON_NAME: untrusted::Input = untrusted::Input::from(&[85, 4, 3]); + +fn common_name(input: untrusted::Input) -> Result { + let inner = &mut untrusted::Reader::new(input); + der::nested(inner, der::Tag::Set, Error::BadDER, |tagged| { + der::nested(tagged, der::Tag::Sequence, Error::BadDER, |tagged| { + let value = der::expect_tag_and_get_value(tagged, der::Tag::OID)?; + if value != COMMON_NAME { + return Err(Error::BadDER); + } + der::expect_tag_and_get_value(tagged, der::Tag::UTF8String) + }) + }) +} diff --git a/tests/integration.rs b/tests/integration.rs index 36490c7e..b29e9e59 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -104,6 +104,25 @@ pub fn cloudflare_dns() { check_addr("2606:4700:4700:0000:0000:0000:0000:6400"); } +#[cfg(feature = "alloc")] +#[test] +pub fn wpt() { + let ee: &[u8] = include_bytes!("wpt/ee.der"); + let ca = include_bytes!("wpt/ca.der"); + + let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()]; + let anchors = webpki::TLSServerTrustAnchors(&anchors); + + #[allow(clippy::unreadable_literal)] // TODO: Make this clear. + let time = webpki::Time::from_seconds_since_unix_epoch(1619256684); + + let cert = webpki::EndEntityCert::try_from(ee).unwrap(); + assert_eq!( + Ok(()), + cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[], time) + ); +} + #[test] pub fn ed25519() { let ee: &[u8] = include_bytes!("ed25519/ee.der"); diff --git a/tests/wpt/ca.der b/tests/wpt/ca.der new file mode 100644 index 0000000000000000000000000000000000000000..f7d00160116c90b9ed17c2d9838a311570dad4d6 GIT binary patch literal 16523 zcmb_jdvKK170+fj;T1p#5FrUMgzzY?eEaR2%EN-dC>BL(TO3Pc32kX$LxPF6JoMVZ z@)V(T7)2y%6(~xJLv4$-rMA2V84BY}?HC(iB-0Gq>Oip-wCCInG|Qgf*$wv3y}$Fi z=bn4NbMG&4%!J1rGd{7^o9oHV^>%F?=u3uE|3@+;F2Aoj9b=+Vu6q`jq-3~u9sTh-T(srU`Sq?2I=a`R7YpR&+bOtY77{H?P~@{ODtEyz}cH zz1(`~;)8D|j;!0>^ow5SAHBcdvnL){F`#Mvlpl7lYH7=@T)C(2!o1sFFDm`ptF66v zPb={jPs@3re(m&QMRT`*uyn!5Ltj49R96t#Ryy;Qo|F2nY{@GxzBIk&q-O`Zt}}(<3f}~0+{2&<{fZAkKNk*G99iv+Na zBm1F{rQ9bY06LUoX*$UW;Cy1xS{xz~Dbd!C3w7c!f(S&Scs^2{C9?yq1)+`$kuaQT z48|6MD;I}I5Y~mm5X9)aD75Bqj3;HqBaI~ zVsI5AI6E-5Fw9#J&eVlbM`7L~aF+z(Ov6wo408~K5d`3T!Vqy`jRtXSFxN4d>j;b> z3av$9-l8}=a6Vy}Hy2t9!FmfrKT(K8;7o%sf&k2D9L5%a)`HNQD`ltH4CMY$?kX)9 z$DnQQ#0Jl5hOj1`4H_8HKvx698W_bOPen;W0DvWC5cS>2u0?6f%7!gU4VZ#b%b=jt z4l8ItwM-vXMVH!X1*Kz9L8&cJ&^Rh5n+UhDNij&X+C|if%}anJ(puF154@N{3RZn>0wV@LApLDx^&T2P`aEdXjrvO zPb`|Qx(f8vqUh3tf`U>Dub}iyp`i2#sGuP=W>=jrJqSX}c6Zwn=ssSO!+{x_Jx4=(WgYZ|Kkk(#PG!fP=}w3|R1 zCn;fP08;Qj8)$Ko5|kepX}KvQ(0V~ek|Nef79wam9ULK%p=JmNnjv!}l_DXR$JJ6c z*=Z$KX)01|k(5+Sf|yhmBxVhynv|T9n44*yBh0W=LvRgAlBbschdB#>Vf}E&NV{8>(HDypsi4WCp(icwMwfI8kZ_Gqcgemz?FYI>BD_<&PCSCJpGX@E#_>||_ zuAR}ZtUb}(qWR9QQm2?(J8MLarjW_`N?(D~J-_Ryd{16p7q7=LrH}J)AD<~*PCtym z)&s8I^cUT*wD3mE!vZ0}!@_$m4-1d&JS@CS^RV#DZu;}iORET$7M`7XSU6e`E6?K+ zC(q*&BX8veHo^>`Acc>)^()HDOuY3A;+I%>mX|nq9+wz+9+&ucYX|t1xObM9n0Fqh zi){L8(Kt2r;<&`Q+c<$8iEn3liEZa`iEHO^iD~C?iD&0=iDkF;rOGFHdQ=nVEAi_# z9%)aI9f@1Fex?4UOw{e1XkyvtT zADnvW07~-lKLrgVeh^Mv~c#mcbany_XxnL9?M!gw;3^0v*y z`C3xg8bBV{f)zl!_=*c8kOkM94byv-j9fn1u)OJ1u(a^mFuaxq&cyF#%kE~&?PksF zTET31T{Ap|$FbW%E~hj>wC33}x}D;4JIUr6KUWFje$ro1AXhAB16!5}iB7V(Do-qx zEZfI4WcqYF%imrDdusyP3dm`_2IkhdaFIDB*bhz@&{6!L6Z9Gx+a%MQkvC0W*3ZG# zCYj!hKIPneex~;S#nYz#lRf>d|J!dK!)~x!^wrzjk{?QYs`W@f9Z}|%I@%+a(ZdtPbU~BR08Pg}9ZS^hx)9!P} z-VNoy*nQsm-=1GOY)zBrwsk)ncX0OcZ~wAz=~thqzVz_igOh$R#(elt`IeVIIk50+ zr|)an{L=%AyRBWayRz+tf0rEH_fq|wU;X%-H}*d}byc{mux-dIQzwuAv`^$7U#^*O zsdZ-I^Jg}e|M}||Yw8cRZC*Mv@7|r~Dhghx&w2Z~FV!s>^NkTJhrPJu`e`%&u({X9 zsymB*clXJCEs?Po_jvEU?w;F^>^U@Q^v-4FpUsF?z1vX#{b9!%-M%07d27|gzrHi& G$^QarS!9*~ literal 0 HcmV?d00001 diff --git a/tests/wpt/ee.der b/tests/wpt/ee.der new file mode 100644 index 0000000000000000000000000000000000000000..7160db5aa8663bb6faf5bd1326faa302844d8188 GIT binary patch literal 8384 zcmb7Jdr(wW7~f@gQ9v{lkcSHjK5-WBy}NsN*GGwy)}#jFqfAi|L{Vf{AQ!6gbH8)o z96c*IN96XcDles%m#X8{A)LRrPfWqv1rPm|N5E17c+WmNn1lp2M~ zq3og{xv!T)sZ><=|GX?BZo|_p*N$(>$)Eb(hdaIu+&JaY&U^Kjv`f8LuemfjP-rT& zS2sU#mW4FcyqO+ZT=h&iv1Dm|`F@`n`R5LdN*J^3#@yoHr^R2i9Nky687u|aQ`ic_?KFh!OZRU{M1qaGYE%Et(9q}!1SXIB4 z3l}u|qlP}y|KL}5?5O6+u7-vg^So3_h4O1hgqm|iC^#R=qqdvci=(PYe|A;}s{_T| zm8Y+ri`&zvTo*fG?6-r9?rUdokrEuO4&j174y^4If2G%r4qKuF3cKAooD}G@r?7Y__4()159H4e+3+RW#;aNUMkk!V=dQ{aB zlxfY;F~nAzg{5t^>DgifTWn^FO>D7|CAQn`EV0!lvc&>J9Bj22SZk8p#0rSyJksY_ z{NyAnyVAN$eaa)teQ90bzZvjYfLvrHllTRoZvpFyKyE_*v-(EL1K`mEeF4agATBfT z#{lYL0dhU)2LY^WV0FG!N5I1a9t-F<5%3#WTZ`7U0J(_#0sUYG^)jM71AYUlBhWX2 zc+DU#3&^Jk>dOQF%_#psE&x9Tz;8kNATBdAKXlH3x|k3T$iD&P&B9#QYBK|UGsv@u z@&NoXg8b+~TmtaZ1o9(-KG%b|j6mNA;?;w7dEmbh$OX`EdgKqtry1l^1nZgrj|t?* zgz^CVH-h{KfX4v(#|ZdMKrVu~^k7{cOoQEOv01BgQAV$Jyv#}`&qd^8@^ihTw4UQ0_VGUyRA;TD| zw&W*=?danRIW4G7h?9m#Xh#Do#Hd*zMh70mM46n?haB3`M-O7O(I7^f8DccZLyR^x z#OQ+xG5Tmgj1ESK(cuCy8uTDW!!W>}9|5dGi0V!s4Pa-r(I5>mI!qx(gABl0h7;y_ zM841<2<%+LGQ?<5ffx-X5TgMfVlG>c zpa<;eyq1ZNE*Q{`mx+%qbkL55U5L?u1~Aut2N*r9$Z#5EdV&Uc%qg>vXt0HLG_*sE zE|36wezvj(HagE}fCqMT*2&a}25o3Zmr97y#SCIb8BTgAz;-gZrUwIPN0()Y(clX) zx_m>79;P5hk4F#_Wa6VoDq!c@o2~-4dnb22bUUcFbZKDQg}2(VKDZJ{&BA^PY_nvw zQ=26V)Vkb+({e;^rcRf!aF$NYNKR$EO-4L7M_WL`U0#EbTkTN^B%HDV{flhnM+B0B zi{OO{D1^2pkV2H?-V5>+<%YA%@2(?}dOn>;yYaNd=FwvwO02?1yNuoGJef#Icu{P9 z-AQ1r`br_W>9X_C&vQ|%$iEb$+o~>l!1#YfRPzCnVy1bnn3`?WXV~rYvKGu)D43HO zB^&6*>Gs33zBw8hLb=96zgNTTVZiArJmR zZpn80!48;*+_0&6xeBmX5oFT@Y-Bt`Ju0o!vOQSkc=dJA2`VDr+%oRj>n^*tZwn&v zv>S|6-PU8r=dyj_nrz!Rxpi47_H=#Zt5PsMY5$*c`p-Q5r(CHB$} Date: Mon, 12 Dec 2022 16:00:02 +0000 Subject: [PATCH 05/12] common_name: do not require a commonName to be present --- src/subject_name/verify.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/subject_name/verify.rs b/src/subject_name/verify.rs index f09bd926..0f2ca818 100644 --- a/src/subject_name/verify.rs +++ b/src/subject_name/verify.rs @@ -203,7 +203,7 @@ fn check_presented_id_conforms_to_constraints_in_subtree( } (GeneralName::DirectoryName(name), GeneralName::DnsName(base)) => { - common_name(name).map(|cn| cn == base) + common_name(name).map(|cn| cn == Some(base)) } (GeneralName::DirectoryName(name), GeneralName::DirectoryName(base)) => Ok( @@ -369,15 +369,20 @@ fn general_name<'a>(input: &mut untrusted::Reader<'a>) -> Result static COMMON_NAME: untrusted::Input = untrusted::Input::from(&[85, 4, 3]); -fn common_name(input: untrusted::Input) -> Result { +fn common_name(input: untrusted::Input) -> Result, Error> { let inner = &mut untrusted::Reader::new(input); der::nested(inner, der::Tag::Set, Error::BadDER, |tagged| { der::nested(tagged, der::Tag::Sequence, Error::BadDER, |tagged| { - let value = der::expect_tag_and_get_value(tagged, der::Tag::OID)?; - if value != COMMON_NAME { - return Err(Error::BadDER); + while !tagged.at_end() { + let name_oid = der::expect_tag_and_get_value(tagged, der::Tag::OID)?; + if name_oid == COMMON_NAME { + return der::expect_tag_and_get_value(tagged, der::Tag::UTF8String).map(Some); + } else { + // discard unused name value + der::read_tag_and_get_value(tagged)?; + } } - der::expect_tag_and_get_value(tagged, der::Tag::UTF8String) + Ok(None) }) }) } From 698daeffbd00841a55dd0c6539fb8d10140cabb0 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Mon, 12 Dec 2022 16:00:42 +0000 Subject: [PATCH 06/12] Generate tests for name constraints This uses cryptography.io and can likely to be extended to test other features. --- tests/name_constraints.rs | 359 ++++++++++++++++++ .../additional_dns_labels.ca.der | Bin 0 -> 831 bytes .../additional_dns_labels.ee.der | Bin 0 -> 846 bytes tests/name_constraints/allow_dns_san.ca.der | Bin 0 -> 822 bytes tests/name_constraints/allow_dns_san.ee.der | Bin 0 -> 783 bytes ...an_and_disallow_subject_common_name.ca.der | Bin 0 -> 923 bytes ...an_and_disallow_subject_common_name.ee.der | Bin 0 -> 889 bytes ...low_dns_san_and_subject_common_name.ca.der | Bin 0 -> 900 bytes ...low_dns_san_and_subject_common_name.ee.der | Bin 0 -> 868 bytes .../allow_subject_common_name.ca.der | Bin 0 -> 846 bytes .../allow_subject_common_name.ee.der | Bin 0 -> 805 bytes .../name_constraints/disallow_dns_san.ca.der | Bin 0 -> 831 bytes .../name_constraints/disallow_dns_san.ee.der | Bin 0 -> 792 bytes ...s_san_and_allow_subject_common_name.ca.der | Bin 0 -> 950 bytes ...s_san_and_allow_subject_common_name.ee.der | Bin 0 -> 914 bytes .../disallow_subject_common_name.ca.der | Bin 0 -> 855 bytes .../disallow_subject_common_name.ee.der | Bin 0 -> 814 bytes tests/name_constraints/generate.py | 322 ++++++++++++++++ ..._constraints_on_unimplemented_names.ca.der | Bin 0 -> 880 bytes ..._constraints_on_unimplemented_names.ee.der | Bin 0 -> 852 bytes .../ip46_mixed_address_san_allowed.ca.der | Bin 0 -> 881 bytes .../ip46_mixed_address_san_allowed.ee.der | Bin 0 -> 820 bytes .../ip4_address_san_allowed.ca.der | Bin 0 -> 831 bytes .../ip4_address_san_allowed.ee.der | Bin 0 -> 788 bytes ...allowed_if_outside_excluded_subtree.ca.der | Bin 0 -> 887 bytes ...allowed_if_outside_excluded_subtree.ee.der | Bin 0 -> 844 bytes ...ted_if_excluded_is_sparse_cidr_mask.ca.der | Bin 0 -> 897 bytes ...ted_if_excluded_is_sparse_cidr_mask.ee.der | Bin 0 -> 854 bytes ...san_rejected_if_in_excluded_subtree.ca.der | Bin 0 -> 879 bytes ...san_rejected_if_in_excluded_subtree.ee.der | Bin 0 -> 836 bytes .../ip6_address_san_allowed.ca.der | Bin 0 -> 855 bytes .../ip6_address_san_allowed.ee.der | Bin 0 -> 800 bytes ...allowed_if_outside_excluded_subtree.ca.der | Bin 0 -> 911 bytes ...allowed_if_outside_excluded_subtree.ee.der | Bin 0 -> 856 bytes ...san_rejected_if_in_excluded_subtree.ca.der | Bin 0 -> 903 bytes ...san_rejected_if_in_excluded_subtree.ee.der | Bin 0 -> 848 bytes .../no_name_constraints.ca.der | Bin 0 -> 795 bytes .../no_name_constraints.ee.der | Bin 0 -> 821 bytes ..._constraints_on_unimplemented_names.ca.der | Bin 0 -> 870 bytes ..._constraints_on_unimplemented_names.ee.der | Bin 0 -> 835 bytes ...ject_unimplemented_name_constraints.ca.der | Bin 0 -> 862 bytes ...ject_unimplemented_name_constraints.ee.der | Bin 0 -> 831 bytes ...on_names_that_do_not_appear_in_cert.ca.der | Bin 0 -> 902 bytes ...on_names_that_do_not_appear_in_cert.ee.der | Bin 0 -> 866 bytes ...name_constraints_on_name_in_subject.ca.der | Bin 0 -> 902 bytes ...name_constraints_on_name_in_subject.ee.der | Bin 0 -> 874 bytes ...wildcard_san_accepted_if_in_subtree.ca.der | Bin 0 -> 858 bytes ...wildcard_san_accepted_if_in_subtree.ee.der | Bin 0 -> 821 bytes ...san_rejected_if_in_excluded_subtree.ca.der | Bin 0 -> 876 bytes ...san_rejected_if_in_excluded_subtree.ee.der | Bin 0 -> 839 bytes 50 files changed, 681 insertions(+) create mode 100644 tests/name_constraints.rs create mode 100644 tests/name_constraints/additional_dns_labels.ca.der create mode 100644 tests/name_constraints/additional_dns_labels.ee.der create mode 100644 tests/name_constraints/allow_dns_san.ca.der create mode 100644 tests/name_constraints/allow_dns_san.ee.der create mode 100644 tests/name_constraints/allow_dns_san_and_disallow_subject_common_name.ca.der create mode 100644 tests/name_constraints/allow_dns_san_and_disallow_subject_common_name.ee.der create mode 100644 tests/name_constraints/allow_dns_san_and_subject_common_name.ca.der create mode 100644 tests/name_constraints/allow_dns_san_and_subject_common_name.ee.der create mode 100644 tests/name_constraints/allow_subject_common_name.ca.der create mode 100644 tests/name_constraints/allow_subject_common_name.ee.der create mode 100644 tests/name_constraints/disallow_dns_san.ca.der create mode 100644 tests/name_constraints/disallow_dns_san.ee.der create mode 100644 tests/name_constraints/disallow_dns_san_and_allow_subject_common_name.ca.der create mode 100644 tests/name_constraints/disallow_dns_san_and_allow_subject_common_name.ee.der create mode 100644 tests/name_constraints/disallow_subject_common_name.ca.der create mode 100644 tests/name_constraints/disallow_subject_common_name.ee.der create mode 100644 tests/name_constraints/generate.py create mode 100644 tests/name_constraints/ignore_name_constraints_on_unimplemented_names.ca.der create mode 100644 tests/name_constraints/ignore_name_constraints_on_unimplemented_names.ee.der create mode 100644 tests/name_constraints/ip46_mixed_address_san_allowed.ca.der create mode 100644 tests/name_constraints/ip46_mixed_address_san_allowed.ee.der create mode 100644 tests/name_constraints/ip4_address_san_allowed.ca.der create mode 100644 tests/name_constraints/ip4_address_san_allowed.ee.der create mode 100644 tests/name_constraints/ip4_address_san_allowed_if_outside_excluded_subtree.ca.der create mode 100644 tests/name_constraints/ip4_address_san_allowed_if_outside_excluded_subtree.ee.der create mode 100644 tests/name_constraints/ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask.ca.der create mode 100644 tests/name_constraints/ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask.ee.der create mode 100644 tests/name_constraints/ip4_address_san_rejected_if_in_excluded_subtree.ca.der create mode 100644 tests/name_constraints/ip4_address_san_rejected_if_in_excluded_subtree.ee.der create mode 100644 tests/name_constraints/ip6_address_san_allowed.ca.der create mode 100644 tests/name_constraints/ip6_address_san_allowed.ee.der create mode 100644 tests/name_constraints/ip6_address_san_allowed_if_outside_excluded_subtree.ca.der create mode 100644 tests/name_constraints/ip6_address_san_allowed_if_outside_excluded_subtree.ee.der create mode 100644 tests/name_constraints/ip6_address_san_rejected_if_in_excluded_subtree.ca.der create mode 100644 tests/name_constraints/ip6_address_san_rejected_if_in_excluded_subtree.ee.der create mode 100644 tests/name_constraints/no_name_constraints.ca.der create mode 100644 tests/name_constraints/no_name_constraints.ee.der create mode 100644 tests/name_constraints/reject_constraints_on_unimplemented_names.ca.der create mode 100644 tests/name_constraints/reject_constraints_on_unimplemented_names.ee.der create mode 100644 tests/name_constraints/reject_unimplemented_name_constraints.ca.der create mode 100644 tests/name_constraints/reject_unimplemented_name_constraints.ee.der create mode 100644 tests/name_constraints/we_ignore_constraints_on_names_that_do_not_appear_in_cert.ca.der create mode 100644 tests/name_constraints/we_ignore_constraints_on_names_that_do_not_appear_in_cert.ee.der create mode 100644 tests/name_constraints/we_incorrectly_ignore_name_constraints_on_name_in_subject.ca.der create mode 100644 tests/name_constraints/we_incorrectly_ignore_name_constraints_on_name_in_subject.ee.der create mode 100644 tests/name_constraints/wildcard_san_accepted_if_in_subtree.ca.der create mode 100644 tests/name_constraints/wildcard_san_accepted_if_in_subtree.ee.der create mode 100644 tests/name_constraints/wildcard_san_rejected_if_in_excluded_subtree.ca.der create mode 100644 tests/name_constraints/wildcard_san_rejected_if_in_excluded_subtree.ee.der diff --git a/tests/name_constraints.rs b/tests/name_constraints.rs new file mode 100644 index 00000000..9fdbe1fc --- /dev/null +++ b/tests/name_constraints.rs @@ -0,0 +1,359 @@ +// Copyright 2022 Joseph Birr-Pixton. +// +// 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 core::convert::TryFrom; +extern crate webpki; + +static ALL_SIGALGS: &[&webpki::SignatureAlgorithm] = &[ + &webpki::ECDSA_P256_SHA256, + &webpki::ECDSA_P256_SHA384, + &webpki::ECDSA_P384_SHA256, + &webpki::ECDSA_P384_SHA384, + &webpki::ED25519, + #[cfg(feature = "alloc")] + &webpki::RSA_PKCS1_2048_8192_SHA256, + #[cfg(feature = "alloc")] + &webpki::RSA_PKCS1_2048_8192_SHA384, + #[cfg(feature = "alloc")] + &webpki::RSA_PKCS1_2048_8192_SHA512, + #[cfg(feature = "alloc")] + &webpki::RSA_PKCS1_3072_8192_SHA384, +]; + +fn check_cert( + ee: &[u8], + ca: &[u8], + valid_names: &[&str], + invalid_names: &[&str], +) -> Result<(), webpki::Error> { + let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()]; + let anchors = webpki::TLSServerTrustAnchors(&anchors); + + let time = webpki::Time::from_seconds_since_unix_epoch(0x1fed_f00d); + let cert = webpki::EndEntityCert::try_from(ee).unwrap(); + cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[], time)?; + + 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(())); + } + + for invalid in invalid_names { + let name = webpki::SubjectNameRef::try_from_ascii_str(invalid).unwrap(); + assert_eq!( + cert.verify_is_valid_for_subject_name(name), + Err(webpki::Error::CertNotValidForName) + ); + } + + Ok(()) +} + +// DO NOT EDIT BELOW: generated by name_constraints/generate.py + +#[test] +#[cfg(feature = "alloc")] +fn no_name_constraints() { + let ee = include_bytes!("name_constraints/no_name_constraints.ee.der"); + let ca = include_bytes!("name_constraints/no_name_constraints.ca.der"); + assert_eq!( + check_cert(ee, ca, &["dns.example.com"], &["subject.example.com"]), + Ok(()) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn additional_dns_labels() { + let ee = include_bytes!("name_constraints/additional_dns_labels.ee.der"); + let ca = include_bytes!("name_constraints/additional_dns_labels.ca.der"); + assert_eq!( + check_cert( + ee, + ca, + &["host1.example.com", "host2.example.com"], + &["subject.example.com"] + ), + Ok(()) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn disallow_subject_common_name() { + let ee = include_bytes!("name_constraints/disallow_subject_common_name.ee.der"); + let ca = include_bytes!("name_constraints/disallow_subject_common_name.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn disallow_dns_san() { + let ee = include_bytes!("name_constraints/disallow_dns_san.ee.der"); + let ca = include_bytes!("name_constraints/disallow_dns_san.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn allow_subject_common_name() { + let ee = include_bytes!("name_constraints/allow_subject_common_name.ee.der"); + let ca = include_bytes!("name_constraints/allow_subject_common_name.ca.der"); + assert_eq!(check_cert(ee, ca, &[], &["allowed.example.com"]), Ok(())); +} + +#[test] +#[cfg(feature = "alloc")] +fn allow_dns_san() { + let ee = include_bytes!("name_constraints/allow_dns_san.ee.der"); + let ca = include_bytes!("name_constraints/allow_dns_san.ca.der"); + assert_eq!(check_cert(ee, ca, &["allowed.example.com"], &[]), Ok(())); +} + +#[test] +#[cfg(feature = "alloc")] +fn allow_dns_san_and_subject_common_name() { + let ee = include_bytes!("name_constraints/allow_dns_san_and_subject_common_name.ee.der"); + let ca = include_bytes!("name_constraints/allow_dns_san_and_subject_common_name.ca.der"); + assert_eq!( + check_cert( + ee, + ca, + &["allowed-san.example.com"], + &["allowed-cn.example.com"] + ), + Ok(()) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn allow_dns_san_and_disallow_subject_common_name() { + let ee = + include_bytes!("name_constraints/allow_dns_san_and_disallow_subject_common_name.ee.der"); + let ca = + include_bytes!("name_constraints/allow_dns_san_and_disallow_subject_common_name.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn disallow_dns_san_and_allow_subject_common_name() { + let ee = + include_bytes!("name_constraints/disallow_dns_san_and_allow_subject_common_name.ee.der"); + let ca = + include_bytes!("name_constraints/disallow_dns_san_and_allow_subject_common_name.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn we_incorrectly_ignore_name_constraints_on_name_in_subject() { + let ee = include_bytes!( + "name_constraints/we_incorrectly_ignore_name_constraints_on_name_in_subject.ee.der" + ); + let ca = include_bytes!( + "name_constraints/we_incorrectly_ignore_name_constraints_on_name_in_subject.ca.der" + ); + assert_eq!(check_cert(ee, ca, &[], &[]), Ok(())); +} + +#[test] +#[cfg(feature = "alloc")] +fn reject_constraints_on_unimplemented_names() { + let ee = include_bytes!("name_constraints/reject_constraints_on_unimplemented_names.ee.der"); + let ca = include_bytes!("name_constraints/reject_constraints_on_unimplemented_names.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn we_ignore_constraints_on_names_that_do_not_appear_in_cert() { + let ee = include_bytes!( + "name_constraints/we_ignore_constraints_on_names_that_do_not_appear_in_cert.ee.der" + ); + let ca = include_bytes!( + "name_constraints/we_ignore_constraints_on_names_that_do_not_appear_in_cert.ca.der" + ); + assert_eq!( + check_cert(ee, ca, &["notexample.com"], &["example.com"]), + Ok(()) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn wildcard_san_accepted_if_in_subtree() { + let ee = include_bytes!("name_constraints/wildcard_san_accepted_if_in_subtree.ee.der"); + let ca = include_bytes!("name_constraints/wildcard_san_accepted_if_in_subtree.ca.der"); + assert_eq!( + check_cert( + ee, + ca, + &["bob.example.com", "jane.example.com"], + &["example.com", "uh.oh.example.com"] + ), + Ok(()) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn wildcard_san_rejected_if_in_excluded_subtree() { + let ee = include_bytes!("name_constraints/wildcard_san_rejected_if_in_excluded_subtree.ee.der"); + let ca = include_bytes!("name_constraints/wildcard_san_rejected_if_in_excluded_subtree.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn ip4_address_san_rejected_if_in_excluded_subtree() { + let ee = + include_bytes!("name_constraints/ip4_address_san_rejected_if_in_excluded_subtree.ee.der"); + let ca = + include_bytes!("name_constraints/ip4_address_san_rejected_if_in_excluded_subtree.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn ip4_address_san_allowed_if_outside_excluded_subtree() { + let ee = include_bytes!( + "name_constraints/ip4_address_san_allowed_if_outside_excluded_subtree.ee.der" + ); + let ca = include_bytes!( + "name_constraints/ip4_address_san_allowed_if_outside_excluded_subtree.ca.der" + ); + assert_eq!(check_cert(ee, ca, &["12.34.56.78"], &[]), Ok(())); +} + +#[test] +#[cfg(feature = "alloc")] +fn ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask() { + let ee = include_bytes!( + "name_constraints/ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask.ee.der" + ); + let ca = include_bytes!( + "name_constraints/ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask.ca.der" + ); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn ip4_address_san_allowed() { + let ee = include_bytes!("name_constraints/ip4_address_san_allowed.ee.der"); + let ca = include_bytes!("name_constraints/ip4_address_san_allowed.ca.der"); + assert_eq!( + check_cert( + ee, + ca, + &["12.34.56.78"], + &[ + "12.34.56.77", + "12.34.56.79", + "0000:0000:0000:0000:0000:ffff:0c22:384e" + ] + ), + Ok(()) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn ip6_address_san_rejected_if_in_excluded_subtree() { + let ee = + include_bytes!("name_constraints/ip6_address_san_rejected_if_in_excluded_subtree.ee.der"); + let ca = + include_bytes!("name_constraints/ip6_address_san_rejected_if_in_excluded_subtree.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn ip6_address_san_allowed_if_outside_excluded_subtree() { + let ee = include_bytes!( + "name_constraints/ip6_address_san_allowed_if_outside_excluded_subtree.ee.der" + ); + let ca = include_bytes!( + "name_constraints/ip6_address_san_allowed_if_outside_excluded_subtree.ca.der" + ); + assert_eq!( + check_cert(ee, ca, &["2001:0db9:0000:0000:0000:0000:0000:0001"], &[]), + Ok(()) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn ip6_address_san_allowed() { + let ee = include_bytes!("name_constraints/ip6_address_san_allowed.ee.der"); + let ca = include_bytes!("name_constraints/ip6_address_san_allowed.ca.der"); + assert_eq!( + check_cert( + ee, + ca, + &["2001:0db9:0000:0000:0000:0000:0000:0001"], + &["12.34.56.78"] + ), + Ok(()) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn ip46_mixed_address_san_allowed() { + let ee = include_bytes!("name_constraints/ip46_mixed_address_san_allowed.ee.der"); + let ca = include_bytes!("name_constraints/ip46_mixed_address_san_allowed.ca.der"); + assert_eq!( + check_cert( + ee, + ca, + &["12.34.56.78", "2001:0db9:0000:0000:0000:0000:0000:0001"], + &[ + "12.34.56.77", + "12.34.56.79", + "0000:0000:0000:0000:0000:ffff:0c22:384e" + ] + ), + Ok(()) + ); +} diff --git a/tests/name_constraints/additional_dns_labels.ca.der b/tests/name_constraints/additional_dns_labels.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..a19b5c70e0becfce2b94092caeb42ae7640dcddb GIT binary patch literal 831 zcmXqLVzxGDVp3kf%*4pVB;pYN=Hffa2QLjC*ctokX6FB&B9~&o%f_kI=F#?@mywa1 zmBGN)P})F}jX9KsnMWwIxVSX6NH4V_F}ENmRWCU|*HF$t1|-SFBbu0!l39|OpO=^u zpORM`pOcuBnp12bC(dhOW@uz&Y+z_=X=oS)=NcIp0J)SJ-^8ed>|jP#2IeM4eg=ak zMlPl%Mn;C;tLEO=#=B0SjeAj9SxS`DSHrKodZ`ZKp9PECwRjR1r1LXBI2e2H)r`M8 z53W+szfm!F^1^Ah`UNf2kE+wFUA|sgm|$bNp^opb zW4+`ttqBTFpV!$6d=IyMby~?^c2V{(&xYTJ=d^fT-*9qY+q}~M9C{0G%l=f_wlet2 z#x$E;{kI~1r#C+KuBy4bm%-^qy?^P!2eE53;@LO)H~lcVext~hd8Oxbw~yOIwXYvj zIWphukM}+vPHDZL)tu#JiA>Cl42+9`fp5SMj3!xOM#ldvtOm?L%0LbzAO{ii3 zUHEk6+VtK2#;xY62JbhX<ueJfMb#IK!8ecpU$OO?&)nkg39pZjw2SE*jE zjfdo`7rK{O&nwm~&z7i?;;fkbXTj<}WhqH|hkJ@&ZN7J=`m>Mfv%a&Jn63-4AGtbX ze{J?n6|pTIYad0=at+<%D4Nt`YVW*7_fz%nG)r^cPuC`$mI>0Bc6#srW2d*SPFlyl z?2qxBT|rwH2Tl_Fdn`WjXtKRP`h~h%d`lv?E* literal 0 HcmV?d00001 diff --git a/tests/name_constraints/additional_dns_labels.ee.der b/tests/name_constraints/additional_dns_labels.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..14e2e09eb24f84d9058752ffa4877976afdac531 GIT binary patch literal 846 zcmXqLV)imwJb}=I>19KB2KZ8LNBNtN>BO}AUFYW(d`03v?>h1V1O9sH@|mhvOsTLOU=K3e;$}U<&?}ZKg-0-$iTSR(ZJq79~jEAf-D*a zYE6O}`NbuM$l(kTGC~$I-~nk6W@P-&!eqdJ9LT_A0SshDhJb3v!q{iZ{hKu%)m9Xg z*ECq(o5XmZ(aYno%g!iQuc}MMwM(Gckor@D++$o`&n?F?dqu~7vxM=a&ZFyB!*gB literal 0 HcmV?d00001 diff --git a/tests/name_constraints/allow_dns_san.ca.der b/tests/name_constraints/allow_dns_san.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..131a79f9e36a581ebc55e31e868e19f3fe1ae950 GIT binary patch literal 822 zcmXqLVm2~pVv<_G%*4pVB$639VcrkvGaGUzS;+*fl{@&~+jo`$FB_*;n@8JsUPeZ4 zRt5u8Lums^Hs(+kW*(u;;^NZOBE8g##N2|MRK4WZLk_e-jn$KM(Ajp#Gusu6Sx`dVe=@k8qooyHIS5!7W;L@T?D{bfVFPb@Zp4TnwtJ_(AJ*rNxcKLc~VSOz zIHmP|R&$n@B{DHHGB7T-GO#e<2S$smFeBrC7FGjhAZ4Hm5|9H403&pPxPfSsFgVIm zQ;;JRIdp;14-8#KhE>_6AvY86hcn_)+|w{@X-`S? zOvc>5>$b^LiGwCTLct)zKd?7iL_B&abhn(NlYWAX93wM}!-o-@@qUQKt|ac@>g z!vp>66WeVL$GJ|nn9ceuleNOrZ2zR}KO!^Fyt@|FZrUqUH>Y>Ie81{V&Ne~umoW{> zyt-Q_1y5gZv-99Op6^eN{3^XaSt?@Lx(i`qXY|iL)&8~ROX7aPv=b&vBE+tk@%JrQ zq9#AHVe+rVMaxW1o-nTe5!Nu>6P@5B7tM>E;y&zTVsClO$$W)W(@%f_kI=F#?@mywa1 zmBGN&P})F}jX9KsnMWwIxVSX6NH4V_F}ENmRWCU|*HFwr1SHAD!<(3slV2X6l2;sG zoS0`IC(dhOW@uz&Y+z_=X=oS)=NcIp0J#PdICVBLDj~apk(GhDiIJZH=xi>gCPqev zyYrY2F;~1@a$r_m%(}|z{dGPDi}KP|M?dXa|1I?S-01Fx#ey7C;*JNrQ^UOU-k#WX z-(~N{4F{_xozBeK_}}pUq2n9=GVeP7SHW%8*P29D^@tk&bt~Ks3lyF-jCJQb?$#E_ zBdMogcc1guo{F%PZT~mt|Ij~|`gQ*C={sfx)lPgpOWlM=+)y-7$(p0j=+q6T-^njN z&2K;a<@=l+TN@1?wmYygdO3c)S(`7w-Fg1o{1q`9*B6A;Zl3x1ih}CD3dZ#NfBddl zgs#slI&p1Ay=kC+mfcmaRqwj_TX=oq4(1ha_V%!jS{V0L_fN~i93|6V>a+LPgm-T& z*!uIVb6CKI@8V3%j0}v64Gi=Q=wZUhPDX#Npay@CAsMD0WnB#16blI(EGaj9M)_gR$Yni{sZ`GcNeTUVT z^T}T>Ox(b=OyXj-NF_sm@w0ai?;be!FSWybdRFR%Fa3va$*P3Luk){*cP;q%($np$ zT>qNLHHq@*-uWJT=FIV0Tl2u8_%(uw93NDpi>3y4Rvg{7X5m>CsWl(2-|}$UF0$d7 z+x_R~EiZoAt9mT$ZQLT)OR*b0zwTw5#dvCkz1U< z!2^%hGr7u7nmr@iwK8I}cJ7PL`V`B{a^{CuuM||eHStq`f^6KZD`&&*qT${&$myJ`a&7+e zKMNMOYw;v3NatsMa4`1Xs~LZH9$clMf1_gVM)S4bV$iwaB0z{mA3Qw7tNeH&+C@; z)$J_59#yATyL`R0Fu}%jLml5=$9la>!-?4s;no(;bb&uQ_x zzTxD)wt1!hIrJ9Xmi?);ZDsJ4jcGQy`fo-2PH%kdT~%{=FN4#KdjHac4`SD7#ItYo zZ~9?!{YH^1^GeU>ZXdUaYF|I5a%8^QAMbrUoYHzft2xWd5}BA885kGG8bllL10z;e zn33^63#$P$kTUQ93CMv2>V&Q*?n*j+`tO${I*FNy6eED4&cXjT`~M zGzN?SMh0~;FWm(no$r}A=I{G1U-O##AphGnb#V*bL)_=d@N2gl<=m)0r&rnd_jb;M z{5N}}eiW^_;O^@nnx%Xs*8cZ}DeE-vSA^}d`E}^tz6;yZ?JXb0O5OULXTfCDEZ>yo z@TAC+q4Mwz!+D24&1&>aJNLUp?0Jf`AiGq;en+2d^@!J*dH%>yms!(C$ay#7i@X5;$6JP zb-ROWTm4;n_weS~TDP6hFzLR)_Re>Uqh8y^*tZi8PnyRi7+3zFGjR9A)#t?DR+}IG V#=*N|iAZwK8wXa^OD^Z$0|0~`blU&` literal 0 HcmV?d00001 diff --git a/tests/name_constraints/allow_dns_san_and_disallow_subject_common_name.ee.der b/tests/name_constraints/allow_dns_san_and_disallow_subject_common_name.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..ea3a021010430b24fe89a3f59d4c6f3a464baeb5 GIT binary patch literal 889 zcmXqLVlFjkVv1eB%*4pVB%*ESfA9;(ZmxwNem$ypv=Qfg5OK?ZmyJ`a&7*0AF)}jTjar%+fO+mvLv55A>T}yt=a`|Jlf7`pu z9h1M+TkQAQqq)?hx4=c=%5t-1*Y*lYFW=;rA2|Ik&!Y$b9U}OjpYY7zEIw~S{-^ft zb5s4l?q!t+%XQ`zN2RGlN#&4ZM}u3#L!IxqsRInKO4d9at$@n!JcT(0$GK z=|4;5gLB;9mvP)Pd(OrGBldY4gJ!FE<&ll56E#ISHl%H0Pc%CJ_K}=;qubQSd7raq QUd_7A_r>f$?%88p05e8pVgLXD literal 0 HcmV?d00001 diff --git a/tests/name_constraints/allow_dns_san_and_subject_common_name.ca.der b/tests/name_constraints/allow_dns_san_and_subject_common_name.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..f2e64b8f4afaf6d105f810635f208ed7d05ea6c4 GIT binary patch literal 900 zcmXqLVs0>KV#-*+%*4pVB%;F0=yA?vMwCGBvwgYF_g#5@Y3wxMW#iOp^Jx3d%gD&h z%3$DYC~Y9g#vIDR%p;UpTwI!3q?cNem|KvOs+XLfYp7?S1Cr$8QBBOr$uEyj$t#X8 zPRxr>%u9(cE=|fxO)iNCs>{vKi_c5UO*N1c=e001G%_+aFf_F^G>n3CjSLKcTxwg_ z#HfVqAx2gP<|amd27@L>E~X|%Muy+3=HA%GyH22udr?_gN|e-B!>_!0sSe?v1&iCY zcoG(*^D{p<7<=#4jK4b%u2RsyQ89P&!fCeqms%DTnQ61;bC(GSvSd1J&km9Hq**E$({V=(H zqsWzcrRQ_EkK07GuOCx6GT-cv_dXs@X}zD-oaJSSOw5c7jEf@-!VLI<(I_j-$oQXy z)qojD890Lk^*l#`(7lJM zR8CIJ&*t4Aw`@Lp!`s_hHSJ;%7mhn0RZ8)0Y+J4P(yVx0m8yw{Oro0Ac1HP?=e~x{ z|LHR6;I}oFXYXky{wVvT5`9@jI9vN;V(N*RA^UybO!M7v1)lBW*MVZm>o{o`#s&S6m&fBo0? IEQb|00DU8DYybcN literal 0 HcmV?d00001 diff --git a/tests/name_constraints/allow_dns_san_and_subject_common_name.ee.der b/tests/name_constraints/allow_dns_san_and_subject_common_name.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..e823eaf2b04a148131aafdb1d4a8b8ece0b1e52e GIT binary patch literal 868 zcmXqLVooqo#WHYGz-nwC&x~#Tk^<&myJ`a&7R1gC%-&CC9gQX zI596iF)t;)xHKs%HMt}ns4h1@FFr3ZH`PE+oY%t4(8$Qxz|hpv&@c+lH8L;&at#6v zPcgDGFgG#sGZ-{6axpbAGBP~V(AYE2pW)rh(kQ7b zao5gGdd6#glJ8vllFP@BM%#UuaZo+x*TR6b3o;Etyc z8vTz%uXUv8A9G1e`d52IrSn|Oo2Z$!hrIYV<*k1HLVvBiMEDtf;U7*m0{_@={H;|G zwp5;_mAmyVt9jd=PaPTx2Ll?dnoFvt9~3wmbxhRd&@9<$1saxi^JIV9y?OR^ozH{Y z6K8yMO5r}_@Ki16uE8Nxs43ubXJTe# zU|eisU}T^K40u^V7GQ)oiNgXP7@5eSZ@>dmBh1M7pM}YQ0XbZOi3J$0j0`(JgsR`& z?68UHq}ek!9VTzLEg_#@m(Ji>{An`J*HDI3!+_&ATBO2eZ|M6oT`qCoq$S6NH!f^> zkXm2j@!VBZGW^Tu&D9F^&K~;(({2j+7M^Uq87|Hk_drx=p>X`yqh`VvoBq7l=lPvD zr)2N^I>UGEhw?sKckS5h*5m3tFKOMXYjPF)8@F>uB&q!@^;&wVh-*GuO=6Jk@uitD7US_eEand9MUN%mxHjlRNyo`+8 ztPBQ@hSCO-Y|No7%sfJw#l@wmMS7_fiMa(isd~xzxrRyx3Lr@?9?8UW#UVL6+ZmNNtIIo47p^=fXfuX6TphcmJ=FgG#s zGZ-{6axpbAGBW&LHTT9g-gN?P+>6S}Qlg~38h+)~OLYkUELhyG#gnigouB!^!PtAR zX8hfGaFv4ojf%OG7f!R?ztpm*$V{6xpSw&zkR{V$dv=g?2`l^4Df$~b+b-U-om`qAO;B+9yv|nOd${eZ(@Orbi?V-tHvB$3r^V~~hLii+=9T{E&|7d@_NUUe zmBCjwrrG4`zZLO2z45VkRn6tS3{E%d{Ywu%h+U%*&%V*W>4(Yn8%3_nD?OjPecUFh zef^lqk@;qSy!Y{NO6&cs<}5EuWMXDyU|eivU}3-yj4oMWM#ldvtOm?L%0Lw)AO{iv zM)U%41JNd7a8#zIAV)NE=mL`kFmxFi^a=!04#li_zRY>zrEd*WYZH#j$t#B6n(FcD zOV-_{M_cD8#`S4Bt()QhU0gu;@RlbT%eVar=`Q;F`{R4*zZ)w*vG+RndgeLmdA}`4 zp4KKladK{{WZ!DxrZ*eTK0KY1&Qd*z&qH%f?K*e)$>Kb>s(zbn)_us!5I(7(%h^OJ zmh;2^g`aliXn$t-74u{s_nA_OO&u)(ZYlq=!elSR9x-J3)A;;&^``l%GiTVFc{(;N zvFNJ*#GcHv`rg-<<;^zQCKhd+ywciv7o6U#u)Ss$H)ZF7X>)V6TYsHsG`sQY^ab&t mO`?ZdKXQs5w0x0$>tTFRM8%Vj$vjGbH-Da8oj?0QvOEB@8dW0z literal 0 HcmV?d00001 diff --git a/tests/name_constraints/allow_subject_common_name.ee.der b/tests/name_constraints/allow_subject_common_name.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..b0a452b8571d3b55d845ade4a2d1c980152d35f3 GIT binary patch literal 805 zcmXqLVpcS0V&YuD%*4pVBogCR-EFWUt~Mw5>Gu66c_R|nDNZ%uW#iOp^Jx3d%gD&h z%3$DVC~Y9g#vIDR%p;UpTwI!3q?cNem|KvOs+XLfYp7(P0Fvb5kxb0V$uEyDE=|fx zO)iNC%H`(g#pfmFrW(kJ^IDi08W|ZI7@Ard8b-mnMg|5zu7Q)GjDZxy3SqD*sVOAe z(Zr~P>~cm{2IeM4eg=akMlPl%Mn;CiX_eff1wslMk_Gp)K6u!BJhHlY?x&wQm-ZRI zB`XDtyOS2FAK7ePeP-(63#E7T^Opo}y^?WWyXoR9U53b*$Q6Ir6g9qeakh{#(GSZK z*OUug{^U=c^55wDoyo`N@GRfFO!Dnf2VZHqK(Sx*|Fm$;*VjraabMHBFi-B`G*$0+ zheFfS1k#`F`KlUg_xOYG#K~!s`1ECSHPRp4kY!wKSs~(SsIEVA*{KB)O2WtQoUIcM z+w{6St1o%UK7r!)zWZle?RI@nj{Y<&O~{_%D~#dJFSRTxGSgz4J^?JU0@Ri{_Ge7&?V!Nzn$9p7KaddXv2 z6BL|2ud@~S9&Y>Uw35HLu4Zjc1Y4N(g;pD!yd8Pk3^cLKf{i(EVW$=}aX*Rj~ zZ$$_obxU;h(k|CY%IuJ8}qb18TK+xTSBvz2R42YXhytyB55&FBH+UmeE} zi&Q=RAM0m_SRLAb@{*X&2J`8{f6^Ce^sQltbaeKpERQPlXiz*mb8*YQ*ubcw(x;~c zLz4exZQpmZ?(~mCy#9aYx-5)Y*SdIZT|Vz(SL3x=;x@0I-!Nt%rlS^=e001G%_+aFf_F^G>n3CjSLKcTmxx*nwuDvkX^#a%D~*j$j<r~;LzUu?~8S@_2(01J*X3ak)o8OqOU*=XV|IWyJ-(5+j1zSt_ocZD_ zT08kwJEkSurE>JL&I~bY(huyuQR3<6{PJmM?Kh|9o85#exFc~l) z2P-gwfx*hi5cKtb22bPFN7oX4!wu)xY5kV@@VemAq0qg)l4ZyCmNyiA?8`E!ZnG|K*>GC^z#-lDlDD!8Q;IKb7h(Ey zs3|Xg{Y;gYHjXjpr&{kw{(WniXW<4@v1av;q3K0gA>x+2c}$mAwA(iCbv5~=X=xvL z(r$5D?b5#?YZW}Zk5olIzslD8crJH@yy1MU_VZ<{Lmd0gF6lpX`D6TFrcaKNDi^{+ z-Yn7XvupR%^O*gzf&EYPhd(n|?XOQiIL9R`EAjEN_bY@I9i8vrVwI0jdw>1wQll%4 WCm$*DRjf<9GWq-wsVMKf?R)@8=|a!| literal 0 HcmV?d00001 diff --git a/tests/name_constraints/disallow_dns_san_and_allow_subject_common_name.ca.der b/tests/name_constraints/disallow_dns_san_and_allow_subject_common_name.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..6213a95e42aa65cdfe5e76423b53ec4080961ba0 GIT binary patch literal 950 zcmXqLV%}uX#58LGGZP~dlZboG;n=5*R+qj-DmWLo{FV8(Eg;)~myJ`a&7+e zKMNMOYw;v3NatsMa4`1Xs~LZH9$clMf1_gVM)S4bV$iwaB0z{mA3Qw7tNeH&+C@; z)$J_59#yATyL`R0Fu}%jLml5=$9la>!-?4s;no(;bb&uQ_x zzTxD)wt1!hIrJ9Xmi?);ZDsJ4jcGQy`fo-2PH%kdT~%{=FN4#KdjHac4`SD7#ItYo zZ~9?!{YH^1^GeU>ZXdUaYF|I5a%8^QAMbrUoYHzft2xWd5}BA885kE=7?c_C10z;e zn33^63#$P$kTOUH3CMv2A`QY9m>NhniG$-eHANQ~-^j_rK%z+uCY6jLvrx`Jrb!Bx zR!>(3u18n@=cB`l>s9PLS;T z45r5Gz29%QYUGHPJh*9Y{Nbo!ZrovpnE0?as-4&Fu+MA%wR?}N{&B^-{{@$BRnd?+ zK9{Y0_hp&8Cy!SNC43TE(Khp9=_z@(NRy}zC1Hc>DRy68-g;Vf{B~#Em-`|{0c||b zHa8lEGzEm{eV$k7Vp95Su6%vu_2(Lo{}k=rFt^g@z0=0M38i1QA8EVN?cK~VGd%3B zgI(-f&;Ld0&&*^t9yW5AbH)7WnvT>tE$y402b*%tkmR^c%X*d{Ji+Q#N1Q^IdNVKGeaXIV*^7|OGCpbIM>L) z0LV3nGL$!vh1e?wHYGJhH#v_wb~iC9A$yaNm4Ug5k)Oe!iIIz`iII_Em6qn2S`eK4bS#<8|MKM@xGjv#?;rFC)A-A`LM?7l`!J8JI29q=~w^_ODq%;t*f z`@AJaPDYNaZR&aNI|$YrR2g(mYmQe-Zd)PyfrTr^I^^wc>yCSd)psZHr%VXDR(IcK zkMz9XOE#Owmzn&yTKq8afRBpgh099L*P`cWUNVY}R=9BN;Y>l1h%BKm3_`UTSwSq5 zU&z}lwRb(PZFYStv3sMu$sVnjhL56Oo|COpiZz+k6_>D7@Tsx)o~B1rCvbiK;={9y VvHRjmH_vN{0`;ATrnZzM0017QY>fZ_ literal 0 HcmV?d00001 diff --git a/tests/name_constraints/disallow_subject_common_name.ca.der b/tests/name_constraints/disallow_subject_common_name.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..2b010237ec90ce70f05a146ef37b96b92982404a GIT binary patch literal 855 zcmXqLVh%QFVzOSq%*4pVBqAL4duL$QI!D{h&qEu<4^(Cf&(kvCW#iOp^Jx3d%gD&h z%3$DPC~Y9g#vIDR%p;UpTwI!3q?cNem|KvOs+XLfYp80V43gyHkx9ucPRz;4FOM%S zP0C75E{O+9=jP|d=OyN*8pw(BT9_Fc85tWGnpzqfM!~s81_nSbm27EZR6=$>BP#=Q z6C*!^K@%evQxhX2!|zpdZ*1dTC(y>dsH`j{O6sfOS6;nThw#sW#qC-=2@BHsnI9aC zz4vOy-<=0nDd^v*m^*plG~4}4EsKiGv|01H%LD{jG99*O2T7N(vOk@ozp=CJ;yvs6 zv*vtzZpFTH|DzuT$DKM1WGfv~vKL%hbZMpSeEvlD4Y@FD*>4 zG2Kwd_t&vr@|e~H1*gyJYz4lD+rB!j3(LAA48TT;9vzbfezC^x%WoH5&2k8~vMpm|VY6NClNDxU{LjK_zzn1eG(ZA! zAOSf8nT1jY5=~;T2u)2vj&bAw2Br^S05dY^*)$Z{F=)2+n8iE^dG6>a@n!p++BLdO zXZc<${eF1#Yg=#XuW3Xmo45N z{CQpakz4otEf;N2&Hu&jwYW%oW3|&gFWv+tM@#*buwC>>94NI+YfJM^BmYz|FJ48zg*wO@Ix^BtEoc5KZUmJ7Eot$ ze7ke)`7?3K4nObmN+tMMRjYsKDbtksD|>uS#=5ZicOp9Yc07N}uh{p%Q&A@};eGX~ qdur|9BfV<#uYR8OeCOFSXNCK(=}$VKcr)qw+c!u4+wy;|2?GE!jak6} literal 0 HcmV?d00001 diff --git a/tests/name_constraints/disallow_subject_common_name.ee.der b/tests/name_constraints/disallow_subject_common_name.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..65ea3faef3f1498195991a514ff80befeff1116d GIT binary patch literal 814 zcmXqLV%9QfViH=w%*4pVB*OGKAz<|^?);xFa&sQN5H8y7sP1FH%f_kI=F#?@mywa1 zmBGNpP})F}jX9KsnMWwIxVSX6NH4V_F}ENmRWCU|*HG0!86?TYBa@O@oS2i7Umjmv znv|8AToMnI&dtw@&r8frHINhMwJKYiBSpJ3yiD`%uS5^3Q z<%)^PJiSCzS7_3<;AMZ7y2MEw*5Zll5`G>mb+fGUPKiyD`ufeO;_r8xKlfiz_4tC@ zMp4#c~DLM>PKI7CT2zk#>D~#djD5z-cJ|+ literal 0 HcmV?d00001 diff --git a/tests/name_constraints/generate.py b/tests/name_constraints/generate.py new file mode 100644 index 00000000..68426e59 --- /dev/null +++ b/tests/name_constraints/generate.py @@ -0,0 +1,322 @@ +""" +Generates test cases that aim to validate name constraints and other +name-related parts of webpki. + +Run this script from tests/name_constraints. It edits the bottom part of +tests/name_constraints.rs and drops files into tests/name_constraints. +""" + + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.serialization import Encoding +from cryptography.hazmat.backends import default_backend +from cryptography.x509.oid import NameOID +import ipaddress +import datetime + +ISSUER_PRIVATE_KEY = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=default_backend() +) +ISSUER_PUBLIC_KEY = ISSUER_PRIVATE_KEY.public_key() + +NOT_BEFORE = datetime.datetime.fromtimestamp(0x1fedf00d - 30) +NOT_AFTER = datetime.datetime.fromtimestamp(0x1fedf00d + 30) + +def name_constraints_test(test_name, + expected_error=None, + subject_common_name=None, + extra_subject_names=[], + valid_names=[], + invalid_names=[], + sans=None, + permitted_subtrees=None, + excluded_subtrees=None): + """ + Generate a test case, writing a rust '#[test]' function into + name_constraints.rs, and writing supporting files into the current + directory. + + - `test_name`: name of the test, must be a rust identifier. + - `expected_error`: item in `webpki::Error` enum, expected error from + webpki `verify_is_valid_tls_server_cert` function. Leave absent to + expect success. + - `subject_common_name`: optional string to put in end-entity certificate + subject common name. + - `extra_subject_names`: optional sequence of `x509.NameAttributes` to add + to end-entity certificate subject. + - `valid_names`: optional sequence of valid names that the end-entity + certificate is expected to pass `verify_is_valid_for_subject_name` for. + - `invalid_names`: optional sequence of invalid names that the end-entity + certificate is expected to fail `verify_is_valid_for_subject_name` with + `CertNotValidForName`. + - `sans`: optional sequence of `x509.GeneralName`s that are the contents of + the subjectAltNames extension. If empty or not provided the end-entity + certificate does not have a subjectAltName extension. + - `permitted_subtrees`: optional sequence of `x509.GeneralName`s that are + the `permittedSubtrees` contents of the `nameConstraints` extension. + If this and `excluded_subtrees` are empty/absent then the end-entity + certificate does not have a `nameConstraints` extension. + - `excluded_subtrees`: optional sequence of `x509.GeneralName`s that are + the `excludedSubtrees` contents of the `nameConstraints` extension. + If this and `permitted_subtrees` are both empty/absent then the + end-entity certificate does not have a `nameConstraints` extension. + """ + + # keys must be valid but are otherwise unimportant for these tests + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=default_backend(), + ) + public_key = private_key.public_key() + + issuer_name = x509.Name([ + x509.NameAttribute(NameOID.COMMON_NAME, u'issuer.example.com'), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, test_name), + ]) + + # end-entity + builder = x509.CertificateBuilder() + builder = builder.subject_name(x509.Name( + ([x509.NameAttribute(NameOID.COMMON_NAME, subject_common_name)] if subject_common_name else []) + + [x509.NameAttribute(NameOID.ORGANIZATION_NAME, test_name) ] + + extra_subject_names + )) + builder = builder.issuer_name(issuer_name) + + builder = builder.not_valid_before(NOT_BEFORE) + builder = builder.not_valid_after(NOT_AFTER) + builder = builder.serial_number(x509.random_serial_number()) + builder = builder.public_key(public_key) + if sans: + builder = builder.add_extension( + x509.SubjectAlternativeName(sans), + critical=False + ) + builder = builder.add_extension( + x509.BasicConstraints(ca=False, path_length=None), critical=True, + ) + certificate = builder.sign( + private_key=ISSUER_PRIVATE_KEY, + algorithm=hashes.SHA256(), + backend=default_backend(), + ) + + with open(test_name + '.ee.der', 'wb') as f: + f.write(certificate.public_bytes(Encoding.DER)) + + # issuer + builder = x509.CertificateBuilder() + builder = builder.subject_name(issuer_name) + builder = builder.issuer_name(issuer_name) + builder = builder.not_valid_before(NOT_BEFORE) + builder = builder.not_valid_after(NOT_AFTER) + builder = builder.serial_number(x509.random_serial_number()) + builder = builder.public_key(ISSUER_PUBLIC_KEY) + builder = builder.add_extension( + x509.BasicConstraints(ca=True, path_length=None), critical=True, + ) + if permitted_subtrees or excluded_subtrees: + builder = builder.add_extension( + x509.NameConstraints(permitted_subtrees, excluded_subtrees), + critical=True + ) + + certificate = builder.sign( + private_key=ISSUER_PRIVATE_KEY, + algorithm=hashes.SHA256(), + backend=default_backend() + ) + + with open(test_name + '.ca.der', 'wb') as f: + f.write(certificate.public_bytes(Encoding.DER)) + + if expected_error is None: + expected = 'Ok(())' + else: + expected = 'Err(webpki::Error::' + expected_error + ')' + + valid_names = ', '.join('"' + name + '"' for name in valid_names) + invalid_names = ', '.join('"' + name + '"' for name in invalid_names) + + print(""" +#[test] +#[cfg(feature = "alloc")] +fn %(test_name)s() { + let ee = include_bytes!("name_constraints/%(test_name)s.ee.der"); + let ca = include_bytes!("name_constraints/%(test_name)s.ca.der"); + assert_eq!( + check_cert(ee, ca, &[%(valid_names)s], &[%(invalid_names)s]), + %(expected)s + ); +}""" % locals(), file=output) + +top = open('../name_constraints.rs', 'r').readlines() +top = top[:top.index('// DO NOT EDIT BELOW: generated by name_constraints/generate.py\n')+1] +output = open('../name_constraints.rs', 'w') +for l in top: + output.write(l) + +name_constraints_test( + 'no_name_constraints', + subject_common_name='subject.example.com', + valid_names=['dns.example.com'], + invalid_names=['subject.example.com'], + sans=[x509.DNSName('dns.example.com')]) + +name_constraints_test( + 'additional_dns_labels', + subject_common_name='subject.example.com', + valid_names=['host1.example.com', 'host2.example.com'], + invalid_names=['subject.example.com'], + sans=[x509.DNSName('host1.example.com'), x509.DNSName('host2.example.com')], + permitted_subtrees=[x509.DNSName('.example.com')]) + + +name_constraints_test( + 'disallow_subject_common_name', + expected_error='UnknownIssuer', + subject_common_name='disallowed.example.com', + excluded_subtrees=[x509.DNSName('disallowed.example.com')]) +name_constraints_test( + 'disallow_dns_san', + expected_error='UnknownIssuer', + sans=[x509.DNSName('disallowed.example.com')], + excluded_subtrees=[x509.DNSName('disallowed.example.com')]) + +name_constraints_test( + 'allow_subject_common_name', + subject_common_name='allowed.example.com', + invalid_names=['allowed.example.com'], + permitted_subtrees=[x509.DNSName('allowed.example.com')]) +name_constraints_test( + 'allow_dns_san', + valid_names=['allowed.example.com'], + sans=[x509.DNSName('allowed.example.com')], + permitted_subtrees=[x509.DNSName('allowed.example.com')]) +name_constraints_test( + 'allow_dns_san_and_subject_common_name', + valid_names=['allowed-san.example.com'], + invalid_names=['allowed-cn.example.com'], + sans=[x509.DNSName('allowed-san.example.com')], + subject_common_name='allowed-cn.example.com', + permitted_subtrees=[x509.DNSName('allowed-san.example.com'), x509.DNSName('allowed-cn.example.com')]) +name_constraints_test( + 'allow_dns_san_and_disallow_subject_common_name', + expected_error='UnknownIssuer', + sans=[x509.DNSName('allowed-san.example.com')], + subject_common_name='disallowed-cn.example.com', + permitted_subtrees=[x509.DNSName('allowed-san.example.com')], + excluded_subtrees=[x509.DNSName('disallowed-cn.example.com')]) +name_constraints_test( + 'disallow_dns_san_and_allow_subject_common_name', + expected_error='UnknownIssuer', + sans=[x509.DNSName('allowed-san.example.com'), x509.DNSName('disallowed-san.example.com')], + subject_common_name='allowed-cn.example.com', + permitted_subtrees=[x509.DNSName('allowed-san.example.com'), x509.DNSName('allowed-cn.example.com')], + excluded_subtrees=[x509.DNSName('disallowed-san.example.com')]) + +# XXX: ideally this test case would be a negative one, because the name constraints +# should apply to the subject name. +# however, because we don't look at email addresses in subjects, it is accepted. +name_constraints_test( + 'we_incorrectly_ignore_name_constraints_on_name_in_subject', + extra_subject_names=[x509.NameAttribute(NameOID.EMAIL_ADDRESS, 'joe@notexample.com')], + permitted_subtrees=[x509.RFC822Name('example.com')]) + +# this does work, however, because we process all SANs +name_constraints_test( + 'reject_constraints_on_unimplemented_names', + expected_error='UnknownIssuer', + sans=[x509.RFC822Name('joe@example.com')], + permitted_subtrees=[x509.RFC822Name('example.com')]) + +# RFC5280 4.2.1.10: +# "If no name of the type is in the certificate, +# the certificate is acceptable." +name_constraints_test( + 'we_ignore_constraints_on_names_that_do_not_appear_in_cert', + sans=[x509.DNSName('notexample.com')], + valid_names=['notexample.com'], + invalid_names=['example.com'], + permitted_subtrees=[x509.RFC822Name('example.com')]) + +name_constraints_test( + 'wildcard_san_accepted_if_in_subtree', + sans=[x509.DNSName('*.example.com')], + valid_names=['bob.example.com', 'jane.example.com'], + invalid_names=['example.com', 'uh.oh.example.com'], + permitted_subtrees=[x509.DNSName('example.com')]) + +name_constraints_test( + 'wildcard_san_rejected_if_in_excluded_subtree', + expected_error='UnknownIssuer', + sans=[x509.DNSName('*.example.com')], + excluded_subtrees=[x509.DNSName('example.com')]) + +name_constraints_test( + 'ip4_address_san_rejected_if_in_excluded_subtree', + expected_error='UnknownIssuer', + sans=[x509.IPAddress(ipaddress.ip_address('12.34.56.78'))], + excluded_subtrees=[x509.IPAddress(ipaddress.ip_network('12.34.56.0/24'))]) + +name_constraints_test( + 'ip4_address_san_allowed_if_outside_excluded_subtree', + valid_names=['12.34.56.78'], + sans=[x509.IPAddress(ipaddress.ip_address('12.34.56.78'))], + excluded_subtrees=[x509.IPAddress(ipaddress.ip_network('12.34.56.252/30'))]) + +sparse_net_addr = ipaddress.ip_network('12.34.56.78/24', strict=False) +sparse_net_addr.netmask = ipaddress.ip_address('255.255.255.1') +name_constraints_test( + 'ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask', + expected_error='UnknownIssuer', + sans=[ + # inside excluded network, if netmask is allowed to be sparse + x509.IPAddress(ipaddress.ip_address('12.34.56.79')), + ], + excluded_subtrees=[x509.IPAddress(sparse_net_addr)]) + + +name_constraints_test( + 'ip4_address_san_allowed', + valid_names=['12.34.56.78'], + invalid_names=['12.34.56.77', '12.34.56.79', '0000:0000:0000:0000:0000:ffff:0c22:384e'], + sans=[x509.IPAddress(ipaddress.ip_address('12.34.56.78'))], + permitted_subtrees=[x509.IPAddress(ipaddress.ip_network('12.34.56.0/24'))]) + +name_constraints_test( + 'ip6_address_san_rejected_if_in_excluded_subtree', + expected_error='UnknownIssuer', + sans=[x509.IPAddress(ipaddress.ip_address('2001:db8::1'))], + excluded_subtrees=[x509.IPAddress(ipaddress.ip_network('2001:db8::/48'))]) + +name_constraints_test( + 'ip6_address_san_allowed_if_outside_excluded_subtree', + valid_names=['2001:0db9:0000:0000:0000:0000:0000:0001'], + sans=[x509.IPAddress(ipaddress.ip_address('2001:db9::1'))], + excluded_subtrees=[x509.IPAddress(ipaddress.ip_network('2001:db8::/48'))]) + +name_constraints_test( + 'ip6_address_san_allowed', + valid_names=['2001:0db9:0000:0000:0000:0000:0000:0001'], + invalid_names=['12.34.56.78'], + sans=[x509.IPAddress(ipaddress.ip_address('2001:db9::1'))], + permitted_subtrees=[x509.IPAddress(ipaddress.ip_network('2001:db9::/48'))]) + +name_constraints_test( + 'ip46_mixed_address_san_allowed', + valid_names=['12.34.56.78', '2001:0db9:0000:0000:0000:0000:0000:0001'], + invalid_names=['12.34.56.77', '12.34.56.79', '0000:0000:0000:0000:0000:ffff:0c22:384e'], + sans=[ + x509.IPAddress(ipaddress.ip_address('12.34.56.78')), + x509.IPAddress(ipaddress.ip_address('2001:db9::1')), + ], + permitted_subtrees=[ + x509.IPAddress(ipaddress.ip_network('12.34.56.0/24')), + x509.IPAddress(ipaddress.ip_network('2001:db9::/48')) + ]) diff --git a/tests/name_constraints/ignore_name_constraints_on_unimplemented_names.ca.der b/tests/name_constraints/ignore_name_constraints_on_unimplemented_names.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..f2dcbbf1569505efcb1bb031f4268e13efa58ec8 GIT binary patch literal 880 zcmXqLV$LyWVhUNn%*4pVB%)XmKHLwEdUN%mxHjlRNyo`+8 ztPBQWhSCO-Y|No7%sfJw#l@wmMS7_fiMa(isd~xzxrXKjrXWc!9=*);y!@in_`Jm2 z)cEB5yyB9g#LT>s;`sc$_|m*gkcQmUypq%uu!>>>IdNVKGeaXIV*^7|OGCpbIM>L) z0LZ18y-kct$X;Y*WngY%|&K7vLm9%DSC!xOk+jfS962wd2?@Q zRfVpVuHiFb{FHLRwR)+=i|b|vJ5)GcXu1DAy8U9*%YvEnWZ6`fEd1V|#-I^8FOX5x zai%_h&e`-9TaI4AgW})cm-qz)FJI#=;v>ZqXx=yf@VubgIx(K{X^oG6SN1NS@v7Wt z`>l>lOR0=rc{xq(+LmBO`C7}=7rRsYgyXA1G`OR@za?&+_Ws*sr^Qdt{yiw9$}1wg zkj?GuQtP;|`Io)(GJd|;aM0r9cMrqfLq|*|N&IU0`fUeurEuNHY;8gNU4=KY?w`4Q ze{sOs{RJy`y88Axyv`})@|bmRs{ih)3Zt*HJz1=KnHd$Cm>C%u7Xu@~fFBsKvcimv z|5;cKn1PgmEJ#2OBp_@cxPae)w~-quDIkX`FyR10m62fv(;C+Id9G<|=44Lt@P5DT zWrRS?u9+|LKfHLJ_))}DQ_CTdFQ$|7wpd|;)7pJmU;V^YUe2DmGnjAB#n(^RZpQSrR($*_8>v{7UYS36`m~2z zIX{R^?cX5PQ+X(F*5$R6-sentrh0UNhyQ+esWTxFu@O;rb3(aGlv0g9$>&{~ck#xK zRJ~cuHl3gLaofIRRex<-sHbx@aNd*2P3lP}`>sT6ik!H~G0$M-hyA)sPv4*DYQOp4 y_N9Eg*Qs3lDf9Sx!nA!EM-I1s-1+C{bh((B%LAqronVj+7G*8FKYvcp8b1JEZe3ge literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ignore_name_constraints_on_unimplemented_names.ee.der b/tests/name_constraints/ignore_name_constraints_on_unimplemented_names.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..12872da8fbe4f0da4d98d2ed39de0887a768e8e6 GIT binary patch literal 852 zcmXqLVh%89VzOAk%*4pVBoa_3ntq0(=;xKDoMn4o@#Wf{3(7FyW#iOp^Jx3d%gD&h z%3u&?C~Y9g#vIDR%p;UpTwI!3q?cNem|KvOs+XLfYiMp@3X*6X&%sGc+EfEY55JEdFk8Gw!+8t4+Pw{SJzGq(x+Pj#4nDnQ(9IgU#d^X)Qk*{=pa z!WZ3+E_~c@Z-3d`TT8?w{;d3&u}5OZoP+MAo>en_om$TGR{JRJx69n$@}+5VyG-w{ z=?fP;Wtu0QG^c-7{E78PG*q_T*5506^YbdnWp^0Q z{`WFkTDV$%v)FFMz6akJj@EAzyTG6S`m|1{d}QE?(>r%{rEnOT>s+0=VePV~OV&u< znbfbf6f6tYN3+}j1S)bRH zeeD&~&eK~DGchwVFfJA_;4|O>Mz5?eBjbM-CIbfKFastIV3;v7sGB@j>hym0ljGyO zUpJpAYO`%BHp+ifc%$Tnf=4jBUT2}#vBXH`jr-z1%#geCL2h@=Po}E7ldZ4CiKYl| z>%DRQZ=*~6&bbG(R)p_jy&Lhf(Au|X>&YuElR7r+opJ2h|AKqE|BpHf)!Z!X6^XCJu%wNyX%aD&APbc?Yw;-L~q7Fcu)~>=S*RT(A*Fs`yP3L z1N)EoikhCdFw=SW=M`&C%RGN4p~ILt@69{I;Po{YPfE;AG+s*+jNssW9abjM*z5HK E03POU9smFU literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip46_mixed_address_san_allowed.ca.der b/tests/name_constraints/ip46_mixed_address_san_allowed.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..8389fa9504399d7709060d137011a238cdd165b0 GIT binary patch literal 881 zcmXqLV$L;aVhUZr%*4pVBvP&YB4tms(i}w}{+5phsuurGupBnvW#iOp^Jx3d%gD&h z%3$DTC~Y9g#vIDR%p;UpTwI!3q?cNem|KvOs+XLfYp8CZ3Xc@{*v7j~ppAP`Sy@Vy)K|l=yn3k);hzPI+qHNS7Nqkt zKR6hB@70XII}ff>(7#bJck;q%w)>Y_78RLkv*vS`2?(-eI&9Alk}hFoe>z2fV`tmN zd)D)3&H47+ihbw)M?VUVJ9QYyRyw3)FSxYm(n{O;{EKEzo#%DS`s#L;UyrKOt6jcc zT9{yCx}lEmuVcOBF|7#-PM_D=3VaW@eRW#NUv^RUFVBYGhv&3-UEgqWU)#LW{~US? zZp;2u+O{(I%EmOCT>ZBqey2A+_O7bAyqCf0M!kRO!3VKxG~(Ge`ZxVBxqhR_m3gJ- zbGMJ%M76IUQ#mr``GQgDDA?7)-*40c9_-gLg6 z(A-Efw>7mP7c37L@hLnPXn9}!Z=GK2mGF)8_PFP1sO~&H=e5(N{Q2sUjCmWlM7$@_P8VB)^bOWCY9-Juxxr^cJAxMw}IO}8XbSqQF5Zc?dkmch9~yQ z3EkoR$B^3ecKvE*CC8auiNB3`CWTG6?|EdQTCu7wfJ69y?&4iXXROZOBJ=OymRZ)> zt5_ z`l#69r`%mG|K1&`b$jfRaDV^w|Buf$>=0d6{NYB{n;UkE>h@^m^IdS!6P{>N65CUq KcRssuvmpRgGF&wP literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip46_mixed_address_san_allowed.ee.der b/tests/name_constraints/ip46_mixed_address_san_allowed.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..85d4f41428fe021a6924448d7a23f190b1f57561 GIT binary patch literal 820 zcmXqLVm2^nVv<iH1xa%8$YmCon8oL2R-~rH zC#Iwnr4|>*7boV$C+6hjm#3x}$cgh>m>C)w85Zl`k>TR+*Uodd_xv^zxXNXy^Gt#Btf>x1=D$bFryh&H^G(S2 z?TWL!w$G-vKTkT{bZTbfx7V4<3v(^j3rS{t*ArI0oVw&t!fJ8GjdR<)nZ!5`&NS^; zc1}FpI_1})Xz80yi8CkG8YHZGaOYR`b-U0Oweqz#Dz?Cbj!*tDB?$Yf*Eu=fkbI zWY^}OCK5O1UuUq@$**BJ_tke^OxSU`y33|>r&_<-*R-)__PvYEp{g950);JaE-eb~ zFiiNj-0)NTocel6^L6v&)LmmASN+&=w%m7%+|}QIr`>0gX`8<0q}`r~>BjdC_FH$y z#2xTdIDhr=oW&<2(nQy><&~)gl?8@8wDV+nd?;{!Yspvro&&p2>z&9ec%)=GH&Q=2 te?iz1r=1pZ6Osd8Cz{Dm4Dqr&E%!I#A|Kyv_xA#Y-0segW-{b{0syk0LW2MR literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip4_address_san_allowed.ca.der b/tests/name_constraints/ip4_address_san_allowed.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..4af1a8c42630d3cdaade531a0cdfe108468ed72e GIT binary patch literal 831 zcmXqLVzxGDVp3kf%*4pVB;uDYHvPk)sdMgZ|IFHTOTY zaeQ%NUVLIsPJVf6ih-OsuZ5YRk&&^1p{b>zVHBKeWMBZ~Qf@&LqY|>C8Ce;an;7{S z44N3Zn3@lD**4qDw1n=kqU`Idz`bE$ge>S$;jLPOoFu zzQ2z3lE<_rC^&syXDjeM-1gOJC4bpP*}ps+ejlFG;&pw)$$f3}O8;}{Ex0ZFQ)%1E z;42%`Y;yJAiuj%0_}IIu=JH+!ryKSDr3W9xuF;5R-{{};!{qvnB3I^>p3mJrZWGnM zeoW=ae6v5^`*=8|^?p`!mX{?mF*7nSF4i~DHQ)zEm8>u$<9`-b17;v)AO#YT0|^Ki z@Gamm;A-dKQL!o6XFD>>etKi_!uwNQLoeA%-3_Pdzz!K>a&Y4mI}Q6f6UVI>y2B__Wmwq z(qk_OoE;jJt9OTY zaeQ%NUVLIsPJVf6ih-OsuZ5YRk&&^1p{b>zVHBKeWMBZ~8YmHAMiZkFvbz{r8JL?G z`5Ay-;bLlHWMnw^@$B<8-O3NYdtUn?%Iqla61!b-)VX4W!zy)q| zKV7JNyO* z`2GdY<~Qp#y)LA+b8sE3wwPJRxJ&To61|nhoXd|XwmHvpog`eMdgL85#)hmQ3x@$)I}4AJg`WWrNI;m8 z@jnZb0RwU%0wWg~h>Q#;OswxMWq9`a;ey@q(<3II}I&9H_O%c2Y<%bCuVXn?B;S0*|__eqH4_c4ZAzlceM&}T$Xg;>0dj0;l1pFawk&vg%4UBor?u{*gDGoQH`s02`Mhn%OWy^n gUp##o`r%I3H*0(O_qV>!s$Avic74i+`4g4_07qOz%m4rY literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip4_address_san_allowed_if_outside_excluded_subtree.ca.der b/tests/name_constraints/ip4_address_san_allowed_if_outside_excluded_subtree.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..825007e17acbcb422b76e6a88fbcda02e424fd8e GIT binary patch literal 887 zcmXqLVlFmlVv1hC%*4pVBof-Qq2J@u+r1+42j3e_71^K4^`zQ>myJ`a&7va*yY zsjr4#dG%5q!aoZZw`=hvEJ){PesD1M-m4jZcOG1&pns!c?&O8jZ1*pZt#nAqUT|sArIohx z`4`QcI?wBt_0{bxzaCYmSG#<@v@pTObVD8AU&ngMV_FjwoIbC!75E-*`|7lkzwDyy zU!D!W56@}wy1wD$zP5R#|2gy)+?M^Rv~6YZm5pgOx%zKK{7!Ft>|IrJc`t+0je7sm zgAZcYXvDK`^l$oMa{We;EAvXv=WZXjiE3XzrgCJy*&pwHJe<;cKdU**%MzKG85tNC z>l^4A@B^b*R+y3TKMSh?GmtWn0tv{01OyED7V;QywR7+&S^W9`|NkH4AO)r$V30C0 zeB8I$_E%4#de9BoN!#D-aWj5p#9iCsesitqg*8%&CEYPf+kbs}$iHuaNxTV%@2MkS z_fP%uGvju&DxaS8GONUfrc8&|_l-Abve*3)Z(E_aL*X3fjeieMKRfV4`kC*d|EVd) z0V&}&_qQ$m(6!*nmkGXVeUCCHOjAGNU%9_@=btO{U3*@gFz@K%D_>=EVo{T3-0YBy9CgmCxXw5Fqt+@`a514zpg_*A)EhxT#-lUZ~p1@OszFmA`hjW?#72 zslsHh;&D;F!EtX-e(cHFVL@B=8K&Cu-rAqX^#fC&I2^J!54 literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip4_address_san_allowed_if_outside_excluded_subtree.ee.der b/tests/name_constraints/ip4_address_san_allowed_if_outside_excluded_subtree.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..990921b51f7a4ec365170a771e11ce0178f4b13b GIT binary patch literal 844 zcmXqLV)ig-Vlr63%*4pVB;sM2(cG(janlriH#;{s*~BGFwrn-vW#iOp^Jx3d%gD&h z%3u&}C~Y9g#vIDR%p;UpTwI!3q?cNem|KvOs+XLfYiMI&1(M|AG0rS7iBC*PDM~Fa zjxSEki%-nS$uCb$iO)=n&o3=0&P+*-PpwGKDNO;26_+NJ6s4vb$cgh>m>C)w85Zl`k>N%_(#m{`HxE~E#%yq` z(lh#MR1o*XaaGtgzWys)qBev{-)2~NWc{H-hq^b;`|(B5BL9WMtw(G>x4xON_i@;b zEb-^fPF!8OQ!V-fJoPHemrmwry5riB`S;*PgM*9N+06Zaxf_>m*tTram)%-#%awT! z+lW4K;J!EGzVgqr-0Xk9m&qnPDiYXRvcHATk1GueEj z(I#`*z&xq{fq6Ub7_R5AH%zw8d_HRdzlgV74F7M{+1#m7uQ=aiUQK6u+@l!Dt;w79 zF_Y`w_qHdju6BhNLpviwcF9cdy=GGt9uru}pmKle(H+ltXXZ-w8La5pH~l9QGb01z zVnqXa1AbsE$_lb@7_hao@F-dM8SsDvgc%wCvoIMjAO|8aQ2+yxks*dptr4hu53Ggx8kriO6-mvg+AIyAQ$%+CDxY6{c(gB};Gzh5_aH}`*y_qR?n z`Lsr+pLH{)KI&YW^nlN^Go|U2@9x(Exl+{oEb=JGZTS E06t?`-~a#s literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask.ca.der b/tests/name_constraints/ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..e9a1cf07ceaa1589acce7664e7cc083f0cbd30a5 GIT binary patch literal 897 zcmXqLVy-o4VoF`W%*4pVB;qS8y=>>-W51mldOpmZU&p@dcg}VLUN%mxHjlRNyo`+8 ztPBPThSCO-Y|No7%sfJw#l@wmMS7_fiMa(isd~xzxrUAg_8>_v9*fKZlla7xl%mw) z;`rjky!fKjtkmR^)Rg$lwD{DDZLk_e-j zn$KM(Ajp#Gusu6Sx`dVe=@k8qooyHIS5!7W;L@T? zD{bfVFPb@Zp4TnwtJ_(AJ*rNxcKLc~VSOzIHmP|R&$n@B{DHH zGB7UIH_$cU2S&E6FeBrC7FGjhAY~v05|9H42pI4!ItOeo?%>k=#@lE9Y<-a6UzoCo$Olq_0Ste5j&$)B% z&z`d0$VMpP$)2q#vsWpb8W?6+**uVsZcjasy=(Fj>+VSv=R_8;h4gM%Vk5_BsB4(YsFx0Ld+2djJ3c literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask.ee.der b/tests/name_constraints/ip4_address_san_rejected_if_excluded_is_sparse_cidr_mask.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..9cfeb213f89839e56678d55b7ace68b55d1ab206 GIT binary patch literal 854 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 r!*!MSavo_rAFCTN)~>U&?~a()Kgsmo$688 literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip6_address_san_allowed.ee.der b/tests/name_constraints/ip6_address_san_allowed.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..d7f51d04b02f2dda37f3983778a434d29180570d GIT binary patch literal 800 zcmXqLVwN#zVq#gq%*4pVB*L(>Md8rb?*iM>ijGcHZMeDk*ZbWDylk9WZ60mkc^MhG zSs4uM4W$hv*_cCFn0bUUi;GKBi}X?}5_1c3QuUJaa}5;?KJmQ%JX7PzBDMhKp z#qq_7dGU!kIr-(ODF$-lycTALMn=X4hNhN=hEZ^?k%0k_YoJ7g8BL5z$nIifWngY% zz?EmcL+fndg*`DW(8!vW7 zda@;+c{KHktD>{S&y1c)sW*f^`(EYf{ByyYL)1SbI^DIUGg+Z?y+h%TC%LcFR=nA{ zc{W3NJX7Lzwd<`9Tk<}hOevggW4HJ=*SF~|+gGr^wRJx%_GVt*OkZ}t1xb&zB_bJ) zuh^>~rJm!o@Kl2I+CLK#d(;!66q#R|ooT*(c)i(+LUkk0DVnDOc+Vf(F4}b1q$*BG zwQ&8fn|!6;?3t@szFA6Fu&=F&ZP;tHJ^uUSIV(2Vt>Vo-Q$SSjMn3i8Ld>g zBj1^nsoG8DP4nBv_%&vtodwep;T{2Z{_7TDn>u)=&J*_KxEAqb{!K}bs>Vs*8OryBUQXtwOC4<=Svv*R29G0yt{Nc0K3vRLjV8( literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip6_address_san_allowed_if_outside_excluded_subtree.ca.der b/tests/name_constraints/ip6_address_san_allowed_if_outside_excluded_subtree.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..7ea0a4f942405c9a141277d0b295384e3b6b1c5e GIT binary patch literal 911 zcmXqLV(vC*Vk%z1%*4pVBx1q*Vw&l#hi1Fa<=?uP_|%HzoYEAaSaE4mNl|L5ft)z6g_)s|k+FfH zsimP|6r5{hU;yOO*77DsC1lSsvNA9?G4eAQG%<29H8C&ct+^)rwupphE`N6^1d#`5v-Fa}8g8q$)xsw-8v)#YcvZ%;Rn>C-i zOhAw&(_wpdkaP(v`_n1<8#~)B-m{)RYtFalR_r_XKl)K{+^NGrw$dRbd%>kemsZ-& z=U+5)>O8Mo)>pT){CZTKUhVSr(!vBA(+zcee;w;3k7-R%aQeK?R^WTM?W@yD{<4d* ze|a|iK0K$z>-vV1``YG}{^!tJa9j4L(zcbsS2m{E~7#{zz>XKSz$)T|17Kq%s|S(2qYi}63{SETc~27)UKew$h(693H%2GgaC3-1Je~S zs2LfyYz;X2C}o-E?|?(mX}!9Ahs|r3&d|PAZR9fBc$0&FmUCA&Yo^i@>{!3${A3=B0MT!m)%>ex7+#lIwRg&My-Bqf`)@Np ze|nkweTuQVW_ik{A8&q~y?*3jdUDi_4_zxyr3ZI^O}x5*D|OwCDw|o;+Bz5`!{<8P z3(vngUG9w`*B3GSweNdNb}OlObaxo+f6XR$@LUPMt?S*!6K8a8h&3N><*{Yzy0~ni zkQz_@o!xc}WlH>!oN~76s$VYq=Up~D8MgiJ;T4|iPS;En|q|DB_*1!;X TCDrpyuej1-bFJG8H*5j`#*lDh literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip6_address_san_allowed_if_outside_excluded_subtree.ee.der b/tests/name_constraints/ip6_address_san_allowed_if_outside_excluded_subtree.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..65657f55781fb127f99f382b74fcf3aca109b02b GIT binary patch literal 856 zcmXqLVh%BAVzODl%*4pVB;vB?m-~Z=PB}eQ!x!!T*DoI{iEcLFW#iOp^Jx3d%gD&h z%3u&}C~Y9g#vIDR%p;UpTwI!3q?cNem|KvOs+XLfYiMI&1(M|AG0rS7i%(2RDM~Fa zjxSEki%-nS$uCb$iO)=n&o3=0&P+*-PpwGKDNO;26_+NJ6s4vb$cgh>m>C)w85Zl`k>Ob7LB?&3A^)9Ti=^6e z`1j6tlv!oBEmEny#cBil{Nx@j zo$BI!r{-Q*VphYr?aw~bogY*c4HiydvP{4Jz+T>1Yu$Pk3+dACWjD_U|J+n&@<7Nt zS1RJyCh<)n#x5Uy7`nm>UQY;AHgUS3*EN%?RN#57*8G@7+FS=24XpRQzB#LqiJ6gs zaj~v}wt+M-7G(ulL=1%51r!*0cQPOVqX7>{QkaqPKMRuq19E@@69_Os85wvY_McTg z;ah507M*<{@k$YEht6fMMLCU!Ov4o*VNQ6+HMiV#>Xlob~=>#<;`2~ zcRSx?Dx6=$c~90*!CoxSP2!^eg7E9@f6r(CiCUYqeyxG1w#Y8W%RK6$62X(--wJ>n}03N$h$^ZZW literal 0 HcmV?d00001 diff --git a/tests/name_constraints/ip6_address_san_rejected_if_in_excluded_subtree.ca.der b/tests/name_constraints/ip6_address_san_rejected_if_in_excluded_subtree.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..86b9d349dc0ef8b03ebd854403a6647eb8aaefdc GIT binary patch literal 903 zcmXqLVs18QV#;2?%*4pVBvMgnIsJs>d&Xc^g+J5JGk*QP^UZYwUN%mxHjlRNyo`+8 ztPBR>hSCO-Y|No7%sfJw#l@wmMS7_fiMa(isd~xzxrP=7W*|u}9{tP$v-rf6l%mw) z;`rjky!fKjtkmR^)Rg$lwD`=t_|%HzoYE8^x41N^q$oAjKu(<3!pzXf$k@Qp)Y8x} z3eGh$FaUCCXK@pw60#>5Ss9p{82K3tni#p5niv@wey^H)V;k=}fi~_%Wo0Q*QeO?f z^6I5Jgnt$+Zr9>TSdh-o{NP~hy;n2-?mW0kLH|a@+{p{4+3sIzSyW`E&6>|$CLqX? z>99RJNVX^Dmk? zb)MHP>#N&Yem$yAuXg!*X<>qm>4rMKzmD~i$FwFWIDKAcEATzs_SI=6f7wOZzdRd$ zAD+|Vb$!FheQonf|8wXqxGnorY1_)+D;v{na`oSe_?_PP*t@Fc@?Hj~8}3i5)W*}u?1QL(~31}FoEmSd3YFAKTSbuEmtb~259L`O6x7TN}w!^bP@6 zmwe;r#q2M`rQWQnnP(fi@>bkrgUe2qiwak3_8hyk>DztbMeMcn-*-;s-X+W>Wu~>W zTFD?~K~m|w;N$-iR<_pD~t6tXG N{M!P5zq3V)na=U9fE+9Pei%f_kI=F#?@mywa1 zmBAp~P})F}jX9KsnMWwIxVSX6NH4V_F}ENmRWCU|*U-Yi3?#|Lqn}w|7N3}sQj}U; z9ABK67hjZ`m6}|Vni8Lx7N40HpIVWeQm>C)w85Zl`kzvj8)gRh!sw~l+amPe!yD5A0 zS>qO=k9J3XObBzfacK8nBlGl|VqeAdy@uvTVuW>hf=_*pF*vPcv+DTzX9_}do6^p_ z{+^lHV#~dFsrSJ~jfosP75*(0^!#jKCAGxo{GoFt754Hy=4N6r+BrwBM*B+!`xEso*Miy&LRNK!ST`C< zOyz5nWLrBm<(9{?kO{u0t?RC4YP3%{;U{y{`N5~?6829KHu}FK-swGbJT7`y_xA0H zT`hG7mcN^5Q;_GL#%ca~&a5+uX7vdj)oEW&ak6~sW7{CB!WGH@@F~#YrXIHZ1 zL4`_d&jtstwv*ilW?yTXc2w?qqDk`6$A)-bzG=NR`^SwqjPEGMy zzXNnvgb9UYH#7;%t&8)&AnUdN!L7z+_V)@d%~!q>+Rd%$&U(Scy>^Yq`P9=sD(J})shH9k2% zuehWrF*C2E*g#I4*TT%u$jI2h(A3hu+u zH8C&ct+^)rwupphE`N6^1d#`5v-Fa}8 zg8q$)xsw-8v)#YcvZ%;Rn>C-iOhAw&(_wpdkaP(v`_n1<8#~)B-m{)RYtFalR_r_X zKl)K{+^NGrw$dRbd%>kemsZ-&=U+5)>O8Mo)>pT){CZTKUhVSr(!vBA(+zcee;w;3 zk7-R%aQeK?R^WTM?W@yD{<4d*e|a|iK0K$z>-vV1``YG}{^!tJa9j4L(zcbsS2m{E zWfSz$)T|17Kq%s>h`*nn{i3^qmvztf2q zzn@6IeJ|tV_n!HOp7--PKFR+nFApKP9eW88!%Xi|HT7LD)6Sa1Fs9&|)?s|NY!h^!dxzY}T4`pV|*Wh^eE6(-n zB)Oz;mae;O^M4(9dge{k@n`-2EX7#bBYhw2(@!!u@OE$Q!)X%P!Iol-OE0)x*7;bn zprJQ@m(i_+<*!OuJOdWR?|goEI$vvx^?dE~mrXP$u+B1=y7GGb$1BznXA6VUy!NLA K@%--RHv<5r=ROYr literal 0 HcmV?d00001 diff --git a/tests/name_constraints/no_name_constraints.ee.der b/tests/name_constraints/no_name_constraints.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..8011d2c7ae5cea3d603262e68820672d12d6955b GIT binary patch literal 821 zcmXqLVm35rVv=0I%*4pVBvRLLXwmOa!k4Vw;)++x*e}|1)#S7RFB_*;n@8JsUPeZ4 zRt5uWLums^Hs(+kW*(u;;^NZOBE8g##N2|MRK4WzVHBKeWMBZ~8rZ<}GxG=+mnLPUCYO+4dK04( zvTGSx8JL?G`56qF7`d357#SIs9@pM-Y{I9)N6cYoWrV)vf4?ju`D?zn_^(@{88drRSUhoH+!_tpod>$@>) z+iwh>X+$mha1l~xt6V4 zYa>2QRB}>P;eM?uhOh;@17=oSli#aWpXdD3hmnbyk%4isj)4|9j${Q{gbf6n_*3$V zk;B-42N-{{!ip{w986 z`m9Nz`f7f>f41ix*(}+TH2cZ~LyvUM39)>(GV}k%v2$JOgy+ubWoNbI zj`v6Sd?{2-y1)LZ49B)5TS`-m8lLIrb*M5Y|LRaZ_|)#6*`*~fRle*`Zp^#pXMZ}n zaqE6#j&qq4U%t=EEIBWA`as1;)@d8RRdM-a=0mM%QGs?I8zDk21Yo@@VSOn+>~8 nSgy0*dg18*cN?w5#mmZC+x^5E5A8C{I+IoS_k3FBb#ER3m{Ux5 literal 0 HcmV?d00001 diff --git a/tests/name_constraints/reject_constraints_on_unimplemented_names.ca.der b/tests/name_constraints/reject_constraints_on_unimplemented_names.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..7c9e2572d4c718c56052b856970993a9c5381c07 GIT binary patch literal 870 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 literal 0 HcmV?d00001 diff --git a/tests/name_constraints/we_ignore_constraints_on_names_that_do_not_appear_in_cert.ca.der b/tests/name_constraints/we_ignore_constraints_on_names_that_do_not_appear_in_cert.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..16dcb26da7a5a970104ae7a4e1dfe27c2d1f24d9 GIT binary patch literal 902 zcmXqLVs0{MV#->;%*4pVB;pZ%DC*45|Bt`CxR}q%RwA*TZ(F1RFB_*;n@8JsUPeZ4 zRtAGaLums^Hs(+kW*(u;;^NZOBE8g##N2|MRK4WUeKq{btC#8!{#mfNU5h7SK{`M4gM+d6Ud{Ns^WZ84{Tmf?Coi04yML)= zQIVN8Yd&|GfFMhz!}jbT=@M4g zf=i1ot+bubzi8&vd0w}yuWo1g^{6_%+U4t|g$XvM8|wJ}I@U`b)0&{*^m(1F!1r+5 zSErTyWfx`t@@)8hcutGg^$jQYwaqL2&!M;Aw(L)(Z7YMXY)rGs)qgAEcY5Pv@2Z;1 zdl{T=)ccnnd=R@vBc6Suf71_>>o;gr_U^5^F=u9};giDp&~{POO14PVYVF}XMCVu_i+eWy7arn&g_@$-b+ zl)ZhL7!>HjalHTQ;v4ZcsbNo|%cCZLmcHq*sXuXl)U`FPa?Jw2YW6+fRN3(Hw1^Bx zMf%NK$G&(KOn|8F7k6eyYuW)iRJ>{(94B0i>e}ieKhHqpCt9+ z)3Te~K_wUetWmC-xc%Rm^V1?M=Q5oS*c^PQUt#IJN3|}oIXBx_zL&P`jO3SaKIsu2 zx^5}+sx`bjcE8=fF0W2zXGi$sP~ou1G+~2lH@co&UbF0TJ+p%S@t()623#&Pf_|{A PRo^DC=wzSJ8Yxi#$`o#L literal 0 HcmV?d00001 diff --git a/tests/name_constraints/we_ignore_constraints_on_names_that_do_not_appear_in_cert.ee.der b/tests/name_constraints/we_ignore_constraints_on_names_that_do_not_appear_in_cert.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..6671d1afe6fab56722f62ece69b60094674c68d7 GIT binary patch literal 866 zcmXqLVvaLtVscx+%*4pVBqBUfc8|)Tm0JeK9D@tNs)`9-Pm z$@zK3B}Ivuc_qd1`FZhqiMgr8@g*6FCGjcw@p<_r@reZmsfk7LnR)TasYN9Qa^k!e zW`;&a#s-F_mWGB=aITSo0g!9pLKRz@7?qIy#>mRR+{DPwV9>9RdgNaY!+s9Q8 zIhMFTl*sY^x4rnYSYAubnxpUcYVuy4xlUo*CYd^wZ~wU;r1QP*QG2uVGjru_ec$OD zKJ`RatHvePW~{IEH!!@n4Vr!vJAWLbLh`kt;l=3N*;gN OX%L)ZJ^#b!%bx(U=W$g4 literal 0 HcmV?d00001 diff --git a/tests/name_constraints/we_incorrectly_ignore_name_constraints_on_name_in_subject.ca.der b/tests/name_constraints/we_incorrectly_ignore_name_constraints_on_name_in_subject.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..7e4161412f6bdf7be938b827e68016d7c1fc3304 GIT binary patch literal 902 zcmXqLVs0{MV#->;%*4pVBvSswMayQvO_^$k%(SO1H(e85yiOVLvT8XXYj67Zs%@ zm*iB&XQt=n7p2DMCFZ8aC+FuCmlP#t=9Lu3=jTDhGV|h#OOvvIiVft%c`eKgjf{*9 z3{5Q!4Wr;(BLf2Bg5}ib8l?pT_@1Sy{N1# zB}(e6;a6V0REO}-g2nAxJP8ZZ`I#RajJ@}2#^0R>S1IV;WXR*OD&6v%(Pka zxyu9uSu!2AX9r1_u(Cg$qQ9}T?czP_`LpJHdv3+PbN{0s1;?E_3}h=EQnD9ZT6Afp z?R@@4GpEk;x@CQJJIk*})#=qPUoS08urb|G$M@Hud$Shugk7t>iDe zDEpUZ!|%g$TD-1rIJvKFUg>`hy#=>re=2QT8GL19noX|$TM@t08y|aD)m+}o;B=$j zzx3dP*fkpQ>>K@?ewbXpQRK?J((}37$8Dn8*N>?jnQ!*Tdmj&{wBFBZ&hoNECT2zk z#>K$+FyIG9x2!ND<9`-b17;v)APW+Z0|^Kl2rl3^;BDkaN+8Ig3QSJGP-SHB({PMP zdMq!JxcOfGnUh~V_i1lB75`iQ($#=^Q?CWb^!EiXnjCBH&VHcaaH7^j*Q&gSJROri z>&}mu=_bzLvUT5@1O@|(-7hc62z|M^`3b*DLiwKiO8>2P38x%Tnke^rk#fb!ZkAua zm)f(7&5L9^UHA2wo|Qwa@$4@Xa~;ZmT06cvFLV4S%f1cGF##cLY3J`$G6Z|fzrTKG zo8hnA4)vY`u|EqYe0y-_UBs1^_ba;F_|`3X9yHy|$-Q8=!n(hwbr<{1y11pXvW2z$ z!kJU2W9xj?Y!>_MZaFZgrquG7m*n3CjSLKcT!VCK*kY(?AP@H$C!?59R(`5OUVaIZXAGJcm5{y9$jZRn#K_M8 z6z5`UVq|347G4^^#J##<=k#~UT_@KxebmT&+@!|Md_7f^A$Lc|9P{SgOFhG#Kdi3e z`Z&wc$!qh1uOGcdPBu4uTfVF=EHBKk+`)+N*jE)_rE@k5ZT|8*_hmnNeaL8Uo|1^E zgm&M4I* zmu4NeT6SD)<`#x~p|SCi2EV^N7h_1uJ&?BbnaRH1+dEUQPFicmxyi@w!HZ&}r_VK1 zT3f^SGC#XvKf`41)!=C}oqpM*zKdTPso(l4E6AV!gz4+VU-5POUv~8+-l$Vke$9V! z=Dzx%$~rBs8$Rxh)A<8tT%Gv-dR|Lu^QwaV7Ox(D+S;_#)Wh$#d5o=&(#GIj4tBe$ zeHQ7bxU{OyJ5_UUwqs1AKxLR9%R+Oh8)E<6*8N%caMiuza`IE%eK!AE_Fd&!#3v0M z3x6%CQ#V5^R@;|#>dsl}wn5>r`MRo$uk+3-ElqlFXz=0Tf{!oQ^M9Y;*z>b0|3mo& LFH`pad)ER0HV9>O literal 0 HcmV?d00001 diff --git a/tests/name_constraints/wildcard_san_accepted_if_in_subtree.ca.der b/tests/name_constraints/wildcard_san_accepted_if_in_subtree.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..d842b38949c4a101053bd47ab9b535637058732b GIT binary patch literal 858 zcmXqLVh%HCVzOJn%*4pVB;w`d-v6lCUS@RyUw*t+BiqTfJEs}&vT8pw(BT9_Fc85tWGnpzqfM!~s81_nSbbuDXR zR6_O)BP#=Q6C*!^K@%evQxhX2!|zpdZ*1dTC(y>dsH`j{O6sfOS6;nThw#sW#qC-= z2@BHsnI9aCz4vOy-<=0nDd^v*m^*plG~4}4EsKiGv|01H%LD{jG99*O2T7N(vOk@o zzp=CJ;yvs6v*vtzZpFTH|DzuT$DKM1WGfv~vKL%hbZMpSeEvlD4Y@FD*>4G2Kwd_t&vr@|e~H1*gyJYz4lD+rB!j3(LAA48TT;9vzbfezC^x%WoH5&2k8~vMpm|VY6 z5) zW*}uC3lfk62?!eqF5oxdZQ@3XbmUM4CJ|t$GBTXGwzHaPn&d4x#dCFTO2>_SHoVyN z-8x)rzv+eTx6DgFA5Ki1FxPEXcYXV%VvTR}d@Q7SDkH=Di_51k*)q3B@>kb}_+_ph z(r5NwT55mI^_qKMa;NvapVJ#e8Rj_sn|`1&zf`pUS7XhcGr`KQw7UaVzSXG-whUOD zcWYvu*zvXcDbHE;HU3!TM*81NoR*Q3>OCbQ;SR%ErI^wie2NdOHy(^JUfi9QT(z_0 zyPn?G=+{!;>{dtYxL#eI%90}G@@7Wwqsa}4Yd;n@tW4f;SZh`0JlP7nvw5-)Ct2U9 sXDNFo`{;bbqA8UH;pyTQZJ`E^Nr!I6C&eGe`@nJjjzFTLvp02ESK;s5{u literal 0 HcmV?d00001 diff --git a/tests/name_constraints/wildcard_san_accepted_if_in_subtree.ee.der b/tests/name_constraints/wildcard_san_accepted_if_in_subtree.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..e7626304549cede84a9e8d08f7debe1ebcc50736 GIT binary patch literal 821 zcmXqLVm35rVv=0I%*4pVBx3)3U7c@D{A&jO{Sv=FPQ5te*TNSDylk9WZ60mkc^MhG zSs4tx4W$hv*_cCFn0bUUi;GKBi}X?}5_1c3QuUJaa}9M2v_O(vJj&&nIVs7BMJe&c ziFxsf$;qh&C8;U#nQ8HvdGW=iNhL+8sRnZ5ycTALMn=X4hNhN=hEZ^?k%0k_YoJG> zc}-^Q=|0b1O8MX?@U>C(f_z zkb};%ZxO8?(+?`PE}mleFjo2g*;~idSfq@nb?hn#T0C>{8sXxz!NPJ2es28Z$$8|} z-4)uio!0G7*bs5i1{?d9U)Ad7UUm5UH>v!FeO~7br7r2-D2%7n z)a45SACBDEzw($=_v4DxjZPL@a$0W5{!iR7cZ=il>_}jx)<&1@1W@c8x*z{QrmXC-ykr@IC#y@@Rh62M{CT2zk#>H9&8U_-;SdtZF z5j5a$;?+WqK?5F;j4&hPe-4tAUrr*925uui!Z%6M*a_~6q@n%-?Gxsa}r=}fqx)&O$^Nf4l zrIsZd{;i!I))Y3gbL#r98=pGwF#Yn=KV;|k*r|`A@)iGA1TB&LF3;`0r=)4F!sQ>e zz16o5n%fG=hW&4J|9a!6reeqa74FSOy@#%_Z}#C|UGz3oyThhxoylk9WZ60mkc^MhG zSs4sM45bYu*_cCFn0bUUi;GKBi}X?}5_1c3QuUJaa}7-mj6srIJUZo>IVs7BMJe&c ziFxrwsadJXC8;U#nQ8HvdGVzVHBKe zWMBZ~(#X~(MkQqLF|sl+H!<=v7&I|*F*PwVGW=dO_r^BfbpmbNi^|GUqNKhWe&y9m zbqN0~Slq6~ldvG2pZUST*n6*L{M~tQm4g0_in)^)PP5&=)Uv3^Oq(^IyG%fkCDUPh zc93)lEBn(a`WrjjF5a`AKWomn=T_`H_dohkaNMcGK(^8$C40f8MVD6E&gWk=bLu>= zTh>>%v;2BgonGzo_0qxw8`BMSe19G5C68%MP;mOZ&Q{=ixb3UcO8&BovVVCt{60LV z#q0Wpll$7{mHy|@TX0+Ur_#2S!B;k>+2rcK74bX0@v(PR&E>rePB-fPOAkJXU851t zzR|zwhspIDMXt;%J)gUM+$O4h{g}#;`DTB-_wjH_>;0_eEH6uBVrFDuTnvl?1Abtn z$_g_w{%2t|UnB% zD#`v*{QqgIEz7Px*1us`=@4L?@o~YsNd7`aA<`(3n>Lusr8k!mygCx0lbjmYxQj!ykQsRpf z^WuwAvr>~wQd8nH)8aGp;!`VA26~qFgG#sGZ-{6axpbAGBRvja&miEtm*lx>5r9qcYhbR?z$$n zZjaK`hMn!-?d?7;kt=9Dd?@wl<{FRZUfua8wtYzDaQ_p2h;w^Kn&kU;+qdh>EIY8E zNc`fKZIOG5tJ4lX>-lg+;ojzx3MpxG7k_{8>=O6qUdhh4+duZcpT4hJU9`gg-$xx^ zu8PKu4l|@?yPUf+>l1Ug?*0|5S6z~Q87Ju#_KEy9*3I1XR?cOXB=3vfJzLM}>~ub< z7iD!mqdueRmPD^?=leCYxEaH1YbJ3`-s))LkaX_CcmI~h^F`0vTeStBW?ri0puQur zO#EB!=l_bU>h|nV`aA6kFVhSmAC8(F@w?1l{;pnCHOah7R<$mNor#%|fpM{xfrfzu zFy3SZSp*IEn|QU5J<1*UM_Qf3>)xT`Wcv?5iK%HN69?%>txyR+Q*x9JiOhnel4z9_q8vL)}{D}8e16Kk_*zAtjs zZ-RC*eR(;xrd<4l-mF{Ng|=lM7Uq3wFF3SCFYw3Tu=fwbyN-9o?YTSo)61`2BDX|# w8Xnw~wEOLC*G5b4(*EX|br)NY1#&aZiftdB*$suWJ*}b*{Do0G3-;R{#J2 literal 0 HcmV?d00001 From 2b0105aca15f97ffd34593c55430872a9faf25b9 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Mon, 12 Dec 2022 15:39:24 +0000 Subject: [PATCH 07/12] Fix name constraints matching further For -- and limited to -- server end-entity certificates, subject commonName can be a DNS name that should be subject to DNS name constraints. Name constraints can and should have an impact on path building and certificate validity, even if we don't actually use the commonName for name validation after this. This is annoying, though, because: - not all end-entity certs have a commonName, because it's no longer required, and a missing one shouldn't be considered a name constraints strike. - only server end-entity certs should be given this treatment: clients don't typically have DNS names (see: https://bugzilla.mozilla.org/show_bug.cgi?id=1523484) --- src/subject_name/mod.rs | 4 +++- src/subject_name/verify.rs | 44 +++++++++++++++++++++++++++----------- src/verify_cert.rs | 18 ++++++++++++---- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/subject_name/mod.rs b/src/subject_name/mod.rs index a3588e39..a27d5920 100644 --- a/src/subject_name/mod.rs +++ b/src/subject_name/mod.rs @@ -29,4 +29,6 @@ pub use ip_address::{AddrParseError, IpAddrRef}; pub use ip_address::IpAddr; mod verify; -pub(super) use verify::{check_name_constraints, verify_cert_subject_name}; +pub(super) use verify::{ + check_name_constraints, verify_cert_subject_name, SubjectCommonNameContents, +}; diff --git a/src/subject_name/verify.rs b/src/subject_name/verify.rs index 0f2ca818..f573bda8 100644 --- a/src/subject_name/verify.rs +++ b/src/subject_name/verify.rs @@ -31,6 +31,7 @@ pub(crate) fn verify_cert_dns_name( iterate_names( Some(cert.subject), cert.subject_alt_name, + SubjectCommonNameContents::Ignore, Err(Error::CertNotValidForName), &|name| { if let GeneralName::DnsName(presented_id) = name { @@ -64,6 +65,7 @@ pub(crate) fn verify_cert_subject_name( // only against Subject Alternative Names. None, cert.inner().subject_alt_name, + SubjectCommonNameContents::Ignore, Err(Error::CertNotValidForName), &|name| { if let GeneralName::IpAddress(presented_id) = name { @@ -84,6 +86,7 @@ pub(crate) fn verify_cert_subject_name( pub(crate) fn check_name_constraints( input: Option<&mut untrusted::Reader>, subordinate_certs: &Cert, + subject_common_name_contents: SubjectCommonNameContents, ) -> Result<(), Error> { let input = match input { Some(input) => input, @@ -110,6 +113,7 @@ pub(crate) fn check_name_constraints( iterate_names( Some(child.subject), child.subject_alt_name, + subject_common_name_contents, Ok(()), &|name| { check_presented_id_conforms_to_constraints( @@ -175,7 +179,7 @@ fn check_presented_id_conforms_to_constraints_in_subtree( let mut has_permitted_subtrees_match = false; let mut has_permitted_subtrees_mismatch = false; - loop { + while !constraints.at_end() { // http://tools.ietf.org/html/rfc5280#section-4.2.1.10: "Within this // profile, the minimum and maximum fields are not used with any name // forms, thus, the minimum MUST be zero, and maximum MUST be absent." @@ -202,10 +206,6 @@ fn check_presented_id_conforms_to_constraints_in_subtree( dns_name::presented_id_matches_constraint(name, base).ok_or(Error::BadDER) } - (GeneralName::DirectoryName(name), GeneralName::DnsName(base)) => { - common_name(name).map(|cn| cn == Some(base)) - } - (GeneralName::DirectoryName(name), GeneralName::DirectoryName(base)) => Ok( presented_directory_name_matches_constraint(name, base, subtrees), ), @@ -228,7 +228,11 @@ fn check_presented_id_conforms_to_constraints_in_subtree( Err(Error::NameConstraintViolation) } - _ => Ok(false), + _ => { + // mismatch between constraint and name types; continue with current + // name and next constraint + continue; + } }; match (subtrees, matches) { @@ -250,10 +254,6 @@ fn check_presented_id_conforms_to_constraints_in_subtree( return NameIteration::Stop(Err(err)); } } - - if constraints.at_end() { - break; - } } if has_permitted_subtrees_mismatch && !has_permitted_subtrees_match { @@ -284,9 +284,16 @@ enum NameIteration { Stop(Result<(), Error>), } +#[derive(Clone, Copy)] +pub(crate) enum SubjectCommonNameContents { + DnsName, + Ignore, +} + fn iterate_names( subject: Option, subject_alt_name: Option, + subject_common_name_contents: SubjectCommonNameContents, result_if_never_stopped_early: Result<(), Error>, f: &dyn Fn(GeneralName) -> NameIteration, ) -> Result<(), Error> { @@ -311,8 +318,21 @@ fn iterate_names( if let Some(subject) = subject { match f(GeneralName::DirectoryName(subject)) { - NameIteration::Stop(result) => result, - NameIteration::KeepGoing => result_if_never_stopped_early, + NameIteration::Stop(result) => return result, + NameIteration::KeepGoing => (), + }; + } + + if let (SubjectCommonNameContents::DnsName, Some(subject)) = + (subject_common_name_contents, subject) + { + match common_name(subject) { + Ok(Some(cn)) => match f(GeneralName::DnsName(cn)) { + NameIteration::Stop(result) => result, + NameIteration::KeepGoing => result_if_never_stopped_early, + }, + Ok(None) => result_if_never_stopped_early, + Err(err) => Err(err), } } else { result_if_never_stopped_early diff --git a/src/verify_cert.rs b/src/verify_cert.rs index c21c22ef..773c7665 100644 --- a/src/verify_cert.rs +++ b/src/verify_cert.rs @@ -51,6 +51,16 @@ pub(crate) fn build_chain( } } + // for the purpose of name constraints checking, only end-entity server certificates + // could plausibly have a DNS name as a subject commonName that could contribute to + // path validity + let subject_common_name_contents = + if required_eku_if_present == EKU_SERVER_AUTH && used_as_ca == UsedAsCa::No { + subject_name::SubjectCommonNameContents::DnsName + } else { + subject_name::SubjectCommonNameContents::Ignore + }; + // TODO: revocation. let result = loop_while_non_fatal_error(trust_anchors, |trust_anchor: &TrustAnchor| { @@ -62,7 +72,7 @@ pub(crate) fn build_chain( let name_constraints = trust_anchor.name_constraints.map(untrusted::Input::from); untrusted::read_all_optional(name_constraints, Error::BadDER, |value| { - subject_name::check_name_constraints(value, cert) + subject_name::check_name_constraints(value, cert, subject_common_name_contents) })?; let trust_anchor_spki = untrusted::Input::from(trust_anchor.spki); @@ -106,7 +116,7 @@ pub(crate) fn build_chain( } untrusted::read_all_optional(potential_issuer.name_constraints, Error::BadDER, |value| { - subject_name::check_name_constraints(value, cert) + subject_name::check_name_constraints(value, cert, subject_common_name_contents) })?; let next_sub_ca_count = match used_as_ca { @@ -201,7 +211,7 @@ fn check_validity(input: &mut untrusted::Reader, time: time::Time) -> Result<(), Ok(()) } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] enum UsedAsCa { Yes, No, @@ -250,7 +260,7 @@ fn check_basic_constraints( } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq)] pub(crate) struct KeyPurposeId { oid_value: untrusted::Input<'static>, } From 8f491a8a42ae606d72eff1f5b153c888dac53793 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Mon, 8 Jun 2020 16:19:05 +0200 Subject: [PATCH 08/12] Reject non-contiguous netmasks Add a specific error for this, and avoid use of BadDER for cases where the DER encoding is actually fine but the syntax of the messages is incorrect. --- src/error.rs | 5 + src/subject_name/ip_address.rs | 189 ++++++++++++++++++++++++++++++++- 2 files changed, 189 insertions(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index 6324bfc9..31d6ab1e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -97,6 +97,11 @@ pub enum Error { /// The signature algorithm for a signature is not in the set of supported /// signature algorithms given. UnsupportedSignatureAlgorithm, + + /// A iPAddress name constraint was invalid: + /// - it had a sparse network mask (ie, cannot be written in CIDR form). + /// - it was too long or short + InvalidNetworkMaskConstraint, } impl fmt::Display for Error { diff --git a/src/subject_name/ip_address.rs b/src/subject_name/ip_address.rs index cecadd39..3459926e 100644 --- a/src/subject_name/ip_address.rs +++ b/src/subject_name/ip_address.rs @@ -240,7 +240,7 @@ pub(super) fn presented_id_matches_constraint( return Err(Error::BadDER); } if constraint.len() != 8 && constraint.len() != 32 { - return Err(Error::BadDER); + return Err(Error::InvalidNetworkMaskConstraint); } // an IPv4 address never matches an IPv6 constraint, and vice versa. @@ -257,10 +257,38 @@ pub(super) fn presented_id_matches_constraint( 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 { + // Iterate through the name, constraint address, and constraint mask + // a byte at a time. 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(); + + // A valid mask consists of a sequence of 1 bits, followed by a + // sequence of 0 bits. Either sequence could be empty. + + let leading = constraint_mask_byte.leading_ones(); + let trailing = constraint_mask_byte.trailing_zeros(); + + // At the resolution of a single octet, a valid mask is one where + // leading_ones() and trailing_zeros() sums to 8. + // This includes all-ones and all-zeroes. + if leading + trailing != 8 { + return Err(Error::InvalidNetworkMaskConstraint); + } + + // There should be no bits set after the first octet with a zero bit is seen. + if seen_zero_bit && constraint_mask_byte != 0x00 { + return Err(Error::InvalidNetworkMaskConstraint); + } + + // Note when a zero bit is seen for later octets. + if constraint_mask_byte != 0xff { + seen_zero_bit = true; + } + if ((name_byte ^ constraint_address_byte) & constraint_mask_byte) != 0 { return Ok(false); } @@ -911,7 +939,7 @@ mod tests { untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]), untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF]), ), - Err(Error::BadDER), + Err(Error::InvalidNetworkMaskConstraint), ); // Unmatching constraint size (longer) @@ -920,7 +948,7 @@ mod tests { untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]), untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00]), ), - Err(Error::BadDER), + Err(Error::InvalidNetworkMaskConstraint), ); // Unmatching constraint size (IPv6 constraint for IPv4 address) @@ -1066,7 +1094,7 @@ mod tests { 0x00, 0x00, 0x00, 0x00, 0x00 ]), ), - Err(Error::BadDER), + Err(Error::InvalidNetworkMaskConstraint), ); // Unmatching constraint size (longer) @@ -1082,7 +1110,7 @@ mod tests { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]), ), - Err(Error::BadDER), + Err(Error::InvalidNetworkMaskConstraint), ); // Unmatching constraint size (IPv4 constraint for IPv6 address) @@ -1156,6 +1184,89 @@ mod tests { Ok(true), ); } + + #[test] + fn presented_id_matches_constraint_rejects_incorrect_length_arguments() { + // wrong length names + 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) + ); + + // wrong length constraints + 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) + ); + + // ipv4-length not considered for ipv6-length name, and vv + 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"))] @@ -1299,6 +1410,43 @@ mod alloc_tests { // Cannot mix IpV4 with IpV6 and viceversa ("2001:db8::", "8.8.8.8", "255.255.255.255", Ok(false)), ("8.8.8.8", "2001:db8::", "ffff::", Ok(false)), + // IpV4 non-contiguous masks + ( + "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), + ), // IpV4 ("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)), @@ -1314,6 +1462,37 @@ mod alloc_tests { ("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)), + // IpV6 non-contiguous masks + ( + "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), + ), // IpV6 ("2001:db8::", "2001:db8::", "ffff:ffff::", Ok(true)), ("2001:db9::", "2001:db8::", "ffff:ffff::", Ok(false)), From 18d5c603443b69a64504e7b910eeca7608ccdc56 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 23 Dec 2022 10:44:22 +0000 Subject: [PATCH 09/12] Document and test directoryName constraints not being implemented --- src/subject_name/verify.rs | 20 ++++++++++++---- tests/name_constraints.rs | 22 ++++++++++++++++++ ...lude_directory_name_not_implemented.ca.der | Bin 0 -> 868 bytes ...lude_directory_name_not_implemented.ee.der | Bin 0 -> 801 bytes tests/name_constraints/generate.py | 14 +++++++++++ ...rmit_directory_name_not_implemented.ca.der | Bin 0 -> 866 bytes ...rmit_directory_name_not_implemented.ee.der | Bin 0 -> 799 bytes 7 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 tests/name_constraints/exclude_directory_name_not_implemented.ca.der create mode 100644 tests/name_constraints/exclude_directory_name_not_implemented.ee.der create mode 100644 tests/name_constraints/permit_directory_name_not_implemented.ca.der create mode 100644 tests/name_constraints/permit_directory_name_not_implemented.ee.der diff --git a/src/subject_name/verify.rs b/src/subject_name/verify.rs index f573bda8..8b958e04 100644 --- a/src/subject_name/verify.rs +++ b/src/subject_name/verify.rs @@ -266,14 +266,26 @@ fn check_presented_id_conforms_to_constraints_in_subtree( } } -// TODO: document this. fn presented_directory_name_matches_constraint( - name: untrusted::Input, - constraint: untrusted::Input, + _name: untrusted::Input, + _constraint: untrusted::Input, subtrees: Subtrees, ) -> bool { + // Reject any uses of directory name constraints; we don't implement this. + // + // Rejecting everything technically confirms to RFC5280: + // + // "If a name constraints extension that is marked as critical imposes constraints + // on a particular name form, and an instance of that name form appears in the + // subject field or subjectAltName extension of a subsequent certificate, then + // the application MUST either process the constraint or _reject the certificate_." + // + // TODO: rustls/webpki#19 + // + // Rejection is achieved by not matching any PermittedSubtrees, and matching all + // ExcludedSubtrees. match subtrees { - Subtrees::PermittedSubtrees => name == constraint, + Subtrees::PermittedSubtrees => false, Subtrees::ExcludedSubtrees => true, } } diff --git a/tests/name_constraints.rs b/tests/name_constraints.rs index 9fdbe1fc..ba76f362 100644 --- a/tests/name_constraints.rs +++ b/tests/name_constraints.rs @@ -357,3 +357,25 @@ fn ip46_mixed_address_san_allowed() { Ok(()) ); } + +#[test] +#[cfg(feature = "alloc")] +fn permit_directory_name_not_implemented() { + let ee = include_bytes!("name_constraints/permit_directory_name_not_implemented.ee.der"); + let ca = include_bytes!("name_constraints/permit_directory_name_not_implemented.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn exclude_directory_name_not_implemented() { + let ee = include_bytes!("name_constraints/exclude_directory_name_not_implemented.ee.der"); + let ca = include_bytes!("name_constraints/exclude_directory_name_not_implemented.ca.der"); + assert_eq!( + check_cert(ee, ca, &[], &[]), + Err(webpki::Error::UnknownIssuer) + ); +} diff --git a/tests/name_constraints/exclude_directory_name_not_implemented.ca.der b/tests/name_constraints/exclude_directory_name_not_implemented.ca.der new file mode 100644 index 0000000000000000000000000000000000000000..080c7de3d2ac1a9e5f929cd9e7218b6899c28afd GIT binary patch literal 868 zcmXqLVooqn3CjSLKcT&mmG z#HfVqB}P^T<|amd27@L>E~X|%MuwFlVzWCkL|@FYR#g!y}JFy^i>Z&ac&k@G}skjl={4_Cj2D#F`I*lbI(VF zu9x9i!sHOPue-}ea&w*QExSk4eG_N@XBMyaIgl@XEiP$?zQ!N_^oR{HF`aelJ=Map z+@b4s)E&?7x#cpKW7@9+YEO-yvqslk<@cE_72Mds=`Hwc^SNUi`x6#)ZY_DiQ5iq| z&BYjg!Tg^Obb^#Ozh_+Z%sW2*_=3YH`U87b*rZf4F*7nSE;chTG2jPAq^vL_<9`-b z17;v)pa>F>0||&5h%OX15L^OG9fsTnoS+zI6J~PuLk?MBVgZIMBSYJICT*^F1>F0L zmnnShou^d!Y7_hP1q#(OgYtXK?(rIjtPcCRll|m(PVSd~a@KpB2qPM#$fX`)jcl18ZJ&qU8G*Prnd3kKfbxEgg-SmGkeJVXE_}I zOYC^$WuZMMCwE$`>DyxZ++V?a)(5#>S*Dy?mQ`ML+`iGSM;Y&HA8mfGB&KvKt9%Q; z7sIsEIc?8oo{jxI-6C7XE-b8XjiS|yM=@%i{Y-Z}yh??>I`63$R7_a7yE19D<%u@{ DYP3^J literal 0 HcmV?d00001 diff --git a/tests/name_constraints/exclude_directory_name_not_implemented.ee.der b/tests/name_constraints/exclude_directory_name_not_implemented.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..2df7ad0b53607a99f7e4aeae4c3c783010bb9084 GIT binary patch literal 801 zcmXqLVwN>%Vq#su%*4pVB;xC_qL$bG`pvYjTbi!0f9pQ$T=mm{myJ`a&7MYv@IVivxddrP__n^bkl@zLI0@&~wXeu(e5I5**iU?cPLcJ8j8_VV@n$}|lhg?b16J$yDg z{6A}EyLGO}W0&ULxAwf_x}?7Ni)e~K)%6LIJy&l^F>+nn&S0r=F;um;)@l9LapLrXA_}R+zqhY^)V_XJuV`zs?m1wT|Hw$FWgb^D9ys~&vf+$^qWuq(hQ^?6%O_(|?#HU|^uo{tD! zFT=Bh$sufCcbARi<~r9~c8{j}CeHrPEMDz%AYb}gT+$AGjX(bB5gTG+I_uPXs)c2_ zL)Y!7JD%Tj%VjRdv|k6*o*F-Ajjp-M?=xE}xUqrLTkzNBbH_IJCoJgPTJnOUGJg7- zi!uCy`9B}%1SxNR&$#HBcYOTu1&2@c2llM6NvULFW@KPoY-V6$zz>W@Sz$)T|17Kq z%s|RO5hNf75)d~KT_9{AxP;$;*O1$Q6BOTU!c5M7$RP_%D!`CsWGHi-%c9vLwOdo1 z*VAg$xmUYagpueB26g1O4?OYo+?J91{iTi%O>-rv3iyZ2pfF8th}{q0om zlHZFj*-I&fzPRWA**hYk@bA2Naq(IcgZh|08Q!_J6HPJRAL7?;R+8U&bJu3Ko6{~z z{MdBvS@fFR^9-`(i$57Xo%&n#N)P9vVB2+NPb4bD9H(CY;-YW(%lH+Wqu>n7UyJ4e E0Ek#xF8}}l literal 0 HcmV?d00001 diff --git a/tests/name_constraints/permit_directory_name_not_implemented.ee.der b/tests/name_constraints/permit_directory_name_not_implemented.ee.der new file mode 100644 index 0000000000000000000000000000000000000000..22ca7651ebb920748616b571cb3787e19d29e50b GIT binary patch literal 799 zcmXqLVwN^&Vq#vv%*4pVB;uNP_txZ;G8KzVHBKeWMBZ~0tr%0 zY+_VGb~7U@19KB2KZ8LNBNtN>BO}AYgVWOfJ7kQNyDKO?Q zHkbQtZS>@j>*WB}{>>LJTVp4kicnj~ zm43W*o}2IfDUvQ~Wm;;dj@}M}N zuHg{I>f>E_u(LI#&Kp6X>%mNsjb^e)nVq4weF zNxhS`cclI#i+Bq9bmRoyw77UZ=;MrB@wXf;8S8Z4GBGnUFfJA_;4|O>#+0luBjbM- zCIbfKFayRjFw7Vk3U@x38+PYR++x;$EDi}Pw)B2@%fQtd_N6nzZCS1Ci`8|SZcIwa zcdo~mA2d>)AypAqxBGRx%ieI?!wi{MZ!Oo6_bb1A*)%3<<;D#mC({EG_Z@n}CtAp) zxBJPQgEDd(;@O|^P3Mbzd#GEzW+L;Qi_15EJY|_T<%>~($cZag-h8ONJD=(QjfmA- z1O9FC_%)?-){ndI-jvubuFPE9!Y$jtDqGLLZpG5@ZMUNIrSrs@*+au*AOET}&ArN4 zd9hqar`jik-S9-jtGE7I5f8UyO<~&`Z8JrBM$o2DA{xg(XYPJ9*LG)~>ZHE)VsVSV a*=iJZ-u!esF39dpe~9$#dp}fq71IH_dQCn6 literal 0 HcmV?d00001 From 5a09c07f603f5f89b1e4c9fc1efef139ea59ce47 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Mon, 9 Jan 2023 16:04:05 +0000 Subject: [PATCH 10/12] Clarify ip_address name constraint length checks --- src/subject_name/ip_address.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/subject_name/ip_address.rs b/src/subject_name/ip_address.rs index 3459926e..9c324949 100644 --- a/src/subject_name/ip_address.rs +++ b/src/subject_name/ip_address.rs @@ -236,17 +236,25 @@ pub(super) fn presented_id_matches_constraint( name: untrusted::Input, constraint: untrusted::Input, ) -> Result { - if name.len() != 4 && name.len() != 16 { - return Err(Error::BadDER); - } - if constraint.len() != 8 && constraint.len() != 32 { - return Err(Error::InvalidNetworkMaskConstraint); - } + match (name.len(), constraint.len()) { + (4, 8) => (), + (16, 32) => (), - // an IPv4 address never matches an IPv6 constraint, and vice versa. - if name.len() * 2 != constraint.len() { - return Ok(false); - } + // an IPv4 address never matches an IPv6 constraint, and vice versa. + (4, 32) | (16, 8) => { + return Ok(false); + } + + // invalid constraint length + (4, _) | (16, _) => { + return Err(Error::InvalidNetworkMaskConstraint); + } + + // invalid name length, or anything else + _ => { + return Err(Error::BadDER); + } + }; let (constraint_address, constraint_mask) = constraint.read_all(Error::BadDER, |value| { let address = value.read_bytes(constraint.len() / 2).unwrap(); From 9281ec54576cfebe7147f16995a9858bca3b70ef Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Mon, 9 Jan 2023 17:18:53 +0000 Subject: [PATCH 11/12] Make Error non_exhaustive This means adding new errors is not a breaking change, at the cost of disabling exhaustive matching of all errors in downstream code. --- src/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/error.rs b/src/error.rs index 31d6ab1e..4c0b9d4d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,6 +16,7 @@ use core::fmt; /// An error that occurs during certificate validation or name validation. #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] pub enum Error { /// The encoding of some ASN.1 DER-encoded item is invalid. // TODO: Rename to `BadDer` in the next release. From 8e9de6e12de6e66b8773acefbf75b0b7e641e2b4 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Tue, 10 Jan 2023 14:55:53 +0000 Subject: [PATCH 12/12] Add changelog items for this branch. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1605cbbf..536bb6d1 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,11 @@ Changelog - Allow verification of certificates with IP address subjectAltNames. `EndEntityCert::verify_is_valid_for_subject_name` was added, and `EndEntityCert::verify_is_valid_for_dns_name` was removed. + - Make `Error` type non-exhaustive. + - Reject non-contiguous netmasks in IP address name constraints. + - Name constraints of type dNSName and iPAddress now work and are tested. + directoryName name constraints are not implemented and will prevent + path building where they appear. * 0.22.0 (2021-04-10) - last upstream release of `webpki` crate.