Skip to content

Commit 8b824e7

Browse files
committed
F-9961 - Encode default cert validity as UTCTime through 2049
1 parent 9ab8a4b commit 8b824e7

3 files changed

Lines changed: 188 additions & 45 deletions

File tree

wolfcrypt/src/asn.c

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28292,15 +28292,32 @@ static WC_INLINE byte itob(int number)
2829228292
}
2829328293

2829428294

28295-
/* write time to output, format */
28296-
static void SetTime(struct tm* date, byte* output)
28295+
/* RFC 5280: validity dates through 2049 encode as UTCTime, 2050 and later as
28296+
* GeneralizedTime. date->tm_year holds the full year here. */
28297+
static byte ValidityTimeFormat(const struct tm* date)
28298+
{
28299+
if (date->tm_year >= 1950 && date->tm_year < 2050)
28300+
return ASN_UTC_TIME;
28301+
return ASN_GENERALIZED_TIME;
28302+
}
28303+
28304+
/* write time value to output in the given ASN.1 format */
28305+
static void SetTime(struct tm* date, byte* output, byte format)
2829728306
{
2829828307
int i = 0;
28308+
int year = date->tm_year;
2829928309

28300-
output[i++] = itob((date->tm_year % 10000) / 1000);
28301-
output[i++] = itob((date->tm_year % 1000) / 100);
28302-
output[i++] = itob((date->tm_year % 100) / 10);
28303-
output[i++] = itob( date->tm_year % 10);
28310+
if (format == ASN_UTC_TIME) {
28311+
year %= 100;
28312+
output[i++] = itob((year / 10) % 10);
28313+
output[i++] = itob( year % 10);
28314+
}
28315+
else {
28316+
output[i++] = itob((year % 10000) / 1000);
28317+
output[i++] = itob((year % 1000) / 100);
28318+
output[i++] = itob((year % 100) / 10);
28319+
output[i++] = itob( year % 10);
28320+
}
2830428321

2830528322
output[i++] = itob(date->tm_mon / 10);
2830628323
output[i++] = itob(date->tm_mon % 10);
@@ -30063,6 +30080,8 @@ static int SetValidity(byte* before, byte* after, int daysValid)
3006330080
{
3006430081
#ifndef NO_ASN_TIME
3006530082
int ret = 0;
30083+
byte format;
30084+
word32 timeSz;
3006630085
time_t now;
3006730086
time_t then;
3006830087
struct tm* tmpTime;
@@ -30093,7 +30112,12 @@ static int SetValidity(byte* before, byte* after, int daysValid)
3009330112
localTime.tm_year += 1900;
3009430113
localTime.tm_mon += 1;
3009530114

30096-
SetTime(&localTime, before);
30115+
format = ValidityTimeFormat(&localTime);
30116+
timeSz = (format == ASN_UTC_TIME) ? ASN_UTC_TIME_SIZE - 1
30117+
: ASN_GEN_TIME_SZ;
30118+
before[0] = format;
30119+
SetLength(timeSz, before + 1);
30120+
SetTime(&localTime, before + 2, format);
3009730121

3009830122
/* add daysValid of seconds */
3009930123
then = now + (daysValid * (time_t)86400);
@@ -30110,7 +30134,12 @@ static int SetValidity(byte* before, byte* after, int daysValid)
3011030134
localTime.tm_year += 1900;
3011130135
localTime.tm_mon += 1;
3011230136

30113-
SetTime(&localTime, after);
30137+
format = ValidityTimeFormat(&localTime);
30138+
timeSz = (format == ASN_UTC_TIME) ? ASN_UTC_TIME_SIZE - 1
30139+
: ASN_GEN_TIME_SZ;
30140+
after[0] = format;
30141+
SetLength(timeSz, after + 1);
30142+
SetTime(&localTime, after + 2, format);
3011430143
}
3011530144

3011630145
return ret;
@@ -30803,6 +30832,8 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
3080330832
int ret = 0;
3080430833
word32 issRawLen = 0;
3080530834
word32 sbjRawLen = 0;
30835+
byte localBefore[MAX_DATE_SIZE];
30836+
byte localAfter[MAX_DATE_SIZE];
3080630837

3080730838
/* Unused without PQC */
3080830839
(void)falconKey;
@@ -31028,16 +31059,35 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
3102831059
}
3102931060
else
3103031061
{
31031-
/* Don't put out UTC before data. */
31032-
dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC].noOut = 1;
31033-
/* Make space for before date data. */
31034-
SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT],
31035-
NULL, ASN_GEN_TIME_SZ);
31036-
/* Don't put out UTC after data. */
31037-
dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC].noOut = 1;
31038-
/* Make space for after date data. */
31039-
SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT],
31040-
NULL, ASN_GEN_TIME_SZ);
31062+
/* Compute default validity dates; SetValidity picks UTCTime or
31063+
* Generalized Time per RFC 5280 based on the year. */
31064+
ret = SetValidity(localBefore, localAfter, cert->daysValid);
31065+
if (ret == 0) {
31066+
if (localBefore[0] == ASN_UTC_TIME) {
31067+
SetASN_Buffer(
31068+
&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC],
31069+
localBefore + 2, ASN_UTC_TIME_SIZE - 1);
31070+
dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT].noOut = 1;
31071+
}
31072+
else {
31073+
dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC].noOut = 1;
31074+
SetASN_Buffer(
31075+
&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT],
31076+
localBefore + 2, ASN_GEN_TIME_SZ);
31077+
}
31078+
if (localAfter[0] == ASN_UTC_TIME) {
31079+
SetASN_Buffer(
31080+
&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC],
31081+
localAfter + 2, ASN_UTC_TIME_SIZE - 1);
31082+
dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT].noOut = 1;
31083+
}
31084+
else {
31085+
dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC].noOut = 1;
31086+
SetASN_Buffer(
31087+
&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT],
31088+
localAfter + 2, ASN_GEN_TIME_SZ);
31089+
}
31090+
}
3104131091
}
3104231092
if (sbjRawLen > 0) {
3104331093
/* Put in encoded subject name. */
@@ -31075,7 +31125,9 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
3107531125
X509CERTASN_IDX_SIGNATURE);
3107631126

