@@ -359,35 +359,37 @@ Rvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUdfvGULUvPciLB
359359-----END PRIVATE KEY-----
360360)" ;
361361
362- // kCRLTestRoot is a test root certificate. It has private key:
363- //
364- // -----BEGIN RSA PRIVATE KEY-----
365- // MIIEpAIBAAKCAQEAo16WiLWZuaymsD8n5SKPmxV1y6jjgr3BS/dUBpbrzd1aeFzN
366- // lI8l2jfAnzUyp+I21RQ+nh/MhqjGElkTtK9xMn1Y+S9GMRh+5R/Du0iCb1tCZIPY
367- // 07Tgrb0KMNWe0v2QKVVruuYSgxIWodBfxlKO64Z8AJ5IbnWpuRqO6rctN9qUoMlT
368- // IAB6dL4G0tDJ/PGFWOJYwOMEIX54bly2wgyYJVBKiRRt4f7n8H922qmvPNA9idmX
369- // 9G1VAtgV6x97XXi7ULORIQvn9lVQF6nTYDBJhyuPB+mLThbLP2o9orxGx7aCtnnB
370- // ZUIxUvHNOI0FaSaZH7Fi0xsZ/GkG2HZe7ImPJwIDAQABAoIBAQCJF9MTHfHGkk+/
371- // DwCXlA0Wg0e6hBuHl10iNobYkMWIl/xXjOknhYiqOqb181py76472SVC5ERprC+r
372- // Lf0PXzqKuA117mnkwT2bYLCL9Skf8WEhoFLQNbVlloF6wYjqXcYgKYKh8HgQbZl4
373- // aLg2YQl2NADTNABsUWj/4H2WEelsODVviqfFs725lFg9KHDI8zxAZXLzDt/M9uVL
374- // GxJiX12tr0AwaeAFZ1oPM/y+LznM3N3+Ht3jHHw3jZ/u8Z1RdAmdpu3bZ6tbwGBr
375- // 9edsH5rKkm9aBvMrY7eX5VHqaqyRNFyG152ZOJh4XiiFG7EmgTPCpaHo50Y018Re
376- // grVtk+FBAoGBANY3lY+V8ZOwMxSHes+kTnoimHO5Ob7nxrOC71i27x+4HHsYUeAr
377- // /zOOghiDIn+oNkuiX5CIOWZKx159Bp65CPpCbTb/fh+HYnSgXFgCw7XptycO7LXM
378- // 5GwR5jSfpfzBFdYxjxoUzDMFBwTEYRTm0HkUHkH+s+ajjw5wqqbcGLcfAoGBAMM8
379- // DKW6Tb66xsf708f0jonAjKYTLZ+WOcwsBEWSFHoY8dUjvW5gqx5acHTEsc5ZTeh4
380- // BCFLa+Mn9cuJWVJNs09k7Xb2PNl92HQ4GN2vbdkJhExbkT6oLDHg1hVD0w8KLfz1
381- // lTAW6pS+6CdOHMEJpvqx89EgU/1GgIQ1fXYczE75AoGAKeJoXdDFkUjsU+FBhAPu
382- // TDcjc80Nm2QaF9NMFR5/lsYa236f06MGnQAKM9zADBHJu/Qdl1brUjLg1HrBppsr
383- // RDNkw1IlSOjhuUf5hkPUHGd8Jijm440SRIcjabqla8wdBupdvo2+d2NOQgJbsQiI
384- // ToQ+fkzcxAXK3Nnuo/1436UCgYBjLH7UNOZHS8OsVM0I1r8NVKVdu4JCfeJQR8/H
385- // s2P5ffBir+wLRMnH+nMDreMQiibcPxMCArkERAlE4jlgaJ38Z62E76KLbLTmnJRt
386- // EC9Bv+bXjvAiHvWMRMUbOj/ddPNVez7Uld+FvdBaHwDWQlvzHzBWfBCOKSEhh7Z6
387- // qDhUqQKBgQDPMDx2i5rfmQp3imV9xUcCkIRsyYQVf8Eo7NV07IdUy/otmksgn4Zt
388- // Lbf3v2dvxOpTNTONWjp2c+iUQo8QxJCZr5Sfb21oQ9Ktcrmc/CY7LeBVDibXwxdM
389- // vRG8kBzvslFWh7REzC3u06GSVhyKDfW93kN2cKVwGoahRlhj7oHuZQ==
390- // -----END RSA PRIVATE KEY-----
362+ // kCRLTestRootKey is the private key for kCRLTestRoot.
363+ static const char kCRLTestRootKey [] = R"(
364+ -----BEGIN RSA PRIVATE KEY-----
365+ MIIEpAIBAAKCAQEAo16WiLWZuaymsD8n5SKPmxV1y6jjgr3BS/dUBpbrzd1aeFzN
366+ lI8l2jfAnzUyp+I21RQ+nh/MhqjGElkTtK9xMn1Y+S9GMRh+5R/Du0iCb1tCZIPY
367+ 07Tgrb0KMNWe0v2QKVVruuYSgxIWodBfxlKO64Z8AJ5IbnWpuRqO6rctN9qUoMlT
368+ IAB6dL4G0tDJ/PGFWOJYwOMEIX54bly2wgyYJVBKiRRt4f7n8H922qmvPNA9idmX
369+ 9G1VAtgV6x97XXi7ULORIQvn9lVQF6nTYDBJhyuPB+mLThbLP2o9orxGx7aCtnnB
370+ ZUIxUvHNOI0FaSaZH7Fi0xsZ/GkG2HZe7ImPJwIDAQABAoIBAQCJF9MTHfHGkk+/
371+ DwCXlA0Wg0e6hBuHl10iNobYkMWIl/xXjOknhYiqOqb181py76472SVC5ERprC+r
372+ Lf0PXzqKuA117mnkwT2bYLCL9Skf8WEhoFLQNbVlloF6wYjqXcYgKYKh8HgQbZl4
373+ aLg2YQl2NADTNABsUWj/4H2WEelsODVviqfFs725lFg9KHDI8zxAZXLzDt/M9uVL
374+ GxJiX12tr0AwaeAFZ1oPM/y+LznM3N3+Ht3jHHw3jZ/u8Z1RdAmdpu3bZ6tbwGBr
375+ 9edsH5rKkm9aBvMrY7eX5VHqaqyRNFyG152ZOJh4XiiFG7EmgTPCpaHo50Y018Re
376+ grVtk+FBAoGBANY3lY+V8ZOwMxSHes+kTnoimHO5Ob7nxrOC71i27x+4HHsYUeAr
377+ /zOOghiDIn+oNkuiX5CIOWZKx159Bp65CPpCbTb/fh+HYnSgXFgCw7XptycO7LXM
378+ 5GwR5jSfpfzBFdYxjxoUzDMFBwTEYRTm0HkUHkH+s+ajjw5wqqbcGLcfAoGBAMM8
379+ DKW6Tb66xsf708f0jonAjKYTLZ+WOcwsBEWSFHoY8dUjvW5gqx5acHTEsc5ZTeh4
380+ BCFLa+Mn9cuJWVJNs09k7Xb2PNl92HQ4GN2vbdkJhExbkT6oLDHg1hVD0w8KLfz1
381+ lTAW6pS+6CdOHMEJpvqx89EgU/1GgIQ1fXYczE75AoGAKeJoXdDFkUjsU+FBhAPu
382+ TDcjc80Nm2QaF9NMFR5/lsYa236f06MGnQAKM9zADBHJu/Qdl1brUjLg1HrBppsr
383+ RDNkw1IlSOjhuUf5hkPUHGd8Jijm440SRIcjabqla8wdBupdvo2+d2NOQgJbsQiI
384+ ToQ+fkzcxAXK3Nnuo/1436UCgYBjLH7UNOZHS8OsVM0I1r8NVKVdu4JCfeJQR8/H
385+ s2P5ffBir+wLRMnH+nMDreMQiibcPxMCArkERAlE4jlgaJ38Z62E76KLbLTmnJRt
386+ EC9Bv+bXjvAiHvWMRMUbOj/ddPNVez7Uld+FvdBaHwDWQlvzHzBWfBCOKSEhh7Z6
387+ qDhUqQKBgQDPMDx2i5rfmQp3imV9xUcCkIRsyYQVf8Eo7NV07IdUy/otmksgn4Zt
388+ Lbf3v2dvxOpTNTONWjp2c+iUQo8QxJCZr5Sfb21oQ9Ktcrmc/CY7LeBVDibXwxdM
389+ vRG8kBzvslFWh7REzC3u06GSVhyKDfW93kN2cKVwGoahRlhj7oHuZQ==
390+ -----END RSA PRIVATE KEY-----
391+ )" ;
392+
391393static const char kCRLTestRoot [] = R"(
392394-----BEGIN CERTIFICATE-----
393395MIIDbzCCAlegAwIBAgIJAODri7v0dDUFMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
@@ -2043,6 +2045,236 @@ TEST(X509Test, TestCRL) {
20432045 ASSERT_EQ (nullptr , X509_OBJECT_get0_X509_CRL (&invalidCRL));
20442046}
20452047
2048+ // Helper to create a GENERAL_NAME with a URI.
2049+ static bssl::UniquePtr<GENERAL_NAME> MakeURIGeneralName (const char *uri) {
2050+ bssl::UniquePtr<GENERAL_NAME> name (GENERAL_NAME_new ());
2051+ if (!name) {
2052+ return nullptr ;
2053+ }
2054+ name->type = GEN_URI;
2055+ name->d .uniformResourceIdentifier = ASN1_IA5STRING_new ();
2056+ if (!name->d .uniformResourceIdentifier ||
2057+ !ASN1_STRING_set (name->d .uniformResourceIdentifier , uri, strlen (uri))) {
2058+ return nullptr ;
2059+ }
2060+ return name;
2061+ }
2062+
2063+ // Helper to create a DIST_POINT_NAME from a URI. Caller takes ownership.
2064+ static DIST_POINT_NAME *MakeDistPointName (const char *uri) {
2065+ DIST_POINT_NAME *dpn = DIST_POINT_NAME_new ();
2066+ if (!dpn) {
2067+ return nullptr ;
2068+ }
2069+ dpn->type = 0 ; // fullname
2070+ dpn->name .fullname = sk_GENERAL_NAME_new_null ();
2071+ if (!dpn->name .fullname ) {
2072+ DIST_POINT_NAME_free (dpn);
2073+ return nullptr ;
2074+ }
2075+ auto gn = MakeURIGeneralName (uri);
2076+ if (!gn || !bssl::PushToStack (dpn->name .fullname , std::move (gn))) {
2077+ DIST_POINT_NAME_free (dpn);
2078+ return nullptr ;
2079+ }
2080+ return dpn;
2081+ }
2082+
2083+ // Helper to create a leaf cert with a CRLDP extension and sign it.
2084+ static bssl::UniquePtr<X509> MakeCRLDPLeaf (
2085+ X509 *issuer_cert, EVP_PKEY *issuer_key, int serial,
2086+ CRL_DIST_POINTS *crldp) {
2087+ bssl::UniquePtr<EVP_PKEY> leaf_key (EVP_PKEY_new ());
2088+ bssl::UniquePtr<RSA> rsa (RSA_new ());
2089+ bssl::UniquePtr<BIGNUM> e (BN_new ());
2090+ if (!leaf_key || !rsa || !e ||
2091+ !BN_set_word (e.get (), RSA_F4) ||
2092+ !RSA_generate_key_ex (rsa.get (), 2048 , e.get (), nullptr ) ||
2093+ !EVP_PKEY_assign_RSA (leaf_key.get (), rsa.release ())) {
2094+ return nullptr ;
2095+ }
2096+ bssl::UniquePtr<X509> leaf (X509_new ());
2097+ if (!leaf ||
2098+ !X509_set_version (leaf.get (), X509_VERSION_3) ||
2099+ !X509_set_issuer_name (leaf.get (),
2100+ X509_get_subject_name (issuer_cert)) ||
2101+ !X509_NAME_add_entry_by_txt (
2102+ X509_get_subject_name (leaf.get ()), " CN" , MBSTRING_UTF8,
2103+ reinterpret_cast <const uint8_t *>(" Leaf" ), -1 , -1 , 0 ) ||
2104+ !X509_set_pubkey (leaf.get (), leaf_key.get ()) ||
2105+ !ASN1_TIME_adj (X509_getm_notBefore (leaf.get ()), kReferenceTime , -1 , 0 ) ||
2106+ !ASN1_TIME_adj (X509_getm_notAfter (leaf.get ()), kReferenceTime , 1 , 0 )) {
2107+ return nullptr ;
2108+ }
2109+ bssl::UniquePtr<ASN1_INTEGER> sn (ASN1_INTEGER_new ());
2110+ if (!sn || !ASN1_INTEGER_set (sn.get (), serial) ||
2111+ !X509_set_serialNumber (leaf.get (), sn.get ())) {
2112+ return nullptr ;
2113+ }
2114+ if (!X509_add1_ext_i2d (leaf.get (), NID_crl_distribution_points, crldp,
2115+ /* crit=*/ 0 , /* flags=*/ 0 )) {
2116+ return nullptr ;
2117+ }
2118+ if (!X509_sign (leaf.get (), issuer_key, EVP_sha256 ())) {
2119+ return nullptr ;
2120+ }
2121+ return leaf;
2122+ }
2123+
2124+ // Helper to create a CRL, optionally with an IDP extension and revoked serials.
2125+ static bssl::UniquePtr<X509_CRL> MakeTestCRL (
2126+ X509 *issuer_cert, EVP_PKEY *key, const char *idp_uri,
2127+ const std::vector<int > &revoked_serials) {
2128+ bssl::UniquePtr<X509_CRL> crl (X509_CRL_new ());
2129+ if (!crl) {
2130+ return nullptr ;
2131+ }
2132+ if (!X509_CRL_set_version (crl.get (), X509_CRL_VERSION_2) ||
2133+ !X509_CRL_set_issuer_name (crl.get (),
2134+ X509_get_subject_name (issuer_cert))) {
2135+ return nullptr ;
2136+ }
2137+ bssl::UniquePtr<ASN1_TIME> last_update (ASN1_TIME_new ());
2138+ if (!last_update ||
2139+ !ASN1_TIME_set_posix (last_update.get (), kReferenceTime ) ||
2140+ !X509_CRL_set1_lastUpdate (crl.get (), last_update.get ())) {
2141+ return nullptr ;
2142+ }
2143+ bssl::UniquePtr<ASN1_TIME> next_update (ASN1_TIME_new ());
2144+ if (!next_update ||
2145+ !ASN1_TIME_adj (next_update.get (), kReferenceTime , 30 , 0 ) ||
2146+ !X509_CRL_set1_nextUpdate (crl.get (), next_update.get ())) {
2147+ return nullptr ;
2148+ }
2149+ for (int serial : revoked_serials) {
2150+ bssl::UniquePtr<X509_REVOKED> rev (X509_REVOKED_new ());
2151+ bssl::UniquePtr<ASN1_INTEGER> sn (ASN1_INTEGER_new ());
2152+ bssl::UniquePtr<ASN1_TIME> rev_time (ASN1_TIME_new ());
2153+ if (!rev || !sn || !rev_time ||
2154+ !ASN1_INTEGER_set (sn.get (), serial) ||
2155+ !X509_REVOKED_set_serialNumber (rev.get (), sn.get ()) ||
2156+ !ASN1_TIME_set_posix (rev_time.get (), kReferenceTime ) ||
2157+ !X509_REVOKED_set_revocationDate (rev.get (), rev_time.get ()) ||
2158+ !X509_CRL_add0_revoked (crl.get (), rev.get ())) {
2159+ return nullptr ;
2160+ }
2161+ rev.release (); // Ownership transferred to crl.
2162+ }
2163+ if (idp_uri) {
2164+ ISSUING_DIST_POINT *idp = ISSUING_DIST_POINT_new ();
2165+ if (!idp) {
2166+ return nullptr ;
2167+ }
2168+ idp->distpoint = MakeDistPointName (idp_uri);
2169+ if (!idp->distpoint ||
2170+ !X509_CRL_add1_ext_i2d (crl.get (), NID_issuing_distribution_point,
2171+ idp, /* crit=*/ 1 , /* flags=*/ 0 )) {
2172+ ISSUING_DIST_POINT_free (idp);
2173+ return nullptr ;
2174+ }
2175+ ISSUING_DIST_POINT_free (idp);
2176+ }
2177+ if (!X509_CRL_sign (crl.get (), key, EVP_sha256 ())) {
2178+ return nullptr ;
2179+ }
2180+ // Re-encode and re-parse so that internal fields like crl->idp and
2181+ // crl->idp_flags are populated from the IDP extension. These are only
2182+ // set during parsing (ASN1_OP_D2I_POST), not programmatic construction.
2183+ uint8_t *der = nullptr ;
2184+ int der_len = i2d_X509_CRL (crl.get (), &der);
2185+ if (der_len <= 0 ) {
2186+ return nullptr ;
2187+ }
2188+ const uint8_t *inp = der;
2189+ crl.reset (d2i_X509_CRL (nullptr , &inp, der_len));
2190+ OPENSSL_free (der);
2191+ return crl;
2192+ }
2193+
2194+ // Test that CRL distribution point scope checking (crl_crldp_check) correctly
2195+ // matches a cert's CRLDP against a CRL's Issuing Distribution Point (IDP).
2196+ TEST (X509Test, CRLDistributionPointScope) {
2197+ bssl::UniquePtr<X509> root (CertFromPEM (kCRLTestRoot ));
2198+ bssl::UniquePtr<EVP_PKEY> key (PrivateKeyFromPEM (kCRLTestRootKey ));
2199+ ASSERT_TRUE (root);
2200+ ASSERT_TRUE (key);
2201+
2202+ const int kLeafSerial = 0x1000 ;
2203+ const char *kCRLURI = " http://example.com/crl.pem" ;
2204+ const char *kOtherURI = " http://other.example.com/crl.pem" ;
2205+
2206+ // Scenario 1: Leaf with a single clean CRLDP (distpoint URI only, no reasons,
2207+ // no CRLissuer). CRL has a matching IDP and revokes the leaf's serial.
2208+ // Verify that the CRL is considered in scope and the cert is revoked.
2209+ {
2210+ bssl::UniquePtr<CRL_DIST_POINTS> crldp (sk_DIST_POINT_new_null ());
2211+ ASSERT_TRUE (crldp);
2212+ bssl::UniquePtr<DIST_POINT> dp (DIST_POINT_new ());
2213+ ASSERT_TRUE (dp);
2214+ dp->distpoint = MakeDistPointName (kCRLURI );
2215+ ASSERT_TRUE (dp->distpoint );
2216+ ASSERT_TRUE (bssl::PushToStack (crldp.get (), std::move (dp)));
2217+
2218+ auto leaf = MakeCRLDPLeaf (root.get (), key.get (), kLeafSerial , crldp.get ());
2219+ ASSERT_TRUE (leaf);
2220+ auto crl = MakeTestCRL (root.get (), key.get (), kCRLURI , {kLeafSerial });
2221+ ASSERT_TRUE (crl);
2222+
2223+ EXPECT_EQ (X509_V_ERR_CERT_REVOKED,
2224+ Verify (leaf.get (), {root.get ()}, {root.get ()},
2225+ {crl.get ()}, X509_V_FLAG_CRL_CHECK));
2226+ }
2227+
2228+ // Scenario 2: Leaf with two DPs:
2229+ // DP1: distpoint=kOtherURI + reasons + CRLissuer (should be skipped)
2230+ // DP2: distpoint=kCRLURI (clean, matches the revoking CRL)
2231+ // CRL-A (IDP=kCRLURI) revokes the leaf. CRL-B (IDP=kOtherURI) has no
2232+ // revocations. DP1 should be skipped and DP2 should match CRL-A.
2233+ {
2234+ bssl::UniquePtr<CRL_DIST_POINTS> crldp (sk_DIST_POINT_new_null ());
2235+ ASSERT_TRUE (crldp);
2236+
2237+ // DP1: distpoint + reasons + CRLissuer
2238+ bssl::UniquePtr<DIST_POINT> dp1 (DIST_POINT_new ());
2239+ ASSERT_TRUE (dp1);
2240+ dp1->distpoint = MakeDistPointName (kOtherURI );
2241+ ASSERT_TRUE (dp1->distpoint );
2242+ dp1->reasons = ASN1_BIT_STRING_new ();
2243+ ASSERT_TRUE (dp1->reasons );
2244+ ASN1_BIT_STRING_set_bit (dp1->reasons , 1 , 1 ); // keyCompromise
2245+ dp1->CRLissuer = sk_GENERAL_NAME_new_null ();
2246+ ASSERT_TRUE (dp1->CRLissuer );
2247+ bssl::UniquePtr<GENERAL_NAME> issuer_name (GENERAL_NAME_new ());
2248+ ASSERT_TRUE (issuer_name);
2249+ issuer_name->type = GEN_DIRNAME;
2250+ issuer_name->d .directoryName = X509_NAME_new ();
2251+ ASSERT_TRUE (issuer_name->d .directoryName );
2252+ ASSERT_TRUE (X509_NAME_add_entry_by_txt (
2253+ issuer_name->d .directoryName , " O" , MBSTRING_ASC,
2254+ reinterpret_cast <const uint8_t *>(" Other Issuer" ), -1 , -1 , 0 ));
2255+ ASSERT_TRUE (bssl::PushToStack (dp1->CRLissuer , std::move (issuer_name)));
2256+ ASSERT_TRUE (bssl::PushToStack (crldp.get (), std::move (dp1)));
2257+
2258+ // DP2: clean distpoint only
2259+ bssl::UniquePtr<DIST_POINT> dp2 (DIST_POINT_new ());
2260+ ASSERT_TRUE (dp2);
2261+ dp2->distpoint = MakeDistPointName (kCRLURI );
2262+ ASSERT_TRUE (dp2->distpoint );
2263+ ASSERT_TRUE (bssl::PushToStack (crldp.get (), std::move (dp2)));
2264+
2265+ auto leaf = MakeCRLDPLeaf (root.get (), key.get (), kLeafSerial , crldp.get ());
2266+ ASSERT_TRUE (leaf);
2267+ auto crl_a = MakeTestCRL (root.get (), key.get (), kCRLURI , {kLeafSerial });
2268+ ASSERT_TRUE (crl_a);
2269+ auto crl_b = MakeTestCRL (root.get (), key.get (), kOtherURI , {});
2270+ ASSERT_TRUE (crl_b);
2271+
2272+ EXPECT_EQ (X509_V_ERR_CERT_REVOKED,
2273+ Verify (leaf.get (), {root.get ()}, {root.get ()},
2274+ {crl_a.get (), crl_b.get ()}, X509_V_FLAG_CRL_CHECK));
2275+ }
2276+ }
2277+
20462278TEST (X509Test, TestX509GettersSetters) {
20472279 bssl::UniquePtr<X509_OBJECT> obj (X509_OBJECT_new ());
20482280 bssl::UniquePtr<X509> x509 (CertFromPEM (kCRLTestRoot ));
0 commit comments