From f8506c3e04e72b90c2a4f2113dbe864c6f69884f Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Thu, 6 Mar 2025 10:21:32 -0500 Subject: [PATCH 1/4] Allow critical alt and basic constraints extensions Also properly track pathlen. --- src/internal.c | 6 +++- src/x509.c | 8 ++++- tests/api.c | 1 + wolfcrypt/src/asn.c | 55 ++++++++++++++++++++++++++++++++-- wolfssl/internal.h | 5 ++++ wolfssl/wolfcrypt/asn.h | 12 ++++---- wolfssl/wolfcrypt/asn_public.h | 4 +++ 7 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/internal.c b/src/internal.c index aa9387267..dc80d01d7 100644 --- a/src/internal.c +++ b/src/internal.c @@ -13476,8 +13476,10 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) x509->altNamesNext = x509->altNames; /* index hint */ x509->isCa = dCert->isCA; + x509->basicConstCrit = dCert->extBasicConstCrit; #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) x509->pathLength = dCert->pathLength; + x509->pathLengthSet = dCert->pathLengthSet; x509->keyUsage = dCert->extKeyUsage; x509->CRLdistSet = dCert->extCRLdistSet; @@ -13531,7 +13533,6 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) } #endif x509->basicConstSet = dCert->extBasicConstSet; - x509->basicConstCrit = dCert->extBasicConstCrit; x509->basicConstPlSet = dCert->pathLengthSet; x509->subjAltNameSet = dCert->extSubjAltNameSet; x509->subjAltNameCrit = dCert->extSubjAltNameCrit; @@ -13644,6 +13645,7 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) if (x509->sapkiDer != NULL) { XMEMCPY(x509->sapkiDer, dCert->sapkiDer, dCert->sapkiLen); x509->sapkiLen = dCert->sapkiLen; + x509->sapkiCrit = dCert->extSapkiCrit; } else { ret = MEMORY_E; @@ -13656,6 +13658,7 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) XMEMCPY(x509->altSigAlgDer, dCert->altSigAlgDer, dCert->altSigAlgLen); x509->altSigAlgLen = dCert->altSigAlgLen; + x509->altSigAlgCrit = dCert->extAltSigAlgCrit; } else { ret = MEMORY_E; @@ -13668,6 +13671,7 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) XMEMCPY(x509->altSigValDer, dCert->altSigValDer, dCert->altSigValLen); x509->altSigValLen = dCert->altSigValLen; + x509->altSigValCrit = dCert->extAltSigValCrit; } else { ret = MEMORY_E; diff --git a/src/x509.c b/src/x509.c index fec65d621..bd4d09464 100644 --- a/src/x509.c +++ b/src/x509.c @@ -10674,7 +10674,10 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) cert->sigType = wolfSSL_X509_get_signature_type(x509); cert->keyType = x509->pubKeyOID; cert->isCA = wolfSSL_X509_get_isCA(x509); + cert->basicConstCrit = x509->basicConstCrit; cert->basicConstSet = x509->basicConstSet; + cert->pathLen = x509->pathLength; + cert->pathLenSet = x509->pathLengthSet; #ifdef WOLFSSL_CERT_EXT if (x509->subjKeyIdSz <= CTC_MAX_SKID_SIZE) { @@ -10741,10 +10744,13 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) /* We point to instance in x509 so DON'T need to be free'd. */ cert->sapkiDer = x509->sapkiDer; cert->sapkiLen = x509->sapkiLen; + cert->sapkiCrit = x509->sapkiCrit; cert->altSigAlgDer = x509->altSigAlgDer; - cert->altSigAlgLen = x509->altSigAlgLen; + cert->altSigAlgLen = x509->altSigAlgLen; + cert->altSigAlgCrit = x509->altSigAlgCrit; cert->altSigValDer = x509->altSigValDer; cert->altSigValLen = x509->altSigValLen; + cert->altSigValCrit = x509->altSigValCrit; #endif /* WOLFSSL_DUAL_ALG_CERTS */ #endif /* WOLFSSL_CERT_EXT */ diff --git a/tests/api.c b/tests/api.c index 6b6596bcf..ae9440458 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1033,6 +1033,7 @@ static int do_dual_alg_root_certgen(byte **out, char *caKeyFile, XMEMSET(scratchBuf, 0, scratchSz); ExpectIntGT(scratchSz = wc_MakeSelfCert(&newCert, scratchBuf, scratchSz, &caKey, &rng), 0); + wc_InitDecodedCert(&preTBS, scratchBuf, scratchSz, 0); ExpectIntEQ(wc_ParseCert(&preTBS, CERT_TYPE, NO_VERIFY, NULL), 0); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index ebb0a8369..647813449 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -21320,6 +21320,7 @@ static int DecodeExtensionType(const byte* input, word32 length, word32 oid, case BASIC_CA_OID: VERIFY_AND_SET_OID(cert->extBasicConstSet); cert->extBasicConstCrit = critical ? 1 : 0; + cert->extBasicConstCrit = critical ? 1 : 0; if (DecodeBasicCaConstraint(input, (int)length, cert) < 0) { ret = ASN_PARSE_E; } @@ -21509,16 +21510,19 @@ static int DecodeExtensionType(const byte* input, word32 length, word32 oid, #ifdef WOLFSSL_DUAL_ALG_CERTS case SUBJ_ALT_PUB_KEY_INFO_OID: VERIFY_AND_SET_OID(cert->extSapkiSet); + cert->extSapkiCrit = critical ? 1 : 0; if (DecodeSubjAltPubKeyInfo(&input[idx], length, cert) < 0) return ASN_PARSE_E; break; case ALT_SIG_ALG_OID: VERIFY_AND_SET_OID(cert->extAltSigAlgSet); + cert->extAltSigAlgCrit = critical ? 1 : 0; if (DecodeAltSigAlg(&input[idx], length, cert) < 0) return ASN_PARSE_E; break; case ALT_SIG_VAL_OID: VERIFY_AND_SET_OID(cert->extAltSigValSet); + cert->extAltSigValCrit = critical ? 1 : 0; if (DecodeAltSigVal(&input[idx], length, cert) < 0) return ASN_PARSE_E; break; @@ -28982,6 +28986,7 @@ static const ASNItem static_certExtsASN[] = { /* Basic Constraints Extension - 4.2.1.9 */ /* BC_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, /* BC_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 }, +/* BC_CRIT */ { 1, ASN_BOOLEAN, 0, 0, 0 }, /* BC_STR */ { 1, ASN_OCTET_STRING, 0, 1, 0 }, /* BC_STR_SEQ */ { 2, ASN_SEQUENCE, 1, 1, 0 }, /* cA */ @@ -29030,12 +29035,15 @@ static const ASNItem static_certExtsASN[] = { #ifdef WOLFSSL_DUAL_ALG_CERTS /* SAPKI_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, /* SAPKI_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 }, +/* SAPKI_CRIT */ { 1, ASN_BOOLEAN, 0, 0, 0 }, /* SAPKI_STR */ { 1, ASN_OCTET_STRING, 0, 0, 0 }, /* ALTSIGALG_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, /* ALTSIGALG_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 }, +/* ALTSIGALG_CRIT*/ { 1, ASN_BOOLEAN, 0, 0, 0 }, /* ALTSIGALG_STR */ { 1, ASN_OCTET_STRING, 0, 0, 0 }, /* ALTSIGVAL_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, /* ALTSIGVAL_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 }, +/* ALTSIGVAL_CRIT*/ { 1, ASN_BOOLEAN, 0, 0, 0 }, /* ALTSIGVAL_STR */ { 1, ASN_OCTET_STRING, 0, 0, 0 }, #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* CUSTOM_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, @@ -29045,6 +29053,7 @@ static const ASNItem static_certExtsASN[] = { enum { CERTEXTSASN_IDX_BC_SEQ = 0, CERTEXTSASN_IDX_BC_OID, + CERTEXTSASN_IDX_BC_CRIT, CERTEXTSASN_IDX_BC_STR, CERTEXTSASN_IDX_BC_STR_SEQ, CERTEXTSASN_IDX_BC_CA, @@ -29084,12 +29093,15 @@ enum { #ifdef WOLFSSL_DUAL_ALG_CERTS CERTEXTSASN_IDX_SAPKI_SEQ, CERTEXTSASN_IDX_SAPKI_OID, + CERTEXTSASN_IDX_SAPKI_CRIT, CERTEXTSASN_IDX_SAPKI_STR, CERTEXTSASN_IDX_ALTSIGALG_SEQ, CERTEXTSASN_IDX_ALTSIGALG_OID, + CERTEXTSASN_IDX_ALTSIGALG_CRIT, CERTEXTSASN_IDX_ALTSIGALG_STR, CERTEXTSASN_IDX_ALTSIGVAL_SEQ, CERTEXTSASN_IDX_ALTSIGVAL_OID, + CERTEXTSASN_IDX_ALTSIGVAL_CRIT, CERTEXTSASN_IDX_ALTSIGVAL_STR, #endif /* WOLFSSL_DUAL_ALG_CERTS */ CERTEXTSASN_IDX_CUSTOM_SEQ, @@ -29181,6 +29193,12 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz, /* Set Basic Constraints to be a Certificate Authority. */ SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CA], 1); SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_BC_OID], bcOID, sizeof(bcOID)); + if (cert->basicConstCrit) { + SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CRIT], 1); + } + else { + dataASN[CERTEXTSASN_IDX_BC_CRIT].noOut = 1; + } if (cert->pathLenSet #ifdef WOLFSSL_CERT_EXT && ((cert->keyUsage & KEYUSE_KEY_CERT_SIGN) || (!cert->keyUsage)) @@ -29197,12 +29215,24 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz, else if (cert->isCaSet) { SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CA], 0); SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_BC_OID], bcOID, sizeof(bcOID)); + if (cert->basicConstCrit) { + SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CRIT], 1); + } + else { + dataASN[CERTEXTSASN_IDX_BC_CRIT].noOut = 1; + } dataASN[CERTEXTSASN_IDX_BC_PATHLEN].noOut = 1; } #endif else if (cert->basicConstSet) { /* Set Basic Constraints to be a non Certificate Authority. */ SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_BC_OID], bcOID, sizeof(bcOID)); + if (cert->basicConstCrit) { + SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CRIT], 1); + } + else { + dataASN[CERTEXTSASN_IDX_BC_CRIT].noOut = 1; + } dataASN[CERTEXTSASN_IDX_BC_CA].noOut = 1; dataASN[CERTEXTSASN_IDX_BC_PATHLEN].noOut = 1; } @@ -29369,9 +29399,16 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz, #ifdef WOLFSSL_DUAL_ALG_CERTS if (cert->sapkiDer != NULL) { - /* Set subject alternative public key info OID and data. */ + /* Set subject alternative public key info OID, criticality and + * data. */ SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SAPKI_OID], sapkiOID, sizeof(sapkiOID)); + if (cert->sapkiCrit) { + SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_SAPKI_CRIT], 1); + } + else { + dataASN[CERTEXTSASN_IDX_SAPKI_CRIT].noOut = 1; + } SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SAPKI_STR], cert->sapkiDer, cert->sapkiLen); } @@ -29382,9 +29419,15 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz, } if (cert->altSigAlgDer != NULL) { - /* Set alternative signature algorithm OID and data. */ + /* Set alternative signature algorithm OID, criticality and data. */ SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGALG_OID], altSigAlgOID, sizeof(altSigAlgOID)); + if (cert->altSigAlgCrit) { + SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_ALTSIGALG_CRIT], 1); + } + else { + dataASN[CERTEXTSASN_IDX_ALTSIGALG_CRIT].noOut = 1; + } SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGALG_STR], cert->altSigAlgDer, cert->altSigAlgLen); } @@ -29395,9 +29438,15 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz, } if (cert->altSigValDer != NULL) { - /* Set alternative signature value OID and data. */ + /* Set alternative signature value OID, criticality and data. */ SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGVAL_OID], altSigValOID, sizeof(altSigValOID)); + if (cert->altSigValCrit) { + SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_ALTSIGVAL_CRIT], 1); + } + else { + dataASN[CERTEXTSASN_IDX_ALTSIGVAL_CRIT].noOut = 1; + } SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGVAL_STR], cert->altSigValDer, cert->altSigValLen); } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 09f218d33..87275d3df 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5405,6 +5405,7 @@ struct WOLFSSL_X509 { byte keyUsageCrit:1; byte extKeyUsageCrit:1; byte subjKeyIdSet:1; + byte pathLengthSet:1; byte subjKeyIdCrit:1; byte basicConstSet:1; @@ -5457,6 +5458,10 @@ struct WOLFSSL_X509 { /* Alternative Signature Value */ byte *altSigValDer; int altSigValLen; + + byte sapkiCrit:1; + byte altSigAlgCrit:1; + byte altSigValCrit:1; #endif /* WOLFSSL_DUAL_ALG_CERTS */ }; diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 15efecd5a..e5ac2d0af 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2133,11 +2133,6 @@ struct DecodedCert { #ifdef WOLFSSL_SUBJ_INFO_ACC WC_BITFIELD extSubjInfoAccSet:1; #endif -#ifdef WOLFSSL_DUAL_ALG_CERTS - WC_BITFIELD extSapkiSet:1; - WC_BITFIELD extAltSigAlgSet:1; - WC_BITFIELD extAltSigValSet:1; -#endif /* WOLFSSL_DUAL_ALG_CERTS */ #ifdef WOLFSSL_SEP WC_BITFIELD extCertPolicyCrit:1; #endif @@ -2164,6 +2159,13 @@ struct DecodedCert { /* Alternative Signature Value */ byte *altSigValDer; int altSigValLen; + + WC_BITFIELD extSapkiSet:1; + WC_BITFIELD extAltSigAlgSet:1; + WC_BITFIELD extAltSigValSet:1; + WC_BITFIELD extSapkiCrit:1; + WC_BITFIELD extAltSigAlgCrit:1; + WC_BITFIELD extAltSigValCrit:1; #endif /* WOLFSSL_DUAL_ALG_CERTS */ }; diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index db4f272a8..08d9cc938 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -527,12 +527,15 @@ typedef struct Cert { /* Subject Alternative Public Key Info */ byte *sapkiDer; int sapkiLen; + byte sapkiCrit; /* Alternative Signature Algorithm */ byte *altSigAlgDer; int altSigAlgLen; + byte altSigAlgCrit; /* Alternative Signature Value */ byte *altSigValDer; int altSigValLen; + byte altSigValCrit; #endif /* WOLFSSL_DUAL_ALG_CERTS */ #ifdef WOLFSSL_CERT_REQ char challengePw[CTC_NAME_SIZE]; @@ -551,6 +554,7 @@ typedef struct Cert { byte* der; /* Pointer to buffer of current DecodedCert cache */ void* heap; /* heap hint */ WC_BITFIELD basicConstSet:1; /* Indicator for when Basic Constraint is set */ + byte basicConstCrit; /* Indicator of criticality of Basic Constraints extension */ #ifdef WOLFSSL_ALLOW_ENCODING_CA_FALSE WC_BITFIELD isCaSet:1; /* Indicator for when isCA is set */ #endif From 6d6c5f520b13b07139487d2d5b6707b74675458c Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Fri, 7 Mar 2025 18:11:45 -0500 Subject: [PATCH 2/4] unit tests --- tests/api.c | 401 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) diff --git a/tests/api.c b/tests/api.c index ae9440458..f88ecc1e8 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1229,6 +1229,400 @@ static int do_dual_alg_tls13_connection(byte *caCert, word32 caCertSz, return EXPECT_RESULT(); } +/** + * Function to generate a root certificate with dual algorithm support and + * configurable criticality for extensions and path length constraints. + * + * @param out [out] Pointer to store the generated certificate + * @param caKeyFile [in] Path to the CA key file + * @param sapkiFile [in] Path to the subject alternative public key info file + * @param altPrivFile [in] Path to the alternative private key file + * @param setCrit [in] Flag to set criticality of extensions (1=critical, 0=non-critical) + * @param setPathLen [in] Flag to set path length constraint (1=set, 0=don't set) + * @param pathLen [in] Path length value (only used if setPathLen=1) + * @return Size of the generated certificate or negative on error + */ +static int do_dual_alg_root_certgen_crit(byte **out, char *caKeyFile, + char *sapkiFile, char *altPrivFile, + int setCrit, int setPathLen, int pathLen) +{ + EXPECT_DECLS; + FILE* file = NULL; + Cert newCert; + DecodedCert preTBS; + + byte caKeyBuf[LARGE_TEMP_SZ]; + word32 caKeySz = LARGE_TEMP_SZ; + byte sapkiBuf[LARGE_TEMP_SZ]; + word32 sapkiSz = LARGE_TEMP_SZ; + byte altPrivBuf[LARGE_TEMP_SZ]; + word32 altPrivSz = LARGE_TEMP_SZ; + byte altSigAlgBuf[LARGE_TEMP_SZ]; + word32 altSigAlgSz = LARGE_TEMP_SZ; + byte scratchBuf[LARGE_TEMP_SZ]; + word32 scratchSz = LARGE_TEMP_SZ; + byte preTbsBuf[LARGE_TEMP_SZ]; + word32 preTbsSz = LARGE_TEMP_SZ; + byte altSigValBuf[LARGE_TEMP_SZ]; + word32 altSigValSz = LARGE_TEMP_SZ; + byte *outBuf = NULL; + word32 outSz = LARGE_TEMP_SZ; + WC_RNG rng; + RsaKey caKey; + ecc_key altCaKey; + word32 idx = 0; + + XMEMSET(&rng, 0, sizeof(WC_RNG)); + XMEMSET(&caKey, 0, sizeof(RsaKey)); + XMEMSET(&altCaKey, 0, sizeof(ecc_key)); + + ExpectNotNull(outBuf = (byte*)XMALLOC(outSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectIntEQ(wc_InitRng(&rng), 0); + XMEMSET(caKeyBuf, 0, caKeySz); + ExpectNotNull(file = fopen(caKeyFile, "rb")); + ExpectIntGT(caKeySz = (word32)fread(caKeyBuf, 1, caKeySz, file), 0); + if (file) { + fclose(file); + file = NULL; + } + ExpectIntEQ(wc_InitRsaKey_ex(&caKey, NULL, INVALID_DEVID), 0); + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(caKeyBuf, &idx, &caKey, caKeySz), + 0); + XMEMSET(sapkiBuf, 0, sapkiSz); + ExpectNotNull(file = fopen(sapkiFile, "rb")); + ExpectIntGT(sapkiSz = (word32)fread(sapkiBuf, 1, sapkiSz, file), 0); + if (file) { + fclose(file); + file = NULL; + } + XMEMSET(altPrivBuf, 0, altPrivSz); + ExpectNotNull(file = fopen(altPrivFile, "rb")); + ExpectIntGT(altPrivSz = (word32)fread(altPrivBuf, 1, altPrivSz, file), 0); + if (file) { + fclose(file); + file = NULL; + } + wc_ecc_init(&altCaKey); + idx = 0; + ExpectIntEQ(wc_EccPrivateKeyDecode(altPrivBuf, &idx, &altCaKey, + (word32)altPrivSz), 0); + XMEMSET(altSigAlgBuf, 0, altSigAlgSz); + ExpectIntGT(altSigAlgSz = SetAlgoID(CTC_SHA256wECDSA, altSigAlgBuf, + oidSigType, 0), 0); + wc_InitCert(&newCert); + strncpy(newCert.subject.country, "US", CTC_NAME_SIZE); + strncpy(newCert.subject.state, "MT", CTC_NAME_SIZE); + strncpy(newCert.subject.locality, "Bozeman", CTC_NAME_SIZE); + strncpy(newCert.subject.org, "wolfSSL", CTC_NAME_SIZE); + strncpy(newCert.subject.unit, "Engineering", CTC_NAME_SIZE); + strncpy(newCert.subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE); + strncpy(newCert.subject.email, "root@wolfssl.com", CTC_NAME_SIZE); + strncpy((char*)newCert.beforeDate, "\x18\x0f""20250101000000Z", + CTC_DATE_SIZE); + newCert.beforeDateSz = 17; + strncpy((char*)newCert.afterDate, "\x18\x0f""20493112115959Z", + CTC_DATE_SIZE); + newCert.afterDateSz = 17; + newCert.sigType = CTC_SHA256wRSA; + newCert.isCA = 1; + + /* Set criticality of basic constraint extension if requested */ + if (setCrit) { + newCert.basicConstCrit = 1; + } + + /* Set pathlen if requested */ + if (setPathLen) { + newCert.pathLen = pathLen; + newCert.pathLenSet = 1; + } + + ExpectIntEQ(wc_SetCustomExtension(&newCert, 0, "1.2.3.4.5", + (const byte *)"This is NOT a critical extension", 32), 0); + ExpectIntEQ(wc_SetCustomExtension(&newCert, setCrit, "2.5.29.72", sapkiBuf, + sapkiSz), 0); + ExpectIntEQ(wc_SetCustomExtension(&newCert, setCrit, "2.5.29.73", + altSigAlgBuf, altSigAlgSz), 0); + + XMEMSET(scratchBuf, 0, scratchSz); + ExpectIntGT(scratchSz = wc_MakeSelfCert(&newCert, scratchBuf, scratchSz, + &caKey, &rng), 0); + + wc_InitDecodedCert(&preTBS, scratchBuf, scratchSz, 0); + ExpectIntEQ(wc_ParseCert(&preTBS, CERT_TYPE, NO_VERIFY, NULL), 0); + + XMEMSET(preTbsBuf, 0, preTbsSz); + ExpectIntGT(preTbsSz = wc_GeneratePreTBS(&preTBS, preTbsBuf, preTbsSz), 0); + XMEMSET(altSigValBuf, 0, altSigValSz); + ExpectIntGT(altSigValSz = wc_MakeSigWithBitStr(altSigValBuf, altSigValSz, + CTC_SHA256wECDSA, preTbsBuf, preTbsSz, ECC_TYPE, &altCaKey, + &rng), 0); + ExpectIntEQ(wc_SetCustomExtension(&newCert, setCrit, "2.5.29.74", + altSigValBuf, altSigValSz), 0); + + /* Finally, generate the new certificate. */ + if (outBuf != NULL) { + XMEMSET(outBuf, 0, outSz); + ExpectIntGT(outSz = wc_MakeSelfCert(&newCert, outBuf, outSz, &caKey, + &rng), 0); + *out = outBuf; + } + else { + outSz = 0; + } + + wc_FreeDecodedCert(&preTBS); + wc_ecc_free(&altCaKey); + wc_FreeRsaKey(&caKey); + wc_FreeRng(&rng); + + return (int)outSz; +} + +/** + * Function to generate a server certificate with dual algorithm support and + * configurable criticality for extensions and path length constraints. + * + * @param out [out] Pointer to store the generated certificate + * @param caKeyFile [in] Path to the CA key file + * @param sapkiFile [in] Path to the subject alternative public key info file + * @param altPrivFile [in] Path to the alternative private key file + * @param serverKeyFile [in] Path to the server key file + * @param caCertBuf [in] Buffer containing the CA certificate + * @param caCertSz [in] Size of the CA certificate buffer + * @param setCrit [in] Flag to set criticality of extensions (1=critical, 0=non-critical) + * @param setPathLen [in] Flag to set path length constraint (1=set, 0=don't set) + * @param pathLen [in] Path length value (only used if setPathLen=1) + * @return Size of the generated certificate or negative on error + */ +static int do_dual_alg_server_certgen_crit(byte **out, char *caKeyFile, + char *sapkiFile, char *altPrivFile, + char *serverKeyFile, + byte *caCertBuf, int caCertSz, + int setCrit) +{ + EXPECT_DECLS; + FILE* file = NULL; + Cert newCert; + DecodedCert preTBS; + + byte serverKeyBuf[LARGE_TEMP_SZ]; + word32 serverKeySz = LARGE_TEMP_SZ; + byte caKeyBuf[LARGE_TEMP_SZ]; + word32 caKeySz = LARGE_TEMP_SZ; + byte sapkiBuf[LARGE_TEMP_SZ]; + word32 sapkiSz = LARGE_TEMP_SZ; + byte altPrivBuf[LARGE_TEMP_SZ]; + word32 altPrivSz = LARGE_TEMP_SZ; + byte altSigAlgBuf[LARGE_TEMP_SZ]; + word32 altSigAlgSz = LARGE_TEMP_SZ; + byte scratchBuf[LARGE_TEMP_SZ]; + word32 scratchSz = LARGE_TEMP_SZ; + byte preTbsBuf[LARGE_TEMP_SZ]; + word32 preTbsSz = LARGE_TEMP_SZ; + byte altSigValBuf[LARGE_TEMP_SZ]; + word32 altSigValSz = LARGE_TEMP_SZ; + byte *outBuf = NULL; + word32 outSz = LARGE_TEMP_SZ; + WC_RNG rng; + RsaKey caKey; + RsaKey serverKey; + ecc_key altCaKey; + word32 idx = 0; + + XMEMSET(&rng, 0, sizeof(WC_RNG)); + XMEMSET(&caKey, 0, sizeof(RsaKey)); + XMEMSET(&serverKey, 0, sizeof(RsaKey)); + XMEMSET(&altCaKey, 0, sizeof(ecc_key)); + + ExpectNotNull(outBuf = (byte*)XMALLOC(outSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectIntEQ(wc_InitRng(&rng), 0); + XMEMSET(serverKeyBuf, 0, serverKeySz); + ExpectNotNull(file = fopen(serverKeyFile, "rb")); + ExpectIntGT(serverKeySz = (word32)fread(serverKeyBuf, 1, serverKeySz, file), + 0); + if (file) { + fclose(file); + file = NULL; + } + ExpectIntEQ(wc_InitRsaKey_ex(&serverKey, NULL, INVALID_DEVID), 0); + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(serverKeyBuf, &idx, &serverKey, + serverKeySz), 0); + XMEMSET(caKeyBuf, 0, caKeySz); + ExpectNotNull(file = fopen(caKeyFile, "rb")); + ExpectIntGT(caKeySz = (word32)fread(caKeyBuf, 1, caKeySz, file), 0); + if (file) { + fclose(file); + file = NULL; + } + ExpectIntEQ(wc_InitRsaKey_ex(&caKey, NULL, INVALID_DEVID), 0); + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(caKeyBuf, &idx, &caKey, caKeySz), 0); + XMEMSET(sapkiBuf, 0, sapkiSz); + ExpectNotNull(file = fopen(sapkiFile, "rb")); + ExpectIntGT(sapkiSz = (word32)fread(sapkiBuf, 1, sapkiSz, file), 0); + if (file) { + fclose(file); + file = NULL; + } + XMEMSET(altPrivBuf, 0, altPrivSz); + ExpectNotNull(file = fopen(altPrivFile, "rb")); + ExpectIntGT(altPrivSz = (word32)fread(altPrivBuf, 1, altPrivSz, file), 0); + if (file) { + fclose(file); + file = NULL; + } + wc_ecc_init(&altCaKey); + idx = 0; + ExpectIntEQ(wc_EccPrivateKeyDecode(altPrivBuf, &idx, &altCaKey, + (word32)altPrivSz), 0); + XMEMSET(altSigAlgBuf, 0, altSigAlgSz); + ExpectIntGT(altSigAlgSz = SetAlgoID(CTC_SHA256wECDSA, altSigAlgBuf, + oidSigType, 0), 0); + wc_InitCert(&newCert); + strncpy(newCert.subject.country, "US", CTC_NAME_SIZE); + strncpy(newCert.subject.state, "MT", CTC_NAME_SIZE); + strncpy(newCert.subject.locality, "Bozeman", CTC_NAME_SIZE); + strncpy(newCert.subject.org, "wolfSSL", CTC_NAME_SIZE); + strncpy(newCert.subject.unit, "Engineering", CTC_NAME_SIZE); + strncpy(newCert.subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE); + strncpy(newCert.subject.email, "server@wolfssl.com", CTC_NAME_SIZE); + strncpy((char*)newCert.beforeDate, "\x18\x0f""20250101000000Z", + CTC_DATE_SIZE); + newCert.beforeDateSz = 17; + strncpy((char*)newCert.afterDate, "\x18\x0f""20493112115959Z", + CTC_DATE_SIZE); + newCert.afterDateSz = 17; + + newCert.sigType = CTC_SHA256wRSA; + newCert.isCA = 0; + ExpectIntEQ(wc_SetIssuerBuffer(&newCert, caCertBuf, caCertSz), 0); + ExpectIntEQ(wc_SetCustomExtension(&newCert, setCrit, "2.5.29.72", sapkiBuf, + sapkiSz), 0); + ExpectIntEQ(wc_SetCustomExtension(&newCert, setCrit, "2.5.29.73", + altSigAlgBuf, altSigAlgSz), 0); + XMEMSET(scratchBuf, 0, scratchSz); + ExpectIntGT(wc_MakeCert(&newCert, scratchBuf, scratchSz, &serverKey, NULL, + &rng), 0); + ExpectIntGT(scratchSz = wc_SignCert(newCert.bodySz, newCert.sigType, + scratchBuf, scratchSz, &caKey, NULL, &rng), 0); + wc_InitDecodedCert(&preTBS, scratchBuf, scratchSz, 0); + ExpectIntEQ(wc_ParseCert(&preTBS, CERT_TYPE, NO_VERIFY, NULL), 0); + XMEMSET(preTbsBuf, 0, preTbsSz); + ExpectIntGT(preTbsSz = wc_GeneratePreTBS(&preTBS, preTbsBuf, preTbsSz), 0); + XMEMSET(altSigValBuf, 0, altSigValSz); + ExpectIntGT(altSigValSz = wc_MakeSigWithBitStr(altSigValBuf, altSigValSz, + CTC_SHA256wECDSA, preTbsBuf, preTbsSz, ECC_TYPE, &altCaKey, + &rng), 0); + ExpectIntEQ(wc_SetCustomExtension(&newCert, setCrit, "2.5.29.74", + altSigValBuf, altSigValSz), 0); + + /* Finally, generate the new certificate. */ + if (outBuf != NULL) { + XMEMSET(outBuf, 0, outSz); + ExpectIntGT(outSz = wc_SignCert(newCert.bodySz, newCert.sigType, scratchBuf, + outSz, &caKey, NULL, &rng), 0); + *out = outBuf; + } + else { + outSz = 0; + } + + wc_FreeDecodedCert(&preTBS); + wc_ecc_free(&altCaKey); + wc_FreeRsaKey(&serverKey); + wc_FreeRsaKey(&caKey); + wc_FreeRng(&rng); + + return (int)outSz; +} + +/** + * Test dual-alg ECDSA + ML-DSA with critical extensions and path length + * constraints: + * - keygen + certgen + * + * TLS tests not designed to pass with these extensions marked critical. No + * TLS connection. + * */ +static int test_dual_alg_crit_ext_support(void) +{ + EXPECT_DECLS; + /* Root CA and server keys will be the same. This is only appropriate for + * testing. */ + char keyFile[] = "./certs/ca-key.der"; + char sapkiFile[] = "./certs/ecc-keyPub.der"; + char altPrivFile[] = "./certs/ecc-key.der"; + byte *serverKey = NULL; + size_t serverKeySz = 0; + byte *root = NULL; + int rootSz = 0; + byte *server = NULL; + int serverSz = 0; + + ExpectIntEQ(load_file(keyFile, &serverKey, &serverKeySz), 0); + + /* Test with critical extensions and pathlen set to 1 */ + if (EXPECT_SUCCESS()) { + rootSz = do_dual_alg_root_certgen_crit(&root, keyFile, sapkiFile, + altPrivFile, 1, 1, 1); + } + ExpectNotNull(root); + ExpectIntGT(rootSz, 0); + if (EXPECT_SUCCESS()) { + serverSz = do_dual_alg_server_certgen_crit(&server, keyFile, sapkiFile, + altPrivFile, keyFile, root, rootSz, 1); + } + ExpectNotNull(server); + ExpectIntGT(serverSz, 0); + XFREE(root, NULL, DYNAMIC_TYPE_TMP_BUFFER); + root = NULL; + XFREE(server, NULL, DYNAMIC_TYPE_TMP_BUFFER); + server = NULL; + + /* Test with critical extensions and pathlen set to 0 */ + if (EXPECT_SUCCESS()) { + rootSz = do_dual_alg_root_certgen_crit(&root, keyFile, sapkiFile, + altPrivFile, 1, 1, 0); + } + ExpectNotNull(root); + ExpectIntGT(rootSz, 0); + if (EXPECT_SUCCESS()) { + serverSz = do_dual_alg_server_certgen_crit(&server, keyFile, sapkiFile, + altPrivFile, keyFile, root, rootSz, 1); + } + ExpectNotNull(server); + ExpectIntGT(serverSz, 0); + XFREE(root, NULL, DYNAMIC_TYPE_TMP_BUFFER); + root = NULL; + XFREE(server, NULL, DYNAMIC_TYPE_TMP_BUFFER); + server = NULL; + + /* Test with critical alt extensions and no pathlen set */ + if (EXPECT_SUCCESS()) { + rootSz = do_dual_alg_root_certgen_crit(&root, keyFile, sapkiFile, + altPrivFile, 1, 0, 0); + } + ExpectNotNull(root); + ExpectIntGT(rootSz, 0); + if (EXPECT_SUCCESS()) { + serverSz = do_dual_alg_server_certgen_crit(&server, keyFile, sapkiFile, + altPrivFile, keyFile, root, rootSz, 0); + } + ExpectNotNull(server); + ExpectIntGT(serverSz, 0); + XFREE(root, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(server, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + free(serverKey); + + return EXPECT_RESULT(); +} + static int test_dual_alg_support(void) { EXPECT_DECLS; @@ -1308,6 +1702,12 @@ static int test_dual_alg_support(void) { return TEST_SKIPPED; } + +static int test_dual_alg_crit_ext_support(void) +{ + return TEST_SKIPPED; +} + #endif /* WOLFSSL_DUAL_ALG_CERTS && !NO_FILESYSTEM */ /** @@ -89495,6 +89895,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_Init), TEST_DECL(test_dual_alg_support), + TEST_DECL(test_dual_alg_crit_ext_support), TEST_DECL(test_dual_alg_ecdsa_mldsa), From b608946549f9b2b6c7663213f425224f8db08ef4 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Mon, 10 Mar 2025 17:32:58 -0400 Subject: [PATCH 3/4] Guard fix. --- src/internal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal.c b/src/internal.c index dc80d01d7..ca1845d88 100644 --- a/src/internal.c +++ b/src/internal.c @@ -13476,8 +13476,8 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) x509->altNamesNext = x509->altNames; /* index hint */ x509->isCa = dCert->isCA; - x509->basicConstCrit = dCert->extBasicConstCrit; #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + x509->basicConstCrit = dCert->extBasicConstCrit; x509->pathLength = dCert->pathLength; x509->pathLengthSet = dCert->pathLengthSet; x509->keyUsage = dCert->extKeyUsage; From 496773804464c64dcb7f813917b5f6bdaef83c32 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Thu, 20 Mar 2025 17:41:14 -0400 Subject: [PATCH 4/4] Delete dupe line --- wolfcrypt/src/asn.c | 1 - 1 file changed, 1 deletion(-) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 647813449..7d06597f9 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -21320,7 +21320,6 @@ static int DecodeExtensionType(const byte* input, word32 length, word32 oid, case BASIC_CA_OID: VERIFY_AND_SET_OID(cert->extBasicConstSet); cert->extBasicConstCrit = critical ? 1 : 0; - cert->extBasicConstCrit = critical ? 1 : 0; if (DecodeBasicCaConstraint(input, (int)length, cert) < 0) { ret = ASN_PARSE_E; }