@@ -21402,6 +21402,226 @@ static int test_MakeCertWithPathLen(void)
2140221402 return EXPECT_RESULT();
2140321403}
2140421404
21405+ static int test_PathLenSelfIssued(void)
21406+ {
21407+ EXPECT_DECLS;
21408+ #if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
21409+ defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
21410+ defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
21411+ (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
21412+ Cert cert;
21413+ DecodedCert decodedCert;
21414+ byte rootDer[FOURK_BUF];
21415+ byte icaDer[FOURK_BUF];
21416+ byte entityDer[FOURK_BUF];
21417+ int rootDerSz = 0;
21418+ int icaDerSz = 0;
21419+ int entityDerSz = 0;
21420+ WC_RNG rng;
21421+ ecc_key rootKey;
21422+ ecc_key icaKey;
21423+ ecc_key entityKey;
21424+ WOLFSSL_CERT_MANAGER* cm = NULL;
21425+
21426+ XMEMSET(&rng, 0, sizeof(WC_RNG));
21427+ XMEMSET(&rootKey, 0, sizeof(ecc_key));
21428+ XMEMSET(&icaKey, 0, sizeof(ecc_key));
21429+ XMEMSET(&entityKey, 0, sizeof(ecc_key));
21430+
21431+ ExpectIntEQ(wc_InitRng(&rng), 0);
21432+ ExpectIntEQ(wc_ecc_init(&rootKey), 0);
21433+ ExpectIntEQ(wc_ecc_init(&icaKey), 0);
21434+ ExpectIntEQ(wc_ecc_init(&entityKey), 0);
21435+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
21436+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
21437+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &entityKey), 0);
21438+
21439+ /* Step 1: Create root CA with pathLen=0 */
21440+ ExpectIntEQ(wc_InitCert(&cert), 0);
21441+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21442+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21443+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21444+ (void)XSTRNCPY(cert.subject.org, "TestCA", CTC_NAME_SIZE);
21445+ (void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21446+ (void)XSTRNCPY(cert.subject.commonName, "TestRootCA", CTC_NAME_SIZE);
21447+ (void)XSTRNCPY(cert.subject.email, "root@test.com", CTC_NAME_SIZE);
21448+ cert.selfSigned = 1;
21449+ cert.isCA = 1;
21450+ cert.pathLen = 0;
21451+ cert.pathLenSet = 1;
21452+ cert.sigType = CTC_SHA256wECDSA;
21453+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21454+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
21455+ 0);
21456+
21457+ ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
21458+ 0);
21459+ ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
21460+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21461+
21462+ /* Step 2: Create self-issued intermediate (same subject DN as root,
21463+ * different key, signed by root) - this should be blocked by pathLen=0 */
21464+ ExpectIntEQ(wc_InitCert(&cert), 0);
21465+ cert.selfSigned = 0;
21466+ cert.isCA = 1;
21467+ cert.sigType = CTC_SHA256wECDSA;
21468+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21469+ /* Set both subject and issuer from the root cert so they match */
21470+ ExpectIntEQ(wc_SetSubjectBuffer(&cert, rootDer, rootDerSz), 0);
21471+ ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
21472+ ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
21473+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
21474+ 0);
21475+
21476+ ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
21477+ ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
21478+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21479+
21480+ /* Step 3: Create entity cert signed by the intermediate */
21481+ ExpectIntEQ(wc_InitCert(&cert), 0);
21482+ cert.selfSigned = 0;
21483+ cert.isCA = 0;
21484+ cert.sigType = CTC_SHA256wECDSA;
21485+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21486+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21487+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21488+ (void)XSTRNCPY(cert.subject.org, "TestEntity", CTC_NAME_SIZE);
21489+ (void)XSTRNCPY(cert.subject.commonName, "entity.test", CTC_NAME_SIZE);
21490+ ExpectIntEQ(wc_SetIssuerBuffer(&cert, icaDer, icaDerSz), 0);
21491+ ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey), 0);
21492+
21493+ ExpectIntGE(wc_MakeCert(&cert, entityDer, FOURK_BUF, NULL, &entityKey,
21494+ &rng), 0);
21495+ ExpectIntGE(entityDerSz = wc_SignCert(cert.bodySz, cert.sigType, entityDer,
21496+ FOURK_BUF, NULL, &icaKey, &rng), 0);
21497+
21498+ /* Step 4: Load root CA into cert manager */
21499+ ExpectNotNull(cm = wolfSSL_CertManagerNew());
21500+ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
21501+ WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
21502+
21503+ /* Step 5: Parse the self-issued intermediate as a chain cert.
21504+ * This simulates TLS chain verification where the intermediate is
21505+ * received as part of the certificate chain.
21506+ * Root CA has pathLen=0, so it should NOT be allowed to sign any
21507+ * intermediate CA (including self-issued ones).
21508+ * BUG: wolfSSL sets selfSigned=1 for this cert (issuer==subject DN),
21509+ * which causes the pathLen enforcement to be entirely skipped. */
21510+ wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
21511+ ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY,
21512+ cm), WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E));
21513+ wc_FreeDecodedCert(&decodedCert);
21514+
21515+ wolfSSL_CertManagerFree(cm);
21516+ wc_ecc_free(&entityKey);
21517+ wc_ecc_free(&icaKey);
21518+ wc_ecc_free(&rootKey);
21519+ wc_FreeRng(&rng);
21520+ #endif
21521+ return EXPECT_RESULT();
21522+ }
21523+
21524+ static int test_PathLenNoKeyUsage(void)
21525+ {
21526+ EXPECT_DECLS;
21527+ #if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
21528+ defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
21529+ defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
21530+ (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
21531+ Cert cert;
21532+ DecodedCert decodedCert;
21533+ byte rootDer[FOURK_BUF];
21534+ byte icaDer[FOURK_BUF];
21535+ int rootDerSz = 0;
21536+ int icaDerSz = 0;
21537+ WC_RNG rng;
21538+ ecc_key rootKey;
21539+ ecc_key icaKey;
21540+ WOLFSSL_CERT_MANAGER* cm = NULL;
21541+
21542+ XMEMSET(&rng, 0, sizeof(WC_RNG));
21543+ XMEMSET(&rootKey, 0, sizeof(ecc_key));
21544+ XMEMSET(&icaKey, 0, sizeof(ecc_key));
21545+
21546+ ExpectIntEQ(wc_InitRng(&rng), 0);
21547+ ExpectIntEQ(wc_ecc_init(&rootKey), 0);
21548+ ExpectIntEQ(wc_ecc_init(&icaKey), 0);
21549+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
21550+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
21551+
21552+ /* Step 1: Create root CA with pathLen=0 and KeyUsage */
21553+ ExpectIntEQ(wc_InitCert(&cert), 0);
21554+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21555+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21556+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21557+ (void)XSTRNCPY(cert.subject.org, "TestCA2", CTC_NAME_SIZE);
21558+ (void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21559+ (void)XSTRNCPY(cert.subject.commonName, "TestRootCA2", CTC_NAME_SIZE);
21560+ (void)XSTRNCPY(cert.subject.email, "root@test2.com", CTC_NAME_SIZE);
21561+ cert.selfSigned = 1;
21562+ cert.isCA = 1;
21563+ cert.pathLen = 0;
21564+ cert.pathLenSet = 1;
21565+ cert.sigType = CTC_SHA256wECDSA;
21566+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21567+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
21568+ 0);
21569+
21570+ ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
21571+ 0);
21572+ ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
21573+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21574+
21575+ /* Step 2: Create intermediate CA WITHOUT KeyUsage extension.
21576+ * Per RFC 5280, when KeyUsage is absent all uses are valid.
21577+ * The root's pathLen=0 should still block this intermediate CA.
21578+ * BUG: pathLen check requires extKeyUsageSet which is false when
21579+ * KeyUsage is absent, so the check is skipped entirely. */
21580+ ExpectIntEQ(wc_InitCert(&cert), 0);
21581+ cert.selfSigned = 0;
21582+ cert.isCA = 1;
21583+ cert.sigType = CTC_SHA256wECDSA;
21584+ /* Intentionally do NOT set keyUsage - test that pathLen is still enforced */
21585+ cert.keyUsage = 0;
21586+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21587+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21588+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21589+ (void)XSTRNCPY(cert.subject.org, "TestICA", CTC_NAME_SIZE);
21590+ (void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21591+ (void)XSTRNCPY(cert.subject.commonName, "TestICA-NoKU", CTC_NAME_SIZE);
21592+ (void)XSTRNCPY(cert.subject.email, "ica@test2.com", CTC_NAME_SIZE);
21593+ ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
21594+ ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
21595+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
21596+ 0);
21597+
21598+ ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
21599+ ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
21600+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21601+
21602+ /* Step 3: Load root CA into cert manager */
21603+ ExpectNotNull(cm = wolfSSL_CertManagerNew());
21604+ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
21605+ WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
21606+
21607+ /* Step 4: Parse the intermediate (no KeyUsage) as a chain cert.
21608+ * Root CA has pathLen=0, this intermediate CA should be rejected.
21609+ * The intermediate does NOT have the KeyUsage extension, but per
21610+ * RFC 5280 4.2.1.3 all key uses are valid when the extension is
21611+ * absent, so pathLen must still be enforced. */
21612+ wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
21613+ ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY,
21614+ cm), WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E));
21615+ wc_FreeDecodedCert(&decodedCert);
21616+
21617+ wolfSSL_CertManagerFree(cm);
21618+ wc_ecc_free(&icaKey);
21619+ wc_ecc_free(&rootKey);
21620+ wc_FreeRng(&rng);
21621+ #endif
21622+ return EXPECT_RESULT();
21623+ }
21624+
2140521625static int test_MakeCertWith0Ser(void)
2140621626{
2140721627 EXPECT_DECLS;
@@ -35277,6 +35497,8 @@ TEST_CASE testCases[] = {
3527735497 TEST_DECL(test_wc_ParseCert),
3527835498 TEST_DECL(test_wc_ParseCert_Error),
3527935499 TEST_DECL(test_MakeCertWithPathLen),
35500+ TEST_DECL(test_PathLenSelfIssued),
35501+ TEST_DECL(test_PathLenNoKeyUsage),
3528035502 TEST_DECL(test_MakeCertWith0Ser),
3528135503 TEST_DECL(test_MakeCertWithCaFalse),
3528235504#ifdef WOLFSSL_CERT_SIGN_CB
0 commit comments