已关闭
fix(pki): harden X509 verification for certificate version, SAN parsing, and security level integration #1399
Ooohui创建于 5月13日关闭于 5月13日
fix(pki): harden X509 verification for certificate version, SAN parsing, and security level integration #1399
已关闭
共 33 个文件变更+1121-32
| @@ -78,6 +78,9 @@ typedef enum { | |||
| 78 | HITLS_X509_ERR_VFY_PATHLEN_EXCEEDED, | 78 | HITLS_X509_ERR_VFY_PATHLEN_EXCEEDED, |
| 79 | HITLS_X509_ERR_VFY_PURPOSE_UNMATCH, | 79 | HITLS_X509_ERR_VFY_PURPOSE_UNMATCH, |
| 80 | HITLS_X509_ERR_KU_IS_NONE, | 80 | HITLS_X509_ERR_KU_IS_NONE, |
| 81 | + HITLS_X509_ERR_VFY_EXTENSIONS_REQUIRE_V3, | ||
| 82 | + HITLS_X509_ERR_VFY_INTERCA_INVALID_VERSION, | ||
| 83 | + HITLS_X509_ERR_VFY_INTERCA_INVALID_BCONS, | ||
| 81 | 84 | ||
| 82 | HITLS_X509_ERR_CERT_NOT_CA = 0x04010001, | 85 | HITLS_X509_ERR_CERT_NOT_CA = 0x04010001, |
| 83 | HITLS_X509_ERR_CERT_EXIST, | 86 | HITLS_X509_ERR_CERT_EXIST, |
| @@ -163,6 +166,7 @@ typedef enum { | |||
| 163 | HITLS_X509_ERR_EXT_NO_BCONS, | 166 | HITLS_X509_ERR_EXT_NO_BCONS, |
| 164 | HITLS_X509_ERR_EXT_UNKNOWN_ERROR, | 167 | HITLS_X509_ERR_EXT_UNKNOWN_ERROR, |
| 165 | HITLS_X509_ERR_SAN_NOT_FOUND, | 168 | HITLS_X509_ERR_SAN_NOT_FOUND, |
| 169 | + HITLS_X509_ERR_PARSE_SAN_ITEM, | ||
| 166 | 170 | ||
| 167 | HITLS_X509_ERR_CSR_INVALID_PUBKEY = 0x04050001, | 171 | HITLS_X509_ERR_CSR_INVALID_PUBKEY = 0x04050001, |
| 168 | HITLS_X509_ERR_CSR_INVALID_SUBJECT_DN, | 172 | HITLS_X509_ERR_CSR_INVALID_SUBJECT_DN, |
| @@ -299,13 +299,17 @@ int32_t HITLS_X509_SetNameList(BslList **dest, void *val, uint32_t valLen) | |||
| 299 | return HITLS_X509_ERR_INVALID_PARAM; | 299 | return HITLS_X509_ERR_INVALID_PARAM; |
| 300 | } | 300 | } |
| 301 | BslList *src = (BslList *)val; | 301 | BslList *src = (BslList *)val; |
| 302 | + if (src == *dest) { | ||
| 303 | + return HITLS_PKI_SUCCESS; | ||
| 304 | + } | ||
| 302 | 305 | ||
| 303 | - BSL_LIST_FREE(*dest, (BSL_LIST_PFUNC_FREE)HITLS_X509_FreeNameNode); | 306 | + BslList *tmp = BSL_LIST_Copy(src, (BSL_LIST_PFUNC_DUP)DupNameNode, (BSL_LIST_PFUNC_FREE)HITLS_X509_FreeNameNode); |
| 304 | - *dest = BSL_LIST_Copy(src, (BSL_LIST_PFUNC_DUP)DupNameNode, (BSL_LIST_PFUNC_FREE)HITLS_X509_FreeNameNode); | 307 | + if (tmp == NULL) { |
| 305 | - if (*dest == NULL) { | ||
| 306 | BSL_ERR_PUSH_ERROR(HITLS_X509_ERR_SET_NAME_LIST); | 308 | BSL_ERR_PUSH_ERROR(HITLS_X509_ERR_SET_NAME_LIST); |
| 307 | return HITLS_X509_ERR_SET_NAME_LIST; | 309 | return HITLS_X509_ERR_SET_NAME_LIST; |
| 308 | } | 310 | } |
| 311 | + BSL_LIST_FREE(*dest, (BSL_LIST_PFUNC_FREE)HITLS_X509_FreeNameNode); | ||
| 312 | + *dest = tmp; | ||
| 309 | return HITLS_PKI_SUCCESS; | 313 | return HITLS_PKI_SUCCESS; |
| 310 | } | 314 | } |
| 311 | 315 | ||
| @@ -383,18 +383,19 @@ int32_t HITLS_X509_ParseGeneralNames(uint8_t *encode, uint32_t encLen, BslList * | |||
| 383 | if (ret != BSL_SUCCESS) { | 383 | if (ret != BSL_SUCCESS) { |
| 384 | break; | 384 | break; |
| 385 | } | 385 | } |
| 386 | - if (nameValueLen == 0) { | ||
| 387 | - continue; | ||
| 388 | - } | ||
| 389 | // value | 386 | // value |
| 390 | - ret = ParseGeneralName(tag, &buff, &buffLen, nameValueLen, list); | 387 | + uint8_t *nameBuff = buff; |
| 388 | + uint32_t nameBuffLen = nameValueLen; | ||
| 389 | + ret = ParseGeneralName(tag, &nameBuff, &nameBuffLen, nameValueLen, list); | ||
| 391 | if (ret != BSL_SUCCESS) { | 390 | if (ret != BSL_SUCCESS) { |
| 392 | break; | 391 | break; |
| 393 | } | 392 | } |
| 394 | - if (tag != HITLS_X509_GENERALNAME_DIR_TAG) { | 393 | + if (tag == HITLS_X509_GENERALNAME_DIR_TAG && nameBuffLen != 0) { |
| 395 | - buff += nameValueLen; | 394 | + ret = HITLS_X509_ERR_PARSE_SAN_ITEM; |
| 396 | - buffLen -= nameValueLen; | 395 | + break; |
| 397 | } | 396 | } |
| 397 | + buff += nameValueLen; | ||
| 398 | + buffLen -= nameValueLen; | ||
| 398 | } | 399 | } |
| 399 | if (ret != BSL_SUCCESS) { | 400 | if (ret != BSL_SUCCESS) { |
| 400 | HITLS_X509_ClearGeneralNames(list); | 401 | HITLS_X509_ClearGeneralNames(list); |
| @@ -1225,9 +1225,6 @@ static int32_t CheckMlKemKeyUsage(HITLS_X509_Cert *cert) | |||
| 1225 | 1225 | ||
| 1226 | static int32_t HITLS_X509_CheckCertExt(void *ctx, HITLS_X509_Cert *cert, int32_t depth) | 1226 | static int32_t HITLS_X509_CheckCertExt(void *ctx, HITLS_X509_Cert *cert, int32_t depth) |
| 1227 | { | 1227 | { |
| 1228 | - if (cert->tbs.version != 2) { // no ext v1 cert, 2 : X509 v3 | ||
| 1229 | - return HITLS_PKI_SUCCESS; | ||
| 1230 | - } | ||
| 1231 | 1228 | ||
| 1232 | HITLS_X509_StoreCtx *storeCtx = (HITLS_X509_StoreCtx *)ctx; | 1229 | HITLS_X509_StoreCtx *storeCtx = (HITLS_X509_StoreCtx *)ctx; |
| 1233 | storeCtx->curCert = cert; | 1230 | storeCtx->curCert = cert; |
| @@ -1235,6 +1232,12 @@ static int32_t HITLS_X509_CheckCertExt(void *ctx, HITLS_X509_Cert *cert, int32_t | |||
| 1235 | 1232 | ||
| 1236 | (void)depth; | 1233 | (void)depth; |
| 1237 | 1234 | ||
| 1235 | + /* RFC5280 4.1.2.1: when extensions are used, as expected in this profile, version MUST be 3 (value is 2). */ | ||
| 1236 | + if (cert->tbs.version < HITLS_X509_VERSION_3) { | ||
| 1237 | + VFYCBK_FAIL_IF(BSL_LIST_COUNT(cert->tbs.ext.extList) > 0, | ||
| 1238 | + (HITLS_X509_StoreCtx *)ctx, cert, depth, HITLS_X509_ERR_VFY_EXTENSIONS_REQUIRE_V3); | ||
| 1239 | + return HITLS_PKI_SUCCESS; | ||
| 1240 | + } | ||
| 1238 | 1241 | ||
| 1239 | int32_t pqcSigKeyUsageRet = CheckPqcSigKeyUsage(cert); | 1242 | int32_t pqcSigKeyUsageRet = CheckPqcSigKeyUsage(cert); |
| 1240 | if (pqcSigKeyUsageRet != HITLS_PKI_SUCCESS) { | 1243 | if (pqcSigKeyUsageRet != HITLS_PKI_SUCCESS) { |
| @@ -1587,39 +1590,49 @@ static int32_t X509_GetVerifyCertChain(HITLS_X509_StoreCtx *storeCtx, HITLS_X509 | |||
| 1587 | int32_t X509_CheckExt(HITLS_X509_StoreCtx *storeCtx, HITLS_X509_List *chain) | 1590 | int32_t X509_CheckExt(HITLS_X509_StoreCtx *storeCtx, HITLS_X509_List *chain) |
| 1588 | { | 1591 | { |
| 1589 | BslListNode *curNode = BSL_LIST_LastNode(chain); | 1592 | BslListNode *curNode = BSL_LIST_LastNode(chain); |
| 1590 | - int32_t depth = BSL_LIST_COUNT(chain) - 1; | 1593 | + int32_t rootDepth = BSL_LIST_COUNT(chain) - 1; |
| 1594 | + int32_t curDepth = rootDepth; | ||
| 1591 | 1595 | ||
| 1592 | while (curNode != NULL) { | 1596 | while (curNode != NULL) { |
| 1593 | HITLS_X509_Cert *cur = (HITLS_X509_Cert *)BSL_LIST_GetData(curNode); | 1597 | HITLS_X509_Cert *cur = (HITLS_X509_Cert *)BSL_LIST_GetData(curNode); |
| 1594 | HITLS_X509_CertExt *curExt = (HITLS_X509_CertExt *)cur->tbs.ext.extData; | 1598 | HITLS_X509_CertExt *curExt = (HITLS_X509_CertExt *)cur->tbs.ext.extData; |
| 1595 | 1599 | ||
| 1596 | - if (depth > 0) { | 1600 | + if (curDepth > 0) { // CA certificates (root and intermediate) |
| 1597 | - /** | 1601 | + /** RFC 5280 Section 6.1.4: |
| 1598 | - * If the basic constraints extension is not present in a version 3 certificate, | 1602 | + * (k) If certificate i is a version 3 certificate, verify that the basicConstraints extension is |
| 1599 | - * or the extension is present but the cA boolean is not asserted, | 1603 | + * present and that cA is set to TRUE. (If certificate i is a version 1 or version 2 certificate, |
| 1600 | - * then the certified public key MUST NOT be used to verify certificate signatures. | 1604 | + * then the application MUST either verify that certificate i is a CA certificate through |
| 1605 | + * out-of-band means or reject the certificate. Conforming implementations may choose to reject | ||
| 1606 | + * all version 1 and version 2 intermediate certificates.) | ||
| 1601 | */ | 1607 | */ |
| 1602 | - VFYCBK_FAIL_IF(((cur->tbs.version == HITLS_X509_VERSION_3) && | 1608 | + if (curDepth == rootDepth) { |
| 1603 | - ((curExt->extFlags & HITLS_X509_EXT_FLAG_BCONS) == 0 || !curExt->isCa)), | 1609 | + VFYCBK_FAIL_IF(((cur->tbs.version == HITLS_X509_VERSION_3) && |
| 1604 | - storeCtx, cur, depth, HITLS_X509_ERR_VFY_INVALID_CA); | 1610 | + ((curExt->extFlags & HITLS_X509_EXT_FLAG_BCONS) == 0 || !curExt->isCa)), |
| 1611 | + storeCtx, cur, curDepth, HITLS_X509_ERR_VFY_INVALID_CA); | ||
| 1612 | + } else { | ||
| 1613 | + VFYCBK_FAIL_IF(cur->tbs.version != HITLS_X509_VERSION_3, | ||
| 1614 | + storeCtx, cur, curDepth, HITLS_X509_ERR_VFY_INTERCA_INVALID_VERSION); | ||
| 1615 | + VFYCBK_FAIL_IF((curExt->extFlags & HITLS_X509_EXT_FLAG_BCONS) == 0 || !curExt->isCa, | ||
| 1616 | + storeCtx, cur, curDepth, HITLS_X509_ERR_VFY_INTERCA_INVALID_BCONS); | ||
| 1617 | + } | ||
| 1605 | /** | 1618 | /** |
| 1606 | * Conforming CAs MUST include this extension in certificates that contain public keys | 1619 | * Conforming CAs MUST include this extension in certificates that contain public keys |
| 1607 | * that are used to validate digital signatures on other public key certificates or CRLs. | 1620 | * that are used to validate digital signatures on other public key certificates or CRLs. |
| 1608 | */ | 1621 | */ |
| 1609 | VFYCBK_FAIL_IF(((curExt->extFlags & HITLS_X509_EXT_FLAG_KUSAGE) != 0) && | 1622 | VFYCBK_FAIL_IF(((curExt->extFlags & HITLS_X509_EXT_FLAG_KUSAGE) != 0) && |
| 1610 | ((curExt->keyUsage & HITLS_X509_EXT_KU_KEY_CERT_SIGN) == 0), | 1623 | ((curExt->keyUsage & HITLS_X509_EXT_KU_KEY_CERT_SIGN) == 0), |
| 1611 | - storeCtx, cur, depth, HITLS_X509_ERR_VFY_KU_NO_CERTSIGN); | 1624 | + storeCtx, cur, curDepth, HITLS_X509_ERR_VFY_KU_NO_CERTSIGN); |
| 1612 | 1625 | ||
| 1613 | if ((curExt->extFlags & HITLS_X509_EXT_FLAG_BCONS) != 0 && curExt->isCa && curExt->maxPathLen >= 0) { | 1626 | if ((curExt->extFlags & HITLS_X509_EXT_FLAG_BCONS) != 0 && curExt->isCa && curExt->maxPathLen >= 0) { |
| 1614 | - VFYCBK_FAIL_IF(curExt->maxPathLen < depth - 1, storeCtx, cur, depth, | 1627 | + VFYCBK_FAIL_IF(curExt->maxPathLen < curDepth - 1, storeCtx, cur, curDepth, |
| 1615 | HITLS_X509_ERR_VFY_PATHLEN_EXCEEDED); | 1628 | HITLS_X509_ERR_VFY_PATHLEN_EXCEEDED); |
| 1616 | } | 1629 | } |
| 1617 | } else { | 1630 | } else { |
| 1618 | int32_t ret = X509_VerifyUsageEE(storeCtx, cur); | 1631 | int32_t ret = X509_VerifyUsageEE(storeCtx, cur); |
| 1619 | - VFYCBK_FAIL_IF(ret != HITLS_PKI_SUCCESS, storeCtx, cur, depth, ret); | 1632 | + VFYCBK_FAIL_IF(ret != HITLS_PKI_SUCCESS, storeCtx, cur, curDepth, ret); |
| 1620 | } | 1633 | } |
| 1621 | curNode = BSL_LIST_GetPrevNode(curNode); | 1634 | curNode = BSL_LIST_GetPrevNode(curNode); |
| 1622 | - depth--; | 1635 | + curDepth--; |
| 1623 | } | 1636 | } |
| 1624 | return HITLS_PKI_SUCCESS; | 1637 | return HITLS_PKI_SUCCESS; |
| 1625 | } | 1638 | } |
| @@ -1591,6 +1591,42 @@ EXIT: | |||
| 1591 | } | 1591 | } |
| 1592 | /* END_CASE */ | 1592 | /* END_CASE */ |
| 1593 | 1593 | ||
| 1594 | +/* BEGIN_CASE */ | ||
| 1595 | +void SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC002(int isCritical, char *certPath, char *nameValue, int nameType) | ||
| 1596 | +{ | ||
| 1597 | + HITLS_X509_Cert *parsedCert = NULL; | ||
| 1598 | + HITLS_X509_ExtSan parsedSan = {0}; | ||
| 1599 | + HITLS_X509_GeneralName *gn = NULL; | ||
| 1600 | + BslList *dnList = GenDNList(); | ||
| 1601 | + uint32_t nameValueLen = (uint32_t)strlen(nameValue); | ||
| 1602 | + | ||
| 1603 | + ASSERT_EQ(HITLS_X509_CertParseFile(BSL_FORMAT_ASN1, certPath, &parsedCert), HITLS_PKI_SUCCESS); | ||
| 1604 | + | ||
| 1605 | + ASSERT_EQ(HITLS_X509_CertCtrl(parsedCert, HITLS_X509_EXT_GET_SAN, &parsedSan, sizeof(HITLS_X509_ExtSan)), 0); | ||
| 1606 | + ASSERT_EQ(parsedSan.critical, isCritical); | ||
| 1607 | + if (nameType == -1) { | ||
| 1608 | + ASSERT_EQ(BSL_LIST_COUNT(parsedSan.names), 0); | ||
| 1609 | + ASSERT_EQ(BSL_LIST_GET_FIRST(parsedSan.names), NULL); | ||
| 1610 | + } else { | ||
| 1611 | + ASSERT_TRUE(BSL_LIST_COUNT(parsedSan.names) >= 1); | ||
| 1612 | + gn = BSL_LIST_GET_FIRST(parsedSan.names); | ||
| 1613 | + ASSERT_EQ(gn->type, nameType); | ||
| 1614 | + ASSERT_EQ(gn->value.dataLen, nameValueLen); | ||
| 1615 | + if (nameValueLen > 0) { | ||
| 1616 | + ASSERT_COMPARE("subject Alternative Name", gn->value.data, gn->value.dataLen, | ||
| 1617 | + nameValue, nameValueLen); | ||
| 1618 | + } | ||
| 1619 | + } | ||
| 1620 | + ASSERT_TRUE(TestIsErrStackEmpty()); | ||
| 1621 | + | ||
| 1622 | +EXIT: | ||
| 1623 | + TestRandDeInit(); | ||
| 1624 | + HITLS_X509_CertFree(parsedCert); | ||
| 1625 | + HITLS_X509_DnListFree(dnList); | ||
| 1626 | + HITLS_X509_ClearSubjectAltName(&parsedSan); | ||
| 1627 | +} | ||
| 1628 | +/* END_CASE */ | ||
| 1629 | + | ||
| 1594 | /* BEGIN_CASE */ | 1630 | /* BEGIN_CASE */ |
| 1595 | void SDV_X509_CERT_WITH_SAN_ALL_PARSE_TC001(int isCritical, char *certPath, int isEdited) | 1631 | void SDV_X509_CERT_WITH_SAN_ALL_PARSE_TC001(int isCritical, char *certPath, int isEdited) |
| 1596 | { | 1632 | { |
| @@ -1663,6 +1699,21 @@ EXIT: | |||
| 1663 | } | 1699 | } |
| 1664 | /* END_CASE */ | 1700 | /* END_CASE */ |
| 1665 | 1701 | ||
| 1702 | +/* BEGIN_CASE */ | ||
| 1703 | +void SDV_X509_CERT_WITH_ILLEGAL_SAN_DIRNAME_0_LEN_PARSE_TEST_TC001(char *certPath) | ||
| 1704 | +{ | ||
| 1705 | + HITLS_X509_Cert *parsedCert = NULL; | ||
| 1706 | + HITLS_X509_ExtSan parsedSan = {0}; | ||
| 1707 | + ASSERT_EQ(HITLS_X509_CertParseFile(BSL_FORMAT_ASN1, certPath, &parsedCert), HITLS_PKI_SUCCESS); | ||
| 1708 | + ASSERT_NE(HITLS_X509_CertCtrl(parsedCert, HITLS_X509_EXT_GET_SAN, | ||
| 1709 | + &parsedSan, sizeof(HITLS_X509_ExtSan)), HITLS_PKI_SUCCESS); | ||
| 1710 | + | ||
| 1711 | +EXIT: | ||
| 1712 | + HITLS_X509_CertFree(parsedCert); | ||
| 1713 | + HITLS_X509_ClearSubjectAltName(&parsedSan); | ||
| 1714 | +} | ||
| 1715 | +/* END_CASE */ | ||
| 1716 | + | ||
| 1666 | /* BEGIN_CASE */ | 1717 | /* BEGIN_CASE */ |
| 1667 | void SDV_X509_CERT_WITH_BCON_GEN_TEST_TC001(int isCritical, int isCa, int maxPathLen, | 1718 | void SDV_X509_CERT_WITH_BCON_GEN_TEST_TC001(int isCritical, int isCa, int maxPathLen, |
| 1668 | int algId, int hashId, int curveId) | 1719 | int algId, int hashId, int curveId) |
| @@ -265,12 +265,21 @@ SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC001:0:"../testdata/cert/asn1/certcheck/cert_ | |||
| 265 | SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC001:uri | 265 | SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC001:uri |
| 266 | SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC001:0:"../testdata/cert/asn1/certcheck/cert_ext_san_parse_7.der":1:"https://www.openhitls.com/api":HITLS_X509_GN_URI | 266 | SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC001:0:"../testdata/cert/asn1/certcheck/cert_ext_san_parse_7.der":1:"https://www.openhitls.com/api":HITLS_X509_GN_URI |
| 267 | 267 | ||
| 268 | +SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC002:san element 0 length | ||
| 269 | +SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC002:0:"../testdata/cert/asn1/certcheck/cert_ext_san_element_0_length.der":"":HITLS_X509_GN_DNS | ||
| 270 | + | ||
| 271 | +SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC002:san no element | ||
| 272 | +SDV_X509_CERT_WITH_SAN_PARSE_TEST_TC002:0:"../testdata/cert/asn1/certcheck/cert_ext_san_no_element.der":"":-1 | ||
| 273 | + | ||
| 268 | SDV_X509_CERT_WITH_SAN_ALL_PARSE_TC001:all | 274 | SDV_X509_CERT_WITH_SAN_ALL_PARSE_TC001:all |
| 269 | SDV_X509_CERT_WITH_SAN_ALL_PARSE_TC001:0:"../testdata/cert/asn1/certcheck/cert_ext_san_parse_8.der":1 | 275 | SDV_X509_CERT_WITH_SAN_ALL_PARSE_TC001:0:"../testdata/cert/asn1/certcheck/cert_ext_san_parse_8.der":1 |
| 270 | 276 | ||
| 271 | SDV_X509_CERT_WITH_ILLEGAL_SAN_PARSE_TEST_TC001:generalName is empty | 277 | SDV_X509_CERT_WITH_ILLEGAL_SAN_PARSE_TEST_TC001:generalName is empty |
| 272 | SDV_X509_CERT_WITH_ILLEGAL_SAN_PARSE_TEST_TC001:0:"../testdata/cert/asn1/certcheck/cert_ext_san_parse_9.der":1 | 278 | SDV_X509_CERT_WITH_ILLEGAL_SAN_PARSE_TEST_TC001:0:"../testdata/cert/asn1/certcheck/cert_ext_san_parse_9.der":1 |
| 273 | 279 | ||
| 280 | +SDV_X509_CERT_WITH_ILLEGAL_SAN_DIRNAME_0_LEN_PARSE_TEST_TC001:directoryName with zero length | ||
| 281 | +SDV_X509_CERT_WITH_ILLEGAL_SAN_DIRNAME_0_LEN_PARSE_TEST_TC001:"../testdata/cert/asn1/certcheck/cert_ext_san_dirname_0_length.der" | ||
| 282 | + | ||
| 274 | SDV_X509_CERT_WITH_BCON_GEN_TEST_TC001:critical, Ca, maxPathLen = 1 | 283 | SDV_X509_CERT_WITH_BCON_GEN_TEST_TC001:critical, Ca, maxPathLen = 1 |
| 275 | SDV_X509_CERT_WITH_BCON_GEN_TEST_TC001:1:1:1:CRYPT_PKEY_RSA:CRYPT_MD_SHA256:0 | 284 | SDV_X509_CERT_WITH_BCON_GEN_TEST_TC001:1:1:1:CRYPT_PKEY_RSA:CRYPT_MD_SHA256:0 |
| 276 | 285 | ||
| @@ -2695,6 +2695,60 @@ EXIT: | |||
| 2695 | } | 2695 | } |
| 2696 | /* END_CASE */ | 2696 | /* END_CASE */ |
| 2697 | 2697 | ||
| 2698 | +/** | ||
| 2699 | + * @test SDV_PKI_VERIFY_HOSTNAME_SAN_ZERO_LEN_TC001 | ||
| 2700 | + * @title Hostname verification with zero-length SAN DNS entry | ||
| 2701 | + */ | ||
| 2702 | +/* BEGIN_CASE */ | ||
| 2703 | +void SDV_PKI_VERIFY_HOSTNAME_SAN_ZERO_LEN_TC001(char *certPath) | ||
| 2704 | +{ | ||
| 2705 | + | ||
| 2706 | + TestMemInit(); | ||
| 2707 | + HITLS_X509_Cert *cert = NULL; | ||
| 2708 | + HITLS_X509_ExtSan san = {0}; | ||
| 2709 | + bool hasZeroLenDns = false; | ||
| 2710 | + | ||
| 2711 | + // Certificate parses successfully (zero-length entry not rejected). | ||
| 2712 | + ASSERT_EQ(HITLS_X509_CertParseFile(BSL_FORMAT_ASN1, certPath, &cert), HITLS_PKI_SUCCESS); | ||
| 2713 | + ASSERT_NE(cert, NULL); | ||
| 2714 | + | ||
| 2715 | + // SAN list contains 4 entries including a zero-length DNS entry. | ||
| 2716 | + ASSERT_EQ(HITLS_X509_CertCtrl(cert, HITLS_X509_EXT_GET_SAN, &san, sizeof(HITLS_X509_ExtSan)), HITLS_PKI_SUCCESS); | ||
| 2717 | + ASSERT_EQ(BSL_LIST_COUNT(san.names), 4); // | ||
| 2718 | + for (BslListNode *node = BSL_LIST_FirstNode(san.names); node != NULL; | ||
| 2719 | + node = BSL_LIST_GetNextNode(san.names, node)) { | ||
| 2720 | + HITLS_X509_GeneralName *gn = (HITLS_X509_GeneralName *)BSL_LIST_GetData(node); | ||
| 2721 | + if (gn != NULL && gn->type == HITLS_X509_GN_DNS && gn->value.dataLen == 0) { | ||
| 2722 | + hasZeroLenDns = true; | ||
| 2723 | + } | ||
| 2724 | + } | ||
| 2725 | + ASSERT_TRUE(hasZeroLenDns); | ||
| 2726 | + | ||
| 2727 | + // "test.example.com" matches via "*.example.com". | ||
| 2728 | + ASSERT_EQ(HITLS_X509_VerifyHostname(cert, 0, "test.example.com", strlen("test.example.com")), HITLS_PKI_SUCCESS); | ||
| 2729 | + ASSERT_EQ(HITLS_X509_VerifyHostname(cert, HITLS_X509_FLAG_VFY_WITH_PARTIAL_WILDCARD, | ||
| 2730 | + "test.example.com", strlen("test.example.com")), HITLS_PKI_SUCCESS); | ||
| 2731 | + | ||
| 2732 | + // "example.com" does NOT match (CN fallback not triggered because SAN has DNS). | ||
| 2733 | + ASSERT_EQ(HITLS_X509_VerifyHostname(cert, 0, "example.com", strlen("example.com")), | ||
| 2734 | + HITLS_X509_ERR_VFY_HOSTNAME_FAIL); | ||
| 2735 | + TestErrClear(); | ||
| 2736 | + ASSERT_EQ(HITLS_X509_VerifyHostname(cert, HITLS_X509_FLAG_VFY_WITH_PARTIAL_WILDCARD, | ||
| 2737 | + "example.com", strlen("example.com")), HITLS_X509_ERR_VFY_HOSTNAME_FAIL); | ||
| 2738 | + TestErrClear(); | ||
| 2739 | + | ||
| 2740 | + ASSERT_TRUE(TestIsErrStackEmpty()); | ||
| 2741 | + | ||
| 2742 | +EXIT: | ||
| 2743 | + HITLS_X509_ClearSubjectAltName(&san); | ||
| 2744 | + HITLS_X509_CertFree(cert); | ||
| 2745 | + | ||
| 2746 | + (void)certPath; | ||
| 2747 | + SKIP_TEST(); | ||
| 2748 | + | ||
| 2749 | +} | ||
| 2750 | +/* END_CASE */ | ||
| 2751 | + | ||
| 2698 | /** | 2752 | /** |
| 2699 | * @test SDV_X509_PARSE_NAME_LIST_UTF8_CACHE_TC001 | 2753 | * @test SDV_X509_PARSE_NAME_LIST_UTF8_CACHE_TC001 |
| 2700 | * @title Parsed name UTF-8 canonical cache test | 2754 | * @title Parsed name UTF-8 canonical cache test |
| @@ -422,6 +422,12 @@ SDV_X509_EXT_ParseGeneralNames_Error_TC001:"8208746573742E636F":BSL_ASN1_ERR_DEC | |||
| 422 | SDV_X509_EXT_ParseGeneralNames_Error_TC001: Unkown name tag | 422 | SDV_X509_EXT_ParseGeneralNames_Error_TC001: Unkown name tag |
| 423 | SDV_X509_EXT_ParseGeneralNames_Error_TC001:"8008746573742E636F6D":HITLS_X509_ERR_PARSE_SAN_ITEM_UNKNOW | 423 | SDV_X509_EXT_ParseGeneralNames_Error_TC001:"8008746573742E636F6D":HITLS_X509_ERR_PARSE_SAN_ITEM_UNKNOW |
| 424 | 424 | ||
| 425 | +SDV_X509_EXT_ParseGeneralNames_Error_TC001: directoryName with zero length | ||
| 426 | +SDV_X509_EXT_ParseGeneralNames_Error_TC001:"A400":BSL_INVALID_ARG | ||
| 427 | + | ||
| 428 | +SDV_X509_EXT_ParseGeneralNames_Error_TC001: directoryName declared length larger than actual content | ||
| 429 | +SDV_X509_EXT_ParseGeneralNames_Error_TC001:"A44F304B310B300906035504061302554B31183016060355040A0C0F4D79204F7267616E697A6174696F6E3110300E060355040B0C074D7920556E69743110300E06035504030C074D79204E616D650000":HITLS_X509_ERR_PARSE_SAN_ITEM | ||
| 430 | + | ||
| 425 | SDV_X509_EXT_ParseSki_TC001: success | 431 | SDV_X509_EXT_ParseSki_TC001: success |
| 426 | SDV_X509_EXT_ParseSki_TC001:"0414BFCB55F18843C3EF58D4D145E373762B7D151DB0":HITLS_PKI_SUCCESS:"BFCB55F18843C3EF58D4D145E373762B7D151DB0" | 432 | SDV_X509_EXT_ParseSki_TC001:"0414BFCB55F18843C3EF58D4D145E373762B7D151DB0":HITLS_PKI_SUCCESS:"BFCB55F18843C3EF58D4D145E373762B7D151DB0" |
| 427 | 433 | ||
| @@ -860,6 +866,9 @@ SDV_PKI_VERIFY_HOSTNAME_WITH_CN_TC001:CRYPT_PKEY_ECDSA:BSL_FORMAT_ASN1:"3082059A | |||
| 860 | SDV_PKI_VERIFY_HOSTNAME_WITH_CN_TC001: cert has no Subject Alternative Name, but has cn, flag = partial Wildcard | 866 | SDV_PKI_VERIFY_HOSTNAME_WITH_CN_TC001: cert has no Subject Alternative Name, but has cn, flag = partial Wildcard |
| 861 | SDV_PKI_VERIFY_HOSTNAME_WITH_CN_TC001:CRYPT_PKEY_ECDSA:BSL_FORMAT_ASN1:"3082059A30820482A00302010202100DC95EA1BD220000000072D70C6C8595300D06092A864886F70D01010B0500303D310B300906035504061302434831123010060355040A1309546573742043657274311A301806035504031311777777206F70656E6869746C7320636F6D301E170D3235303632363030303030305A170D3235303932343233353935395A306A310B3009060355040613024348310E300C060355040813056368696E61311430120603550407130B68656C6C6F20776F726C64311B3019060355040A1312666F722074657374206F70656E6869746C733118301606035504030C0F2A2E6F70656E6869746C732E636F6D3059301306072A8648CE3D020106082A8648CE3D030107034200042923EEA24672F8ECE66EA99597FB7EBFF8516985E2FAAE05F78B22550216889A9940E8819D61E24DC512F3C63621701B2374744D630E47851EE82B8732956DD7A38203323082032E301F0603551D23041830168014748580C066C7DF370000000000AA031DBEEDCD17301D0603551D0E041604148ECE54DB00000000002AFE6763D64199828A1BF5304A0603551D2004433041303F060667810C0102023035303306082B06010505070201162768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73300E0603551D0F0101FF040403020388301D0603551D250416301406082B0601050507030106082B0601050507030230670603551D1F0460305E302DA02BA029862768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73302DA02BA029862768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73307806082B06010505070101046C306A303306082B06010505073001862768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73303306082B06010505073002862768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73300C0603551D130101FF040230003082017E060A2B06010401D6790204020482016E0482016A0168007500DDDC003495D7E11605E79532FAC79FF83D1C00DFDB003A1412760A2CACBBC82A00000097A99BDE320000040300463044022000521DDF121D0D505B968854FB65FFD3006772D618DF11E0F194549F33D40DC60020034A74A3D91E98E407C40BFCBDF4004EC9FFFDC4E839F5E733D5DF50CC37004C007600ED3C4BD6E806C2A4A2005700CB24E23801DF512FEDC486C5700F2000B73E3FE000000197A99BDE3B000004000047304502204EA4AEA62FDF1031D700A115354FD9F0CDB836EC7D83CCC5CC00CF88CB1D605A022100F82B894E77EC002B73B4607F124C7CDAC08D8F0DD7AA00E5F87CF272B80565C1007700A442C500496061548F0FD4EA9CFB7A2D26454D00A97F2FDF4559F6274F3A845400000100A99BDE4300000403004830460221000024DD43FCBB213F700F98E49244D096003F5A44F3DA7271B39972DD6CC2BF09002100B621932342ACF1FCC043CAA7910012BAA80BA3DC59EC9FE24CA5FADFFD008C0B300D06092A864886F70D01010B05000382010100ADF2434900149CFE8C0025A116D1597E0A9BA1A900A4BA8A2700C23432644DE74619EBD000B70EDBFA00B7FD7C570CFBF9BB1BA300ACE36F7800C1B28AA0BB445CF3C38E006F71D364005EA731956E966A73082C00F401D9BD003AC15D40913DD6637B2E0015E0AA4700A73C386AAE5CD9FE77D8009075D4DF00488FE57B8E71D6701096003BD5BA370054CFCD624912B003B25300B160A38000B46F3940AAF1F9494DD700FDC354A100DBA05FF2C29A6A142160007549187800F68AE9D324643B4C587000EE33FC690038591992A5B9B7FF9221006F0BF97D007EE94717C8C63000607800BD5F96F000B5B12435688FB5105DF900799481FE006047E6CF71A1":0 | 867 | SDV_PKI_VERIFY_HOSTNAME_WITH_CN_TC001:CRYPT_PKEY_ECDSA:BSL_FORMAT_ASN1:"3082059A30820482A00302010202100DC95EA1BD220000000072D70C6C8595300D06092A864886F70D01010B0500303D310B300906035504061302434831123010060355040A1309546573742043657274311A301806035504031311777777206F70656E6869746C7320636F6D301E170D3235303632363030303030305A170D3235303932343233353935395A306A310B3009060355040613024348310E300C060355040813056368696E61311430120603550407130B68656C6C6F20776F726C64311B3019060355040A1312666F722074657374206F70656E6869746C733118301606035504030C0F2A2E6F70656E6869746C732E636F6D3059301306072A8648CE3D020106082A8648CE3D030107034200042923EEA24672F8ECE66EA99597FB7EBFF8516985E2FAAE05F78B22550216889A9940E8819D61E24DC512F3C63621701B2374744D630E47851EE82B8732956DD7A38203323082032E301F0603551D23041830168014748580C066C7DF370000000000AA031DBEEDCD17301D0603551D0E041604148ECE54DB00000000002AFE6763D64199828A1BF5304A0603551D2004433041303F060667810C0102023035303306082B06010505070201162768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73300E0603551D0F0101FF040403020388301D0603551D250416301406082B0601050507030106082B0601050507030230670603551D1F0460305E302DA02BA029862768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73302DA02BA029862768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73307806082B06010505070101046C306A303306082B06010505073001862768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73303306082B06010505073002862768747470733A2F2F676974636F64652E636F6D2F6F70656E4869544C532F6F70656E6869746C73300C0603551D130101FF040230003082017E060A2B06010401D6790204020482016E0482016A0168007500DDDC003495D7E11605E79532FAC79FF83D1C00DFDB003A1412760A2CACBBC82A00000097A99BDE320000040300463044022000521DDF121D0D505B968854FB65FFD3006772D618DF11E0F194549F33D40DC60020034A74A3D91E98E407C40BFCBDF4004EC9FFFDC4E839F5E733D5DF50CC37004C007600ED3C4BD6E806C2A4A2005700CB24E23801DF512FEDC486C5700F2000B73E3FE000000197A99BDE3B000004000047304502204EA4AEA62FDF1031D700A115354FD9F0CDB836EC7D83CCC5CC00CF88CB1D605A022100F82B894E77EC002B73B4607F124C7CDAC08D8F0DD7AA00E5F87CF272B80565C1007700A442C500496061548F0FD4EA9CFB7A2D26454D00A97F2FDF4559F6274F3A845400000100A99BDE4300000403004830460221000024DD43FCBB213F700F98E49244D096003F5A44F3DA7271B39972DD6CC2BF09002100B621932342ACF1FCC043CAA7910012BAA80BA3DC59EC9FE24CA5FADFFD008C0B300D06092A864886F70D01010B05000382010100ADF2434900149CFE8C0025A116D1597E0A9BA1A900A4BA8A2700C23432644DE74619EBD000B70EDBFA00B7FD7C570CFBF9BB1BA300ACE36F7800C1B28AA0BB445CF3C38E006F71D364005EA731956E966A73082C00F401D9BD003AC15D40913DD6637B2E0015E0AA4700A73C386AAE5CD9FE77D8009075D4DF00488FE57B8E71D6701096003BD5BA370054CFCD624912B003B25300B160A38000B46F3940AAF1F9494DD700FDC354A100DBA05FF2C29A6A142160007549187800F68AE9D324643B4C587000EE33FC690038591992A5B9B7FF9221006F0BF97D007EE94717C8C63000607800BD5F96F000B5B12435688FB5105DF900799481FE006047E6CF71A1":0 |
| 862 | 868 | ||
| 869 | +SDV_PKI_VERIFY_HOSTNAME_SAN_ZERO_LEN_TC001: hostname verification with zero-length SAN DNS entry | ||
| 870 | +SDV_PKI_VERIFY_HOSTNAME_SAN_ZERO_LEN_TC001:"../testdata/cert/asn1/certcheck/cert_ext_san_element_0_length.der" | ||
| 871 | + | ||
| 863 | SDV_X509_CRL_PARSE_NAME_LIST_TC001 HITLS_X509_ParseNameNode encounters the error BSL_ASN1_ERR_DECODE_LEN at the start | 872 | SDV_X509_CRL_PARSE_NAME_LIST_TC001 HITLS_X509_ParseNameNode encounters the error BSL_ASN1_ERR_DECODE_LEN at the start |
| 864 | SDV_X509_CRL_PARSE_NAME_LIST_TC001:"3100310031023000" | 873 | SDV_X509_CRL_PARSE_NAME_LIST_TC001:"3100310031023000" |
| 865 | 874 | ||
| @@ -1683,10 +1683,10 @@ void SDV_X509_VERIFY_CERT_CHAIN_FUNC_TC001(void) | |||
| 1683 | ASSERT_TRUE(TestIsErrStackEmpty()); | 1683 | ASSERT_TRUE(TestIsErrStackEmpty()); |
| 1684 | HITLS_X509_CertExt *certExt = (HITLS_X509_CertExt *)ca->tbs.ext.extData; | 1684 | HITLS_X509_CertExt *certExt = (HITLS_X509_CertExt *)ca->tbs.ext.extData; |
| 1685 | certExt->extFlags &= ~HITLS_X509_EXT_FLAG_BCONS; | 1685 | certExt->extFlags &= ~HITLS_X509_EXT_FLAG_BCONS; |
| 1686 | - ASSERT_EQ(HITLS_X509_CertVerify(store, chain), HITLS_X509_ERR_VFY_INVALID_CA); | 1686 | + ASSERT_EQ(HITLS_X509_CertVerify(store, chain), HITLS_X509_ERR_VFY_INTERCA_INVALID_BCONS); |
| 1687 | certExt->extFlags |= HITLS_X509_EXT_FLAG_BCONS; | 1687 | certExt->extFlags |= HITLS_X509_EXT_FLAG_BCONS; |
| 1688 | certExt->isCa = false; | 1688 | certExt->isCa = false; |
| 1689 | - ASSERT_EQ(HITLS_X509_CertVerify(store, chain), HITLS_X509_ERR_VFY_INVALID_CA); | 1689 | + ASSERT_EQ(HITLS_X509_CertVerify(store, chain), HITLS_X509_ERR_VFY_INTERCA_INVALID_BCONS); |
| 1690 | certExt->isCa = true; | 1690 | certExt->isCa = true; |
| 1691 | certExt->extFlags |= HITLS_X509_EXT_FLAG_KUSAGE; | 1691 | certExt->extFlags |= HITLS_X509_EXT_FLAG_KUSAGE; |
| 1692 | certExt->keyUsage &= ~HITLS_X509_EXT_KU_KEY_CERT_SIGN; | 1692 | certExt->keyUsage &= ~HITLS_X509_EXT_KU_KEY_CERT_SIGN; |
| @@ -3598,7 +3598,7 @@ void SDV_X509_VFY_BC_MISSING_FAIL_TC001(void) | |||
| 3598 | ASSERT_TRUE(TestIsErrStackEmpty()); | 3598 | ASSERT_TRUE(TestIsErrStackEmpty()); |
| 3599 | 3599 | ||
| 3600 | int32_t ret = HITLS_X509_CertVerify(store, chain); | 3600 | int32_t ret = HITLS_X509_CertVerify(store, chain); |
| 3601 | - ASSERT_EQ(ret, HITLS_X509_ERR_VFY_INVALID_CA); | 3601 | + ASSERT_EQ(ret, HITLS_X509_ERR_VFY_INTERCA_INVALID_BCONS); |
| 3602 | 3602 | ||
| 3603 | EXIT: | 3603 | EXIT: |
| 3604 | HITLS_X509_StoreCtxFree(store); | 3604 | HITLS_X509_StoreCtxFree(store); |
| @@ -3644,7 +3644,7 @@ void SDV_X509_VFY_BC_CA_FALSE_FAIL_TC002(void) | |||
| 3644 | ASSERT_TRUE(TestIsErrStackEmpty()); | 3644 | ASSERT_TRUE(TestIsErrStackEmpty()); |
| 3645 | 3645 | ||
| 3646 | int32_t ret = HITLS_X509_CertVerify(store, chain); | 3646 | int32_t ret = HITLS_X509_CertVerify(store, chain); |
| 3647 | - ASSERT_EQ(ret, HITLS_X509_ERR_VFY_INVALID_CA); | 3647 | + ASSERT_EQ(ret, HITLS_X509_ERR_VFY_INTERCA_INVALID_BCONS); |
| 3648 | 3648 | ||
| 3649 | EXIT: | 3649 | EXIT: |
| 3650 | HITLS_X509_StoreCtxFree(store); | 3650 | HITLS_X509_StoreCtxFree(store); |
| @@ -5277,3 +5277,124 @@ EXIT: | |||
| 5277 | BSL_LIST_FREE(chain, (BSL_LIST_PFUNC_FREE)HITLS_X509_CertFree); | 5277 | BSL_LIST_FREE(chain, (BSL_LIST_PFUNC_FREE)HITLS_X509_CertFree); |
| 5278 | } | 5278 | } |
| 5279 | /* END_CASE */ | 5279 | /* END_CASE */ |
| 5280 | + | ||
| 5281 | +/** | ||
| 5282 | + * @test SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001 | ||
| 5283 | + * @title Reject v1/v2 certificates carrying extensions during verification. | ||
| 5284 | + * @brief Non-v3 certificates that carry X.509 extensions must be rejected in the verification phase | ||
| 5285 | + * @expect HITLS_X509_ERR_VFY_EXTENSIONS_REQUIRE_V3 for non-v3-with-extensions certs. | ||
| 5286 | + */ | ||
| 5287 | +/* BEGIN_CASE */ | ||
| 5288 | +void SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001(char *leafPath, char *interPath, char *rootPath, int exp) | ||
| 5289 | +{ | ||
| 5290 | + | ||
| 5291 | + TestMemInit(); | ||
| 5292 | + HITLS_X509_Cert *root = NULL; | ||
| 5293 | + HITLS_X509_Cert *inter = NULL; | ||
| 5294 | + HITLS_X509_Cert *leaf = NULL; | ||
| 5295 | + int32_t (*testCallback)(int32_t, HITLS_X509_StoreCtx*) = X509StoreCtrlCbkSuc; | ||
| 5296 | + uint64_t flag = HITLS_X509_VFY_FLAG_DISABLE_TIME_CHECK; | ||
| 5297 | + | ||
| 5298 | + HITLS_X509_StoreCtx *storeCtx = HITLS_X509_StoreCtxNew(); | ||
| 5299 | + HITLS_X509_List *chain = BSL_LIST_New(sizeof(HITLS_X509_Cert *)); | ||
| 5300 | + ASSERT_TRUE(storeCtx != NULL && chain != NULL); | ||
| 5301 | + | ||
| 5302 | + ASSERT_EQ(HITLS_X509_CertParseFile(BSL_FORMAT_ASN1, leafPath, &leaf), HITLS_PKI_SUCCESS); | ||
| 5303 | + ASSERT_EQ(HITLS_X509_CertParseFile(BSL_FORMAT_ASN1, rootPath, &root), HITLS_PKI_SUCCESS); | ||
| 5304 | + | ||
| 5305 | + ASSERT_EQ(X509_AddCertToChainTest(chain, leaf), HITLS_PKI_SUCCESS); | ||
| 5306 | + if (strlen(interPath) > 0) { | ||
| 5307 | + ASSERT_EQ(HITLS_X509_CertParseFile(BSL_FORMAT_ASN1, interPath, &inter), HITLS_PKI_SUCCESS); | ||
| 5308 | + ASSERT_EQ(X509_AddCertToChainTest(chain, inter), HITLS_PKI_SUCCESS); | ||
| 5309 | + } | ||
| 5310 | + ASSERT_EQ(X509_AddCertToChainTest(chain, root), HITLS_PKI_SUCCESS); | ||
| 5311 | + | ||
| 5312 | + ASSERT_EQ(HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_DEEP_COPY_SET_CA, root, sizeof(HITLS_X509_Cert)), 0); | ||
| 5313 | + ASSERT_EQ(HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_SET_PARAM_FLAGS, &flag, sizeof(flag)), 0); | ||
| 5314 | + | ||
| 5315 | + ASSERT_EQ(HITLS_X509_CertVerify(storeCtx, chain), exp); | ||
| 5316 | + if (exp != HITLS_PKI_SUCCESS) { | ||
| 5317 | + ASSERT_EQ(HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_SET_VERIFY_CB, | ||
| 5318 | + testCallback, sizeof(testCallback)), HITLS_PKI_SUCCESS); | ||
| 5319 | + ASSERT_EQ(HITLS_X509_CertVerify(storeCtx, chain), HITLS_PKI_SUCCESS); | ||
| 5320 | + } | ||
| 5321 | +EXIT: | ||
| 5322 | + HITLS_X509_StoreCtxFree(storeCtx); | ||
| 5323 | + if (chain != NULL) { | ||
| 5324 | + BSL_LIST_FREE(chain, (BSL_LIST_PFUNC_FREE)HITLS_X509_CertFree); | ||
| 5325 | + } | ||
| 5326 | + HITLS_X509_CertFree(leaf); | ||
| 5327 | + HITLS_X509_CertFree(inter); | ||
| 5328 | + HITLS_X509_CertFree(root); | ||
| 5329 | + | ||
| 5330 | + (void)leafPath; | ||
| 5331 | + (void)interPath; | ||
| 5332 | + (void)rootPath; | ||
| 5333 | + (void)exp; | ||
| 5334 | + SKIP_TEST(); | ||
| 5335 | + | ||
| 5336 | +} | ||
| 5337 | +/* END_CASE */ | ||
| 5338 | + | ||
| 5339 | +/** | ||
| 5340 | + * @test SDV_X509_VFY_V1_INTER_CA_TC001 | ||
| 5341 | + * @title v1/v2 non-trust-anchor intermediate CAs must be rejected; v1 trust anchors must be accepted. | ||
| 5342 | + * @brief | ||
| 5343 | + * TC1: v1 intermediate (no extensions) in chain → HITLS_X509_ERR_VFY_INVALID_CA | ||
| 5344 | + * TC2: v2 intermediate (no extensions) in chain → HITLS_X509_ERR_VFY_INVALID_CA | ||
| 5345 | + * TC3: v1 self-signed root (no extensions) as trust anchor → HITLS_PKI_SUCCESS | ||
| 5346 | + * @expect | ||
| 5347 | + * TC1/TC2: HITLS_X509_ERR_VFY_INVALID_CA | ||
| 5348 | + * TC3: HITLS_PKI_SUCCESS | ||
| 5349 | + */ | ||
| 5350 | +/* BEGIN_CASE */ | ||
| 5351 | +void SDV_X509_VFY_V1_INTER_CA_TC001(char *leafPath, char *interPath, char *rootPath, int exp) | ||
| 5352 | +{ | ||
| 5353 | + | ||
| 5354 | + TestMemInit(); | ||
| 5355 | + HITLS_X509_Cert *root = NULL; | ||
| 5356 | + HITLS_X509_Cert *inter = NULL; | ||
| 5357 | + HITLS_X509_Cert *leaf = NULL; | ||
| 5358 | + int32_t (*testCallback)(int32_t, HITLS_X509_StoreCtx*) = X509StoreCtrlCbkSuc; | ||
| 5359 | + uint64_t flag = HITLS_X509_VFY_FLAG_DISABLE_TIME_CHECK; | ||
| 5360 | + | ||
| 5361 | + HITLS_X509_StoreCtx *storeCtx = HITLS_X509_StoreCtxNew(); | ||
| 5362 | + HITLS_X509_List *chain = BSL_LIST_New(sizeof(HITLS_X509_Cert *)); | ||
| 5363 | + ASSERT_TRUE(storeCtx != NULL && chain != NULL); | ||
| 5364 | + | ||
| 5365 | + ASSERT_EQ(HITLS_X509_CertParseFile(BSL_FORMAT_UNKNOWN, leafPath, &leaf), HITLS_PKI_SUCCESS); | ||
| 5366 | + ASSERT_EQ(HITLS_X509_CertParseFile(BSL_FORMAT_UNKNOWN, rootPath, &root), HITLS_PKI_SUCCESS); | ||
| 5367 | + | ||
| 5368 | + ASSERT_EQ(X509_AddCertToChainTest(chain, leaf), HITLS_PKI_SUCCESS); | ||
| 5369 | + if (strlen(interPath) > 0) { | ||
| 5370 | + ASSERT_EQ(HITLS_X509_CertParseFile(BSL_FORMAT_UNKNOWN, interPath, &inter), HITLS_PKI_SUCCESS); | ||
| 5371 | + ASSERT_EQ(X509_AddCertToChainTest(chain, inter), HITLS_PKI_SUCCESS); | ||
| 5372 | + } | ||
| 5373 | + ASSERT_EQ(X509_AddCertToChainTest(chain, root), HITLS_PKI_SUCCESS); | ||
| 5374 | + | ||
| 5375 | + ASSERT_EQ(HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_DEEP_COPY_SET_CA, root, sizeof(HITLS_X509_Cert)), 0); | ||
| 5376 | + ASSERT_EQ(HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_SET_PARAM_FLAGS, &flag, sizeof(flag)), 0); | ||
| 5377 | + | ||
| 5378 | + ASSERT_EQ(HITLS_X509_CertVerify(storeCtx, chain), exp); | ||
| 5379 | + if (exp != HITLS_PKI_SUCCESS) { | ||
| 5380 | + ASSERT_EQ(HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_SET_VERIFY_CB, | ||
| 5381 | + testCallback, sizeof(testCallback)), HITLS_PKI_SUCCESS); | ||
| 5382 | + ASSERT_EQ(HITLS_X509_CertVerify(storeCtx, chain), HITLS_PKI_SUCCESS); | ||
| 5383 | + } | ||
| 5384 | +EXIT: | ||
| 5385 | + HITLS_X509_StoreCtxFree(storeCtx); | ||
| 5386 | + if (chain != NULL) { | ||
| 5387 | + BSL_LIST_FREE(chain, (BSL_LIST_PFUNC_FREE)HITLS_X509_CertFree); | ||
| 5388 | + } | ||
| 5389 | + HITLS_X509_CertFree(leaf); | ||
| 5390 | + HITLS_X509_CertFree(inter); | ||
| 5391 | + HITLS_X509_CertFree(root); | ||
| 5392 | + | ||
| 5393 | + (void)leafPath; | ||
| 5394 | + (void)interPath; | ||
| 5395 | + (void)rootPath; | ||
| 5396 | + (void)exp; | ||
| 5397 | + SKIP_TEST(); | ||
| 5398 | + | ||
| 5399 | +} | ||
| 5400 | +/* END_CASE */ | ||
| @@ -478,3 +478,33 @@ SDV_X509_SECBITS_COMBINED_TC001:"../testdata/cert/chain/secbits/h_mldsa44_leaf.d | |||
| 478 | 478 | ||
| 479 | SDV_X509_SECBITS_COMBINED_TC001: H_256: secbits=256, Root:256>=256 PASS, Inter:192<256 FAIL, Leaf:128<256 FAIL | 479 | SDV_X509_SECBITS_COMBINED_TC001: H_256: secbits=256, Root:256>=256 PASS, Inter:192<256 FAIL, Leaf:128<256 FAIL |
| 480 | SDV_X509_SECBITS_COMBINED_TC001:"../testdata/cert/chain/secbits/h_mldsa44_leaf.der":"../testdata/cert/chain/secbits/h_mldsa65_inter.der":"../testdata/cert/chain/secbits/h_mldsa87_root.der":256:HITLS_X509_ERR_VFY_CHECK_SECBITS | 480 | SDV_X509_SECBITS_COMBINED_TC001:"../testdata/cert/chain/secbits/h_mldsa44_leaf.der":"../testdata/cert/chain/secbits/h_mldsa65_inter.der":"../testdata/cert/chain/secbits/h_mldsa87_root.der":256:HITLS_X509_ERR_VFY_CHECK_SECBITS |
| 481 | + | ||
| 482 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001: TC1 v1 leaf with extensions | ||
| 483 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001:"../testdata/cert/chain/v3_exts/a_v1_ext_leaf.der":"":"../testdata/cert/chain/v3_exts/a_v3_root.der":HITLS_X509_ERR_VFY_EXTENSIONS_REQUIRE_V3 | ||
| 484 | + | ||
| 485 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001: TC2 v2 leaf with extensions | ||
| 486 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001:"../testdata/cert/chain/v3_exts/a_v2_ext_leaf.der":"":"../testdata/cert/chain/v3_exts/a_v3_root.der":HITLS_X509_ERR_VFY_EXTENSIONS_REQUIRE_V3 | ||
| 487 | + | ||
| 488 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001: TC3 v1 intermediate with extensions | ||
| 489 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001:"../testdata/cert/chain/v3_exts/a_v3_leaf.der":"../testdata/cert/chain/v3_exts/a_v1_ext_inter.der":"../testdata/cert/chain/v3_exts/a_v3_root.der":HITLS_X509_ERR_VFY_EXTENSIONS_REQUIRE_V3 | ||
| 490 | + | ||
| 491 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001: TC4 v2 intermediate with extensions | ||
| 492 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001:"../testdata/cert/chain/v3_exts/a_v3_leaf.der":"../testdata/cert/chain/v3_exts/a_v2_ext_inter.der":"../testdata/cert/chain/v3_exts/a_v3_root.der":HITLS_X509_ERR_VFY_EXTENSIONS_REQUIRE_V3 | ||
| 493 | + | ||
| 494 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001: TC5 v1 root with extensions as trust anchor | ||
| 495 | +SDV_X509_VFY_EXTENSIONS_REQUIRE_V3_TC001:"../testdata/cert/chain/v3_exts/b_v3_leaf.der":"":"../testdata/cert/chain/v3_exts/b_v1_ext_root.der":HITLS_X509_ERR_VFY_EXTENSIONS_REQUIRE_V3 | ||
| 496 | + | ||
| 497 | +SDV_X509_VFY_V1_INTER_CA_TC001: TC1 v1 intermediate without extensions rejected | ||
| 498 | +SDV_X509_VFY_V1_INTER_CA_TC001:"../testdata/cert/chain/v1_inter/v3_leaf.der":"../testdata/cert/chain/v1_inter/v1_inter.der":"../testdata/cert/chain/v1_inter/v3_root.der":HITLS_X509_ERR_VFY_INTERCA_INVALID_VERSION | ||
| 499 | + | ||
| 500 | +SDV_X509_VFY_V1_INTER_CA_TC001: TC2 v2 intermediate without extensions rejected | ||
| 501 | +SDV_X509_VFY_V1_INTER_CA_TC001:"../testdata/cert/chain/v1_inter/v3_leaf.der":"../testdata/cert/chain/v1_inter/v2_inter.der":"../testdata/cert/chain/v1_inter/v3_root.der":HITLS_X509_ERR_VFY_INTERCA_INVALID_VERSION | ||
| 502 | + | ||
| 503 | +SDV_X509_VFY_V1_INTER_CA_TC001: TC3 v1 trust anchor without extensions accepted | ||
| 504 | +SDV_X509_VFY_V1_INTER_CA_TC001:"../testdata/cert/chain/v1_inter/v3_leaf_b.der":"":"../testdata/cert/chain/v1_inter/v1_root.der":HITLS_PKI_SUCCESS | ||
| 505 | + | ||
| 506 | +SDV_X509_VFY_V1_INTER_CA_TC001: TC4 v3 intermediate missing BasicConstraints | ||
| 507 | +SDV_X509_VFY_V1_INTER_CA_TC001:"../testdata/cert/chain/bcExt/bc_leaf_missing_bc.pem":"../testdata/cert/chain/bcExt/bc_inter_missing_bc.pem":"../testdata/cert/chain/bcExt/bc_root_general.pem":HITLS_X509_ERR_VFY_INTERCA_INVALID_BCONS | ||
| 508 | + | ||
| 509 | +SDV_X509_VFY_V1_INTER_CA_TC001: TC5 v3 intermediate BasicConstraints CA=false | ||
| 510 | +SDV_X509_VFY_V1_INTER_CA_TC001:"../testdata/cert/chain/bcExt/bc_leaf_ca_false.pem":"../testdata/cert/chain/bcExt/bc_inter_ca_false.pem":"../testdata/cert/chain/bcExt/bc_root_general.pem":HITLS_X509_ERR_VFY_INTERCA_INVALID_BCONS | ||
Mtestcode/sdv/testcase/tls/consistency/tls13/test_suite_sdv_frame_tls13_consistency_rfc8446_cert.c+124-1
| @@ -2432,4 +2432,127 @@ EXIT: | |||
| 2432 | FRAME_FreeLink(client); | 2432 | FRAME_FreeLink(client); |
| 2433 | FRAME_FreeLink(server); | 2433 | FRAME_FreeLink(server); |
| 2434 | } | 2434 | } |
| 2435 | -/* END_CASE */ | 2435 | +/* END_CASE */ |
| 2436 | + | ||
| 2437 | +/** | ||
| 2438 | + * Verify that SetAuthLevel propagates the TLS security level to the X509 verification layer secbits check, | ||
| 2439 | + * and SHA-1 signatures (63 bits < 80 bits) are correctly rejected | ||
| 2440 | + */ | ||
| 2441 | +/* BEGIN_CASE */ | ||
| 2442 | +void SDV_TLS13_SECBITS_AUTH_LEVEL_SHA1_FAIL_TC001(void) | ||
| 2443 | +{ | ||
| 2444 | + FRAME_Init(); | ||
| 2445 | + HITLS_Config *config = HITLS_CFG_NewTLS13Config(); | ||
| 2446 | + ASSERT_TRUE(config != NULL); | ||
| 2447 | + FRAME_CertInfo certInfo = { | ||
| 2448 | + "ecdsa_sha1/ca-nist521.der", | ||
| 2449 | + "ecdsa_sha1/inter-nist521.der", | ||
| 2450 | + "ecdsa_sha1/end384-sha1.der", | ||
| 2451 | + 0, | ||
| 2452 | + "ecdsa_sha1/end384-sha1.key.der", | ||
| 2453 | + 0, | ||
| 2454 | + }; | ||
| 2455 | + FRAME_LinkObj *client = FRAME_CreateLinkWithCert(config, BSL_UIO_TCP, &certInfo); | ||
| 2456 | + ASSERT_TRUE(client != NULL); | ||
| 2457 | + FRAME_LinkObj *server = FRAME_CreateLinkWithCert(config, BSL_UIO_TCP, &certInfo); | ||
| 2458 | + ASSERT_TRUE(server != NULL); | ||
| 2459 | + HITLS_SetSecurityLevel(client->ssl, 1); | ||
| 2460 | + ASSERT_EQ(FRAME_CreateConnection(client, server, true, HS_STATE_BUTT), | ||
| 2461 | + HITLS_CERT_ERR_VERIFY_CERT_CHAIN); | ||
| 2462 | +EXIT: | ||
| 2463 | + HITLS_CFG_FreeConfig(config); | ||
| 2464 | + FRAME_FreeLink(client); | ||
| 2465 | + FRAME_FreeLink(server); | ||
| 2466 | +} | ||
| 2467 | +/* END_CASE */ | ||
| 2468 | + | ||
| 2469 | +/** | ||
| 2470 | + * When verifying security level 0, the secbits check is not triggered, and weak signature certificates can pass. | ||
| 2471 | + */ | ||
| 2472 | +/* BEGIN_CASE */ | ||
| 2473 | +void SDV_TLS13_SECBITS_AUTH_LEVEL_SHA1_LEVEL0_PASS_TC002(void) | ||
| 2474 | +{ | ||
| 2475 | + FRAME_Init(); | ||
| 2476 | + HITLS_Config *config = HITLS_CFG_NewTLS13Config(); | ||
| 2477 | + ASSERT_TRUE(config != NULL); | ||
| 2478 | + FRAME_CertInfo certInfo = { | ||
| 2479 | + "ecdsa_sha1/ca-nist521.der", | ||
| 2480 | + "ecdsa_sha1/inter-nist521.der", | ||
| 2481 | + "ecdsa_sha1/end384-sha1.der", | ||
| 2482 | + 0, | ||
| 2483 | + "ecdsa_sha1/end384-sha1.key.der", | ||
| 2484 | + 0, | ||
| 2485 | + }; | ||
| 2486 | + FRAME_LinkObj *client = FRAME_CreateLinkWithCert(config, BSL_UIO_TCP, &certInfo); | ||
| 2487 | + ASSERT_TRUE(client != NULL); | ||
| 2488 | + FRAME_LinkObj *server = FRAME_CreateLinkWithCert(config, BSL_UIO_TCP, &certInfo); | ||
| 2489 | + ASSERT_TRUE(server != NULL); | ||
| 2490 | + ASSERT_EQ(FRAME_CreateConnection(client, server, true, HS_STATE_BUTT), HITLS_SUCCESS); | ||
| 2491 | +EXIT: | ||
| 2492 | + HITLS_CFG_FreeConfig(config); | ||
| 2493 | + FRAME_FreeLink(client); | ||
| 2494 | + FRAME_FreeLink(server); | ||
| 2495 | +} | ||
| 2496 | +/* END_CASE */ | ||
| 2497 | + | ||
| 2498 | +/** | ||
| 2499 | + * Verification of normally strong certificate chains passes verification at security level 2. | ||
| 2500 | + */ | ||
| 2501 | +/* BEGIN_CASE */ | ||
| 2502 | +void SDV_TLS13_SECBITS_AUTH_LEVEL_RSA_LEVEL2_PASS_TC003(void) | ||
| 2503 | +{ | ||
| 2504 | + FRAME_Init(); | ||
| 2505 | + HITLS_Config *config = HITLS_CFG_NewTLS13Config(); | ||
| 2506 | + ASSERT_TRUE(config != NULL); | ||
| 2507 | + FRAME_CertInfo certInfo = { | ||
| 2508 | + "rsa_sha/ca-3072.der", | ||
| 2509 | + "rsa_sha/inter-3072.der", | ||
| 2510 | + "rsa_sha/end-sha256.der", | ||
| 2511 | + 0, | ||
| 2512 | + "rsa_sha/end-sha256.key.der", | ||
| 2513 | + 0, | ||
| 2514 | + }; | ||
| 2515 | + FRAME_LinkObj *client = FRAME_CreateLinkWithCert(config, BSL_UIO_TCP, &certInfo); | ||
| 2516 | + ASSERT_TRUE(client != NULL); | ||
| 2517 | + FRAME_LinkObj *server = FRAME_CreateLinkWithCert(config, BSL_UIO_TCP, &certInfo); | ||
| 2518 | + ASSERT_TRUE(server != NULL); | ||
| 2519 | + HITLS_SetSecurityLevel(client->ssl, 2); | ||
| 2520 | + HITLS_SetSecurityLevel(server->ssl, 2); | ||
| 2521 | + ASSERT_EQ(FRAME_CreateConnection(client, server, true, HS_STATE_BUTT), HITLS_SUCCESS); | ||
| 2522 | +EXIT: | ||
| 2523 | + HITLS_CFG_FreeConfig(config); | ||
| 2524 | + FRAME_FreeLink(client); | ||
| 2525 | + FRAME_FreeLink(server); | ||
| 2526 | +} | ||
| 2527 | +/* END_CASE */ | ||
| 2528 | + | ||
| 2529 | +/** | ||
| 2530 | + * Certificates with insufficient key strength are intercepted by the TLS layer key check when verifying at security level 3. | ||
| 2531 | + */ | ||
| 2532 | +/* BEGIN_CASE */ | ||
| 2533 | +void SDV_TLS13_SECBITS_AUTH_LEVEL_WEAK_KEY_FAIL_TC004(void) | ||
| 2534 | +{ | ||
| 2535 | + FRAME_Init(); | ||
| 2536 | + HITLS_Config *config = HITLS_CFG_NewTLS13Config(); | ||
| 2537 | + ASSERT_TRUE(config != NULL); | ||
| 2538 | + FRAME_CertInfo certInfo = { | ||
| 2539 | + "rsa_sha/ca-3072.der", | ||
| 2540 | + "rsa_sha/inter-3072.der", | ||
| 2541 | + "rsa_sha/end-sha256.der", | ||
| 2542 | + 0, | ||
| 2543 | + "rsa_sha/end-sha256.key.der", | ||
| 2544 | + 0, | ||
| 2545 | + }; | ||
| 2546 | + FRAME_LinkObj *client = FRAME_CreateLinkWithCert(config, BSL_UIO_TCP, &certInfo); | ||
| 2547 | + ASSERT_TRUE(client != NULL); | ||
| 2548 | + FRAME_LinkObj *server = FRAME_CreateLinkWithCert(config, BSL_UIO_TCP, &certInfo); | ||
| 2549 | + ASSERT_TRUE(server != NULL); | ||
| 2550 | + HITLS_SetSecurityLevel(client->ssl, 3); | ||
| 2551 | + ASSERT_EQ(FRAME_CreateConnection(client, server, true, HS_STATE_BUTT), | ||
| 2552 | + HITLS_CERT_ERR_EE_KEY_WITH_INSECURE_SECBITS); | ||
| 2553 | +EXIT: | ||
| 2554 | + HITLS_CFG_FreeConfig(config); | ||
| 2555 | + FRAME_FreeLink(client); | ||
| 2556 | + FRAME_FreeLink(server); | ||
| 2557 | +} | ||
| 2558 | +/* END_CASE */ | ||
Mtestcode/sdv/testcase/tls/consistency/tls13/test_suite_sdv_frame_tls13_consistency_rfc8446_cert.data+13-1
| @@ -132,4 +132,16 @@ UT_TLS_TLS13_RFC8446_SIGALG_SELECT_TC001 ec prefer | |||
| 132 | UT_TLS_TLS13_RFC8446_SIGALG_SELECT_TC001:1 | 132 | UT_TLS_TLS13_RFC8446_SIGALG_SELECT_TC001:1 |
| 133 | 133 | ||
| 134 | UT_TLS_TLS13_RFC8446_SIGALG_SELECT_TC001 rsa prefer | 134 | UT_TLS_TLS13_RFC8446_SIGALG_SELECT_TC001 rsa prefer |
| 135 | -UT_TLS_TLS13_RFC8446_SIGALG_SELECT_TC001:0 | 135 | +UT_TLS_TLS13_RFC8446_SIGALG_SELECT_TC001:0 |
| 136 | + | ||
| 137 | +SDV_TLS13_SECBITS_AUTH_LEVEL_SHA1_FAIL_TC001 | ||
| 138 | +SDV_TLS13_SECBITS_AUTH_LEVEL_SHA1_FAIL_TC001: | ||
| 139 | + | ||
| 140 | +SDV_TLS13_SECBITS_AUTH_LEVEL_SHA1_LEVEL0_PASS_TC002 | ||
| 141 | +SDV_TLS13_SECBITS_AUTH_LEVEL_SHA1_LEVEL0_PASS_TC002: | ||
| 142 | + | ||
| 143 | +SDV_TLS13_SECBITS_AUTH_LEVEL_RSA_LEVEL2_PASS_TC003 | ||
| 144 | +SDV_TLS13_SECBITS_AUTH_LEVEL_RSA_LEVEL2_PASS_TC003: | ||
| 145 | + | ||
| 146 | +SDV_TLS13_SECBITS_AUTH_LEVEL_WEAK_KEY_FAIL_TC004 | ||
| 147 | +SDV_TLS13_SECBITS_AUTH_LEVEL_WEAK_KEY_FAIL_TC004: | ||
Binary files do not support preview
Binary files do not support preview
Binary files do not support preview
| @@ -0,0 +1,301 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +# -*- coding: utf-8 -*- | ||
| 3 | +# This file is part of the openHiTLS project. | ||
| 4 | +# | ||
| 5 | +# openHiTLS is licensed under the Mulan PSL v2. | ||
| 6 | +# You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 7 | +# You may obtain a copy of Mulan PSL v2 at: | ||
| 8 | +# | ||
| 9 | +# http://license.coscl.org.cn/MulanPSL2 | ||
| 10 | +# | ||
| 11 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 12 | +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 13 | +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 14 | +# See the Mulan PSL v2 for more details. | ||
| 15 | +""" | ||
| 16 | +Generate test certificates for v1/v2 intermediate CA rejection tests (Problem 2 fix). | ||
| 17 | + | ||
| 18 | +Tests that v1/v2 non-trust-anchor intermediate CAs are rejected, while v1 trust anchors are accepted. | ||
| 19 | + | ||
| 20 | +Strategy for v1/v2 certs WITHOUT extensions: | ||
| 21 | + 1. Build a TBS without any extensions using the cryptography library | ||
| 22 | + 2. Sign to get a v3 cert (the library always outputs v3) | ||
| 23 | + 3. Patch the version byte in the TBS DER from 0x02 (v3) to 0x00 (v1) or 0x01 (v2) | ||
| 24 | + 4. Re-sign the patched TBS with the issuer's private key | ||
| 25 | + 5. Assemble the final Certificate SEQUENCE | ||
| 26 | + | ||
| 27 | +Output files: | ||
| 28 | + v3_root.der - v3 root CA (trust anchor, with BC:CA=TRUE, KU:keyCertSign) | ||
| 29 | + v1_inter.der - v1 intermediate WITHOUT extensions, signed by root (TC1) | ||
| 30 | + v2_inter.der - v2 intermediate WITHOUT extensions, signed by root (TC2) | ||
| 31 | + v3_leaf.der - v3 leaf cert, signed by intermediate's key (for TC1/TC2 chain) | ||
| 32 | + v1_root.der - v1 self-signed root WITHOUT extensions (TC3 trust anchor) | ||
| 33 | + v3_leaf_b.der - v3 leaf cert, signed by v1 root's key (for TC3 chain) | ||
| 34 | +""" | ||
| 35 | + | ||
| 36 | +import os | ||
| 37 | +import datetime | ||
| 38 | +from cryptography import x509 | ||
| 39 | +from cryptography.x509.oid import NameOID | ||
| 40 | +from cryptography.hazmat.primitives import hashes, serialization | ||
| 41 | +from cryptography.hazmat.primitives.asymmetric import rsa, padding | ||
| 42 | + | ||
| 43 | + | ||
| 44 | +def gen_key(): | ||
| 45 | + return rsa.generate_private_key(public_exponent=65537, key_size=2048) | ||
| 46 | + | ||
| 47 | + | ||
| 48 | +def make_root_cn(): | ||
| 49 | + return x509.Name([ | ||
| 50 | + x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"), | ||
| 51 | + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "V1InterTest"), | ||
| 52 | + x509.NameAttribute(NameOID.COMMON_NAME, "V1InterTest Root CA"), | ||
| 53 | + ]) | ||
| 54 | + | ||
| 55 | + | ||
| 56 | +def make_inter_cn(): | ||
| 57 | + return x509.Name([ | ||
| 58 | + x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"), | ||
| 59 | + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "V1InterTest"), | ||
| 60 | + x509.NameAttribute(NameOID.COMMON_NAME, "V1InterTest Intermediate"), | ||
| 61 | + ]) | ||
| 62 | + | ||
| 63 | + | ||
| 64 | +def make_leaf_cn(): | ||
| 65 | + return x509.Name([ | ||
| 66 | + x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"), | ||
| 67 | + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "V1InterTest"), | ||
| 68 | + x509.NameAttribute(NameOID.COMMON_NAME, "V1InterTest Leaf"), | ||
| 69 | + ]) | ||
| 70 | + | ||
| 71 | + | ||
| 72 | +def make_root_b_cn(): | ||
| 73 | + return x509.Name([ | ||
| 74 | + x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"), | ||
| 75 | + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "V1InterTest"), | ||
| 76 | + x509.NameAttribute(NameOID.COMMON_NAME, "V1InterTest SelfRoot"), | ||
| 77 | + ]) | ||
| 78 | + | ||
| 79 | + | ||
| 80 | +def build_ca_tbs(subject, issuer, pub_key, path_length): | ||
| 81 | + return ( | ||
| 82 | + x509.CertificateBuilder() | ||
| 83 | + .subject_name(subject) | ||
| 84 | + .issuer_name(issuer) | ||
| 85 | + .public_key(pub_key) | ||
| 86 | + .serial_number(x509.random_serial_number()) | ||
| 87 | + .not_valid_before(datetime.datetime(2024, 1, 1)) | ||
| 88 | + .not_valid_after(datetime.datetime(2050, 12, 31)) | ||
| 89 | + .add_extension( | ||
| 90 | + x509.BasicConstraints(ca=True, path_length=path_length), | ||
| 91 | + critical=True, | ||
| 92 | + ) | ||
| 93 | + .add_extension( | ||
| 94 | + x509.KeyUsage( | ||
| 95 | + digital_signature=True, key_cert_sign=True, crl_sign=True, | ||
| 96 | + content_commitment=False, key_encipherment=False, | ||
| 97 | + data_encipherment=False, key_agreement=False, | ||
| 98 | + encipher_only=False, decipher_only=False, | ||
| 99 | + ), | ||
| 100 | + critical=True, | ||
| 101 | + ) | ||
| 102 | + ) | ||
| 103 | + | ||
| 104 | + | ||
| 105 | +def build_leaf_tbs(subject, issuer, pub_key): | ||
| 106 | + return ( | ||
| 107 | + x509.CertificateBuilder() | ||
| 108 | + .subject_name(subject) | ||
| 109 | + .issuer_name(issuer) | ||
| 110 | + .public_key(pub_key) | ||
| 111 | + .serial_number(x509.random_serial_number()) | ||
| 112 | + .not_valid_before(datetime.datetime(2024, 1, 1)) | ||
| 113 | + .not_valid_after(datetime.datetime(2050, 12, 31)) | ||
| 114 | + .add_extension( | ||
| 115 | + x509.BasicConstraints(ca=False, path_length=None), | ||
| 116 | + critical=True, | ||
| 117 | + ) | ||
| 118 | + .add_extension( | ||
| 119 | + x509.KeyUsage( | ||
| 120 | + digital_signature=True, key_encipherment=True, | ||
| 121 | + content_commitment=False, key_agreement=False, | ||
| 122 | + key_cert_sign=False, crl_sign=False, | ||
| 123 | + data_encipherment=False, encipher_only=False, decipher_only=False, | ||
| 124 | + ), | ||
| 125 | + critical=True, | ||
| 126 | + ) | ||
| 127 | + ) | ||
| 128 | + | ||
| 129 | + | ||
| 130 | +def build_no_ext_tbs(subject, issuer, pub_key): | ||
| 131 | + """Build a TBS without any extensions. Used for v1/v2 certs.""" | ||
| 132 | + return ( | ||
| 133 | + x509.CertificateBuilder() | ||
| 134 | + .subject_name(subject) | ||
| 135 | + .issuer_name(issuer) | ||
| 136 | + .public_key(pub_key) | ||
| 137 | + .serial_number(x509.random_serial_number()) | ||
| 138 | + .not_valid_before(datetime.datetime(2024, 1, 1)) | ||
| 139 | + .not_valid_after(datetime.datetime(2050, 12, 31)) | ||
| 140 | + ) | ||
| 141 | + | ||
| 142 | + | ||
| 143 | +def to_der(cert): | ||
| 144 | + return cert.public_bytes(serialization.Encoding.DER) | ||
| 145 | + | ||
| 146 | + | ||
| 147 | +def patch_version_in_der(der_bytes, new_version): | ||
| 148 | + der = bytearray(der_bytes) | ||
| 149 | + pattern = bytes([0xA0, 0x03, 0x02, 0x01]) | ||
| 150 | + idx = der.find(pattern) | ||
| 151 | + if idx < 0: | ||
| 152 | + raise ValueError("version field pattern not found in DER") | ||
| 153 | + der[idx + 4] = new_version | ||
| 154 | + return bytes(der) | ||
| 155 | + | ||
| 156 | + | ||
| 157 | +def extract_tbs_der(cert_der): | ||
| 158 | + if cert_der[0] != 0x30: | ||
| 159 | + raise ValueError("expected SEQUENCE tag") | ||
| 160 | + idx = 1 | ||
| 161 | + if cert_der[1] & 0x80: | ||
| 162 | + idx += 1 + (cert_der[1] & 0x7F) | ||
| 163 | + else: | ||
| 164 | + idx += 1 | ||
| 165 | + tbs_start = idx | ||
| 166 | + if cert_der[idx] != 0x30: | ||
| 167 | + raise ValueError("expected TBS SEQUENCE tag") | ||
| 168 | + idx += 1 | ||
| 169 | + if cert_der[idx] & 0x80: | ||
| 170 | + num_len_bytes = cert_der[idx] & 0x7F | ||
| 171 | + idx += 1 | ||
| 172 | + tbs_content_len = int.from_bytes(cert_der[idx:idx + num_len_bytes], 'big') | ||
| 173 | + idx += num_len_bytes | ||
| 174 | + else: | ||
| 175 | + tbs_content_len = cert_der[idx] | ||
| 176 | + idx += 1 | ||
| 177 | + tbs_end = idx + tbs_content_len | ||
| 178 | + return cert_der[tbs_start:tbs_end] | ||
| 179 | + | ||
| 180 | + | ||
| 181 | +def extract_sig_alg_and_value(cert_der): | ||
| 182 | + idx = 1 | ||
| 183 | + if cert_der[1] & 0x80: | ||
| 184 | + idx += 1 + (cert_der[1] & 0x7F) | ||
| 185 | + else: | ||
| 186 | + idx += 1 | ||
| 187 | + if cert_der[idx] != 0x30: | ||
| 188 | + raise ValueError("expected TBS SEQUENCE") | ||
| 189 | + idx += 1 | ||
| 190 | + if cert_der[idx] & 0x80: | ||
| 191 | + num_len_bytes = cert_der[idx] & 0x7F | ||
| 192 | + tbs_len = int.from_bytes(cert_der[idx + 1:idx + 1 + num_len_bytes], 'big') | ||
| 193 | + idx += 1 + num_len_bytes + tbs_len | ||
| 194 | + else: | ||
| 195 | + tbs_len = cert_der[idx] | ||
| 196 | + idx += 1 + tbs_len | ||
| 197 | + sig_alg_start = idx | ||
| 198 | + if cert_der[idx] != 0x30: | ||
| 199 | + raise ValueError("expected sigAlg SEQUENCE") | ||
| 200 | + idx += 1 | ||
| 201 | + if cert_der[idx] & 0x80: | ||
| 202 | + num_len_bytes = cert_der[idx] & 0x7F | ||
| 203 | + sig_alg_len = int.from_bytes(cert_der[idx + 1:idx + 1 + num_len_bytes], 'big') | ||
| 204 | + idx += 1 + num_len_bytes + sig_alg_len | ||
| 205 | + else: | ||
| 206 | + sig_alg_len = cert_der[idx] | ||
| 207 | + idx += 1 + sig_alg_len | ||
| 208 | + sig_alg_end = idx | ||
| 209 | + sig_val_start = idx | ||
| 210 | + sig_val_end = len(cert_der) | ||
| 211 | + return cert_der[sig_alg_start:sig_alg_end], cert_der[sig_val_start:sig_val_end] | ||
| 212 | + | ||
| 213 | + | ||
| 214 | +def _encode_length(length): | ||
| 215 | + if length < 0x80: | ||
| 216 | + return bytes([length]) | ||
| 217 | + elif length < 0x100: | ||
| 218 | + return bytes([0x81, length]) | ||
| 219 | + elif length < 0x10000: | ||
| 220 | + return bytes([0x82, (length >> 8) & 0xFF, length & 0xFF]) | ||
| 221 | + else: | ||
| 222 | + return bytes([0x83, (length >> 16) & 0xFF, (length >> 8) & 0xFF, length & 0xFF]) | ||
| 223 | + | ||
| 224 | + | ||
| 225 | +def build_versioned_cert_no_ext(tbs_builder, signing_key, version): | ||
| 226 | + """Build a certificate without extensions, with a specific version (0=v1, 1=v2). | ||
| 227 | + | ||
| 228 | + 1. Sign a v3 TBS (without extensions) to get a valid cert structure | ||
| 229 | + 2. Extract TBS DER | ||
| 230 | + 3. Patch version byte to desired value | ||
| 231 | + 4. Re-sign patched TBS | ||
| 232 | + 5. Assemble final certificate | ||
| 233 | + """ | ||
| 234 | + v3_cert = tbs_builder.sign(signing_key, hashes.SHA256()) | ||
| 235 | + v3_der = to_der(v3_cert) | ||
| 236 | + | ||
| 237 | + tbs_der = extract_tbs_der(v3_der) | ||
| 238 | + patched_tbs = patch_version_in_der(tbs_der, version) | ||
| 239 | + | ||
| 240 | + sig_alg_der, _ = extract_sig_alg_and_value(v3_der) | ||
| 241 | + | ||
| 242 | + new_signature = signing_key.sign(patched_tbs, padding.PKCS1v15(), hashes.SHA256()) | ||
| 243 | + sig_content = b'\x00' + new_signature | ||
| 244 | + sig_value_der = b'\x03' + _encode_length(len(sig_content)) + sig_content | ||
| 245 | + | ||
| 246 | + inner = patched_tbs + sig_alg_der + sig_value_der | ||
| 247 | + cert_der = b'\x30' + _encode_length(len(inner)) + inner | ||
| 248 | + return cert_der | ||
| 249 | + | ||
| 250 | + | ||
| 251 | +def save(der_bytes, filename, out_dir): | ||
| 252 | + path = os.path.join(out_dir, filename) | ||
| 253 | + with open(path, "wb") as f: | ||
| 254 | + f.write(der_bytes) | ||
| 255 | + print(f" wrote {filename} ({len(der_bytes)} bytes)") | ||
| 256 | + | ||
| 257 | + | ||
| 258 | +def main(): | ||
| 259 | + out_dir = os.path.dirname(os.path.abspath(__file__)) | ||
| 260 | + | ||
| 261 | + print("Generating keys...") | ||
| 262 | + root_key = gen_key() | ||
| 263 | + inter_key = gen_key() | ||
| 264 | + leaf_key = gen_key() | ||
| 265 | + root_b_key = gen_key() | ||
| 266 | + | ||
| 267 | + root_name = make_root_cn() | ||
| 268 | + inter_name = make_inter_cn() | ||
| 269 | + leaf_name = make_leaf_cn() | ||
| 270 | + root_b_name = make_root_b_cn() | ||
| 271 | + | ||
| 272 | + print("Generating certificates...") | ||
| 273 | + | ||
| 274 | + # v3 root CA (trust anchor for TC1/TC2, with BC:CA=TRUE, KU:keyCertSign) | ||
| 275 | + a_root = build_ca_tbs(root_name, root_name, root_key.public_key(), 2).sign(root_key, hashes.SHA256()) | ||
| 276 | + save(to_der(a_root), "v3_root.der", out_dir) | ||
| 277 | + | ||
| 278 | + # v1 intermediate WITHOUT extensions, signed by root (TC1) | ||
| 279 | + tbs_inter_no_ext = build_no_ext_tbs(inter_name, root_name, inter_key.public_key()) | ||
| 280 | + save(build_versioned_cert_no_ext(tbs_inter_no_ext, root_key, 0x00), "v1_inter.der", out_dir) | ||
| 281 | + | ||
| 282 | + # v2 intermediate WITHOUT extensions, signed by root (TC2) | ||
| 283 | + save(build_versioned_cert_no_ext(tbs_inter_no_ext, root_key, 0x01), "v2_inter.der", out_dir) | ||
| 284 | + | ||
| 285 | + # v3 leaf signed by intermediate's key (for TC1/TC2 chain) | ||
| 286 | + a_leaf = build_leaf_tbs(leaf_name, inter_name, leaf_key.public_key()).sign(inter_key, hashes.SHA256()) | ||
| 287 | + save(to_der(a_leaf), "v3_leaf.der", out_dir) | ||
| 288 | + | ||
| 289 | + # v1 self-signed root WITHOUT extensions (TC3 trust anchor) | ||
| 290 | + tbs_root_b_no_ext = build_no_ext_tbs(root_b_name, root_b_name, root_b_key.public_key()) | ||
| 291 | + save(build_versioned_cert_no_ext(tbs_root_b_no_ext, root_b_key, 0x00), "v1_root.der", out_dir) | ||
| 292 | + | ||
| 293 | + # v3 leaf signed by v1 root's key (for TC3 chain) | ||
| 294 | + b_leaf = build_leaf_tbs(leaf_name, root_b_name, leaf_key.public_key()).sign(root_b_key, hashes.SHA256()) | ||
| 295 | + save(to_der(b_leaf), "v3_leaf_b.der", out_dir) | ||
| 296 | + | ||
| 297 | + print("Done.") | ||
| 298 | + | ||
| 299 | + | ||
| 300 | +if __name__ == "__main__": | ||
| 301 | + main() | ||
| @@ -0,0 +1,333 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +# -*- coding: utf-8 -*- | ||
| 3 | +# This file is part of the openHiTLS project. | ||
| 4 | +# | ||
| 5 | +# openHiTLS is licensed under the Mulan PSL v2. | ||
| 6 | +# You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 7 | +# You may obtain a copy of Mulan PSL v2 at: | ||
| 8 | +# | ||
| 9 | +# http://license.coscl.org.cn/MulanPSL2 | ||
| 10 | +# | ||
| 11 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 12 | +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 13 | +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 14 | +# See the Mulan PSL v2 for more details. | ||
| 15 | +""" | ||
| 16 | +Generate test certificates for TC1-TC5 of the EXTENSIONS_REQUIRE_V3 verification tests. | ||
| 17 | + | ||
| 18 | +Strategy: construct v1/v2 certificates with extensions by manually building DER: | ||
| 19 | + 1. Build a v3 TBS using the cryptography library (with extensions) | ||
| 20 | + 2. Patch the version byte in the TBS DER from 0x02 (v3) to 0x00 (v1) or 0x01 (v2) | ||
| 21 | + 3. Re-sign the patched TBS with the issuer's private key | ||
| 22 | + 4. Assemble the final Certificate SEQUENCE: [patched TBS, signatureAlgorithm, signatureValue] | ||
| 23 | + | ||
| 24 | +This produces v1/v2 certificates with extensions AND valid signatures, so both OpenSSL | ||
| 25 | +and openHiTLS can parse them. OpenSSL should reject them with | ||
| 26 | +X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3; openHiTLS should reject them with | ||
| 27 | +HITLS_X509_ERR_VFY_EXTENSIONS_REQUIRE_V3. | ||
| 28 | + | ||
| 29 | +Output files: | ||
| 30 | + a_v3_root.der - v3 root CA (trust anchor for TC1-TC4) | ||
| 31 | + a_v1_ext_leaf.der - TC1: v1 leaf with extensions, valid signature from root | ||
| 32 | + a_v2_ext_leaf.der - TC2: v2 leaf with extensions, valid signature from root | ||
| 33 | + a_v3_inter.der - v3 intermediate CA, signed by root | ||
| 34 | + a_v3_leaf.der - v3 leaf, signed by intermediate (for TC3/TC4 chain) | ||
| 35 | + a_v1_ext_inter.der - TC3: v1 intermediate with extensions, valid signature from root | ||
| 36 | + a_v2_ext_inter.der - TC4: v2 intermediate with extensions, valid signature from root | ||
| 37 | + b_v1_ext_root.der - TC5: v1 root with extensions, valid self-signature | ||
| 38 | + b_v3_leaf.der - v3 leaf, signed by root B (for TC5 chain) | ||
| 39 | +""" | ||
| 40 | + | ||
| 41 | +import os | ||
| 42 | +import datetime | ||
| 43 | +from cryptography import x509 | ||
| 44 | +from cryptography.x509.oid import NameOID | ||
| 45 | +from cryptography.hazmat.primitives import hashes, serialization | ||
| 46 | +from cryptography.hazmat.primitives.asymmetric import rsa, padding, utils | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +def gen_key(): | ||
| 50 | + return rsa.generate_private_key(public_exponent=65537, key_size=2048) | ||
| 51 | + | ||
| 52 | + | ||
| 53 | +def make_root_cn(): | ||
| 54 | + return x509.Name([ | ||
| 55 | + x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"), | ||
| 56 | + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "v3exts Test"), | ||
| 57 | + x509.NameAttribute(NameOID.COMMON_NAME, "v3exts Root CA"), | ||
| 58 | + ]) | ||
| 59 | + | ||
| 60 | + | ||
| 61 | +def make_inter_cn(): | ||
| 62 | + return x509.Name([ | ||
| 63 | + x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"), | ||
| 64 | + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "v3exts Test"), | ||
| 65 | + x509.NameAttribute(NameOID.COMMON_NAME, "v3exts Intermediate CA"), | ||
| 66 | + ]) | ||
| 67 | + | ||
| 68 | + | ||
| 69 | +def make_leaf_cn(): | ||
| 70 | + return x509.Name([ | ||
| 71 | + x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"), | ||
| 72 | + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "v3exts Test"), | ||
| 73 | + x509.NameAttribute(NameOID.COMMON_NAME, "v3exts Leaf"), | ||
| 74 | + ]) | ||
| 75 | + | ||
| 76 | + | ||
| 77 | +def make_root_b_cn(): | ||
| 78 | + return x509.Name([ | ||
| 79 | + x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"), | ||
| 80 | + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "v3exts Test"), | ||
| 81 | + x509.NameAttribute(NameOID.COMMON_NAME, "v3exts Root B CA"), | ||
| 82 | + ]) | ||
| 83 | + | ||
| 84 | + | ||
| 85 | +def build_ca_tbs(subject, issuer, pub_key, path_length): | ||
| 86 | + builder = ( | ||
| 87 | + x509.CertificateBuilder() | ||
| 88 | + .subject_name(subject) | ||
| 89 | + .issuer_name(issuer) | ||
| 90 | + .public_key(pub_key) | ||
| 91 | + .serial_number(x509.random_serial_number()) | ||
| 92 | + .not_valid_before(datetime.datetime(2024, 1, 1)) | ||
| 93 | + .not_valid_after(datetime.datetime(2050, 12, 31)) | ||
| 94 | + .add_extension( | ||
| 95 | + x509.BasicConstraints(ca=True, path_length=path_length), | ||
| 96 | + critical=True, | ||
| 97 | + ) | ||
| 98 | + .add_extension( | ||
| 99 | + x509.KeyUsage( | ||
| 100 | + digital_signature=True, key_cert_sign=True, crl_sign=True, | ||
| 101 | + content_commitment=False, key_encipherment=False, | ||
| 102 | + data_encipherment=False, key_agreement=False, | ||
| 103 | + encipher_only=False, decipher_only=False, | ||
| 104 | + ), | ||
| 105 | + critical=True, | ||
| 106 | + ) | ||
| 107 | + ) | ||
| 108 | + return builder | ||
| 109 | + | ||
| 110 | + | ||
| 111 | +def build_leaf_tbs(subject, issuer, pub_key): | ||
| 112 | + builder = ( | ||
| 113 | + x509.CertificateBuilder() | ||
| 114 | + .subject_name(subject) | ||
| 115 | + .issuer_name(issuer) | ||
| 116 | + .public_key(pub_key) | ||
| 117 | + .serial_number(x509.random_serial_number()) | ||
| 118 | + .not_valid_before(datetime.datetime(2024, 1, 1)) | ||
| 119 | + .not_valid_after(datetime.datetime(2050, 12, 31)) | ||
| 120 | + .add_extension( | ||
| 121 | + x509.BasicConstraints(ca=False, path_length=None), | ||
| 122 | + critical=True, | ||
| 123 | + ) | ||
| 124 | + .add_extension( | ||
| 125 | + x509.KeyUsage( | ||
| 126 | + digital_signature=True, key_encipherment=True, | ||
| 127 | + content_commitment=False, key_agreement=False, | ||
| 128 | + key_cert_sign=False, crl_sign=False, | ||
| 129 | + data_encipherment=False, encipher_only=False, decipher_only=False, | ||
| 130 | + ), | ||
| 131 | + critical=True, | ||
| 132 | + ) | ||
| 133 | + ) | ||
| 134 | + return builder | ||
| 135 | + | ||
| 136 | + | ||
| 137 | +def to_der(cert): | ||
| 138 | + return cert.public_bytes(serialization.Encoding.DER) | ||
| 139 | + | ||
| 140 | + | ||
| 141 | +def patch_version_in_der(der_bytes, new_version): | ||
| 142 | + der = bytearray(der_bytes) | ||
| 143 | + pattern = bytes([0xA0, 0x03, 0x02, 0x01]) | ||
| 144 | + idx = der.find(pattern) | ||
| 145 | + if idx < 0: | ||
| 146 | + raise ValueError("version field pattern not found in DER") | ||
| 147 | + der[idx + 4] = new_version | ||
| 148 | + return bytes(der) | ||
| 149 | + | ||
| 150 | + | ||
| 151 | +def extract_tbs_der(cert_der): | ||
| 152 | + """Extract the TBSCertificate DER bytes from a Certificate SEQUENCE.""" | ||
| 153 | + # Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue } | ||
| 154 | + # The outer SEQUENCE tag+length is at position 0. | ||
| 155 | + # Inside, the first element is TBSCertificate. | ||
| 156 | + # Parse the outer SEQUENCE | ||
| 157 | + if cert_der[0] != 0x30: | ||
| 158 | + raise ValueError("expected SEQUENCE tag") | ||
| 159 | + # After outer SEQUENCE tag+length, the first child is TBS | ||
| 160 | + idx = 1 | ||
| 161 | + if cert_der[1] & 0x80: | ||
| 162 | + idx += 1 + (cert_der[1] & 0x7F) | ||
| 163 | + else: | ||
| 164 | + idx += 1 | ||
| 165 | + # Now idx points to the start of the first child (TBSCertificate) | ||
| 166 | + # Parse TBSCertificate tag+length | ||
| 167 | + tbs_start = idx | ||
| 168 | + if cert_der[idx] != 0x30: | ||
| 169 | + raise ValueError("expected TBS SEQUENCE tag") | ||
| 170 | + idx += 1 | ||
| 171 | + if cert_der[idx] & 0x80: | ||
| 172 | + num_len_bytes = cert_der[idx] & 0x7F | ||
| 173 | + idx += 1 | ||
| 174 | + tbs_content_len = int.from_bytes(cert_der[idx:idx + num_len_bytes], 'big') | ||
| 175 | + idx += num_len_bytes | ||
| 176 | + else: | ||
| 177 | + tbs_content_len = cert_der[idx] | ||
| 178 | + idx += 1 | ||
| 179 | + tbs_end = idx + tbs_content_len | ||
| 180 | + return cert_der[tbs_start:tbs_end] | ||
| 181 | + | ||
| 182 | + | ||
| 183 | +def extract_sig_alg_and_value(cert_der): | ||
| 184 | + """Extract signatureAlgorithm and signatureValue DER from a Certificate.""" | ||
| 185 | + # Parse outer SEQUENCE | ||
| 186 | + idx = 1 | ||
| 187 | + if cert_der[1] & 0x80: | ||
| 188 | + idx += 1 + (cert_der[1] & 0x7F) | ||
| 189 | + else: | ||
| 190 | + idx += 1 | ||
| 191 | + # Skip TBSCertificate | ||
| 192 | + if cert_der[idx] != 0x30: | ||
| 193 | + raise ValueError("expected TBS SEQUENCE") | ||
| 194 | + idx += 1 | ||
| 195 | + if cert_der[idx] & 0x80: | ||
| 196 | + num_len_bytes = cert_der[idx] & 0x7F | ||
| 197 | + tbs_len = int.from_bytes(cert_der[idx + 1:idx + 1 + num_len_bytes], 'big') | ||
| 198 | + idx += 1 + num_len_bytes + tbs_len | ||
| 199 | + else: | ||
| 200 | + tbs_len = cert_der[idx] | ||
| 201 | + idx += 1 + tbs_len | ||
| 202 | + # Now at signatureAlgorithm | ||
| 203 | + sig_alg_start = idx | ||
| 204 | + # Skip signatureAlgorithm SEQUENCE | ||
| 205 | + if cert_der[idx] != 0x30: | ||
| 206 | + raise ValueError("expected sigAlg SEQUENCE") | ||
| 207 | + idx += 1 | ||
| 208 | + if cert_der[idx] & 0x80: | ||
| 209 | + num_len_bytes = cert_der[idx] & 0x7F | ||
| 210 | + sig_alg_len = int.from_bytes(cert_der[idx + 1:idx + 1 + num_len_bytes], 'big') | ||
| 211 | + idx += 1 + num_len_bytes + sig_alg_len | ||
| 212 | + else: | ||
| 213 | + sig_alg_len = cert_der[idx] | ||
| 214 | + idx += 1 + sig_alg_len | ||
| 215 | + sig_alg_end = idx | ||
| 216 | + # Now at signatureValue BIT STRING | ||
| 217 | + sig_val_start = idx | ||
| 218 | + sig_val_end = len(cert_der) | ||
| 219 | + return cert_der[sig_alg_start:sig_alg_end], cert_der[sig_val_start:sig_val_end] | ||
| 220 | + | ||
| 221 | + | ||
| 222 | +def build_versioned_cert(tbs_builder, signing_key, version): | ||
| 223 | + """Build a certificate with a specific version (v1=0, v2=1) but with extensions. | ||
| 224 | + | ||
| 225 | + 1. Sign a v3 TBS to get a valid certificate | ||
| 226 | + 2. Extract the TBS DER | ||
| 227 | + 3. Patch the version byte to the desired version | ||
| 228 | + 4. Re-sign the patched TBS | ||
| 229 | + 5. Assemble final certificate | ||
| 230 | + """ | ||
| 231 | + # First, build a normal v3 cert to get the structure and signature algorithm | ||
| 232 | + v3_cert = tbs_builder.sign(signing_key, hashes.SHA256()) | ||
| 233 | + v3_der = to_der(v3_cert) | ||
| 234 | + | ||
| 235 | + # Extract TBS and patch version | ||
| 236 | + tbs_der = extract_tbs_der(v3_der) | ||
| 237 | + patched_tbs = patch_version_in_der(tbs_der, version) | ||
| 238 | + | ||
| 239 | + # Re-sign the patched TBS | ||
| 240 | + sig_alg_der, _ = extract_sig_alg_and_value(v3_der) | ||
| 241 | + | ||
| 242 | + # Compute new signature over patched TBS | ||
| 243 | + new_signature = signing_key.sign( | ||
| 244 | + patched_tbs, | ||
| 245 | + padding.PKCS1v15(), | ||
| 246 | + hashes.SHA256(), | ||
| 247 | + ) | ||
| 248 | + # Wrap in BIT STRING: tag=0x03, then length, then 0x00 (unused bits), then signature bytes | ||
| 249 | + sig_content = b'\x00' + new_signature | ||
| 250 | + sig_value_der = b'\x03' + _encode_length(len(sig_content)) + sig_content | ||
| 251 | + | ||
| 252 | + # Assemble Certificate SEQUENCE | ||
| 253 | + inner = patched_tbs + sig_alg_der + sig_value_der | ||
| 254 | + cert_der = b'\x30' + _encode_length(len(inner)) + inner | ||
| 255 | + return cert_der | ||
| 256 | + | ||
| 257 | + | ||
| 258 | +def _encode_length(length): | ||
| 259 | + if length < 0x80: | ||
| 260 | + return bytes([length]) | ||
| 261 | + elif length < 0x100: | ||
| 262 | + return bytes([0x81, length]) | ||
| 263 | + elif length < 0x10000: | ||
| 264 | + return bytes([0x82, (length >> 8) & 0xFF, length & 0xFF]) | ||
| 265 | + else: | ||
| 266 | + return bytes([0x83, (length >> 16) & 0xFF, (length >> 8) & 0xFF, length & 0xFF]) | ||
| 267 | + | ||
| 268 | + | ||
| 269 | +def save(der_bytes, filename, out_dir): | ||
| 270 | + path = os.path.join(out_dir, filename) | ||
| 271 | + with open(path, "wb") as f: | ||
| 272 | + f.write(der_bytes) | ||
| 273 | + print(f" wrote {filename} ({len(der_bytes)} bytes)") | ||
| 274 | + | ||
| 275 | + | ||
| 276 | +def main(): | ||
| 277 | + out_dir = os.path.dirname(os.path.abspath(__file__)) | ||
| 278 | + | ||
| 279 | + print("Generating keys...") | ||
| 280 | + root_key = gen_key() | ||
| 281 | + inter_key = gen_key() | ||
| 282 | + leaf_key = gen_key() | ||
| 283 | + root_b_key = gen_key() | ||
| 284 | + | ||
| 285 | + root_name = make_root_cn() | ||
| 286 | + inter_name = make_inter_cn() | ||
| 287 | + leaf_name = make_leaf_cn() | ||
| 288 | + root_b_name = make_root_b_cn() | ||
| 289 | + | ||
| 290 | + print("Generating certificates...") | ||
| 291 | + | ||
| 292 | + # v3 root CA (trust anchor, normal) | ||
| 293 | + a_root = build_ca_tbs(root_name, root_name, root_key.public_key(), 2).sign(root_key, hashes.SHA256()) | ||
| 294 | + save(to_der(a_root), "a_v3_root.der", out_dir) | ||
| 295 | + | ||
| 296 | + # v3 intermediate CA (normal, signed by root) | ||
| 297 | + a_inter = build_ca_tbs(inter_name, root_name, inter_key.public_key(), 0).sign(root_key, hashes.SHA256()) | ||
| 298 | + save(to_der(a_inter), "a_v3_inter.der", out_dir) | ||
| 299 | + | ||
| 300 | + # v3 leaf signed by intermediate | ||
| 301 | + a_leaf = build_leaf_tbs(leaf_name, inter_name, leaf_key.public_key()).sign(inter_key, hashes.SHA256()) | ||
| 302 | + save(to_der(a_leaf), "a_v3_leaf.der", out_dir) | ||
| 303 | + | ||
| 304 | + # TC1: v1 leaf signed by root (with extensions) | ||
| 305 | + tbs_leaf_direct = build_leaf_tbs(leaf_name, root_name, leaf_key.public_key()) | ||
| 306 | + save(build_versioned_cert(tbs_leaf_direct, root_key, 0x00), "a_v1_ext_leaf.der", out_dir) | ||
| 307 | + | ||
| 308 | + # TC2: v2 leaf signed by root (with extensions) | ||
| 309 | + save(build_versioned_cert(tbs_leaf_direct, root_key, 0x01), "a_v2_ext_leaf.der", out_dir) | ||
| 310 | + | ||
| 311 | + # TC3: v1 intermediate signed by root (with extensions) | ||
| 312 | + tbs_inter = build_ca_tbs(inter_name, root_name, inter_key.public_key(), 0) | ||
| 313 | + save(build_versioned_cert(tbs_inter, root_key, 0x00), "a_v1_ext_inter.der", out_dir) | ||
| 314 | + | ||
| 315 | + # TC4: v2 intermediate signed by root (with extensions) | ||
| 316 | + save(build_versioned_cert(tbs_inter, root_key, 0x01), "a_v2_ext_inter.der", out_dir) | ||
| 317 | + | ||
| 318 | + # Group b: separate root for TC5 | ||
| 319 | + b_root = build_ca_tbs(root_b_name, root_b_name, root_b_key.public_key(), 2).sign(root_b_key, hashes.SHA256()) | ||
| 320 | + | ||
| 321 | + # TC5: v1 root (self-signed, with extensions) | ||
| 322 | + tbs_root_b = build_ca_tbs(root_b_name, root_b_name, root_b_key.public_key(), 2) | ||
| 323 | + save(build_versioned_cert(tbs_root_b, root_b_key, 0x00), "b_v1_ext_root.der", out_dir) | ||
| 324 | + | ||
| 325 | + # v3 leaf signed by root B | ||
| 326 | + b_leaf = build_leaf_tbs(leaf_name, root_b_name, leaf_key.public_key()).sign(root_b_key, hashes.SHA256()) | ||
| 327 | + save(to_der(b_leaf), "b_v3_leaf.der", out_dir) | ||
| 328 | + | ||
| 329 | + print("Done.") | ||
| 330 | + | ||
| 331 | + | ||
| 332 | +if __name__ == "__main__": | ||
| 333 | + main() | ||
| @@ -24,6 +24,8 @@ | |||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | + | ||
| 28 | + | ||
| 27 | 29 | ||
| 28 | 30 | ||
| 29 | static int32_t BuildArrayFromList(HITLS_X509_List *list, HITLS_CERT_X509 **listArray, uint32_t *num) | 31 | static int32_t BuildArrayFromList(HITLS_X509_List *list, HITLS_CERT_X509 **listArray, uint32_t *num) |
| @@ -91,6 +93,21 @@ int32_t HITLS_X509_Adapt_BuildCertChain(HITLS_Config *config, HITLS_CERT_Store * | |||
| 91 | return ret; | 93 | return ret; |
| 92 | } | 94 | } |
| 93 | 95 | ||
| 96 | + | ||
| 97 | +static int32_t SetAuthLevel(HITLS_Ctx *ctx, HITLS_X509_StoreCtx *storeCtx) | ||
| 98 | +{ | ||
| 99 | + int32_t securityLevel = 0; | ||
| 100 | + int32_t ret = HITLS_CFG_GetSecurityLevel(&ctx->config.tlsConfig, &securityLevel); | ||
| 101 | + if (ret != HITLS_SUCCESS) { | ||
| 102 | + BSL_ERR_PUSH_ERROR(ret); | ||
| 103 | + return ret; | ||
| 104 | + } | ||
| 105 | + int32_t secBits = SECURITY_GetSecbits(securityLevel); | ||
| 106 | + uint32_t bits = (secBits > 0) ? (uint32_t)secBits : 0; | ||
| 107 | + return HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_SET_SECBITS, &bits, sizeof(bits)); | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | + | ||
| 94 | int32_t HITLS_X509_Adapt_VerifyCertChain(HITLS_Ctx *ctx, HITLS_CERT_Store *store, HITLS_CERT_X509 **list, uint32_t num) | 111 | int32_t HITLS_X509_Adapt_VerifyCertChain(HITLS_Ctx *ctx, HITLS_CERT_Store *store, HITLS_CERT_X509 **list, uint32_t num) |
| 95 | { | 112 | { |
| 96 | HITLS_X509_StoreCtx *storeCtx = (HITLS_X509_StoreCtx *)store; | 113 | HITLS_X509_StoreCtx *storeCtx = (HITLS_X509_StoreCtx *)store; |
| @@ -106,6 +123,13 @@ int32_t HITLS_X509_Adapt_VerifyCertChain(HITLS_Ctx *ctx, HITLS_CERT_Store *store | |||
| 106 | 123 | ||
| 107 | int32_t purpose = ctx->isClient ? HITLS_X509_VFY_PURPOSE_TLS_SERVER : HITLS_X509_VFY_PURPOSE_TLS_CLIENT; | 124 | int32_t purpose = ctx->isClient ? HITLS_X509_VFY_PURPOSE_TLS_SERVER : HITLS_X509_VFY_PURPOSE_TLS_CLIENT; |
| 108 | HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_SET_PURPOSE, &purpose, sizeof(purpose)); | 125 | HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_SET_PURPOSE, &purpose, sizeof(purpose)); |
| 126 | + | ||
| 127 | + ret = SetAuthLevel(ctx, storeCtx); | ||
| 128 | + if (ret != HITLS_SUCCESS) { | ||
| 129 | + BSL_ERR_PUSH_ERROR(ret); | ||
| 130 | + return ret; | ||
| 131 | + } | ||
| 132 | + | ||
| 109 | 133 | ||
| 110 | HITLS_VerifyCb verCb = NULL; | 134 | HITLS_VerifyCb verCb = NULL; |
| 111 | ret = HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_SET_USR_DATA, ctx, sizeof(void *)); | 135 | ret = HITLS_X509_StoreCtxCtrl(storeCtx, HITLS_X509_STORECTX_SET_USR_DATA, ctx, sizeof(void *)); |