forked from espressif/esp-idf
Merge branch 'contrib/github_pr_13204' into 'master'
Reduce RAM usage by modifying the certificate bundle format (GitHub PR) Closes IDFGH-12148 See merge request espressif/esp-idf!29595
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,156 @@ 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 +213,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 */
|
||||
@@ -94,120 +226,119 @@ int esp_crt_verify_callback(void *buf, mbedtls_x509_crt *crt, int depth, uint32_
|
||||
}
|
||||
|
||||
|
||||
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 +349,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 +358,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.
@@ -6,8 +6,9 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* SPDX-FileContributor: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
@@ -41,6 +42,7 @@ extern const uint8_t server_cert_chain_pem_end[] asm("_binary_server_cert_chai
|
||||
extern const uint8_t server_pk_start[] asm("_binary_prvtkey_pem_start");
|
||||
extern const uint8_t server_pk_end[] asm("_binary_prvtkey_pem_end");
|
||||
|
||||
// `server_cert_bundle_corrupt` is created by generating the cert bundle using `server_root.pem`
|
||||
extern const uint8_t server_cert_bundle_start[] asm("_binary_server_cert_bundle_start");
|
||||
extern const uint8_t server_cert_bundle_end[] asm("_binary_server_cert_bundle_end");
|
||||
|
||||
@@ -272,7 +274,7 @@ void client_task(void *pvParameters)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Test with default crt bundle that doesnt contain the ca crt */
|
||||
/* Test with default crt bundle that does not contain the ca crt */
|
||||
ESP_LOGI(TAG, "Connecting to %s:%s...", SERVER_ADDRESS, SERVER_PORT);
|
||||
if ((ret = mbedtls_net_connect(&client.client_fd, SERVER_ADDRESS, SERVER_PORT, MBEDTLS_NET_PROTO_TCP)) != 0) {
|
||||
ESP_LOGE(TAG, "mbedtls_net_connect returned -%x", -ret);
|
||||
@@ -300,7 +302,7 @@ void client_task(void *pvParameters)
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Certificate verification failed!");
|
||||
}
|
||||
TEST_ASSERT(res == ESP_CRT_VALIDATE_FAIL);
|
||||
TEST_ASSERT_EQUAL(ESP_CRT_VALIDATE_FAIL, res);
|
||||
|
||||
// Reset session before new connection
|
||||
mbedtls_ssl_close_notify(&client.ssl);
|
||||
@@ -338,7 +340,7 @@ void client_task(void *pvParameters)
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Certificate verification failed!");
|
||||
}
|
||||
TEST_ASSERT(res == ESP_CRT_VALIDATE_OK);
|
||||
TEST_ASSERT_EQUAL(ESP_CRT_VALIDATE_OK, res);
|
||||
|
||||
// Reset session before new connection
|
||||
mbedtls_ssl_close_notify(&client.ssl);
|
||||
@@ -406,7 +408,7 @@ TEST_CASE("custom certificate bundle - weak hash", "[mbedtls]")
|
||||
|
||||
mbedtls_x509_crt_init( &crt );
|
||||
mbedtls_x509_crt_parse(&crt, bad_md_crt_pem_start, bad_md_crt_pem_end - bad_md_crt_pem_start);
|
||||
TEST_ASSERT(mbedtls_x509_crt_verify(&crt, NULL, NULL, NULL, &flags, esp_crt_verify_callback, NULL) == 0);
|
||||
TEST_ASSERT_EQUAL(0, mbedtls_x509_crt_verify(&crt, NULL, NULL, NULL, &flags, esp_crt_verify_callback, NULL));
|
||||
|
||||
mbedtls_x509_crt_free(&crt);
|
||||
|
||||
@@ -426,62 +428,119 @@ TEST_CASE("custom certificate bundle - wrong signature", "[mbedtls]")
|
||||
/* esp32.com cert chain where 1 byte in the signature is changed */
|
||||
printf("Testing certificate with wrong signature\n");
|
||||
mbedtls_x509_crt_parse(&crt, wrong_sig_crt_pem_start, wrong_sig_crt_pem_end - wrong_sig_crt_pem_start);
|
||||
TEST_ASSERT(mbedtls_x509_crt_verify(&crt, NULL, NULL, NULL, &flags, esp_crt_verify_callback, NULL) != 0);
|
||||
TEST_ASSERT_NOT_EQUAL(0, mbedtls_x509_crt_verify(&crt, NULL, NULL, NULL, &flags, esp_crt_verify_callback, NULL));
|
||||
mbedtls_x509_crt_free(&crt);
|
||||
|
||||
mbedtls_x509_crt_init( &crt );
|
||||
/* the correct esp32.com cert chain*/
|
||||
printf("Testing certificate with correct signature\n");
|
||||
mbedtls_x509_crt_parse(&crt, correct_sig_crt_pem_start, correct_sig_crt_pem_end - correct_sig_crt_pem_start);
|
||||
TEST_ASSERT(mbedtls_x509_crt_verify(&crt, NULL, NULL, NULL, &flags, esp_crt_verify_callback, NULL) == 0);
|
||||
TEST_ASSERT_EQUAL(0, mbedtls_x509_crt_verify(&crt, NULL, NULL, NULL, &flags, esp_crt_verify_callback, NULL));
|
||||
mbedtls_x509_crt_free(&crt);
|
||||
|
||||
esp_crt_bundle_detach(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("custom certificate bundle init API - bound checking", "[mbedtls]")
|
||||
TEST_CASE("custom certificate bundle init API - bound checking - NULL certificate bundle", "[mbedtls]")
|
||||
{
|
||||
|
||||
uint8_t test_bundle[256] = {0};
|
||||
esp_err_t esp_ret;
|
||||
|
||||
/* The API should fail when NULL is passed as the bundle */
|
||||
esp_ret = esp_crt_bundle_set(NULL, 0);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
|
||||
}
|
||||
|
||||
TEST_CASE("custom certificate bundle init API - bound checking - Invalid size of certificate bundle", "[mbedtls]")
|
||||
{
|
||||
uint8_t test_bundle[1024] = {0};
|
||||
esp_err_t esp_ret;
|
||||
|
||||
/* The API should fail with bundle size given as 1 */
|
||||
esp_ret = esp_crt_bundle_set(test_bundle, 1);
|
||||
TEST_ASSERT( esp_ret == ESP_ERR_INVALID_ARG);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
|
||||
}
|
||||
|
||||
TEST_CASE("custom certificate bundle init API - bound checking - Invalid first certificate offset", "[mbedtls]")
|
||||
{
|
||||
uint8_t test_bundle[1024] = {0};
|
||||
esp_err_t esp_ret;
|
||||
|
||||
/* Check that the esp_crt_bundle_set API will not accept
|
||||
* the first offset to be invalid */
|
||||
|
||||
/* The first certificate must start after N uint32_t offset values,
|
||||
* thus, it cannot start from the 0th position */
|
||||
test_bundle[0] = 0;
|
||||
esp_ret = esp_crt_bundle_set(test_bundle, sizeof(test_bundle));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
|
||||
|
||||
/* The first certificate must start after N uint32_t offset values, thus,
|
||||
* the offset from where the it would start should be divisible by sizeof(uint32_t) */
|
||||
test_bundle[0] = 1;
|
||||
esp_ret = esp_crt_bundle_set(test_bundle, sizeof(test_bundle));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
|
||||
|
||||
/* Check that the esp_crt_bundle_set API will not accept a bundle
|
||||
* which in which the first cert starts beyond end of bundle*/
|
||||
uint8_t *dummy_test_bundle = test_bundle + sizeof(uint32_t);
|
||||
|
||||
esp_ret = esp_crt_bundle_set(dummy_test_bundle, sizeof(test_bundle));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
|
||||
}
|
||||
|
||||
TEST_CASE("custom certificate bundle init API - bound checking - Certificates count overflow", "[mbedtls]")
|
||||
{
|
||||
uint8_t test_bundle[1024] = {0};
|
||||
esp_err_t esp_ret;
|
||||
|
||||
memset(test_bundle, 0, sizeof(test_bundle));
|
||||
|
||||
/* Check that the esp_crt_bundle_set API will not accept a bundle
|
||||
* which has more no. of certs than configured in
|
||||
* CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS */
|
||||
|
||||
uint8_t rand;
|
||||
esp_fill_random(&rand, 1);
|
||||
test_bundle[0] = rand;
|
||||
|
||||
/* Make sure that the number of certs will always be greater than
|
||||
* CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS */
|
||||
test_bundle[1] = rand + CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS;
|
||||
*((uint32_t*) test_bundle) = ((CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS + 1) * sizeof(uint32_t));
|
||||
|
||||
esp_ret = esp_crt_bundle_set(test_bundle, sizeof(test_bundle));
|
||||
TEST_ASSERT( esp_ret == ESP_ERR_INVALID_ARG);
|
||||
|
||||
/* The API should fail with bundle_size < BUNDLE_HEADER_OFFSET (2) + CRT_HEADER_OFFSET (4) */
|
||||
test_bundle[0] = 0;
|
||||
test_bundle[1] = 1; /* set num_certs = 1 */
|
||||
esp_ret = esp_crt_bundle_set(test_bundle, 5);
|
||||
TEST_ASSERT(esp_ret == ESP_ERR_INVALID_ARG);
|
||||
|
||||
/* Cert number is greater than actual certs present, The API should fail */
|
||||
/* Actual No. of certs present in bundle = 1, setting num_certs to 5 */
|
||||
test_bundle[1] = 5; /* num_certs */
|
||||
test_bundle[3] = 5; /* cert_1_name_len */
|
||||
test_bundle[5] = 10; /* cert_1_pub_key_len */
|
||||
/* Actual bundle size becomes BUNDLE_HEADER_OFFSET (2) + CRT_HEADER_OFFSET (4) + cert_1_name_len(5) + cert_1_pub_key_len(10)
|
||||
* i.e. 21 bytes */
|
||||
esp_ret = esp_crt_bundle_set(test_bundle, 21);
|
||||
TEST_ASSERT(esp_ret == ESP_ERR_INVALID_ARG);
|
||||
|
||||
/* The API should fail if bundle_size < BUNDLE_HEADER_OFFSET (2) + CRT_HEADER_OFFSET (4) + cert_1_name_len(5) + cert_1_pub_key_len(10) */
|
||||
esp_ret = esp_crt_bundle_set(test_bundle, 20);
|
||||
TEST_ASSERT(esp_ret == ESP_ERR_INVALID_ARG);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
|
||||
|
||||
esp_crt_bundle_detach(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("custom certificate bundle init API - bound checking - Incorrect certificate offset", "[mbedtls]")
|
||||
{
|
||||
uint8_t test_bundle[1024] = {0};
|
||||
esp_err_t esp_ret;
|
||||
|
||||
memset(test_bundle, 0, sizeof(test_bundle));
|
||||
|
||||
/* Check that the esp_crt_bundle_set API will not accept a bundle where
|
||||
all offsets are not consistent with certificate data */
|
||||
|
||||
/*
|
||||
| offset 1 | offset 2 | Cert 1 name len | Cert 1 key len | Cert 1 name | Cert 1 key | Cert 2 name len | ..... |
|
||||
| ----- offsets ----- |
|
||||
| ---------------------- Certificate 1 ---------------------- |
|
||||
| ---- Certificate 2 ---- |
|
||||
*/
|
||||
|
||||
*((uint32_t*) &test_bundle[0]) = (2 * sizeof(uint32_t));
|
||||
*((uint16_t*) &test_bundle[8]) = 2; // Cert 1 name len
|
||||
*((uint16_t*) &test_bundle[10]) = 4; // Cert 1 key len
|
||||
|
||||
/* Correct offset of certificate 2 should be
|
||||
= 2 * sizeof(uint32_t) (Offsets of 2 certs) + 2 * sizeof(uint16_t) (Cert name and len) + 2 (Cert 1 name len) + 4 (Cert 1 key len);
|
||||
= 18
|
||||
*/
|
||||
*((uint32_t*) &test_bundle[4]) = 16; // Incorrect certificate 2 offset
|
||||
|
||||
esp_ret = esp_crt_bundle_set(test_bundle, sizeof(test_bundle));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
|
||||
|
||||
/* Check that the esp_crt_bundle_set API will not accept a bundle where
|
||||
all offsets are not consistency with certificate data and the certificate
|
||||
offsets exceeds the bundle size */
|
||||
*((uint32_t*) &test_bundle[4]) = sizeof(test_bundle) + 1; // Offset exceeds the test_bundle size
|
||||
|
||||
esp_ret = esp_crt_bundle_set(test_bundle, sizeof(test_bundle));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret);
|
||||
}
|
||||
|
@@ -43,11 +43,24 @@
|
||||
#include "esp_tls.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
|
||||
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL
|
||||
#define MAX_URLS 9
|
||||
#else
|
||||
#define MAX_URLS 2
|
||||
#endif
|
||||
|
||||
static const char *web_urls[MAX_URLS] = {
|
||||
"https://www.howsmyssl.com/a/check",
|
||||
"https://espressif.com",
|
||||
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL
|
||||
"https://letsencrypt.org",
|
||||
"https://www.identrust.com",
|
||||
"https://www.globalsign.com",
|
||||
"https://www.sectigo.com",
|
||||
"https://www.digicert.com",
|
||||
"https://www.godaddy.com",
|
||||
"https://rainmaker.espressif.com", // Amazon
|
||||
#endif
|
||||
};
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
@@ -1,10 +1,9 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from common_test_methods import get_env_config_variable
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@@ -21,17 +20,11 @@ def test_examples_protocol_https_x509_bundle(dut: Dut) -> None:
|
||||
binary_file = os.path.join(dut.app.binary_path, 'https_x509_bundle.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
logging.info('https_x509_bundle_bin_size : {}KB'.format(bin_size // 1024))
|
||||
# Connect to AP
|
||||
if dut.app.sdkconfig.get('EXAMPLE_WIFI_SSID_PWD_FROM_STDIN') is True:
|
||||
dut.expect('Please input ssid password:')
|
||||
env_name = 'wifi_ap'
|
||||
ap_ssid = get_env_config_variable(env_name, 'ap_ssid')
|
||||
ap_password = get_env_config_variable(env_name, 'ap_password')
|
||||
dut.write(f'{ap_ssid} {ap_password}')
|
||||
dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)
|
||||
# start test
|
||||
num_URLS = int(dut.expect(r'Connecting to (\d+) URLs', timeout=30)[1].decode())
|
||||
dut.expect(r'Connection established to ([\s\S]*)', timeout=30)
|
||||
for _ in range(num_URLS):
|
||||
dut.expect(r'Connection established to ([\s\S]*)', timeout=30)
|
||||
dut.expect('Completed {} connections'.format(num_URLS), timeout=60)
|
||||
|
||||
|
||||
@@ -44,15 +37,24 @@ def test_examples_protocol_https_x509_bundle_dynamic_buffer(dut: Dut) -> None:
|
||||
binary_file = os.path.join(dut.app.binary_path, 'https_x509_bundle.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
logging.info('https_x509_bundle_bin_size : {}KB'.format(bin_size // 1024))
|
||||
# Connect to AP
|
||||
if dut.app.sdkconfig.get('EXAMPLE_WIFI_SSID_PWD_FROM_STDIN') is True:
|
||||
dut.expect('Please input ssid password:')
|
||||
env_name = 'wifi_ap'
|
||||
ap_ssid = get_env_config_variable(env_name, 'ap_ssid')
|
||||
ap_password = get_env_config_variable(env_name, 'ap_password')
|
||||
dut.write(f'{ap_ssid} {ap_password}')
|
||||
dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)
|
||||
# start test
|
||||
num_URLS = int(dut.expect(r'Connecting to (\d+) URLs', timeout=30)[1].decode())
|
||||
dut.expect(r'Connection established to ([\s\S]*)', timeout=30)
|
||||
dut.expect('Completed {} connections'.format(num_URLS), timeout=60)
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.ethernet
|
||||
@pytest.mark.parametrize('config', ['default_crt_bundle',], indirect=True)
|
||||
def test_examples_protocol_https_x509_bundle_default_crt_bundle_stress_test(dut: Dut) -> None:
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut.app.binary_path, 'https_x509_bundle.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
logging.info('https_x509_bundle_bin_size : {}KB'.format(bin_size // 1024))
|
||||
dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)
|
||||
# start test
|
||||
num_URLS = int(dut.expect(r'Connecting to (\d+) URLs', timeout=30)[1].decode())
|
||||
for _ in range(num_URLS):
|
||||
dut.expect(r'Connection established to ([\s\S]*)', timeout=30)
|
||||
dut.expect('Completed {} connections'.format(num_URLS), timeout=60)
|
||||
|
@@ -0,0 +1,10 @@
|
||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y
|
||||
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
Reference in New Issue
Block a user