Validate timestamps in mbedtls_x509write_crt_set_validity

Only the length of not_before and not_after was checked, so malformed
timestamps were written into the certificate as is, and a year before
1950 was written as a UTCTime that reads back as 20YY.

Signed-off-by: Naveed <naveed@bugqore.com>
This commit is contained in:
Naveed 2026-09-20 15:45:20 +05:30
parent 3361ae00b4
commit b74390eb70
3 changed files with 60 additions and 3 deletions

View File

@ -0,0 +1,7 @@
Bugfix
* mbedtls_x509write_crt_set_validity() only checked the length of the
not_before and not_after strings, so timestamps with non-digit
characters or out-of-range fields were written into the certificate
as is, and a year before 1950 was written as a UTCTime that reads
back as a year in 2000-2049. Such timestamps are now rejected with
MBEDTLS_ERR_X509_BAD_INPUT_DATA.

View File

@ -975,7 +975,9 @@ int mbedtls_x509write_crt_set_serial_raw(mbedtls_x509write_cert *ctx,
* \param not_after not_after timestamp
*
* \return 0 if timestamp was parsed successfully, or
* a specific error code
* #MBEDTLS_ERR_X509_BAD_INPUT_DATA if a timestamp is not
* a valid date and time in the above format, or if its
* year is before 1950.
*/
int mbedtls_x509write_crt_set_validity(mbedtls_x509write_cert *ctx, const char *not_before,
const char *not_after);

View File

@ -105,12 +105,60 @@ int mbedtls_x509write_crt_set_serial_raw(mbedtls_x509write_cert *ctx,
return 0;
}
/*
* Check that a timestamp is a valid date and time in the format
* "YYYYMMDDhhmmss" that x509_write_time() can encode.
*/
static int x509_write_check_time(const char *t)
{
static const unsigned char days_in_month[12] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/* century, year in century, month, day, hour, minute, second */
unsigned int field[7];
unsigned int year, mon, month_days;
if (strlen(t) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1) {
return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
}
for (size_t i = 0; i < 7; i++) {
unsigned int d1 = (unsigned int) ((unsigned char) t[2 * i] - '0');
unsigned int d2 = (unsigned int) ((unsigned char) t[2 * i + 1] - '0');
if (d1 > 9 || d2 > 9) {
return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
}
field[i] = d1 * 10 + d2;
}
year = field[0] * 100 + field[1];
mon = field[2];
/* Years before 2050 are written as UTCTime, which cannot represent
* a year before 1950: it would be read back as 20YY (RFC 5280
* section 4.1.2.5.1). */
if (year < 1950 || mon < 1 || mon > 12) {
return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
}
month_days = days_in_month[mon - 1];
if (mon == 2 && year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) {
month_days = 29;
}
if (field[3] < 1 || field[3] > month_days ||
field[4] > 23 || field[5] > 59 || field[6] > 59) {
return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
}
return 0;
}
int mbedtls_x509write_crt_set_validity(mbedtls_x509write_cert *ctx,
const char *not_before,
const char *not_after)
{
if (strlen(not_before) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1 ||
strlen(not_after) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1) {
if (x509_write_check_time(not_before) != 0 ||
x509_write_check_time(not_after) != 0) {
return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
}
strncpy(ctx->not_before, not_before, MBEDTLS_X509_RFC5280_UTC_TIME_LEN);