Merge branch 'fix/crt_bundle_cert_header_oob' into 'master'

Validate cert header extent before reading it in bundle check

See merge request espressif/esp-idf!51982
This commit is contained in:
Mahavir Jain
2026-09-02 13:41:47 +05:30
2 changed files with 32 additions and 1 deletions
@@ -442,6 +442,9 @@ static bool esp_crt_check_bundle(const uint8_t* const x509_bundle, const size_t
// Check all offsets for consistency with certificate data
for (uint32_t i = 0; i < num_certs - 1; ++i) {
const uint32_t off = esp_crt_get_cert_offset(x509_bundle, i);
if (unlikely((uint64_t)off + CRT_HEADER_SIZE > bundle_size)) {
return false;
}
cert_t cert = x509_bundle + off;
// The next offset in the list must point to right after the current cert
const uint32_t expected_next_offset = off + esp_crt_get_len(cert);
@@ -455,7 +458,7 @@ static bool esp_crt_check_bundle(const uint8_t* const x509_bundle, const size_t
// The loop above stops at num_certs - 1, so the final certificate's extent is never
// validated; check it explicitly so its key data cannot run past the bundle (CWE-125).
const uint32_t last_off = esp_crt_get_cert_offset(x509_bundle, num_certs - 1);
if (unlikely(last_off >= bundle_size)) {
if (unlikely((uint64_t)last_off + CRT_HEADER_SIZE > bundle_size)) {
return false;
}
const uint32_t last_len = esp_crt_get_len(x509_bundle + last_off);
@@ -621,6 +621,34 @@ TEST_CASE("custom certificate bundle init API - bound checking - Incorrect certi
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
}
TEST_CASE("custom certificate bundle init API - bound checking - Certificate extent beyond end of bundle", "[mbedtls]")
{
uint8_t test_bundle[1024] = {0};
const size_t bundle_size = 64;
esp_err_t esp_ret;
/* Check that the esp_crt_bundle_set API will not accept a bundle whose only
certificate declares name/key lengths that run past the end of the bundle */
*((uint32_t*) &test_bundle[0]) = sizeof(uint32_t); // 1 cert, starting right after the offset list
*((uint16_t*) &test_bundle[4]) = 100; // Cert 1 name len
*((uint16_t*) &test_bundle[6]) = 100; // Cert 1 key len: 4 + 100 + 100 > bundle_size
esp_ret = esp_crt_bundle_set(test_bundle, bundle_size);
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
/* Check that the esp_crt_bundle_set API will not accept a bundle where the last
certificate starts so close to the end that its 4-byte header (name len +
key len) would straddle the end of the bundle */
memset(test_bundle, 0, sizeof(test_bundle));
*((uint32_t*) &test_bundle[0]) = 2 * sizeof(uint32_t); // 2 certs
*((uint32_t*) &test_bundle[4]) = bundle_size - 2; // Cert 2 offset: 2 bytes before the end
*((uint16_t*) &test_bundle[8]) = 25; // Cert 1 name len
*((uint16_t*) &test_bundle[10]) = 25; // Cert 1 key len: cert 2 expected at 8 + 4 + 25 + 25 = bundle_size - 2
esp_ret = esp_crt_bundle_set(test_bundle, bundle_size);
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
}
#if defined(CONFIG_MBEDTLS_HAVE_TIME_DATE)
TEST_CASE("certificate bundle - expired cert rejected with time-date check", "[mbedtls]")
{