Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions tests/api/test_asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -2711,6 +2711,106 @@ int test_wc_DecodeKeyUsage_decipherOnly(void)
return EXPECT_RESULT();
}

#if !defined(NO_ASN) && !defined(NO_RSA) && !defined(NO_CERTS) && \
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \
defined(WOLFSSL_EKU_OID) && !defined(NO_SHA256) && \
defined(USE_CERT_BUFFERS_2048) && !defined(NO_ASN_TIME) && \
!defined(WC_NO_RNG) && !defined(NO_ASN_CRYPT) && !defined(NO_VERIFY_OID)
/* NO_VERIFY_OID compiles out the OID check this covers. */
#define TEST_EKU_OID_SUM_COLLISION
#endif

/* A KeyPurposeId whose wc_oid_sum() collides with id-kp-serverAuth must not
* authorize server authentication, while the real OID still does. */
int test_wc_DecodeExtKeyUsage_oidSumCollision(void)
{
EXPECT_DECLS;
#ifdef TEST_EKU_OID_SUM_COLLISION
static const struct {
const char* oid; /* custom EKU OID, NULL to use a named purpose */
const char* name; /* named purpose, used when oid is NULL */
byte expected;
} ekuCases[] = {
/* id-kp-serverAuth. */
{ NULL, "serverAuth", EXTKEYUSE_SERVER_AUTH },
#ifdef WOLFSSL_OLD_OID_SUM
/* 1.5.6.1.5.5.3.1, the same byte sum (71) as id-kp-serverAuth. */
{ "1.5.6.1.5.5.3.1", NULL, 0 },
#else
/* 1.19.6.1.5.21.7.3.1, the same XOR-shift sum (0x0402012e) as
* id-kp-serverAuth. */
{ "1.19.6.1.5.21.7.3.1", NULL, 0 },
#endif
};
WC_RNG rng;
RsaKey key;
byte* der = NULL;
word32 idx = 0;
int rngInit = 0;
int keyInit = 0;
size_t c;

XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(&key, 0, sizeof(key));

ExpectIntEQ(wc_InitRng(&rng), 0);
if (EXPECT_SUCCESS()) rngInit = 1;

ExpectNotNull(der = (byte*)XMALLOC(FOURK_BUF, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER));

ExpectIntEQ(wc_InitRsaKey_ex(&key, HEAP_HINT, testDevId), 0);
if (EXPECT_SUCCESS()) keyInit = 1;
ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &key,
sizeof_server_key_der_2048), 0);

for (c = 0; c < XELEM_CNT(ekuCases); c++) {
Cert cert;
DecodedCert dCert;
int dCertInit = 0;
int derSz = 0;

if (!EXPECT_SUCCESS()) break;

XMEMSET(&cert, 0, sizeof(cert));
ExpectIntEQ(wc_InitCert(&cert), 0);
if (EXPECT_SUCCESS()) {
cert.sigType = CTC_SHA256wRSA;
cert.isCA = 0;
XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
XSTRNCPY(cert.subject.org, "wolfSSL", CTC_NAME_SIZE);
XSTRNCPY(cert.subject.commonName, "extKeyUsage", CTC_NAME_SIZE);
}
if (ekuCases[c].oid != NULL) {
ExpectIntEQ(wc_SetExtKeyUsageOID(&cert, ekuCases[c].oid,
(word32)XSTRLEN(ekuCases[c].oid), 0, HEAP_HINT), 0);
}
else {
ExpectIntEQ(wc_SetExtKeyUsage(&cert, ekuCases[c].name), 0);
}
ExpectIntGT(derSz = wc_MakeSelfCert(&cert, der, FOURK_BUF, &key, &rng),
0);

if (EXPECT_SUCCESS() && (der != NULL)) {
wc_InitDecodedCert(&dCert, der, (word32)derSz, HEAP_HINT);
dCertInit = 1;
ExpectIntEQ(wc_ParseCert(&dCert, CERT_TYPE, NO_VERIFY, NULL), 0);
/* The extension must be seen in both cases - an unrecognized
* KeyPurposeId is skipped, not an error - but only the real OID
* may authorize a purpose. */
ExpectIntNE(dCert.extExtKeyUsageSet, 0);
ExpectIntEQ(dCert.extExtKeyUsage, ekuCases[c].expected);
}
if (dCertInit) wc_FreeDecodedCert(&dCert);
}

