Merge pull request #6003 from gstrauss/x509_time

mbedtls_x509_time performance and reduce memory use
This commit is contained in:
Tom Cosgrove 2023-07-06 09:28:14 +01:00 committed by GitHub
commit 836aed7cf8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 147 additions and 162 deletions

View file

@ -0,0 +1,3 @@
Features
* Improve mbedtls_x509_time performance and reduce memory use.
* Reduce syscalls to time() during certificate verification.

View file

@ -366,6 +366,31 @@ static inline mbedtls_x509_name *mbedtls_x509_dn_get_next(
*/ */
int mbedtls_x509_serial_gets(char *buf, size_t size, const mbedtls_x509_buf *serial); int mbedtls_x509_serial_gets(char *buf, size_t size, const mbedtls_x509_buf *serial);
/**
* \brief Compare pair of mbedtls_x509_time.
*
* \param t1 mbedtls_x509_time to compare
* \param t2 mbedtls_x509_time to compare
*
* \return < 0 if t1 is before t2
* 0 if t1 equals t2
* > 0 if t1 is after t2
*/
int mbedtls_x509_time_cmp(const mbedtls_x509_time *t1, const mbedtls_x509_time *t2);
#if defined(MBEDTLS_HAVE_TIME_DATE)
/**
* \brief Fill mbedtls_x509_time with provided mbedtls_time_t.
*
* \param tt mbedtls_time_t to convert
* \param now mbedtls_x509_time to fill with converted mbedtls_time_t
*
* \return \c 0 on success
* \return A non-zero return value on failure.
*/
int mbedtls_x509_time_gmtime(mbedtls_time_t tt, mbedtls_x509_time *now);
#endif /* MBEDTLS_HAVE_TIME_DATE */
/** /**
* \brief Check a given mbedtls_x509_time against the system time * \brief Check a given mbedtls_x509_time against the system time
* and tell if it's in the past. * and tell if it's in the past.

View file

@ -573,117 +573,82 @@ error:
return ret; return ret;
} }
static int x509_parse_int(unsigned char **p, size_t n, int *res) static int x509_date_is_valid(const mbedtls_x509_time *t)
{ {
*res = 0; unsigned int month_days;
unsigned int year;
for (; n > 0; --n) { switch (t->mon) {
if ((**p < '0') || (**p > '9')) { case 1: case 3: case 5: case 7: case 8: case 10: case 12:
month_days = 31;
break;
case 4: case 6: case 9: case 11:
month_days = 30;
break;
case 2:
year = (unsigned int) t->year;
month_days = ((year & 3) || (!(year % 100)
&& (year % 400)))
? 28 : 29;
break;
default:
return MBEDTLS_ERR_X509_INVALID_DATE; return MBEDTLS_ERR_X509_INVALID_DATE;
} }
*res *= 10; if ((unsigned int) (t->day - 1) >= month_days || /* (1 - days in month) */
*res += (*(*p)++ - '0'); /* (unsigned int) (t->mon - 1) >= 12 || */ /* (1 - 12) checked above */
(unsigned int) t->year > 9999 || /* (0 - 9999) */
(unsigned int) t->hour > 23 || /* (0 - 23) */
(unsigned int) t->min > 59 || /* (0 - 59) */
(unsigned int) t->sec > 59) { /* (0 - 59) */
return MBEDTLS_ERR_X509_INVALID_DATE;
} }
return 0; return 0;
} }
static int x509_date_is_valid(const mbedtls_x509_time *t) static int x509_parse2_int(const unsigned char *p)
{ {
int ret = MBEDTLS_ERR_X509_INVALID_DATE; uint32_t d1 = p[0] - '0';
int month_len; uint32_t d2 = p[1] - '0';
return (d1 < 10 && d2 < 10) ? (int) (d1 * 10 + d2) : -1;
CHECK_RANGE(0, 9999, t->year);
CHECK_RANGE(0, 23, t->hour);
CHECK_RANGE(0, 59, t->min);
CHECK_RANGE(0, 59, t->sec);
switch (t->mon) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
month_len = 31;
break;
case 4: case 6: case 9: case 11:
month_len = 30;
break;
case 2:
if ((!(t->year % 4) && t->year % 100) ||
!(t->year % 400)) {
month_len = 29;
} else {
month_len = 28;
}
break;
default:
return ret;
}
CHECK_RANGE(1, month_len, t->day);
return 0;
} }
/* /*
* Parse an ASN1_UTC_TIME (yearlen=2) or ASN1_GENERALIZED_TIME (yearlen=4) * Parse an ASN1_UTC_TIME (yearlen=2) or ASN1_GENERALIZED_TIME (yearlen=4)
* field. * field.
*/ */
static int x509_parse_time(unsigned char **p, size_t len, size_t yearlen, static int x509_parse_time(const unsigned char *p, mbedtls_x509_time *tm,
mbedtls_x509_time *tm) size_t yearlen)
{ {
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; int x;
/* /*
* Minimum length is 10 or 12 depending on yearlen * Parse year, month, day, hour, minute, second
*/ */
if (len < yearlen + 8) { tm->year = x509_parse2_int(p);
if (tm->year < 0) {
return MBEDTLS_ERR_X509_INVALID_DATE; return MBEDTLS_ERR_X509_INVALID_DATE;
} }
len -= yearlen + 8;
/* if (4 == yearlen) {
* Parse year, month, day, hour, minute x = tm->year * 100;
*/ p += 2;
CHECK(x509_parse_int(p, yearlen, &tm->year)); tm->year = x509_parse2_int(p);
if (2 == yearlen) { if (tm->year < 0) {
if (tm->year < 50) { return MBEDTLS_ERR_X509_INVALID_DATE;
tm->year += 100;
} }
tm->year += 1900;
}
CHECK(x509_parse_int(p, 2, &tm->mon));
CHECK(x509_parse_int(p, 2, &tm->day));
CHECK(x509_parse_int(p, 2, &tm->hour));
CHECK(x509_parse_int(p, 2, &tm->min));
/*
* Parse seconds if present
*/
if (len >= 2) {
CHECK(x509_parse_int(p, 2, &tm->sec));
len -= 2;
} else { } else {
return MBEDTLS_ERR_X509_INVALID_DATE; x = (tm->year < 50) ? 2000 : 1900;
} }
tm->year += x;
/* tm->mon = x509_parse2_int(p + 2);
* Parse trailing 'Z' if present tm->day = x509_parse2_int(p + 4);
*/ tm->hour = x509_parse2_int(p + 6);
if (1 == len && 'Z' == **p) { tm->min = x509_parse2_int(p + 8);
(*p)++; tm->sec = x509_parse2_int(p + 10);
len--;
}
/* return x509_date_is_valid(tm);
* We should have parsed all characters at this point
*/
if (0 != len) {
return MBEDTLS_ERR_X509_INVALID_DATE;
}
CHECK(x509_date_is_valid(tm));
return 0;
} }
/* /*
@ -721,7 +686,14 @@ int mbedtls_x509_get_time(unsigned char **p, const unsigned char *end,
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_DATE, ret); return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_DATE, ret);
} }
return x509_parse_time(p, len, year_len, tm); /* len is 12 or 14 depending on year_len, plus optional trailing 'Z' */
if (len != year_len + 10 &&
!(len == year_len + 11 && (*p)[(len - 1)] == 'Z')) {
return MBEDTLS_ERR_X509_INVALID_DATE;
}
(*p) += len;
return x509_parse_time(*p - len, tm, year_len);
} }
int mbedtls_x509_get_sig(unsigned char **p, const unsigned char *end, mbedtls_x509_buf *sig) int mbedtls_x509_get_sig(unsigned char **p, const unsigned char *end, mbedtls_x509_buf *sig)
@ -1002,81 +974,45 @@ int mbedtls_x509_key_size_helper(char *buf, size_t buf_size, const char *name)
return 0; return 0;
} }
#if defined(MBEDTLS_HAVE_TIME_DATE) int mbedtls_x509_time_cmp(const mbedtls_x509_time *t1,
/* const mbedtls_x509_time *t2)
* Set the time structure to the current time.
* Return 0 on success, non-zero on failure.
*/
static int x509_get_current_time(mbedtls_x509_time *now)
{ {
struct tm *lt, tm_buf; int x;
mbedtls_time_t tt;
int ret = 0;
tt = mbedtls_time(NULL); x = (((t1->year << 9) | (t1->mon << 5) | (t1->day)) -
lt = mbedtls_platform_gmtime_r(&tt, &tm_buf); ((t2->year << 9) | (t2->mon << 5) | (t2->day)));
if (x != 0) {
if (lt == NULL) { return x;
ret = -1;
} else {
now->year = lt->tm_year + 1900;
now->mon = lt->tm_mon + 1;
now->day = lt->tm_mday;
now->hour = lt->tm_hour;
now->min = lt->tm_min;
now->sec = lt->tm_sec;
} }
return ret; x = (((t1->hour << 12) | (t1->min << 6) | (t1->sec)) -
((t2->hour << 12) | (t2->min << 6) | (t2->sec)));
return x;
} }
/* #if defined(MBEDTLS_HAVE_TIME_DATE)
* Return 0 if before <= after, 1 otherwise int mbedtls_x509_time_gmtime(mbedtls_time_t tt, mbedtls_x509_time *now)
*/
static int x509_check_time(const mbedtls_x509_time *before, const mbedtls_x509_time *after)
{ {
if (before->year > after->year) { struct tm tm;
return 1;
} if (mbedtls_platform_gmtime_r(&tt, &tm) == NULL) {
return -1;
if (before->year == after->year &&
before->mon > after->mon) {
return 1;
}
if (before->year == after->year &&
before->mon == after->mon &&
before->day > after->day) {
return 1;
}
if (before->year == after->year &&
before->mon == after->mon &&
before->day == after->day &&
before->hour > after->hour) {
return 1;
}
if (before->year == after->year &&
before->mon == after->mon &&
before->day == after->day &&
before->hour == after->hour &&
before->min > after->min) {
return 1;
}
if (before->year == after->year &&
before->mon == after->mon &&
before->day == after->day &&
before->hour == after->hour &&
before->min == after->min &&
before->sec > after->sec) {
return 1;
} }
now->year = tm.tm_year + 1900;
now->mon = tm.tm_mon + 1;
now->day = tm.tm_mday;
now->hour = tm.tm_hour;
now->min = tm.tm_min;
now->sec = tm.tm_sec;
return 0; return 0;
} }
static int x509_get_current_time(mbedtls_x509_time *now)
{
return mbedtls_x509_time_gmtime(mbedtls_time(NULL), now);
}
int mbedtls_x509_time_is_past(const mbedtls_x509_time *to) int mbedtls_x509_time_is_past(const mbedtls_x509_time *to)
{ {
mbedtls_x509_time now; mbedtls_x509_time now;
@ -1085,7 +1021,7 @@ int mbedtls_x509_time_is_past(const mbedtls_x509_time *to)
return 1; return 1;
} }
return x509_check_time(&now, to); return mbedtls_x509_time_cmp(to, &now) < 0;
} }
int mbedtls_x509_time_is_future(const mbedtls_x509_time *from) int mbedtls_x509_time_is_future(const mbedtls_x509_time *from)
@ -1096,7 +1032,7 @@ int mbedtls_x509_time_is_future(const mbedtls_x509_time *from)
return 1; return 1;
} }
return x509_check_time(from, &now); return mbedtls_x509_time_cmp(from, &now) > 0;
} }
#else /* MBEDTLS_HAVE_TIME_DATE */ #else /* MBEDTLS_HAVE_TIME_DATE */

