@@ -21620,6 +21620,326 @@ static int test_MakeCertWithPathLen(void)
2162021620 return EXPECT_RESULT();
2162121621}
2162221622
21623+ static int test_PathLenSelfIssued(void)
21624+ {
21625+ EXPECT_DECLS;
21626+ #if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
21627+ defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
21628+ defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
21629+ (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
21630+ Cert cert;
21631+ DecodedCert decodedCert;
21632+ byte rootDer[FOURK_BUF];
21633+ byte icaDer[FOURK_BUF];
21634+ byte entityDer[FOURK_BUF];
21635+ int rootDerSz = 0;
21636+ int icaDerSz = 0;
21637+ int entityDerSz = 0;
21638+ WC_RNG rng;
21639+ ecc_key rootKey;
21640+ ecc_key icaKey;
21641+ ecc_key entityKey;
21642+ WOLFSSL_CERT_MANAGER* cm = NULL;
21643+
21644+ XMEMSET(&rng, 0, sizeof(WC_RNG));
21645+ XMEMSET(&rootKey, 0, sizeof(ecc_key));
21646+ XMEMSET(&icaKey, 0, sizeof(ecc_key));
21647+ XMEMSET(&entityKey, 0, sizeof(ecc_key));
21648+
21649+ ExpectIntEQ(wc_InitRng(&rng), 0);
21650+ ExpectIntEQ(wc_ecc_init(&rootKey), 0);
21651+ ExpectIntEQ(wc_ecc_init(&icaKey), 0);
21652+ ExpectIntEQ(wc_ecc_init(&entityKey), 0);
21653+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
21654+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
21655+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &entityKey), 0);
21656+
21657+ /* Step 1: Create root CA with pathLen=0 */
21658+ ExpectIntEQ(wc_InitCert(&cert), 0);
21659+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21660+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21661+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21662+ (void)XSTRNCPY(cert.subject.org, "TestCA", CTC_NAME_SIZE);
21663+ (void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21664+ (void)XSTRNCPY(cert.subject.commonName, "TestRootCA", CTC_NAME_SIZE);
21665+ (void)XSTRNCPY(cert.subject.email, "root@test.com", CTC_NAME_SIZE);
21666+ cert.selfSigned = 1;
21667+ cert.isCA = 1;
21668+ cert.pathLen = 0;
21669+ cert.pathLenSet = 1;
21670+ cert.sigType = CTC_SHA256wECDSA;
21671+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21672+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
21673+ 0);
21674+
21675+ ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
21676+ 0);
21677+ ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
21678+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21679+
21680+ /* Step 2: Create self-issued intermediate (same subject DN as root,
21681+ * different key, signed by root) - this should be blocked by pathLen=0 */
21682+ ExpectIntEQ(wc_InitCert(&cert), 0);
21683+ cert.selfSigned = 0;
21684+ cert.isCA = 1;
21685+ cert.sigType = CTC_SHA256wECDSA;
21686+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21687+ /* Set both subject and issuer from the root cert so they match */
21688+ ExpectIntEQ(wc_SetSubjectBuffer(&cert, rootDer, rootDerSz), 0);
21689+ ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
21690+ ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
21691+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
21692+ 0);
21693+
21694+ ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
21695+ ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
21696+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21697+
21698+ /* Step 3: Create entity cert signed by the intermediate */
21699+ ExpectIntEQ(wc_InitCert(&cert), 0);
21700+ cert.selfSigned = 0;
21701+ cert.isCA = 0;
21702+ cert.sigType = CTC_SHA256wECDSA;
21703+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21704+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21705+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21706+ (void)XSTRNCPY(cert.subject.org, "TestEntity", CTC_NAME_SIZE);
21707+ (void)XSTRNCPY(cert.subject.commonName, "entity.test", CTC_NAME_SIZE);
21708+ ExpectIntEQ(wc_SetIssuerBuffer(&cert, icaDer, icaDerSz), 0);
21709+ ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey), 0);
21710+
21711+ ExpectIntGE(wc_MakeCert(&cert, entityDer, FOURK_BUF, NULL, &entityKey,
21712+ &rng), 0);
21713+ ExpectIntGE(entityDerSz = wc_SignCert(cert.bodySz, cert.sigType, entityDer,
21714+ FOURK_BUF, NULL, &icaKey, &rng), 0);
21715+
21716+ /* Step 4: Load root CA into cert manager */
21717+ ExpectNotNull(cm = wolfSSL_CertManagerNew());
21718+ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
21719+ WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
21720+
21721+ /* Step 5: Parse the self-issued intermediate as a chain cert.
21722+ * This simulates TLS chain verification where the intermediate is
21723+ * received as part of the certificate chain.
21724+ * Root CA has pathLen=0, so it should NOT be allowed to sign any
21725+ * intermediate CA (including self-issued ones).
21726+ * BUG: wolfSSL sets selfSigned=1 for this cert (issuer==subject DN),
21727+ * which causes the pathLen enforcement to be entirely skipped. */
21728+ wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
21729+ ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY,
21730+ cm), WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E));
21731+ wc_FreeDecodedCert(&decodedCert);
21732+
21733+ wolfSSL_CertManagerFree(cm);
21734+ wc_ecc_free(&entityKey);
21735+ wc_ecc_free(&icaKey);
21736+ wc_ecc_free(&rootKey);
21737+ wc_FreeRng(&rng);
21738+ #endif
21739+ return EXPECT_RESULT();
21740+ }
21741+
21742+ /* Verifies that a self-issued intermediate under a CA with pathLen > 0 is
21743+ * accepted AND that maxPathLen is propagated as min(ca->maxPathLen,
21744+ * cert->pathLength) without being decremented (RFC 5280 6.1.4(l)).
21745+ * Pins the `else` branch in asn.c so deletion or an accidental decrement
21746+ * (like the non-self-issued path) is detected. */
21747+ static int test_PathLenSelfIssuedAllowed(void)
21748+ {
21749+ EXPECT_DECLS;
21750+ #if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
21751+ defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
21752+ defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
21753+ (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
21754+ Cert cert;
21755+ DecodedCert decodedCert;
21756+ byte rootDer[FOURK_BUF];
21757+ byte icaDer[FOURK_BUF];
21758+ int rootDerSz = 0;
21759+ int icaDerSz = 0;
21760+ WC_RNG rng;
21761+ ecc_key rootKey;
21762+ ecc_key icaKey;
21763+ WOLFSSL_CERT_MANAGER* cm = NULL;
21764+
21765+ XMEMSET(&rng, 0, sizeof(WC_RNG));
21766+ XMEMSET(&rootKey, 0, sizeof(ecc_key));
21767+ XMEMSET(&icaKey, 0, sizeof(ecc_key));
21768+
21769+ ExpectIntEQ(wc_InitRng(&rng), 0);
21770+ ExpectIntEQ(wc_ecc_init(&rootKey), 0);
21771+ ExpectIntEQ(wc_ecc_init(&icaKey), 0);
21772+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
21773+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
21774+
21775+ /* Step 1: Create root CA with pathLen=1 */
21776+ ExpectIntEQ(wc_InitCert(&cert), 0);
21777+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21778+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21779+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21780+ (void)XSTRNCPY(cert.subject.org, "TestCA3", CTC_NAME_SIZE);
21781+ (void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21782+ (void)XSTRNCPY(cert.subject.commonName, "TestRootCA3", CTC_NAME_SIZE);
21783+ (void)XSTRNCPY(cert.subject.email, "root@test3.com", CTC_NAME_SIZE);
21784+ cert.selfSigned = 1;
21785+ cert.isCA = 1;
21786+ cert.pathLen = 1;
21787+ cert.pathLenSet = 1;
21788+ cert.sigType = CTC_SHA256wECDSA;
21789+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21790+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
21791+ 0);
21792+
21793+ ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
21794+ 0);
21795+ ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
21796+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21797+
21798+ /* Step 2: Create a self-issued intermediate with its OWN pathLen=5.
21799+ * The intentionally-larger pathLen lets the test distinguish:
21800+ * - Correct self-issued path: maxPathLen = min(1, 5) = 1
21801+ * - Deleted else branch: maxPathLen stays at 5 (cert->pathLength)
21802+ * - Mutated to decrement: maxPathLen = min(0, 5) = 0 */
21803+ ExpectIntEQ(wc_InitCert(&cert), 0);
21804+ cert.selfSigned = 0;
21805+ cert.isCA = 1;
21806+ cert.pathLen = 5;
21807+ cert.pathLenSet = 1;
21808+ cert.sigType = CTC_SHA256wECDSA;
21809+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21810+ /* Same subject/issuer DN as root -> self-issued */
21811+ ExpectIntEQ(wc_SetSubjectBuffer(&cert, rootDer, rootDerSz), 0);
21812+ ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
21813+ ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
21814+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
21815+ 0);
21816+
21817+ ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
21818+ ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
21819+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21820+
21821+ /* Step 3: Load root CA into cert manager */
21822+ ExpectNotNull(cm = wolfSSL_CertManagerNew());
21823+ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
21824+ WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
21825+
21826+ /* Step 4: Parse the self-issued intermediate. Must be accepted AND
21827+ * maxPathLen must be exactly 1 (honors root's constraint without
21828+ * decrementing). */
21829+ wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
21830+ ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY, cm), 0);
21831+ ExpectIntEQ(decodedCert.maxPathLen, 1);
21832+ wc_FreeDecodedCert(&decodedCert);
21833+
21834+ wolfSSL_CertManagerFree(cm);
21835+ wc_ecc_free(&icaKey);
21836+ wc_ecc_free(&rootKey);
21837+ wc_FreeRng(&rng);
21838+ #endif
21839+ return EXPECT_RESULT();
21840+ }
21841+
21842+ static int test_PathLenNoKeyUsage(void)
21843+ {
21844+ EXPECT_DECLS;
21845+ #if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
21846+ defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
21847+ defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
21848+ (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
21849+ Cert cert;
21850+ DecodedCert decodedCert;
21851+ byte rootDer[FOURK_BUF];
21852+ byte icaDer[FOURK_BUF];
21853+ int rootDerSz = 0;
21854+ int icaDerSz = 0;
21855+ WC_RNG rng;
21856+ ecc_key rootKey;
21857+ ecc_key icaKey;
21858+ WOLFSSL_CERT_MANAGER* cm = NULL;
21859+
21860+ XMEMSET(&rng, 0, sizeof(WC_RNG));
21861+ XMEMSET(&rootKey, 0, sizeof(ecc_key));
21862+ XMEMSET(&icaKey, 0, sizeof(ecc_key));
21863+
21864+ ExpectIntEQ(wc_InitRng(&rng), 0);
21865+ ExpectIntEQ(wc_ecc_init(&rootKey), 0);
21866+ ExpectIntEQ(wc_ecc_init(&icaKey), 0);
21867+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
21868+ ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
21869+
21870+ /* Step 1: Create root CA with pathLen=0 and KeyUsage */
21871+ ExpectIntEQ(wc_InitCert(&cert), 0);
21872+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21873+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21874+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21875+ (void)XSTRNCPY(cert.subject.org, "TestCA2", CTC_NAME_SIZE);
21876+ (void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21877+ (void)XSTRNCPY(cert.subject.commonName, "TestRootCA2", CTC_NAME_SIZE);
21878+ (void)XSTRNCPY(cert.subject.email, "root@test2.com", CTC_NAME_SIZE);
21879+ cert.selfSigned = 1;
21880+ cert.isCA = 1;
21881+ cert.pathLen = 0;
21882+ cert.pathLenSet = 1;
21883+ cert.sigType = CTC_SHA256wECDSA;
21884+ cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21885+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
21886+ 0);
21887+
21888+ ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
21889+ 0);
21890+ ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
21891+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21892+
21893+ /* Step 2: Create intermediate CA WITHOUT KeyUsage extension.
21894+ * Per RFC 5280, when KeyUsage is absent all uses are valid.
21895+ * The root's pathLen=0 should still block this intermediate CA.
21896+ * BUG: pathLen check requires extKeyUsageSet which is false when
21897+ * KeyUsage is absent, so the check is skipped entirely. */
21898+ ExpectIntEQ(wc_InitCert(&cert), 0);
21899+ cert.selfSigned = 0;
21900+ cert.isCA = 1;
21901+ cert.sigType = CTC_SHA256wECDSA;
21902+ /* Intentionally do NOT set keyUsage - test that pathLen is still enforced */
21903+ cert.keyUsage = 0;
21904+ (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21905+ (void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21906+ (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21907+ (void)XSTRNCPY(cert.subject.org, "TestICA", CTC_NAME_SIZE);
21908+ (void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21909+ (void)XSTRNCPY(cert.subject.commonName, "TestICA-NoKU", CTC_NAME_SIZE);
21910+ (void)XSTRNCPY(cert.subject.email, "ica@test2.com", CTC_NAME_SIZE);
21911+ ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
21912+ ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
21913+ ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
21914+ 0);
21915+
21916+ ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
21917+ ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
21918+ FOURK_BUF, NULL, &rootKey, &rng), 0);
21919+
21920+ /* Step 3: Load root CA into cert manager */
21921+ ExpectNotNull(cm = wolfSSL_CertManagerNew());
21922+ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
21923+ WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
21924+
21925+ /* Step 4: Parse the intermediate (no KeyUsage) as a chain cert.
21926+ * Root CA has pathLen=0, this intermediate CA should be rejected.
21927+ * The intermediate does NOT have the KeyUsage extension, but per
21928+ * RFC 5280 4.2.1.3 all key uses are valid when the extension is
21929+ * absent, so pathLen must still be enforced. */
21930+ wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
21931+ ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY,
21932+ cm), WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E));
21933+ wc_FreeDecodedCert(&decodedCert);
21934+
21935+ wolfSSL_CertManagerFree(cm);
21936+ wc_ecc_free(&icaKey);
21937+ wc_ecc_free(&rootKey);
21938+ wc_FreeRng(&rng);
21939+ #endif
21940+ return EXPECT_RESULT();
21941+ }
21942+
2162321943static int test_MakeCertWith0Ser(void)
2162421944{
2162521945 EXPECT_DECLS;
@@ -35807,6 +36127,9 @@ TEST_CASE testCases[] = {
3580736127 TEST_DECL(test_wc_ParseCert),
3580836128 TEST_DECL(test_wc_ParseCert_Error),
3580936129 TEST_DECL(test_MakeCertWithPathLen),
36130+ TEST_DECL(test_PathLenSelfIssued),
36131+ TEST_DECL(test_PathLenSelfIssuedAllowed),
36132+ TEST_DECL(test_PathLenNoKeyUsage),
3581036133 TEST_DECL(test_MakeCertWith0Ser),
3581136134 TEST_DECL(test_MakeCertWithCaFalse),
3581236135#ifdef WOLFSSL_CERT_SIGN_CB
0 commit comments