diff --git a/tests/api/test_ossl_x509_crypto.c b/tests/api/test_ossl_x509_crypto.c index ddc5b83f13..71650077b3 100644 --- a/tests/api/test_ossl_x509_crypto.c +++ b/tests/api/test_ossl_x509_crypto.c @@ -520,25 +520,25 @@ int test_wolfSSL_X509_sign(void) * with the MSB set. See GenerateInteger in asn.c */ #ifndef USE_CERT_BUFFERS_1024 #ifndef WOLFSSL_ALT_NAMES - /* Valid case - size should be 781-786 with 16 byte serial number */ - ExpectTrue((781 + snSz <= ret) && (ret <= 781 + 5 + snSz)); + /* Valid case - size should be 777-782 with 16 byte serial number */ + ExpectTrue((777 + snSz <= ret) && (ret <= 777 + 5 + snSz)); #elif defined(WOLFSSL_IP_ALT_NAME) - /* Valid case - size should be 955-960 with 16 byte serial number */ - ExpectTrue((939 + snSz <= ret) && (ret <= 939 + 5 + snSz)); + /* Valid case - size should be 951-956 with 16 byte serial number */ + ExpectTrue((935 + snSz <= ret) && (ret <= 935 + 5 + snSz)); #else - /* Valid case - size should be 926-931 with 16 byte serial number */ - ExpectTrue((910 + snSz <= ret) && (ret <= 910 + 5 + snSz)); + /* Valid case - size should be 922-927 with 16 byte serial number */ + ExpectTrue((906 + snSz <= ret) && (ret <= 906 + 5 + snSz)); #endif #else #ifndef WOLFSSL_ALT_NAMES - /* Valid case - size should be 537-542 with 16 byte serial number */ - ExpectTrue((521 + snSz <= ret) && (ret <= 521 + 5 + snSz)); + /* Valid case - size should be 533-538 with 16 byte serial number */ + ExpectTrue((517 + snSz <= ret) && (ret <= 517 + 5 + snSz)); #elif defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME) - /* Valid case - size should be 695-670 with 16 byte serial number */ - ExpectTrue((679 + snSz <= ret) && (ret <= 679 + 5 + snSz)); + /* Valid case - size should be 691-696 with 16 byte serial number */ + ExpectTrue((675 + snSz <= ret) && (ret <= 675 + 5 + snSz)); #else - /* Valid case - size should be 666-671 with 16 byte serial number */ - ExpectTrue((650 + snSz <= ret) && (ret <= 650 + 5 + snSz)); + /* Valid case - size should be 662-667 with 16 byte serial number */ + ExpectTrue((646 + snSz <= ret) && (ret <= 646 + 5 + snSz)); #endif #endif /* check that issuer name is as expected after signature */ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 5b48f90caf..8a58729513 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -28292,15 +28292,32 @@ static WC_INLINE byte itob(int number) } -/* write time to output, format */ -static void SetTime(struct tm* date, byte* output) +/* RFC 5280: validity dates through 2049 encode as UTCTime, 2050 and later as + * GeneralizedTime. date->tm_year holds the full year here. */ +static byte ValidityTimeFormat(const struct tm* date) +{ + if (date->tm_year >= 1950 && date->tm_year < 2050) + return ASN_UTC_TIME; + return ASN_GENERALIZED_TIME; +} + +/* write time value to output in the given ASN.1 format */ +static void SetTime(struct tm* date, byte* output, byte format) { int i = 0; + int year = date->tm_year; - output[i++] = itob((date->tm_year % 10000) / 1000); - output[i++] = itob((date->tm_year % 1000) / 100); - output[i++] = itob((date->tm_year % 100) / 10); - output[i++] = itob( date->tm_year % 10); + if (format == ASN_UTC_TIME) { + year %= 100; + output[i++] = itob((year / 10) % 10); + output[i++] = itob( year % 10); + } + else { + output[i++] = itob((year % 10000) / 1000); + output[i++] = itob((year % 1000) / 100); + output[i++] = itob((year % 100) / 10); + output[i++] = itob( year % 10); + } output[i++] = itob(date->tm_mon / 10); output[i++] = itob(date->tm_mon % 10); @@ -30063,6 +30080,8 @@ static int SetValidity(byte* before, byte* after, int daysValid) { #ifndef NO_ASN_TIME int ret = 0; + byte format; + word32 timeSz; time_t now; time_t then; struct tm* tmpTime; @@ -30093,7 +30112,12 @@ static int SetValidity(byte* before, byte* after, int daysValid) localTime.tm_year += 1900; localTime.tm_mon += 1; - SetTime(&localTime, before); + format = ValidityTimeFormat(&localTime); + timeSz = (format == ASN_UTC_TIME) ? ASN_UTC_TIME_SIZE - 1 + : ASN_GEN_TIME_SZ; + before[0] = format; + SetLength(timeSz, before + 1); + SetTime(&localTime, before + 2, format); /* add daysValid of seconds */ then = now + (daysValid * (time_t)86400); @@ -30110,7 +30134,12 @@ static int SetValidity(byte* before, byte* after, int daysValid) localTime.tm_year += 1900; localTime.tm_mon += 1; - SetTime(&localTime, after); + format = ValidityTimeFormat(&localTime); + timeSz = (format == ASN_UTC_TIME) ? ASN_UTC_TIME_SIZE - 1 + : ASN_GEN_TIME_SZ; + after[0] = format; + SetLength(timeSz, after + 1); + SetTime(&localTime, after + 2, format); } return ret; @@ -30803,6 +30832,8 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz, int ret = 0; word32 issRawLen = 0; word32 sbjRawLen = 0; + byte localBefore[MAX_DATE_SIZE]; + byte localAfter[MAX_DATE_SIZE]; /* Unused without PQC */ (void)falconKey; @@ -31028,16 +31059,35 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz, } else { - /* Don't put out UTC before data. */ - dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC].noOut = 1; - /* Make space for before date data. */ - SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT], - NULL, ASN_GEN_TIME_SZ); - /* Don't put out UTC after data. */ - dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC].noOut = 1; - /* Make space for after date data. */ - SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT], - NULL, ASN_GEN_TIME_SZ); + /* Compute default validity dates; SetValidity picks UTCTime or + * Generalized Time per RFC 5280 based on the year. */ + ret = SetValidity(localBefore, localAfter, cert->daysValid); + if (ret == 0) { + if (localBefore[0] == ASN_UTC_TIME) { + SetASN_Buffer( + &dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC], + localBefore + 2, ASN_UTC_TIME_SIZE - 1); + dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT].noOut = 1; + } + else { + dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC].noOut = 1; + SetASN_Buffer( + &dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT], + localBefore + 2, ASN_GEN_TIME_SZ); + } + if (localAfter[0] == ASN_UTC_TIME) { + SetASN_Buffer( + &dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC], + localAfter + 2, ASN_UTC_TIME_SIZE - 1); + dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT].noOut = 1; + } + else { + dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC].noOut = 1; + SetASN_Buffer( + &dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT], + localAfter + 2, ASN_GEN_TIME_SZ); + } + } } if (sbjRawLen > 0) { /* Put in encoded subject name. */ @@ -31075,7 +31125,9 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz, X509CERTASN_IDX_SIGNATURE); /* Calculate encoded certificate body size. */ - ret = SizeASN_Items(x509CertASN, dataASN, x509CertASN_Length, &sz); + if (ret >= 0) { + ret = SizeASN_Items(x509CertASN, dataASN, x509CertASN_Length, &sz); + } } /* Check buffer is big enough for encoded data. */ if ((ret == 0) && (sz > derSz)) { @@ -31106,18 +31158,6 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz, dataASN[X509CERTASN_IDX_TBS_SUBJECT_SEQ].data.buffer.length, &cert->subject, cert->heap); } - if (ret >= 0) { - if (cert->beforeDateSz == 0 || cert->afterDateSz == 0) - { - /* Encode validity into buffer. */ - /* safe casts -- the pointers are actually inside derBuffer. */ - ret = SetValidity( - (byte*)(wc_ptr_t)dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT] - .data.buffer.data, - (byte*)(wc_ptr_t)dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT] - .data.buffer.data, cert->daysValid); - } - } if (ret >= 0) { /* Encode public key into buffer. */ /* safe cast -- the pointer is actually inside derBuffer. */ diff --git a/wolfcrypt/src/asn_orig.c b/wolfcrypt/src/asn_orig.c index 655b9f0193..94617c3585 100644 --- a/wolfcrypt/src/asn_orig.c +++ b/wolfcrypt/src/asn_orig.c @@ -5911,10 +5911,6 @@ int SetNameEx(byte* output, word32 outputSz, CertName* name, void* heap) /* Set Date validity from now until now + daysValid * return size in bytes written to output, 0 on error */ -/* TODO https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5 - * "MUST always encode certificate validity dates through the year 2049 as - * UTCTime; certificate validity dates in 2050 or later MUST be encoded as - * GeneralizedTime." */ static int SetValidity(byte* output, int daysValid) { #ifndef NO_ASN_TIME @@ -5922,6 +5918,8 @@ static int SetValidity(byte* output, int daysValid) byte after[MAX_DATE_SIZE]; word32 beforeSz, afterSz, seqSz; + word32 timeSz; + byte format; time_t now; time_t then; @@ -5941,9 +5939,6 @@ static int SetValidity(byte* output, int daysValid) now = wc_Time(0); /* before now */ - before[0] = ASN_GENERALIZED_TIME; - beforeSz = SetLength(ASN_GEN_TIME_SZ, before + 1) + 1; /* gen tag */ - /* subtract 1 day of seconds for more compliance */ then = now - 86400; expandedTime = XGMTIME(&then, tmpTime); @@ -5957,11 +5952,13 @@ static int SetValidity(byte* output, int daysValid) localTime.tm_year += 1900; localTime.tm_mon += 1; - SetTime(&localTime, before + beforeSz); - beforeSz += ASN_GEN_TIME_SZ; - - after[0] = ASN_GENERALIZED_TIME; - afterSz = SetLength(ASN_GEN_TIME_SZ, after + 1) + 1; /* gen tag */ + format = ValidityTimeFormat(&localTime); + timeSz = (format == ASN_UTC_TIME) ? ASN_UTC_TIME_SIZE - 1 + : ASN_GEN_TIME_SZ; + before[0] = format; + beforeSz = SetLength(timeSz, before + 1) + 1; + SetTime(&localTime, before + beforeSz, format); + beforeSz += timeSz; /* add daysValid of seconds */ then = now + (daysValid * (time_t)86400); @@ -5976,8 +5973,13 @@ static int SetValidity(byte* output, int daysValid) localTime.tm_year += 1900; localTime.tm_mon += 1; - SetTime(&localTime, after + afterSz); - afterSz += ASN_GEN_TIME_SZ; + format = ValidityTimeFormat(&localTime); + timeSz = (format == ASN_UTC_TIME) ? ASN_UTC_TIME_SIZE - 1 + : ASN_GEN_TIME_SZ; + after[0] = format; + afterSz = SetLength(timeSz, after + 1) + 1; + SetTime(&localTime, after + afterSz, format); + afterSz += timeSz; /* headers and output */ seqSz = SetSequence(beforeSz + afterSz, output); diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index ec68a86093..0ea17aac70 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -30820,6 +30820,120 @@ static wc_test_ret_t rsa_even_mod_test(WC_RNG* rng, RsaKey* key) #endif /* WOLFSSL_HAVE_SP_RSA */ #if defined(WOLFSSL_CERT_GEN) && !defined(NO_ASN_TIME) && !defined(WOLFSSL_NO_MALLOC) +#ifdef WOLFSSL_TEST_CERT +/* RFC 5280: validity dates through 2049 encode as UTCTime, 2050 and later as + * GeneralizedTime. Returns 0 when the decoded date TLV honors that rule. */ +static int cert_time_format_ok(const byte* dateTLV) +{ + int year; + + if (dateTLV == NULL) + return -1; + if (dateTLV[0] == ASN_UTC_TIME) + return 0; + if (dateTLV[0] != ASN_GENERALIZED_TIME) + return -1; + year = (dateTLV[2] - '0') * 1000 + (dateTLV[3] - '0') * 100 + + (dateTLV[4] - '0') * 10 + (dateTLV[5] - '0'); + return ((year < 1950) || (year >= 2050)) ? 0 : -1; +} + +/* Decode the calendar year from an ASN.1 date TLV, inverting the two-digit + * UTCTime year, so a wrong encoded year is caught. Returns -1 on error. */ +static int cert_date_year(const byte* dateTLV) +{ + int yy; + + if (dateTLV == NULL) + return -1; + if (dateTLV[0] == ASN_UTC_TIME) { + yy = (dateTLV[2] - '0') * 10 + (dateTLV[3] - '0'); + return (yy < 50) ? 2000 + yy : 1900 + yy; + } + if (dateTLV[0] == ASN_GENERALIZED_TIME) { + return (dateTLV[2] - '0') * 1000 + (dateTLV[3] - '0') * 100 + + (dateTLV[4] - '0') * 10 + (dateTLV[5] - '0'); + } + return -1; +} + +static time_t certGenBoundaryTime; +static time_t cert_gen_boundary_time_cb(time_t* t) +{ + if (t != NULL) + *t = certGenBoundaryTime; + return certGenBoundaryTime; +} + +/* Deterministically exercise the RFC 5280 UTCTime/GeneralizedTime boundary by + * forcing the clock to fixed mid-2049 and mid-2050 dates. Those epochs need a + * 64-bit time_t, so 32-bit-time_t builds skip the check. */ +static wc_test_ret_t cert_gen_time_boundary_test(Cert* cert, byte* der, + RsaKey* key, WC_RNG* rng, DecodedCert* decode) +{ + static const struct { + time_t now; + int daysValid; + byte beforeTag; + byte afterTag; + int beforeYear; + int afterYear; + } cases[] = { + { (time_t)2508710400UL, 100, ASN_UTC_TIME, ASN_UTC_TIME, + 2049, 2049 }, + { (time_t)2540246400UL, 100, ASN_GENERALIZED_TIME, ASN_GENERALIZED_TIME, + 2050, 2050 }, + { (time_t)2508710400UL, 400, ASN_UTC_TIME, ASN_GENERALIZED_TIME, + 2049, 2050 } + }; + wc_test_ret_t ret = 0; + int tSz = (int)sizeof(time_t); + int tSigned = ((time_t)-1 < 0); + int certSz; + int i; + + /* The mid-2050 epoch needs a time_t that reaches past 2038: 64-bit, or + * unsigned 32-bit (good to 2106). Signed 32-bit cannot, so skip it. */ + if ((tSz < 8) && tSigned) + return 0; + + for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) { + certGenBoundaryTime = cases[i].now; + ret = wc_SetTimeCb(cert_gen_boundary_time_cb); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + + cert->daysValid = cases[i].daysValid; + ret = 0; + WC_TEST_RSA_ASYNC_DO(&key->asyncDev, + wc_MakeSelfCert(cert, der, FOURK_BUF, key, rng)); + (void)wc_SetTimeCb(NULL); + if (ret < 0) + return WC_TEST_RET_ENC_EC(ret); + certSz = (int)ret; + + InitDecodedCert(decode, der, certSz, HEAP_HINT); + ret = ParseCert(decode, CERT_TYPE, NO_VERIFY, 0); + if (ret == 0) { + if ((decode->beforeDate == NULL) || (decode->afterDate == NULL) || + (decode->beforeDate[0] != cases[i].beforeTag) || + (decode->afterDate[0] != cases[i].afterTag) || + (cert_date_year(decode->beforeDate) != cases[i].beforeYear) || + (cert_date_year(decode->afterDate) != cases[i].afterYear)) { + ret = WC_TEST_RET_ENC_NC; + } + } + else { + ret = WC_TEST_RET_ENC_EC(ret); + } + FreeDecodedCert(decode); + if (ret != 0) + return ret; + } + + return 0; +} +#endif static wc_test_ret_t rsa_certgen_test(RsaKey* key, RsaKey* keypub, WC_RNG* rng, byte* tmp) { #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) @@ -30946,6 +31060,12 @@ static wc_test_ret_t rsa_certgen_test(RsaKey* key, RsaKey* keypub, WC_RNG* rng, FreeDecodedCert(decode); ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa); } + /* Verify the generated validity dates use the RFC 5280 time format. */ + if ((cert_time_format_ok(decode->beforeDate) != 0) || + (cert_time_format_ok(decode->afterDate) != 0)) { + FreeDecodedCert(decode); + ERROR_OUT(WC_TEST_RET_ENC_NC, exit_rsa); + } FreeDecodedCert(decode); #endif @@ -30955,6 +31075,13 @@ static wc_test_ret_t rsa_certgen_test(RsaKey* key, RsaKey* keypub, WC_RNG* rng, goto exit_rsa; } +#ifdef WOLFSSL_TEST_CERT + ret = cert_gen_time_boundary_test(myCert, der, key, rng, decode); + if (ret != 0) { + goto exit_rsa; + } +#endif + /* Setup Certificate */ ret = wc_InitCert_ex(myCert, HEAP_HINT, devId); if (ret < 0) {