@@ -47,23 +47,53 @@ fn uri_san_rejected_against_uri_permitted_subtree() {
4747 ) ;
4848}
4949
50+ /// Since we don't have real constraint matching implemented for URI names, fail closed.
51+ #[ test]
52+ fn uri_san_rejected_against_uri_excluded_subtree ( ) {
53+ let ca_key = KeyPair :: generate ( ) . unwrap ( ) ;
54+ let mut ca_params = issuer_params ( "issuer.example.com" ) . unwrap ( ) ;
55+ ca_params
56+ . custom_extensions
57+ . push ( uri_excluded_name_constraints ( b"https://evil.example.com" ) ) ;
58+ let issuer = CertifiedIssuer :: self_signed ( ca_params, ca_key) . expect ( "failed to generate CA" ) ;
59+
60+ let ee = generate_cert (
61+ vec ! [ SanType :: URI ( "https://evil.example.com" . try_into( ) . unwrap( ) ) ] ,
62+ & issuer,
63+ ) ;
64+ assert_eq ! (
65+ check_cert( ee. der( ) , issuer. der( ) , & [ ] , & [ ] , & [ ] ) ,
66+ Err ( webpki:: Error :: NameConstraintViolation ) ,
67+ ) ;
68+ }
69+
5070// Hand-encode a NameConstraints extension (OID 2.5.29.30) with a single
5171// permittedSubtree containing a URI GeneralName. rcgen's GeneralSubtree enum
5272// doesn't expose a URI variant, so we emit the DER directly.
5373fn uri_permitted_name_constraints ( uri : & [ u8 ] ) -> CustomExtension {
74+ uri_name_constraints ( uri, 0xa0 ) // permittedSubtrees [0] IMPLICIT
75+ }
76+
77+ // Hand-encode a NameConstraints extension (OID 2.5.29.30) with a single
78+ // excludedSubtree containing a URI GeneralName.
79+ fn uri_excluded_name_constraints ( uri : & [ u8 ] ) -> CustomExtension {
80+ uri_name_constraints ( uri, 0xa1 ) // excludedSubtrees [1] IMPLICIT
81+ }
82+
83+ fn uri_name_constraints ( uri : & [ u8 ] , subtrees_tag : u8 ) -> CustomExtension {
5484 assert ! ( uri. len( ) < 128 ) ;
5585 // URI GeneralName: [6] IMPLICIT IA5String
5686 let mut uri_gn = vec ! [ 0x86 , uri. len( ) as u8 ] ;
5787 uri_gn. extend_from_slice ( uri) ;
5888 // GeneralSubtree SEQUENCE { base GeneralName, ... }
5989 let mut subtree = vec ! [ 0x30 , uri_gn. len( ) as u8 ] ;
6090 subtree. extend_from_slice ( & uri_gn) ;
61- // permittedSubtrees [0] IMPLICIT GeneralSubtrees
62- let mut permitted = vec ! [ 0xa0 , subtree. len( ) as u8 ] ;
63- permitted . extend_from_slice ( & subtree) ;
91+ // permittedSubtrees [0] or excludedSubtrees [1] IMPLICIT GeneralSubtrees
92+ let mut subtrees = vec ! [ subtrees_tag , subtree. len( ) as u8 ] ;
93+ subtrees . extend_from_slice ( & subtree) ;
6494 // NameConstraints SEQUENCE
65- let mut nc = vec ! [ 0x30 , permitted . len( ) as u8 ] ;
66- nc. extend_from_slice ( & permitted ) ;
95+ let mut nc = vec ! [ 0x30 , subtrees . len( ) as u8 ] ;
96+ nc. extend_from_slice ( & subtrees ) ;
6797
6898 let mut ext = CustomExtension :: from_oid_content ( & [ 2 , 5 , 29 , 30 ] , nc) ;
6999 ext. set_criticality ( true ) ;
0 commit comments