View file

@ -2020,7 +2020,8 @@ int mbedtls_x509_crt_is_revoked(const mbedtls_x509_crt *crt, const mbedtls_x509_
*/ */
static int x509_crt_verifycrl(mbedtls_x509_crt *crt, mbedtls_x509_crt *ca, static int x509_crt_verifycrl(mbedtls_x509_crt *crt, mbedtls_x509_crt *ca,
mbedtls_x509_crl *crl_list, mbedtls_x509_crl *crl_list,
const mbedtls_x509_crt_profile *profile) const mbedtls_x509_crt_profile *profile,
const mbedtls_x509_time *now)
{ {
int flags = 0; int flags = 0;
unsigned char hash[MBEDTLS_MD_MAX_SIZE]; unsigned char hash[MBEDTLS_MD_MAX_SIZE];
@ -2098,16 +2099,20 @@ static int x509_crt_verifycrl(mbedtls_x509_crt *crt, mbedtls_x509_crt *ca,
break; break;
} }
#if defined(MBEDTLS_HAVE_TIME_DATE)
/* /*
* Check for validity of CRL (Do not drop out) * Check for validity of CRL (Do not drop out)
*/ */
if (mbedtls_x509_time_is_past(&crl_list->next_update)) { if (mbedtls_x509_time_cmp(&crl_list->next_update, now) < 0) {
flags |= MBEDTLS_X509_BADCRL_EXPIRED; flags |= MBEDTLS_X509_BADCRL_EXPIRED;
} }
if (mbedtls_x509_time_is_future(&crl_list->this_update)) { if (mbedtls_x509_time_cmp(&crl_list->this_update, now) > 0) {
flags |= MBEDTLS_X509_BADCRL_FUTURE; flags |= MBEDTLS_X509_BADCRL_FUTURE;
} }
#else
((void) now);
#endif
/* /*
* Check if certificate is revoked * Check if certificate is revoked
@ -2265,7 +2270,8 @@ static int x509_crt_find_parent_in(
int top, int top,
unsigned path_cnt, unsigned path_cnt,
unsigned self_cnt, unsigned self_cnt,
mbedtls_x509_crt_restart_ctx *rs_ctx) mbedtls_x509_crt_restart_ctx *rs_ctx,
const mbedtls_x509_time *now)
{ {
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
mbedtls_x509_crt *parent, *fallback_parent; mbedtls_x509_crt *parent, *fallback_parent;
@ -2328,9 +2334,10 @@ check_signature:
continue; continue;
} }
#if defined(MBEDTLS_HAVE_TIME_DATE)
/* optional time check */ /* optional time check */
if (mbedtls_x509_time_is_past(&parent->valid_to) || if (mbedtls_x509_time_cmp(&parent->valid_to, now) < 0 || /* past */
mbedtls_x509_time_is_future(&parent->valid_from)) { mbedtls_x509_time_cmp(&parent->valid_from, now) > 0) { /* future */
if (fallback_parent == NULL) { if (fallback_parent == NULL) {
fallback_parent = parent; fallback_parent = parent;
fallback_signature_is_good = signature_is_good; fallback_signature_is_good = signature_is_good;
@ -2338,6 +2345,9 @@ check_signature:
continue; continue;
} }
#else
((void) now);
#endif
*r_parent = parent; *r_parent = parent;
*r_signature_is_good = signature_is_good; *r_signature_is_good = signature_is_good;
@ -2383,7 +2393,8 @@ static int x509_crt_find_parent(
int *signature_is_good, int *signature_is_good,
unsigned path_cnt, unsigned path_cnt,
unsigned self_cnt, unsigned self_cnt,
mbedtls_x509_crt_restart_ctx *rs_ctx) mbedtls_x509_crt_restart_ctx *rs_ctx,
const mbedtls_x509_time *now)
{ {
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
mbedtls_x509_crt *search_list; mbedtls_x509_crt *search_list;
@ -2404,7 +2415,7 @@ static int x509_crt_find_parent(
ret = x509_crt_find_parent_in(child, search_list, ret = x509_crt_find_parent_in(child, search_list,
parent, signature_is_good, parent, signature_is_good,
*parent_is_trusted, *parent_is_trusted,
path_cnt, self_cnt, rs_ctx); path_cnt, self_cnt, rs_ctx, now);
#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) #if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE)
if (rs_ctx != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { if (rs_ctx != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS) {
@ -2525,6 +2536,13 @@ static int x509_crt_verify_chain(
int signature_is_good; int signature_is_good;
unsigned self_cnt; unsigned self_cnt;
mbedtls_x509_crt *cur_trust_ca = NULL; mbedtls_x509_crt *cur_trust_ca = NULL;
mbedtls_x509_time now;
#if defined(MBEDTLS_HAVE_TIME_DATE)
if (mbedtls_x509_time_gmtime(mbedtls_time(NULL), &now) != 0) {
return MBEDTLS_ERR_X509_FATAL_ERROR;
}
#endif
#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) #if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE)
/* resume if we had an operation in progress */ /* resume if we had an operation in progress */
@ -2555,14 +2573,16 @@ static int x509_crt_verify_chain(
ver_chain->len++; ver_chain->len++;
flags = &cur->flags; flags = &cur->flags;
#if defined(MBEDTLS_HAVE_TIME_DATE)
/* Check time-validity (all certificates) */ /* Check time-validity (all certificates) */
if (mbedtls_x509_time_is_past(&child->valid_to)) { if (mbedtls_x509_time_cmp(&child->valid_to, &now) < 0) {
*flags |= MBEDTLS_X509_BADCERT_EXPIRED; *flags |= MBEDTLS_X509_BADCERT_EXPIRED;
} }
if (mbedtls_x509_time_is_future(&child->valid_from)) { if (mbedtls_x509_time_cmp(&child->valid_from, &now) > 0) {
*flags |= MBEDTLS_X509_BADCERT_FUTURE; *flags |= MBEDTLS_X509_BADCERT_FUTURE;
} }
#endif
/* Stop here for trusted roots (but not for trusted EE certs) */ /* Stop here for trusted roots (but not for trusted EE certs) */
if (child_is_trusted) { if (child_is_trusted) {
@ -2613,7 +2633,8 @@ find_parent:
/* Look for a parent in trusted CAs or up the chain */ /* Look for a parent in trusted CAs or up the chain */
ret = x509_crt_find_parent(child, cur_trust_ca, &parent, ret = x509_crt_find_parent(child, cur_trust_ca, &parent,
&parent_is_trusted, &signature_is_good, &parent_is_trusted, &signature_is_good,
ver_chain->len - 1, self_cnt, rs_ctx); ver_chain->len - 1, self_cnt, rs_ctx,
&now);
#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) #if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE)
if (rs_ctx != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { if (rs_ctx != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS) {
@ -2662,7 +2683,7 @@ find_parent:
#if defined(MBEDTLS_X509_CRL_PARSE_C) #if defined(MBEDTLS_X509_CRL_PARSE_C)
/* Check trusted CA's CRL for the given crt */ /* Check trusted CA's CRL for the given crt */
*flags |= x509_crt_verifycrl(child, parent, ca_crl, profile); *flags |= x509_crt_verifycrl(child, parent, ca_crl, profile, &now);
#else #else
(void) ca_crl; (void) ca_crl;
#endif #endif