diff --git a/tests/api.c b/tests/api.c index 43774eb6a..7d9690f3e 100644 --- a/tests/api.c +++ b/tests/api.c @@ -49915,6 +49915,64 @@ static int test_MakeCertWithPathLen(void) return EXPECT_RESULT(); } +static int test_MakeCertWithCaFalse(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \ + defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) + const byte expectedIsCaSet = 1; + const byte expectedIsCa = 0; + Cert cert; + DecodedCert decodedCert; + byte der[FOURK_BUF]; + int derSize = 0; + WC_RNG rng; + ecc_key key; + int ret; + + XMEMSET(&rng, 0, sizeof(WC_RNG)); + XMEMSET(&key, 0, sizeof(ecc_key)); + XMEMSET(&cert, 0, sizeof(Cert)); + XMEMSET(&decodedCert, 0, sizeof(DecodedCert)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ecc_init(&key), 0); + ExpectIntEQ(wc_ecc_make_key(&rng, 32, &key), 0); + ExpectIntEQ(wc_InitCert(&cert), 0); + + (void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE); + (void)XSTRNCPY(cert.subject.state, "state", CTC_NAME_SIZE); + (void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE); + (void)XSTRNCPY(cert.subject.org, "yourOrgNameHere", CTC_NAME_SIZE); + (void)XSTRNCPY(cert.subject.unit, "yourUnitNameHere", CTC_NAME_SIZE); + (void)XSTRNCPY(cert.subject.commonName, "www.yourDomain.com", + CTC_NAME_SIZE); + (void)XSTRNCPY(cert.subject.email, "yourEmail@yourDomain.com", + CTC_NAME_SIZE); + + cert.selfSigned = 1; + cert.isCA = expectedIsCa; + cert.isCaSet = expectedIsCaSet; + cert.sigType = CTC_SHA256wECDSA; + + ExpectIntGE(wc_MakeCert(&cert, der, FOURK_BUF, NULL, &key, &rng), 0); + ExpectIntGE(derSize = wc_SignCert(cert.bodySz, cert.sigType, der, + FOURK_BUF, NULL, &key, &rng), 0); + + wc_InitDecodedCert(&decodedCert, der, derSize, NULL); + ExpectIntEQ(wc_ParseCert(&decodedCert, CERT_TYPE, NO_VERIFY, NULL), 0); + ExpectIntEQ(decodedCert.isCA, expectedIsCa); + + wc_FreeDecodedCert(&decodedCert); + ret = wc_ecc_free(&key); + ExpectIntEQ(ret, 0); + ret = wc_FreeRng(&rng); + ExpectIntEQ(ret, 0); +#endif + return EXPECT_RESULT(); +} + /*----------------------------------------------------------------------------* | wolfCrypt ECC *----------------------------------------------------------------------------*/ @@ -67760,6 +67818,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wc_ParseCert), TEST_DECL(test_wc_ParseCert_Error), TEST_DECL(test_MakeCertWithPathLen), + TEST_DECL(test_MakeCertWithCaFalse), TEST_DECL(test_wc_SetKeyUsage), TEST_DECL(test_wc_SetAuthKeyIdFromPublicKey_ex), TEST_DECL(test_wc_SetSubjectBuffer), diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index f0f9cefbb..eb7bd2abe 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -18548,6 +18548,12 @@ static int DecodeBasicCaConstraint(const byte* input, int sz, DecodedCert* cert) WOLFSSL_MSG("\tfail: constraint not valid BOOLEAN, set default FALSE"); ret = 0; } +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + else { + /* CA Boolean asserted, GetBoolean didn't return error. */ + cert->isCaSet = 1; + } +#endif cert->isCA = ret ? 1 : 0; @@ -18584,14 +18590,18 @@ static int DecodeBasicCaConstraint(const byte* input, int sz, DecodedCert* cert) /* Empty SEQUENCE is OK - nothing to store. */ if ((ret == 0) && (dataASN[BASICCONSASN_IDX_SEQ].length != 0)) { + #if !defined(OPENSSL_EXTRA) && !defined(OPENSSL_EXTRA_X509_SMALL) /* Bad encoding when CA Boolean is false * (default when not present). */ -#ifndef ASN_TEMPLATE_SKIP_ISCA_CHECK if ((dataASN[BASICCONSASN_IDX_CA].length != 0) && (!isCA)) { WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E); ret = ASN_PARSE_E; } -#endif + #else + if (dataASN[BASICCONSASN_IDX_CA].length != 0) { + cert->isCaSet = 1; + } + #endif /* Path length must be a 7-bit value. */ if ((ret == 0) && (cert->pathLength >= (1 << 7))) { WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E); @@ -26019,10 +26029,9 @@ static int SetCaWithPathLen(byte* out, word32 outSz, byte pathLen) return (int)sizeof(caPathLenBasicConstASN1); } - -/* encode CA basic constraints true +/* encode CA basic constraints * return total bytes written */ -static int SetCa(byte* out, word32 outSz) +static int SetCaEx(byte* out, word32 outSz, byte isCa) { /* ASN1->DER sequence for Basic Constraints True */ const byte caBasicConstASN1[] = { @@ -26038,9 +26047,20 @@ static int SetCa(byte* out, word32 outSz) XMEMCPY(out, caBasicConstASN1, sizeof(caBasicConstASN1)); + if (!isCa) { + XMEMCPY(out + (sizeof(caBasicConstASN1) - 1U), &isCa, sizeof(isCa)); + } + return (int)sizeof(caBasicConstASN1); } +/* encode CA basic constraints true + * return total bytes written */ +static int SetCa(byte* out, word32 outSz) +{ + return SetCaEx(out, outSz, 1); +} + /* encode basic constraints without CA Boolean * return total bytes written */ static int SetBC(byte* out, word32 outSz) @@ -27791,6 +27811,13 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz, dataASN[CERTEXTSASN_IDX_BC_PATHLEN].noOut = 1; } } + #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + else if (cert->isCaSet) { + SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CA], 0); + SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_BC_OID], bcOID, sizeof(bcOID)); + 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)); @@ -28439,7 +28466,17 @@ static int EncodeCert(Cert* cert, DerCert* der, RsaKey* rsaKey, ecc_key* eccKey, der->extensionsSz += der->caSz; } +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) /* Set CA */ + else if (cert->isCaSet) { + der->caSz = SetCaEx(der->ca, sizeof(der->ca), cert->isCA); + if (der->caSz <= 0) + return EXTENSIONS_E; + + der->extensionsSz += der->caSz; + } +#endif + /* Set CA true */ else if (cert->isCA) { der->caSz = SetCa(der->ca, sizeof(der->ca)); if (der->caSz <= 0) @@ -29837,7 +29874,17 @@ static int EncodeCertReq(Cert* cert, DerCert* der, RsaKey* rsaKey, der->extensionsSz += der->caSz; } +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) /* Set CA */ + else if (cert->isCaSet) { + der->caSz = SetCaEx(der->ca, sizeof(der->ca), cert->isCA); + if (der->caSz <= 0) + return EXTENSIONS_E; + + der->extensionsSz += der->caSz; + } +#endif + /* Set CA true */ else if (cert->isCA) { der->caSz = SetCa(der->ca, sizeof(der->ca)); if (der->caSz <= 0) diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 6837ffa65..b33463d5a 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1882,6 +1882,9 @@ struct DecodedCert { byte extNameConstraintSet : 1; #endif byte isCA : 1; /* CA basic constraint true */ +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + byte isCaSet : 1; /* CA basic constraint set */ +#endif byte pathLengthSet : 1; /* CA basic const path length set */ byte weOwnAltNames : 1; /* altNames haven't been given to copy */ byte extKeyUsageSet : 1; diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 7e73bc42d..d4438b428 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -530,6 +530,9 @@ typedef struct Cert { byte* der; /* Pointer to buffer of current DecodedCert cache */ void* heap; /* heap hint */ byte basicConstSet:1; /* Indicator for when Basic Constraint is set */ +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + byte isCaSet:1; /* Indicator for when isCA is set */ +#endif byte pathLenSet:1; /* Indicator for when path length is set */ #ifdef WOLFSSL_ALT_NAMES byte altNamesCrit:1; /* Indicator of criticality of SAN extension */