@@ -21521,6 +21521,106 @@ static int test_PathLenSelfIssued(void)
2152121521 return EXPECT_RESULT();
2152221522}
2152321523
21524+ /* Verifies that a self-issued intermediate under a CA with pathLen > 0 is
21525+ * accepted AND that maxPathLen is propagated as min(ca->maxPathLen,
21526+ * cert->pathLength) without being decremented (RFC 5280 6.1.4(l)).
21527+ * Pins the `else` branch in asn.c so deletion or an accidental decrement
21528+ * (like the non-self-issued path) is detected. */
21529+ static int test_PathLenSelfIssuedAllowed(void)
21530+ {
21531+ EXPECT_DECLS;
21532+ #if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
21533+ defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
21534+ defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
21535+ (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
21536+ Cert cert;
21537+ DecodedCert decodedCert;
21538+ byte rootDer[FOURK_BUF];
21539+ byte icaDer[FOURK_BUF];
21540+ int rootDerSz = 0;
21541+ int icaDerSz = 0;
21542+ WC_RNG rng;
21543+ ecc_key rootKey;
21544+ ecc_key icaKey;
21545+ WOLFSSL_CERT_MANAGER* cm = NULL;
21546+
21547+ XMEMSET(&rng, 0, sizeof(WC_RNG));
21548+ XMEMSET(&rootKey, 0, sizeof(ecc_key));
21549+ XMEMSET(&icaKey, 0, sizeof(ecc_key));
21550+
21551+ ExpectIntEQ(wc_InitRng(&rng), 0);
21552+ ExpectIntEQ(wc_ecc_init(&rootKey), 0);
21553+ ExpectIntEQ(wc_ecc_init(&icaKey), 0);
21554+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
21555+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
21556+
21557+ /* Step 1: Create root CA with pathLen=1 */
21558+ ExpectIntEQ(wc_InitCert(&cert), 0);
21559+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21560+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21561+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21562+ (void)XSTRNCPY(cert.subject.org, "TestCA3", CTC_NAME_SIZE);
21563+ (void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21564+ (void)XSTRNCPY(cert.subject.commonName, "TestRootCA3", CTC_NAME_SIZE);
21565+ (void)XSTRNCPY(cert.subject.email, "root@test3.com", CTC_NAME_SIZE);
21566+ cert.selfSigned = 1;
21567+ cert.isCA = 1;
21568+ cert.pathLen = 1;
21569+ cert.pathLenSet = 1;
21570+ cert.sigType = CTC_SHA256wECDSA;
21571+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21572+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
21573+ 0);
21574+
21575+ ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
21576+ 0);
21577+ ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
21578+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21579+
21580+ /* Step 2: Create a self-issued intermediate with its OWN pathLen=5.
21581+ * The intentionally-larger pathLen lets the test distinguish:
21582+ * - Correct self-issued path: maxPathLen = min(1, 5) = 1
21583+ * - Deleted else branch: maxPathLen stays at 5 (cert->pathLength)
21584+ * - Mutated to decrement: maxPathLen = min(0, 5) = 0 */
21585+ ExpectIntEQ(wc_InitCert(&cert), 0);
21586+ cert.selfSigned = 0;
21587+ cert.isCA = 1;
21588+ cert.pathLen = 5;
21589+ cert.pathLenSet = 1;
21590+ cert.sigType = CTC_SHA256wECDSA;
21591+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21592+ /* Same subject/issuer DN as root -> self-issued */
21593+ ExpectIntEQ(wc_SetSubjectBuffer(&cert, rootDer, rootDerSz), 0);
21594+ ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
21595+ ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
21596+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
21597+ 0);
21598+
21599+ ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
21600+ ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
21601+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21602+
21603+ /* Step 3: Load root CA into cert manager */
21604+ ExpectNotNull(cm = wolfSSL_CertManagerNew());
21605+ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
21606+ WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
21607+
21608+ /* Step 4: Parse the self-issued intermediate. Must be accepted AND
21609+ * maxPathLen must be exactly 1 (honors root's constraint without
21610+ * decrementing). */
21611+ wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
21612+ ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY, cm), 0);
21613+ ExpectIntEQ(decodedCert.maxPathLen, 1);
21614+ wc_FreeDecodedCert(&decodedCert);
21615+
21616+ wolfSSL_CertManagerFree(cm);
21617+ wc_ecc_free(&icaKey);
21618+ wc_ecc_free(&rootKey);
21619+ wc_FreeRng(&rng);
21620+ #endif
21621+ return EXPECT_RESULT();
21622+ }
21623+
2152421624static int test_PathLenNoKeyUsage(void)
2152521625{
2152621626 EXPECT_DECLS;
@@ -35498,6 +35598,7 @@ TEST_CASE testCases[] = {
3549835598 TEST_DECL(test_wc_ParseCert_Error),
3549935599 TEST_DECL(test_MakeCertWithPathLen),
3550035600 TEST_DECL(test_PathLenSelfIssued),
35601+ TEST_DECL(test_PathLenSelfIssuedAllowed),
3550135602 TEST_DECL(test_PathLenNoKeyUsage),
3550235603 TEST_DECL(test_MakeCertWith0Ser),
3550335604 TEST_DECL(test_MakeCertWithCaFalse),
0 commit comments