if (keyInit) wc_FreeRsaKey(&key);
if (rngInit) wc_FreeRng(&rng);
XFREE(der, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
#endif /* TEST_EKU_OID_SUM_COLLISION */
return EXPECT_RESULT();
}

int test_wc_SignCert_buffer_bounds(void)
{
EXPECT_DECLS;
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_asn.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ int test_ToTraditional_ex_negative(void);
int test_ToTraditional_ex_mldsa_bad_params(void);
int test_wc_SignCert_buffer_bounds(void);
int test_wc_DecodeKeyUsage_decipherOnly(void);
int test_wc_DecodeExtKeyUsage_oidSumCollision(void);
int test_wc_AsnDecisionCoverage(void);
int test_wc_AsnFeatureCoverage(void);

Expand All @@ -70,6 +71,7 @@ int test_wc_AsnFeatureCoverage(void);
TEST_DECL_GROUP("asn", test_ToTraditional_ex_mldsa_bad_params), \
TEST_DECL_GROUP("asn", test_wc_SignCert_buffer_bounds), \
TEST_DECL_GROUP("asn", test_wc_DecodeKeyUsage_decipherOnly), \
TEST_DECL_GROUP("asn", test_wc_DecodeExtKeyUsage_oidSumCollision), \
TEST_DECL_GROUP("asn", test_wc_AsnDecisionCoverage), \
TEST_DECL_GROUP("asn", test_wc_AsnFeatureCoverage)

Expand Down
26 changes: 26 additions & 0 deletions tests/unit-mcdc/test_asn_ext_whitebox.c
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,32 @@ static void wb_decode_ext_key_usage(void)
&ssh, NULL);
WB_CHECK(ret == 0, "extExtKeyUsageOidCnt==NULL out-ptr (2nd operand false)");

#ifndef NO_VERIFY_OID
/* An OID that is not id-kp-serverAuth but whose wc_oid_sum() collides with
* it must not be treated as serverAuth. */
{
#ifdef WOLFSSL_OLD_OID_SUM
/* 1.5.6.1.5.5.3.1, the same byte sum (71) as serverAuth. */
static const byte ekuCollide[] = {
0x30,0x0A,
0x06,0x08, 0x2D,0x06,0x01,0x05,0x05,0x05,0x03,0x01
};
#else
/* 1.19.6.1.5.21.7.3.1, the same XOR-shift sum (0x0402012e) as
* serverAuth. */
static const byte ekuCollide[] = {
0x30,0x0A,
0x06,0x08, 0x3B,0x06,0x01,0x05,0x15,0x07,0x03,0x01
};
#endif
src = NULL; srcSz = 0; count = 0; usage = 0; ssh = 0; oidCnt = 0;
ret = DecodeExtKeyUsage(ekuCollide, sizeof(ekuCollide), &src, &srcSz,
&count, &usage, &ssh, &oidCnt);
WB_CHECK(ret == 0 && usage == 0 && oidCnt == 1,
"OID colliding with serverAuth checksum not set as serverAuth");
}
#endif /* !NO_VERIFY_OID */

/* Empty SEQUENCE OF -> loop condition false immediately. */
{
static const byte ekuEmpty[] = { 0x30, 0x00 };
Expand Down
19 changes: 18 additions & 1 deletion wolfcrypt/src/asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -20967,8 +20967,25 @@ int DecodeExtKeyUsage(const byte* input, word32 sz,
ret = 0;
}
else if (ret == 0) {
word32 oidSum = dataASN[KEYPURPOSEIDASN_IDX_OID].data.oid.sum;
#ifndef NO_VERIFY_OID
const byte* knownOid;
word32 knownOidSz = 0;

/* The sum is only a checksum, so confirm the bytes match the OID
* it maps to - oidIgnoreType above skipped that check. */
knownOid = OidFromId(oidSum, oidCertKeyUseType, &knownOidSz);
if ((knownOid == NULL) ||
(knownOidSz !=
dataASN[KEYPURPOSEIDASN_IDX_OID].data.oid.length) ||
(XMEMCMP(dataASN[KEYPURPOSEIDASN_IDX_OID].data.oid.data,
knownOid, knownOidSz) != 0)) {
/* Not the OID this sum stands for - treat as unknown. */
oidSum = 0;
}
#endif
/* Store the bit for the OID. */
switch (dataASN[KEYPURPOSEIDASN_IDX_OID].data.oid.sum) {
switch (oidSum) {
case EKU_ANY_OID:
*extExtKeyUsage |= EXTKEYUSE_ANY;
break;
Expand Down
Loading