3107731127
/* Calculate encoded certificate body size. */
31078-
ret = SizeASN_Items(x509CertASN, dataASN, x509CertASN_Length, &sz);
31128+
if (ret >= 0) {
31129+
ret = SizeASN_Items(x509CertASN, dataASN, x509CertASN_Length, &sz);
31130+
}
3107931131
}
3108031132
/* Check buffer is big enough for encoded data. */
3108131133
if ((ret == 0) && (sz > derSz)) {
@@ -31106,18 +31158,6 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
3110631158
dataASN[X509CERTASN_IDX_TBS_SUBJECT_SEQ].data.buffer.length,
3110731159
&cert->subject, cert->heap);
3110831160
}
31109-
if (ret >= 0) {
31110-
if (cert->beforeDateSz == 0 || cert->afterDateSz == 0)
31111-
{
31112-
/* Encode validity into buffer. */
31113-
/* safe casts -- the pointers are actually inside derBuffer. */
31114-
ret = SetValidity(
31115-
(byte*)(wc_ptr_t)dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT]
31116-
.data.buffer.data,
31117-
(byte*)(wc_ptr_t)dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT]
31118-
.data.buffer.data, cert->daysValid);
31119-
}
31120-
}
3112131161
if (ret >= 0) {
3112231162
/* Encode public key into buffer. */
3112331163
/* safe cast -- the pointer is actually inside derBuffer. */

wolfcrypt/src/asn_orig.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5911,17 +5911,15 @@ int SetNameEx(byte* output, word32 outputSz, CertName* name, void* heap)
59115911

59125912
/* Set Date validity from now until now + daysValid
59135913
* return size in bytes written to output, 0 on error */
5914-
/* TODO https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5
5915-
* "MUST always encode certificate validity dates through the year 2049 as
5916-
* UTCTime; certificate validity dates in 2050 or later MUST be encoded as
5917-
* GeneralizedTime." */
59185914
static int SetValidity(byte* output, int daysValid)
59195915
{
59205916
#ifndef NO_ASN_TIME
59215917
byte before[MAX_DATE_SIZE];
59225918
byte after[MAX_DATE_SIZE];
59235919

59245920
word32 beforeSz, afterSz, seqSz;
5921+
word32 timeSz;
5922+
byte format;
59255923

59265924
time_t now;
59275925
time_t then;
@@ -5941,9 +5939,6 @@ static int SetValidity(byte* output, int daysValid)
59415939
now = wc_Time(0);
59425940

59435941
/* before now */
5944-
before[0] = ASN_GENERALIZED_TIME;
5945-
beforeSz = SetLength(ASN_GEN_TIME_SZ, before + 1) + 1; /* gen tag */
5946-
59475942
/* subtract 1 day of seconds for more compliance */
59485943
then = now - 86400;
59495944
expandedTime = XGMTIME(&then, tmpTime);
@@ -5957,11 +5952,13 @@ static int SetValidity(byte* output, int daysValid)
59575952
localTime.tm_year += 1900;
59585953
localTime.tm_mon += 1;
59595954

5960-
SetTime(&localTime, before + beforeSz);
5961-
beforeSz += ASN_GEN_TIME_SZ;
5962-
5963-
after[0] = ASN_GENERALIZED_TIME;
5964-
afterSz = SetLength(ASN_GEN_TIME_SZ, after + 1) + 1; /* gen tag */
5955+
format = ValidityTimeFormat(&localTime);
5956+
timeSz = (format == ASN_UTC_TIME) ? ASN_UTC_TIME_SIZE - 1
5957+
: ASN_GEN_TIME_SZ;
5958+
before[0] = format;
5959+
beforeSz = SetLength(timeSz, before + 1) + 1;
5960+
SetTime(&localTime, before + beforeSz, format);
5961+
beforeSz += timeSz;
59655962

59665963
/* add daysValid of seconds */
59675964
then = now + (daysValid * (time_t)86400);
@@ -5976,8 +5973,13 @@ static int SetValidity(byte* output, int daysValid)
59765973
localTime.tm_year += 1900;
59775974
localTime.tm_mon += 1;
59785975

5979-
SetTime(&localTime, after + afterSz);
5980-
afterSz += ASN_GEN_TIME_SZ;
5976+
format = ValidityTimeFormat(&localTime);
5977+
timeSz = (format == ASN_UTC_TIME) ? ASN_UTC_TIME_SIZE - 1
5978+
: ASN_GEN_TIME_SZ;
5979+
after[0] = format;
5980+
afterSz = SetLength(timeSz, after + 1) + 1;
5981+
SetTime(&localTime, after + afterSz, format);
5982+
afterSz += timeSz;
59815983

59825984
/* headers and output */
59835985
seqSz = SetSequence(beforeSz + afterSz, output);

wolfcrypt/test/test.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30820,6 +30820,94 @@ static wc_test_ret_t rsa_even_mod_test(WC_RNG* rng, RsaKey* key)
3082030820
#endif /* WOLFSSL_HAVE_SP_RSA */
3082130821

3082230822
#if defined(WOLFSSL_CERT_GEN) && !defined(NO_ASN_TIME) && !defined(WOLFSSL_NO_MALLOC)
30823+
#ifdef WOLFSSL_TEST_CERT
30824+
/* RFC 5280: validity dates through 2049 encode as UTCTime, 2050 and later as
30825+
* GeneralizedTime. Returns 0 when the decoded date TLV honors that rule. */
30826+
static int cert_time_format_ok(const byte* dateTLV)
30827+
{
30828+
int year;
30829+
30830+
if (dateTLV == NULL)
30831+
return -1;
30832+
if (dateTLV[0] == ASN_UTC_TIME)
30833+
return 0;
30834+
if (dateTLV[0] != ASN_GENERALIZED_TIME)
30835+
return -1;
30836+
year = (dateTLV[2] - '0') * 1000 + (dateTLV[3] - '0') * 100 +
30837+
(dateTLV[4] - '0') * 10 + (dateTLV[5] - '0');
30838+
return ((year < 1950) || (year >= 2050)) ? 0 : -1;
30839+
}
30840+
30841+
static time_t certGenBoundaryTime;
30842+
static time_t cert_gen_boundary_time_cb(time_t* t)
30843+
{
30844+
if (t != NULL)
30845+
*t = certGenBoundaryTime;
30846+
return certGenBoundaryTime;
30847+
}
30848+
30849+
/* Deterministically exercise the RFC 5280 UTCTime/GeneralizedTime boundary by
30850+
* forcing the clock to fixed mid-2049 and mid-2050 dates. Those epochs need a
30851+
* 64-bit time_t, so 32-bit-time_t builds skip the check. */
30852+
static wc_test_ret_t cert_gen_time_boundary_test(Cert* cert, byte* der,
30853+
RsaKey* key, WC_RNG* rng, DecodedCert* decode)
30854+
{
30855+
static const struct {
30856+
time_t now;
30857+
int daysValid;
30858+
byte beforeTag;
30859+
byte afterTag;
30860+
} cases[] = {
30861+
{ (time_t)2508710400UL, 100, ASN_UTC_TIME, ASN_UTC_TIME },
30862+
{ (time_t)2540246400UL, 100, ASN_GENERALIZED_TIME, ASN_GENERALIZED_TIME },
30863+
{ (time_t)2508710400UL, 400, ASN_UTC_TIME, ASN_GENERALIZED_TIME }
30864+
};
30865+
wc_test_ret_t ret = 0;
30866+
int tSz = (int)sizeof(time_t);
30867+
int tSigned = ((time_t)-1 < 0);
30868+
int certSz;
30869+
int i;
30870+
30871+
/* The mid-2050 epoch needs a time_t that reaches past 2038: 64-bit, or
30872+
* unsigned 32-bit (good to 2106). Signed 32-bit cannot, so skip it. */
30873+
if ((tSz < 8) && tSigned)
30874+
return 0;
30875+
30876+
for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) {
30877+
certGenBoundaryTime = cases[i].now;
30878+
ret = wc_SetTimeCb(cert_gen_boundary_time_cb);
30879+
if (ret != 0)
30880+
return WC_TEST_RET_ENC_EC(ret);
30881+
30882+
cert->daysValid = cases[i].daysValid;
30883+
ret = 0;
30884+
WC_TEST_RSA_ASYNC_DO(&key->asyncDev,
30885+
wc_MakeSelfCert(cert, der, FOURK_BUF, key, rng));
30886+
(void)wc_SetTimeCb(NULL);
30887+
if (ret < 0)
30888+
return WC_TEST_RET_ENC_EC(ret);
30889+
certSz = (int)ret;
30890+
30891+
InitDecodedCert(decode, der, certSz, HEAP_HINT);
30892+
ret = ParseCert(decode, CERT_TYPE, NO_VERIFY, 0);
30893+
if (ret == 0) {
30894+
if ((decode->beforeDate == NULL) || (decode->afterDate == NULL) ||
30895+
(decode->beforeDate[0] != cases[i].beforeTag) ||
30896+
(decode->afterDate[0] != cases[i].afterTag)) {
30897+
ret = WC_TEST_RET_ENC_NC;
30898+
}
30899+
}
30900+
else {
30901+
ret = WC_TEST_RET_ENC_EC(ret);
30902+
}
30903+
FreeDecodedCert(decode);
30904+
if (ret != 0)
30905+
return ret;
30906+
}
30907+
30908+
return 0;
30909+
}
30910+
#endif
3082330911
static wc_test_ret_t rsa_certgen_test(RsaKey* key, RsaKey* keypub, WC_RNG* rng, byte* tmp)
3082430912
{
3082530913
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
@@ -30946,6 +31034,12 @@ static wc_test_ret_t rsa_certgen_test(RsaKey* key, RsaKey* keypub, WC_RNG* rng,
3094631034
FreeDecodedCert(decode);
3094731035
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa);
3094831036
}
31037+
/* Verify the generated validity dates use the RFC 5280 time format. */
31038+
if ((cert_time_format_ok(decode->beforeDate) != 0) ||
31039+
(cert_time_format_ok(decode->afterDate) != 0)) {
31040+
FreeDecodedCert(decode);
31041+
ERROR_OUT(WC_TEST_RET_ENC_NC, exit_rsa);
31042+
}
3094931043
FreeDecodedCert(decode);
3095031044
#endif
3095131045

@@ -30955,6 +31049,13 @@ static wc_test_ret_t rsa_certgen_test(RsaKey* key, RsaKey* keypub, WC_RNG* rng,
3095531049
goto exit_rsa;
3095631050
}
3095731051

31052+
#ifdef WOLFSSL_TEST_CERT
31053+
ret = cert_gen_time_boundary_test(myCert, der, key, rng, decode);
31054+
if (ret != 0) {
31055+
goto exit_rsa;
31056+
}
31057+
#endif
31058+
3095831059
/* Setup Certificate */
3095931060
ret = wc_InitCert_ex(myCert, HEAP_HINT, devId);
3096031061
if (ret < 0) {

0 commit comments

Comments
 (0)