forked from espressif/esp-idf
feat(mbedtls/esp_crt_bundle): Reduced RAM & stack use of cert bundle
Closes https://github.com/espressif/esp-idf/pull/13204 Signed-off-by: harshal.patil <harshal.patil@espressif.com>
This commit is contained in:
@@ -61,7 +61,7 @@ if(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
|
||||
list(APPEND crt_paths ${custom_bundle_path})
|
||||
|
||||
endif()
|
||||
list(APPEND args --input ${crt_paths} -q)
|
||||
list(APPEND args --input ${crt_paths} -q --max-certs "${CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS}")
|
||||
|
||||
get_filename_component(crt_bundle
|
||||
${bundle_name}
|
||||
|
@@ -1,15 +1,54 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define BUNDLE_HEADER_OFFSET 2
|
||||
#define CRT_HEADER_OFFSET 4
|
||||
#include "mbedtls/pk.h"
|
||||
#include "mbedtls/oid.h"
|
||||
#include "mbedtls/asn1.h"
|
||||
|
||||
/*
|
||||
Format of certificate bundle:
|
||||
First, n uint32 "offset" entries, each describing the start of one certificate's data in terms of
|
||||
bytes from the beginning of the bundle. This offset list is immediately followed by the 1st...n-th
|
||||
certificate data. Hence, the first offset entry, i.e. the uint32 at the very start of the bundle,
|
||||
is equal to the size of the offset list in bytes and therefore the # of certificates in the bundle
|
||||
is [first offset]/sizeof(uint32_t)
|
||||
[offset of 1st certificate](u32)
|
||||
[offset of 2nd certificate](u32)
|
||||
...
|
||||
[offset of n-th certificate](u32)
|
||||
[1st certificate](variable)
|
||||
...
|
||||
[n-th certificate](variable)
|
||||
|
||||
Structure of each certificate:
|
||||
[length of CN](u16)
|
||||
[length of key](u16)
|
||||
[CN](variable)
|
||||
[key](variable)
|
||||
|
||||
The offset list is used for fast random access to any certificate by index.
|
||||
For verification, a certificate is looked up by its CN via binary search; for this reason,
|
||||
the offset list *must* be sorted by CN (ascending) and the first certificate must be the
|
||||
one with the least CN in the bundle, so that the first offset in the list still refers to the
|
||||
first certificate after the list (see above).
|
||||
|
||||
*/
|
||||
|
||||
#define CRT_NAME_LEN_OFFSET 0 //<! offset of certificate name length value
|
||||
#define CRT_KEY_LEN_OFFSET (CRT_NAME_LEN_OFFSET + sizeof(uint16_t)) //<! offset of certificate key length value
|
||||
#define CRT_NAME_OFFSET (CRT_KEY_LEN_OFFSET + sizeof(uint16_t)) //<! certificate name data starts here
|
||||
|
||||
#define CRT_HEADER_SIZE CRT_NAME_OFFSET //<! size of certificate header
|
||||
|
||||
static const char *TAG = "esp-x509-crt-bundle";
|
||||
|
||||
@@ -17,63 +56,147 @@ static const char *TAG = "esp-x509-crt-bundle";
|
||||
* cacert_ptr passes non-NULL check during handshake */
|
||||
static mbedtls_x509_crt s_dummy_crt;
|
||||
|
||||
|
||||
extern const uint8_t x509_crt_imported_bundle_bin_start[] asm("_binary_x509_crt_bundle_start");
|
||||
extern const uint8_t x509_crt_imported_bundle_bin_end[] asm("_binary_x509_crt_bundle_end");
|
||||
|
||||
typedef const uint8_t* bundle_t;
|
||||
typedef const uint8_t* cert_t;
|
||||
|
||||
typedef struct crt_bundle_t {
|
||||
const uint8_t **crts;
|
||||
uint16_t num_certs;
|
||||
size_t x509_crt_bundle_len;
|
||||
} crt_bundle_t;
|
||||
static bundle_t s_crt_bundle;
|
||||
|
||||
static crt_bundle_t s_crt_bundle;
|
||||
// Read a 16-bit value stored in little-endian format from the given address
|
||||
static uint16_t get16_le (const uint8_t* ptr) {
|
||||
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
return *((const uint16_t*)ptr);
|
||||
#else
|
||||
return (((uint16_t)ptr[1]) << 8) | ptr[0];
|
||||
#endif
|
||||
}
|
||||
|
||||
static int esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len);
|
||||
static uint16_t esp_crt_get_name_len(const cert_t cert) {
|
||||
return get16_le(cert + CRT_NAME_LEN_OFFSET);
|
||||
}
|
||||
|
||||
static const uint8_t* esp_crt_get_name(const cert_t cert) {
|
||||
return cert + CRT_NAME_OFFSET;
|
||||
}
|
||||
|
||||
static int esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len)
|
||||
static uint16_t esp_crt_get_key_len(const cert_t cert) {
|
||||
return get16_le(cert + CRT_KEY_LEN_OFFSET);
|
||||
}
|
||||
|
||||
static const uint8_t* esp_crt_get_key(const cert_t cert) {
|
||||
return esp_crt_get_name(cert) + esp_crt_get_name_len(cert);
|
||||
}
|
||||
|
||||
static uint16_t esp_crt_get_len(const cert_t cert) {
|
||||
return CRT_HEADER_SIZE + esp_crt_get_name_len(cert) + esp_crt_get_key_len(cert);
|
||||
}
|
||||
|
||||
static uint32_t esp_crt_get_cert_offset(const bundle_t bundle, const uint32_t index) {
|
||||
return ((const uint32_t*)bundle)[index];
|
||||
}
|
||||
|
||||
static uint32_t esp_crt_get_certcount(const bundle_t bundle) {
|
||||
// Offset of 1st certificate == end of offset list == size of offset list == # of certs * sizeof(uint32_t)
|
||||
return esp_crt_get_cert_offset(bundle, 0) / sizeof(uint32_t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the certificate at the given index within a bundle.
|
||||
*
|
||||
* @param bundle pointer to the \c bundle_t
|
||||
* @param index of the certificate; must be less than \c esp_crt_get_certcount(...) !
|
||||
* @return pointer to the certificate
|
||||
*/
|
||||
static cert_t esp_crt_get_cert(const bundle_t bundle, const uint32_t index) {
|
||||
return bundle + esp_crt_get_cert_offset(bundle, index);
|
||||
}
|
||||
|
||||
static int esp_crt_check_signature(const mbedtls_x509_crt* child, const uint8_t* pub_key_buf, const size_t pub_key_len)
|
||||
{
|
||||
int ret = 0;
|
||||
mbedtls_x509_crt parent;
|
||||
mbedtls_pk_context pubkey;
|
||||
const mbedtls_md_info_t *md_info;
|
||||
unsigned char hash[MBEDTLS_MD_MAX_SIZE];
|
||||
|
||||
mbedtls_x509_crt_init(&parent);
|
||||
mbedtls_pk_init(&pubkey);
|
||||
|
||||
if ( (ret = mbedtls_pk_parse_public_key(&parent.pk, pub_key_buf, pub_key_len) ) != 0) {
|
||||
ESP_LOGE(TAG, "PK parse failed with error %X", ret);
|
||||
if ( unlikely( (ret = mbedtls_pk_parse_public_key(&pubkey, pub_key_buf, pub_key_len)) != 0 ) ) {
|
||||
ESP_LOGE(TAG, "PK parse failed with error 0x%x", -ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
// Fast check to avoid expensive computations when not necessary
|
||||
if (!mbedtls_pk_can_do(&parent.pk, child->MBEDTLS_PRIVATE(sig_pk))) {
|
||||
ESP_LOGE(TAG, "Simple compare failed");
|
||||
ret = -1;
|
||||
if ( unlikely( !mbedtls_pk_can_do( &pubkey, child->MBEDTLS_PRIVATE(sig_pk)) ) ) {
|
||||
ESP_LOGE(TAG, "Unsuitable public key");
|
||||
ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
md_info = mbedtls_md_info_from_type(child->MBEDTLS_PRIVATE(sig_md));
|
||||
if ( (ret = mbedtls_md( md_info, child->tbs.p, child->tbs.len, hash )) != 0 ) {
|
||||
ESP_LOGE(TAG, "Internal mbedTLS error %X", ret);
|
||||
|
||||
if( unlikely( md_info == NULL) ) {
|
||||
ESP_LOGE(TAG, "Unknown message digest");
|
||||
ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ( (ret = mbedtls_pk_verify_ext( child->MBEDTLS_PRIVATE(sig_pk), child->MBEDTLS_PRIVATE(sig_opts), &parent.pk,
|
||||
child->MBEDTLS_PRIVATE(sig_md), hash, mbedtls_md_get_size( md_info ),
|
||||
child->MBEDTLS_PRIVATE(sig).p, child->MBEDTLS_PRIVATE(sig).len )) != 0 ) {
|
||||
unsigned char hash[MBEDTLS_MD_MAX_SIZE];
|
||||
const unsigned char md_size = mbedtls_md_get_size(md_info);
|
||||
|
||||
ESP_LOGE(TAG, "PK verify failed with error %X", ret);
|
||||
if ((ret = mbedtls_md(md_info, child->tbs.p, child->tbs.len, hash)) != 0) {
|
||||
ESP_LOGE(TAG, "MD failed with error 0x%x", -ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ( unlikely( (ret = mbedtls_pk_verify_ext(child->MBEDTLS_PRIVATE(sig_pk), child->MBEDTLS_PRIVATE(sig_opts), &pubkey,
|
||||
child->MBEDTLS_PRIVATE(sig_md), hash, md_size,
|
||||
child->MBEDTLS_PRIVATE(sig).p, child->MBEDTLS_PRIVATE(sig).len)) != 0 ) ) {
|
||||
ESP_LOGE(TAG, "PK verify failed with error 0x%x", -ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
mbedtls_x509_crt_free(&parent);
|
||||
|
||||
mbedtls_pk_free(&pubkey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cert_t esp_crt_find_cert(const unsigned char* const issuer, const size_t issuer_len)
|
||||
{
|
||||
if (unlikely( issuer == NULL || issuer_len == 0 )) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int end = esp_crt_get_certcount(s_crt_bundle)-1;
|
||||
int middle = (start + end) / 2;
|
||||
|
||||
cert_t cert = NULL;
|
||||
size_t cert_name_len = 0;
|
||||
|
||||
/* Look for the certificate using binary search on subject name */
|
||||
while (start <= end) {
|
||||
cert = esp_crt_get_cert(s_crt_bundle, middle);
|
||||
cert_name_len = esp_crt_get_name_len(cert);
|
||||
|
||||
// Issuers are in DER encoding, with lengths encoded in the content; if valid DER, differing lengths
|
||||
// are reflected in differing content.
|
||||
// Still, we won't try to memcmp beyond the given length:
|
||||
int cmp_res = memcmp(issuer, esp_crt_get_name(cert), MIN(issuer_len, cert_name_len) );
|
||||
|
||||
if ( unlikely( cmp_res == 0 ) ) {
|
||||
return cert;
|
||||
} else if (cmp_res < 0) {
|
||||
end = middle - 1;
|
||||
} else {
|
||||
start = middle + 1;
|
||||
}
|
||||
middle = (start + end) / 2;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This callback is called for every certificate in the chain. If the chain
|
||||
* is proper each intermediate certificate is validated through its parent
|
||||
@@ -81,9 +204,9 @@ cleanup:
|
||||
* only verify the first untrusted link in the chain is signed by the
|
||||
* root certificate in the trusted bundle
|
||||
*/
|
||||
int esp_crt_verify_callback(void *buf, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
|
||||
int esp_crt_verify_callback(void *buf, mbedtls_x509_crt* const crt, const int depth, uint32_t* const flags)
|
||||
{
|
||||
mbedtls_x509_crt *child = crt;
|
||||
const mbedtls_x509_crt* const child = crt;
|
||||
|
||||
/* It's OK for a trusted cert to have a weak signature hash alg.
|
||||
as we already trust this certificate */
|
||||
@@ -93,121 +216,119 @@ int esp_crt_verify_callback(void *buf, mbedtls_x509_crt *crt, int depth, uint32_
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (s_crt_bundle.crts == NULL) {
|
||||
if( unlikely( s_crt_bundle == NULL ) ) {
|
||||
ESP_LOGE(TAG, "No certificates in bundle");
|
||||
return MBEDTLS_ERR_X509_FATAL_ERROR;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "%d certificates in bundle", s_crt_bundle.num_certs);
|
||||
ESP_LOGD(TAG, "%" PRIu16 " certificates in bundle", (uint16_t)esp_crt_get_certcount(s_crt_bundle));
|
||||
|
||||
size_t name_len = 0;
|
||||
const uint8_t *crt_name;
|
||||
cert_t cert = esp_crt_find_cert(child->issuer_raw.p, child->issuer_raw.len);
|
||||
|
||||
bool crt_found = false;
|
||||
int start = 0;
|
||||
int end = s_crt_bundle.num_certs - 1;
|
||||
int middle = (end - start) / 2;
|
||||
if ( likely( cert != NULL ) ) {
|
||||
|
||||
/* Look for the certificate using binary search on subject name */
|
||||
while (start <= end) {
|
||||
name_len = s_crt_bundle.crts[middle][0] << 8 | s_crt_bundle.crts[middle][1];
|
||||
crt_name = s_crt_bundle.crts[middle] + CRT_HEADER_OFFSET;
|
||||
const int ret = esp_crt_check_signature(child, esp_crt_get_key(cert), esp_crt_get_key_len(cert));
|
||||
|
||||
int cmp_res = memcmp(child->issuer_raw.p, crt_name, name_len );
|
||||
if (cmp_res == 0) {
|
||||
crt_found = true;
|
||||
break;
|
||||
} else if (cmp_res < 0) {
|
||||
end = middle - 1;
|
||||
if ( likely( ret == 0 ) ) {
|
||||
ESP_LOGI(TAG, "Certificate validated");
|
||||
*flags = 0;
|
||||
return 0;
|
||||
} else {
|
||||
start = middle + 1;
|
||||
ESP_LOGE(TAG, "Certificate matched but signature verification failed");
|
||||
#if (CONFIG_LOG_DEFAULT_LEVEL_DEBUG || CONFIG_LOG_DEFAULT_LEVEL_VERBOSE)
|
||||
char *cert_name = malloc((esp_crt_get_name_len(cert) + 1) * sizeof(char));
|
||||
if (cert_name) {
|
||||
memcpy(cert_name, esp_crt_get_name(cert), esp_crt_get_name_len(cert));
|
||||
cert_name[esp_crt_get_name_len(cert)] = '\0';
|
||||
ESP_LOGE(TAG, "Certificate matched with %s but signature verification failed", cert_name);
|
||||
free(cert_name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
middle = (start + end) / 2;
|
||||
}
|
||||
|
||||
int ret = MBEDTLS_ERR_X509_FATAL_ERROR;
|
||||
if (crt_found) {
|
||||
size_t key_len = s_crt_bundle.crts[middle][2] << 8 | s_crt_bundle.crts[middle][3];
|
||||
ret = esp_crt_check_signature(child, s_crt_bundle.crts[middle] + CRT_HEADER_OFFSET + name_len, key_len);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ESP_LOGI(TAG, "Certificate validated");
|
||||
*flags = 0;
|
||||
return 0;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "No matching trusted root certificate found");
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Failed to verify certificate");
|
||||
return MBEDTLS_ERR_X509_FATAL_ERROR;
|
||||
return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform some consistency checks on the user-provided bundle data to try and make sure
|
||||
* it actually is a certificate bundle.
|
||||
*
|
||||
* @param x509_bundle pointer to the bundle data
|
||||
* @param bundle_size size of bundle data
|
||||
* @return true the given bundle data is consistent
|
||||
* @return false the given bundle data is invalid
|
||||
*/
|
||||
static bool esp_crt_check_bundle(const uint8_t* const x509_bundle, const size_t bundle_size)
|
||||
{
|
||||
if (unlikely( x509_bundle == NULL || bundle_size <= (sizeof(uint32_t) + CRT_HEADER_SIZE)) ) {
|
||||
// Bundle is too small for even one offset and one certificate
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Initialize the bundle into an array so we can do binary search for certs,
|
||||
// Pointer to the first offset entry
|
||||
const uint32_t* offsets = (const uint32_t*)x509_bundle;
|
||||
|
||||
if(unlikely( offsets[0] == 0 || (offsets[0] % sizeof(uint32_t)) != 0) ) {
|
||||
// First offset is invalid.
|
||||
// The first certificate must start after N uint32_t offset values.
|
||||
return false;
|
||||
}
|
||||
|
||||
if(unlikely( offsets[0] >= bundle_size )) {
|
||||
// First cert starts beyond end of bundle
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t num_certs = esp_crt_get_certcount(x509_bundle);
|
||||
|
||||
if(unlikely( num_certs > CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS )) {
|
||||
ESP_LOGE(TAG, "Cert bundle certificates exceed max allowed certificates");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check all offsets for consistency with certificate data
|
||||
for (uint32_t i = 0; i < num_certs-1; ++i ) {
|
||||
const uint32_t off = offsets[i];
|
||||
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);
|
||||
|
||||
if( unlikely( offsets[i+1] != expected_next_offset || expected_next_offset >= bundle_size ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All checks passed.
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
the bundle generated by the python utility is already presorted by subject name
|
||||
*/
|
||||
static esp_err_t esp_crt_bundle_init(const uint8_t *x509_bundle, size_t bundle_size)
|
||||
static esp_err_t esp_crt_bundle_init(const uint8_t* const x509_bundle, const size_t bundle_size)
|
||||
{
|
||||
if (bundle_size < BUNDLE_HEADER_OFFSET + CRT_HEADER_OFFSET) {
|
||||
ESP_LOGE(TAG, "Invalid certificate bundle");
|
||||
if ( likely( esp_crt_check_bundle(x509_bundle, bundle_size) ) ) {
|
||||
s_crt_bundle = x509_bundle;
|
||||
return ESP_OK;
|
||||
} else {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint16_t num_certs = (x509_bundle[0] << 8) | x509_bundle[1];
|
||||
if (num_certs > CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS) {
|
||||
ESP_LOGE(TAG, "No. of certs in the certificate bundle = %d exceeds\n"
|
||||
"Max allowed certificates in the certificate bundle = %d\n"
|
||||
"Please update the menuconfig option with appropriate value", num_certs, CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
const uint8_t **crts = calloc(num_certs, sizeof(x509_bundle));
|
||||
if (crts == NULL) {
|
||||
ESP_LOGE(TAG, "Unable to allocate memory for bundle");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
const uint8_t *cur_crt;
|
||||
/* This is the maximum region that is allowed to access */
|
||||
const uint8_t *bundle_end = x509_bundle + bundle_size;
|
||||
cur_crt = x509_bundle + BUNDLE_HEADER_OFFSET;
|
||||
|
||||
for (int i = 0; i < num_certs; i++) {
|
||||
crts[i] = cur_crt;
|
||||
if (cur_crt + CRT_HEADER_OFFSET > bundle_end) {
|
||||
ESP_LOGE(TAG, "Invalid certificate bundle");
|
||||
free(crts);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
size_t name_len = cur_crt[0] << 8 | cur_crt[1];
|
||||
size_t key_len = cur_crt[2] << 8 | cur_crt[3];
|
||||
cur_crt = cur_crt + CRT_HEADER_OFFSET + name_len + key_len;
|
||||
}
|
||||
|
||||
if (cur_crt > bundle_end) {
|
||||
ESP_LOGE(TAG, "Invalid certificate bundle");
|
||||
free(crts);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* The previous crt bundle is only updated when initialization of the
|
||||
* current crt_bundle is successful */
|
||||
/* Free previous crt_bundle */
|
||||
free(s_crt_bundle.crts);
|
||||
s_crt_bundle.num_certs = num_certs;
|
||||
s_crt_bundle.crts = crts;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_crt_bundle_attach(void *conf)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
// If no bundle has been set by the user then use the bundle embedded in the binary
|
||||
if (s_crt_bundle.crts == NULL) {
|
||||
if (s_crt_bundle == NULL) {
|
||||
ret = esp_crt_bundle_init(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
|
||||
}
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
if ( unlikely(ret != ESP_OK) ) {
|
||||
ESP_LOGE(TAG, "Failed to attach bundle");
|
||||
return ret;
|
||||
}
|
||||
@@ -218,8 +339,7 @@ esp_err_t esp_crt_bundle_attach(void *conf)
|
||||
* cacert_ptr passes non-NULL check during handshake
|
||||
*/
|
||||
mbedtls_ssl_config *ssl_conf = (mbedtls_ssl_config *)conf;
|
||||
mbedtls_x509_crt_init(&s_dummy_crt);
|
||||
mbedtls_ssl_conf_ca_chain(ssl_conf, &s_dummy_crt, NULL);
|
||||
mbedtls_ssl_conf_ca_chain(ssl_conf, (mbedtls_x509_crt*)&s_dummy_crt, NULL);
|
||||
mbedtls_ssl_conf_verify(ssl_conf, esp_crt_verify_callback, NULL);
|
||||
}
|
||||
|
||||
@@ -228,8 +348,7 @@ esp_err_t esp_crt_bundle_attach(void *conf)
|
||||
|
||||
void esp_crt_bundle_detach(mbedtls_ssl_config *conf)
|
||||
{
|
||||
free(s_crt_bundle.crts);
|
||||
s_crt_bundle.crts = NULL;
|
||||
s_crt_bundle = NULL;
|
||||
if (conf) {
|
||||
mbedtls_ssl_conf_verify(conf, NULL, NULL);
|
||||
}
|
||||
|
@@ -8,18 +8,16 @@
|
||||
# The bundle will have the format: number of certificates; crt 1 subject name length; crt 1 public key length;
|
||||
# crt 1 subject name; crt 1 public key; crt 2...
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
from io import open
|
||||
|
||||
DEFAULT_CERT_BUNDLE_MAX_CERTS = 200
|
||||
|
||||
try:
|
||||
from cryptography import x509
|
||||
@@ -54,9 +52,6 @@ class CertificateBundle:
|
||||
self.certificates = []
|
||||
self.compressed_crts = []
|
||||
|
||||
if os.path.isfile(ca_bundle_bin_file):
|
||||
os.remove(ca_bundle_bin_file)
|
||||
|
||||
def add_from_path(self, crts_path):
|
||||
|
||||
found = False
|
||||
@@ -116,11 +111,21 @@ class CertificateBundle:
|
||||
self.certificates.append(x509.load_der_x509_certificate(crt_str, default_backend()))
|
||||
status('Successfully added 1 certificate')
|
||||
|
||||
def create_bundle(self):
|
||||
def create_bundle(self, max_certs=DEFAULT_CERT_BUNDLE_MAX_CERTS):
|
||||
if max_certs < len(self.certificates):
|
||||
critical(f'No. of certs in the certificate bundle = {len(self.certificates)} exceeds\n \
|
||||
Max allowed certificates in the certificate bundle = {max_certs} \
|
||||
Please update the menuconfig option with appropriate value')
|
||||
raise ValueError
|
||||
|
||||
# Sort certificates in order to do binary search when looking up certificates
|
||||
self.certificates = sorted(self.certificates, key=lambda cert: cert.subject.public_bytes(default_backend()))
|
||||
|
||||
bundle = struct.pack('>H', len(self.certificates))
|
||||
# List of offsets in bytes from the start of the bundle to each certificate inside
|
||||
offsets = []
|
||||
len_offsets = 4 * len(self.certificates) # final size of the offsets list
|
||||
|
||||
bundle = b''
|
||||
|
||||
for crt in self.certificates:
|
||||
""" Read the public key as DER format """
|
||||
@@ -132,12 +137,18 @@ class CertificateBundle:
|
||||
|
||||
name_len = len(sub_name_der)
|
||||
key_len = len(pub_key_der)
|
||||
len_data = struct.pack('>HH', name_len, key_len)
|
||||
len_data = struct.pack('<HH', name_len, key_len)
|
||||
|
||||
# Certificate starts at this position in the bundle
|
||||
offsets.append(len_offsets + len(bundle))
|
||||
|
||||
bundle += len_data
|
||||
bundle += sub_name_der
|
||||
bundle += pub_key_der
|
||||
|
||||
# Output all offsets before the first certificate
|
||||
bundle = struct.pack('<{0:d}L'.format(len(offsets)), *offsets) + bundle
|
||||
|
||||
return bundle
|
||||
|
||||
def add_with_filter(self, crts_path, filter_path):
|
||||
@@ -182,6 +193,8 @@ def main():
|
||||
help='Paths to the custom certificate folders or files to parse, parses all .pem or .der files')
|
||||
parser.add_argument('--filter', '-f', help='Path to CSV-file where the second columns contains the name of the certificates \
|
||||
that should be included from cacrt_all.pem')
|
||||
parser.add_argument('--max-certs', '-m', help='Maximum number of certificates allowed in the certificate bundle',
|
||||
type=int, default=DEFAULT_CERT_BUNDLE_MAX_CERTS)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -202,7 +215,7 @@ def main():
|
||||
|
||||
status('Successfully added %d certificates in total' % len(bundle.certificates))
|
||||
|
||||
crt_bundle = bundle.create_bundle()
|
||||
crt_bundle = bundle.create_bundle(args.max_certs)
|
||||
|
||||
with open(ca_bundle_bin_file, 'wb') as f:
|
||||
f.write(crt_bundle)
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user