Add ESP certificate bundle feature
Adds the ESP certificate bundle feature that enables users to bundle a root certificate bundle together with their application. Default bundle includes all Mozilla root certificates Closes IDF-296
This commit is contained in:
parent
8e1442f0e7
commit
947e3e94ed
48 changed files with 5030 additions and 147 deletions
|
@ -197,6 +197,10 @@ typedef struct esp_tls_cfg {
|
|||
then PSK authentication is enabled with configured setup.
|
||||
Important note: the pointer must be valid for connection */
|
||||
|
||||
esp_err_t (*crt_bundle_attach)(mbedtls_ssl_config *conf);
|
||||
/*!< Function pointer to esp_crt_bundle_attach. Enables the use of certification
|
||||
bundle for server verification, must be enabled in menuconfig */
|
||||
|
||||
} esp_tls_cfg_t;
|
||||
|
||||
#ifdef CONFIG_ESP_TLS_SERVER
|
||||
|
|
|
@ -26,6 +26,11 @@
|
|||
#include <errno.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
#include "esp_crt_bundle.h"
|
||||
#endif
|
||||
|
||||
|
||||
static const char *TAG = "esp-tls-mbedtls";
|
||||
static mbedtls_x509_crt *global_cacert = NULL;
|
||||
|
||||
|
@ -402,7 +407,17 @@ esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t
|
|||
return ESP_ERR_INVALID_STATE;
|
||||
#endif
|
||||
}
|
||||
if (cfg->use_global_ca_store == true) {
|
||||
|
||||
if (cfg->crt_bundle_attach != NULL) {
|
||||
#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
ESP_LOGD(TAG, "Use certificate bundle");
|
||||
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||
cfg->crt_bundle_attach(&tls->conf);
|
||||
#else //CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
ESP_LOGE(TAG, "use_crt_bundle configured but not enabled in menuconfig: Please enable MBEDTLS_CERTIFICATE_BUNDLE option");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
#endif
|
||||
} else if (cfg->use_global_ca_store == true) {
|
||||
esp_err_t esp_ret = set_global_ca_store(tls);
|
||||
if (esp_ret != ESP_OK) {
|
||||
return esp_ret;
|
||||
|
|
|
@ -188,6 +188,11 @@ static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls
|
|||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (cfg->crt_bundle_attach != NULL) {
|
||||
ESP_LOGE(TAG,"use_crt_bundle not supported in wolfssl");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
tls->priv_ssl =(void *)wolfSSL_new( (WOLFSSL_CTX *)tls->priv_ctx);
|
||||
if (!tls->priv_ssl) {
|
||||
ESP_LOGE(TAG, "Create wolfSSL failed");
|
||||
|
|
|
@ -1,10 +1,54 @@
|
|||
idf_build_get_property(idf_target IDF_TARGET)
|
||||
|
||||
idf_component_register(INCLUDE_DIRS "port/include" "mbedtls/include"
|
||||
idf_component_register(SRCS "esp_crt_bundle/esp_crt_bundle.c"
|
||||
INCLUDE_DIRS "port/include" "mbedtls/include" "esp_crt_bundle/include"
|
||||
REQUIRES lwip
|
||||
PRIV_REQUIRES soc
|
||||
)
|
||||
|
||||
if(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
|
||||
set(bundle_name "x509_crt_bundle")
|
||||
set(DEFAULT_CRT_DIR ${COMPONENT_DIR}/esp_crt_bundle)
|
||||
|
||||
# Generate custom certificate bundle using the generate_cert_bundle utility
|
||||
set(GENERATE_CERT_BUNDLEPY ${python} ${COMPONENT_DIR}/esp_crt_bundle/gen_crt_bundle.py)
|
||||
|
||||
if(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL)
|
||||
list(APPEND crt_paths ${DEFAULT_CRT_DIR}/cacrt_all.pem)
|
||||
elseif(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN)
|
||||
list(APPEND crt_paths ${DEFAULT_CRT_DIR}/cacrt_all.pem)
|
||||
list(APPEND args --filter ${DEFAULT_CRT_DIR}/cmn_crt_authorities.csv)
|
||||
endif()
|
||||
|
||||
if(CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE)
|
||||
get_filename_component(custom_bundle_path
|
||||
${CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH} ABSOLUTE BASE_DIR "${project_dir}")
|
||||
list(APPEND crt_paths ${custom_bundle_path})
|
||||
|
||||
endif()
|
||||
list(APPEND args --input ${crt_paths} -q)
|
||||
|
||||
get_filename_component(crt_bundle
|
||||
${bundle_name}
|
||||
ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
# Generate bundle according to config
|
||||
add_custom_command(OUTPUT ${crt_bundle}
|
||||
COMMAND ${GENERATE_CERT_BUNDLEPY} ${args}
|
||||
DEPENDS ${custom_bundle_path}
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(custom_bundle DEPENDS ${cert_bundle})
|
||||
add_dependencies(${COMPONENT_LIB} custom_bundle)
|
||||
|
||||
|
||||
target_add_binary_data(${COMPONENT_LIB} ${crt_bundle} BINARY)
|
||||
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
|
||||
"${crt_bundle}")
|
||||
endif()
|
||||
|
||||
|
||||
# Only build mbedtls libraries
|
||||
set(ENABLE_TESTING CACHE BOOL OFF)
|
||||
set(ENABLE_PROGRAMS CACHE BOOL OFF)
|
||||
|
@ -26,6 +70,8 @@ set(mbedtls_targets mbedtls mbedcrypto mbedx509)
|
|||
target_sources(mbedtls PRIVATE "${COMPONENT_DIR}/port/mbedtls_debug.c"
|
||||
"${COMPONENT_DIR}/port/net_sockets.c")
|
||||
|
||||
|
||||
|
||||
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/esp_hardware.c"
|
||||
"${COMPONENT_DIR}/port/esp_mem.c"
|
||||
"${COMPONENT_DIR}/port/esp_sha.c"
|
||||
|
@ -46,3 +92,4 @@ set_property(TARGET mbedcrypto APPEND PROPERTY LINK_INTERFACE_LIBRARIES mbedtls)
|
|||
# Link mbedtls libraries to component library
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE ${mbedtls_targets})
|
||||
|
||||
|
||||
|
|
|
@ -127,6 +127,52 @@ menu "mbedTLS"
|
|||
default 3 if MBEDTLS_DEBUG_LEVEL_DEBUG
|
||||
default 4 if MBEDTLS_DEBUG_LEVEL_VERBOSE
|
||||
|
||||
|
||||
menu "Certificate Bundle"
|
||||
|
||||
config MBEDTLS_CERTIFICATE_BUNDLE
|
||||
bool "Enable trusted root certificate bundle"
|
||||
default y
|
||||
help
|
||||
Enable support for large number of default root certificates
|
||||
|
||||
When enabled this option allows user to store default as well
|
||||
as customer specific root certificates in compressed format rather
|
||||
than storing full certificate. For the root certificates the public key and the subject name
|
||||
will be stored.
|
||||
|
||||
choice MBEDTLS_DEFAULT_CERTIFICATE_BUNDLE
|
||||
bool "Default certificate bundle options"
|
||||
depends on MBEDTLS_CERTIFICATE_BUNDLE
|
||||
default MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL
|
||||
|
||||
config MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL
|
||||
bool "Use the full default certificate bundle"
|
||||
config MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN
|
||||
bool "Use only the most common certificates from the default bundles"
|
||||
help
|
||||
Use only the most common certificates from the default bundles, reducing the size with 50%,
|
||||
while still having around 99% coverage.
|
||||
config MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE
|
||||
bool "Do not use the default certificate bundle"
|
||||
endchoice
|
||||
|
||||
config MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE
|
||||
depends on MBEDTLS_CERTIFICATE_BUNDLE
|
||||
default n
|
||||
bool "Add custom certificates to the default bundle"
|
||||
config MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH
|
||||
depends on MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE
|
||||
string "Custom certificate bundle path"
|
||||
help
|
||||
Name of the custom certificate directory or file. This path is evaluated
|
||||
relative to the project root directory.
|
||||
endmenu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
config MBEDTLS_ECP_RESTARTABLE
|
||||
bool "Enable mbedTLS ecp restartable"
|
||||
default n
|
||||
|
|
|
@ -2,11 +2,44 @@
|
|||
# Component Makefile
|
||||
#
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := port/include mbedtls/include
|
||||
COMPONENT_ADD_INCLUDEDIRS := port/include mbedtls/include esp_crt_bundle/include
|
||||
|
||||
COMPONENT_SRCDIRS := mbedtls/library port port/$(IDF_TARGET)
|
||||
COMPONENT_SRCDIRS := mbedtls/library port port/$(IDF_TARGET) esp_crt_bundle
|
||||
|
||||
COMPONENT_OBJEXCLUDE := mbedtls/library/net_sockets.o
|
||||
|
||||
COMPONENT_SUBMODULES += mbedtls
|
||||
|
||||
|
||||
ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
|
||||
GEN_CRT_BUNDLEPY := $(PYTHON) $(COMPONENT_PATH)/esp_crt_bundle/gen_crt_bundle.py
|
||||
DEFAULT_CRT_DIR := ${COMPONENT_PATH}/esp_crt_bundle
|
||||
X509_CERTIFICATE_BUNDLE := $(abspath x509_crt_bundle)
|
||||
CUSTOM_BUNDLE_PATH := $(PROJECT_PATH)/$(CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH)
|
||||
|
||||
ifdef CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE
|
||||
CRT_PATHS += $(CUSTOM_BUNDLE_PATH)
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL
|
||||
CRT_PATHS += ${DEFAULT_CRT_DIR}/cacrt_all.pem
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN
|
||||
CRT_PATHS += ${DEFAULT_CRT_DIR}/cacrt_all.pem
|
||||
ARGS += --filter ${DEFAULT_CRT_DIR}/cmn_crt_authorities.csv
|
||||
endif
|
||||
|
||||
ARGS += --input $(CRT_PATHS) -q
|
||||
|
||||
# Generate certificate bundle using generate_cert_bundle.py
|
||||
$(X509_CERTIFICATE_BUNDLE) : $(SDKCONFIG_MAKEFILE)
|
||||
$(GEN_CRT_BUNDLEPY) $(ARGS)
|
||||
|
||||
COMPONENT_EXTRA_CLEAN += $(X509_CERTIFICATE_BUNDLE)
|
||||
|
||||
COMPONENT_EMBED_FILES := $(X509_CERTIFICATE_BUNDLE)
|
||||
|
||||
endif
|
||||
|
||||
|
|
3401
components/mbedtls/esp_crt_bundle/cacrt_all.pem
Normal file
3401
components/mbedtls/esp_crt_bundle/cacrt_all.pem
Normal file
File diff suppressed because it is too large
Load diff
39
components/mbedtls/esp_crt_bundle/cmn_crt_authorities.csv
Normal file
39
components/mbedtls/esp_crt_bundle/cmn_crt_authorities.csv
Normal file
|
@ -0,0 +1,39 @@
|
|||
Owner,Common Name or Certificate Name
|
||||
Amazon Trust Services,Amazon Root CA 1
|
||||
Amazon Trust Services,Amazon Root CA 2
|
||||
Amazon Trust Services,Amazon Root CA 3
|
||||
Amazon Trust Services,Amazon Root CA 4
|
||||
Amazon Trust Services,Starfield Services Root Certificate Authority - G2
|
||||
DigiCert,Baltimore CyberTrust Root
|
||||
DigiCert,Cybertrust Global Root
|
||||
DigiCert,DigiCert Assured ID Root CA
|
||||
DigiCert,DigiCert Assured ID Root G2
|
||||
DigiCert,DigiCert Assured ID Root G3
|
||||
DigiCert,DigiCert Global Root CA
|
||||
DigiCert,DigiCert Global Root G2
|
||||
DigiCert,DigiCert Global Root G3
|
||||
DigiCert,DigiCert High Assurance EV Root CA
|
||||
DigiCert,DigiCert Trusted Root G4
|
||||
GlobalSign,GlobalSign ECC Root CA - R5
|
||||
GlobalSign,GlobalSign Root CA - R3
|
||||
GlobalSign,GlobalSign Root CA - R6
|
||||
GlobalSign,GlobalSign Root CA
|
||||
GoDaddy,Go Daddy Class 2 CA
|
||||
GoDaddy,Go Daddy Root Certificate Authority - G2
|
||||
GoDaddy,Starfield Class 2 CA
|
||||
GoDaddy,Starfield Root Certificate Authority - G2
|
||||
Google Trust Services LLC (GTS),GlobalSign ECC Root CA - R4
|
||||
Google Trust Services LLC (GTS),GlobalSign Root CA - R2
|
||||
Google Trust Services LLC (GTS),GTS Root R1
|
||||
Google Trust Services LLC (GTS),GTS Root R2
|
||||
Google Trust Services LLC (GTS),GTS Root R3
|
||||
Google Trust Services LLC (GTS),GTS Root R4
|
||||
"IdenTrust Services, LLC",DST Root CA X3
|
||||
"IdenTrust Services, LLC",IdenTrust Commercial Root CA 1
|
||||
"IdenTrust Services, LLC",IdenTrust Public Sector Root CA 1
|
||||
Sectigo,Comodo AAA Services root
|
||||
Sectigo,COMODO Certification Authority
|
||||
Sectigo,COMODO ECC Certification Authority
|
||||
Sectigo,COMODO RSA Certification Authority
|
||||
Sectigo,USERTrust ECC Certification Authority
|
||||
Sectigo,USERTrust RSA Certification Authority
|
|
219
components/mbedtls/esp_crt_bundle/esp_crt_bundle.c
Normal file
219
components/mbedtls/esp_crt_bundle/esp_crt_bundle.c
Normal file
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_system.h>
|
||||
#include "esp_crt_bundle.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
|
||||
#define BUNDLE_HEADER_OFFSET 2
|
||||
#define CRT_HEADER_OFFSET 4
|
||||
|
||||
static const char *TAG = "esp-x509-crt-bundle";
|
||||
|
||||
/* a dummy certificate so that
|
||||
* 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 struct crt_bundle_t {
|
||||
const uint8_t **crts;
|
||||
uint16_t num_certs;
|
||||
size_t x509_crt_bundle_len;
|
||||
} crt_bundle_t;
|
||||
|
||||
static crt_bundle_t s_crt_bundle;
|
||||
|
||||
static int esp_crt_verify_callback(void *buf, mbedtls_x509_crt *crt, int data, uint32_t *flags);
|
||||
static esp_err_t esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len);
|
||||
|
||||
|
||||
static esp_err_t esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len)
|
||||
{
|
||||
int ret = ESP_FAIL;
|
||||
mbedtls_x509_crt parent;
|
||||
const mbedtls_md_info_t *md_info;
|
||||
unsigned char hash[MBEDTLS_MD_MAX_SIZE];
|
||||
|
||||
mbedtls_x509_crt_init(&parent);
|
||||
|
||||
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);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
// Fast check to avoid expensive computations when not necessary
|
||||
if (!mbedtls_pk_can_do(&parent.pk, child->sig_pk)) {
|
||||
ESP_LOGE(TAG, "Simple compare failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
md_info = mbedtls_md_info_from_type(child->sig_md);
|
||||
if ( (ret = mbedtls_md( md_info, child->tbs.p, child->tbs.len, hash )) != 0 ) {
|
||||
ESP_LOGE(TAG, "Internal mbedTLS error %X", ret);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = mbedtls_pk_verify_ext( child->sig_pk, child->sig_opts, &parent.pk,
|
||||
child->sig_md, hash, mbedtls_md_get_size( md_info ),
|
||||
child->sig.p, child->sig.len ) ;
|
||||
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "PK verify failed with error %X", ret);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
/* This callback is called for every certificate in the chain. If the chain
|
||||
* is proper each intermediate certificate is validated through its parent
|
||||
* in the x509_crt_verify_chain() function. So this callback should
|
||||
* 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 data, uint32_t *flags)
|
||||
{
|
||||
mbedtls_x509_crt *child = crt;
|
||||
|
||||
if (*flags != MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (s_crt_bundle.crts == 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);
|
||||
|
||||
size_t name_len = 0;
|
||||
const uint8_t *crt_name;
|
||||
|
||||
bool crt_found = false;
|
||||
int start = 0;
|
||||
int end = s_crt_bundle.num_certs - 1;
|
||||
int middle = (end - start) / 2;
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
} else {
|
||||
start = middle + 1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Failed to verify certificate");
|
||||
return MBEDTLS_ERR_X509_FATAL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the bundle into an array so we can do binary search for certs,
|
||||
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)
|
||||
{
|
||||
s_crt_bundle.num_certs = (x509_bundle[0] << 8) | x509_bundle[1];
|
||||
s_crt_bundle.crts = calloc(s_crt_bundle.num_certs, sizeof(x509_bundle));
|
||||
|
||||
if (s_crt_bundle.crts == NULL) {
|
||||
ESP_LOGE(TAG, "Unable to allocate memory for bundle");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
const uint8_t *cur_crt;
|
||||
cur_crt = x509_bundle + BUNDLE_HEADER_OFFSET;
|
||||
|
||||
for (int i = 0; i < s_crt_bundle.num_certs; i++) {
|
||||
s_crt_bundle.crts[i] = cur_crt;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_crt_bundle_attach(mbedtls_ssl_config *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) {
|
||||
ret = esp_crt_bundle_init(x509_crt_imported_bundle_bin_start);
|
||||
}
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to attach bundle");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (conf) {
|
||||
/* point to a dummy certificate
|
||||
* This is only required so that the
|
||||
* cacert_ptr passes non-NULL check during handshake
|
||||
*/
|
||||
mbedtls_x509_crt_init(&s_dummy_crt);
|
||||
conf->ca_chain = &s_dummy_crt;
|
||||
mbedtls_ssl_conf_verify(conf, esp_crt_verify_callback, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void esp_crt_bundle_detach(mbedtls_ssl_config *conf)
|
||||
{
|
||||
free(s_crt_bundle.crts);
|
||||
if (conf) {
|
||||
mbedtls_ssl_conf_verify(conf, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_crt_bundle_set(const uint8_t *x509_bundle)
|
||||
{
|
||||
// Free any previously used bundle
|
||||
free(s_crt_bundle.crts);
|
||||
esp_crt_bundle_init(x509_bundle);
|
||||
}
|
||||
|
226
components/mbedtls/esp_crt_bundle/gen_crt_bundle.py
Executable file
226
components/mbedtls/esp_crt_bundle/gen_crt_bundle.py
Executable file
|
@ -0,0 +1,226 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# ESP32 x509 certificate bundle generation utility
|
||||
#
|
||||
# Converts PEM and DER certificates to a custom bundle format which stores just the
|
||||
# subject name and public key to reduce space
|
||||
#
|
||||
# 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...
|
||||
#
|
||||
# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http:#www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
import argparse
|
||||
import csv
|
||||
import re
|
||||
|
||||
try:
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
except ImportError:
|
||||
print('The cryptography package is not installed.'
|
||||
'Please refer to the Get Started section of the ESP-IDF Programming Guide for '
|
||||
'setting up the required packages.')
|
||||
raise
|
||||
|
||||
ca_bundle_bin_file = 'x509_crt_bundle'
|
||||
|
||||
quiet = False
|
||||
|
||||
|
||||
def status(msg):
|
||||
""" Print status message to stderr """
|
||||
if not quiet:
|
||||
critical(msg)
|
||||
|
||||
|
||||
def critical(msg):
|
||||
""" Print critical message to stderr """
|
||||
sys.stderr.write('gen_crt_bundle.py: ')
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.write('\n')
|
||||
|
||||
|
||||
class CertificateBundle:
|
||||
def __init__(self):
|
||||
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
|
||||
for file_path in os.listdir(crts_path):
|
||||
found |= self.add_from_file(os.path.join(crts_path, file_path))
|
||||
|
||||
if found is False:
|
||||
raise InputError('No valid x509 certificates found in %s' % crts_path)
|
||||
|
||||
def add_from_file(self, file_path):
|
||||
try:
|
||||
if file_path.endswith('.pem'):
|
||||
status("Parsing certificates from %s" % file_path)
|
||||
with open(file_path, 'r') as f:
|
||||
crt_str = f.read()
|
||||
self.add_from_pem(crt_str)
|
||||
return True
|
||||
|
||||
elif file_path.endswith('.der'):
|
||||
status("Parsing certificates from %s" % file_path)
|
||||
with open(file_path, 'rb') as f:
|
||||
crt_str = f.read()
|
||||
self.add_from_der(crt_str)
|
||||
return True
|
||||
|
||||
except ValueError:
|
||||
critical("Invalid certificate in %s" % file_path)
|
||||
raise InputError("Invalid certificate")
|
||||
|
||||
return False
|
||||
|
||||
def add_from_pem(self, crt_str):
|
||||
""" A single PEM file may have multiple certificates """
|
||||
|
||||
crt = ''
|
||||
count = 0
|
||||
start = False
|
||||
|
||||
for strg in crt_str.splitlines(True):
|
||||
if strg == '-----BEGIN CERTIFICATE-----\n' and start is False:
|
||||
crt = ''
|
||||
start = True
|
||||
elif strg == '-----END CERTIFICATE-----\n' and start is True:
|
||||
crt += strg + '\n'
|
||||
start = False
|
||||
self.certificates.append(x509.load_pem_x509_certificate(crt.encode(), default_backend()))
|
||||
count += 1
|
||||
if start is True:
|
||||
crt += strg
|
||||
|
||||
if(count == 0):
|
||||
raise InputError("No certificate found")
|
||||
|
||||
status("Successfully added %d certificates" % count)
|
||||
|
||||
def add_from_der(self, crt_str):
|
||||
self.certificates.append(x509.load_der_x509_certificate(crt_str, default_backend()))
|
||||
status("Successfully added 1 certificate")
|
||||
|
||||
def create_bundle(self):
|
||||
# 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))
|
||||
|
||||
for crt in self.certificates:
|
||||
""" Read the public key as DER format """
|
||||
pub_key = crt.public_key()
|
||||
pub_key_der = pub_key.public_bytes(serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo)
|
||||
|
||||
""" Read the subject name as DER format """
|
||||
sub_name_der = crt.subject.public_bytes(default_backend())
|
||||
|
||||
name_len = len(sub_name_der)
|
||||
key_len = len(pub_key_der)
|
||||
len_data = struct.pack('>HH', name_len, key_len)
|
||||
|
||||
bundle += len_data
|
||||
bundle += sub_name_der
|
||||
bundle += pub_key_der
|
||||
|
||||
return bundle
|
||||
|
||||
def add_with_filter(self, crts_path, filter_path):
|
||||
|
||||
filter_set = set()
|
||||
with open(filter_path, 'r') as f:
|
||||
csv_reader = csv.reader(f, delimiter=',')
|
||||
|
||||
# Skip header
|
||||
next(csv_reader)
|
||||
for row in csv_reader:
|
||||
filter_set.add(row[1])
|
||||
|
||||
status("Parsing certificates from %s" % crts_path)
|
||||
crt_str = []
|
||||
with open(crts_path, 'r') as f:
|
||||
crt_str = f.read()
|
||||
|
||||
# Split all certs into a list of (name, certificate string) tuples
|
||||
pem_crts = re.findall(r'(^.+?)\n(=+\n[\s\S]+?END CERTIFICATE-----\n)', crt_str, re.MULTILINE)
|
||||
|
||||
filtered_crts = ''
|
||||
for name, crt in pem_crts:
|
||||
if name in filter_set:
|
||||
filtered_crts += crt
|
||||
|
||||
self.add_from_pem(filtered_crts)
|
||||
|
||||
|
||||
class InputError(RuntimeError):
|
||||
def __init__(self, e):
|
||||
super(InputError, self).__init__(e)
|
||||
|
||||
|
||||
def main():
|
||||
global quiet
|
||||
|
||||
parser = argparse.ArgumentParser(description='ESP-IDF x509 certificate bundle utility')
|
||||
|
||||
parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true')
|
||||
parser.add_argument('--input', '-i', nargs='+', required=True,
|
||||
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')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
quiet = args.quiet
|
||||
|
||||
bundle = CertificateBundle()
|
||||
|
||||
for path in args.input:
|
||||
if os.path.isfile(path):
|
||||
if os.path.basename(path) == "cacrt_all.pem" and args.filter:
|
||||
bundle.add_with_filter(path, args.filter)
|
||||
else:
|
||||
bundle.add_from_file(path)
|
||||
elif os.path.isdir(path):
|
||||
bundle.add_from_path(path)
|
||||
else:
|
||||
raise InputError("Invalid --input=%s, is neither file nor folder" % args.input)
|
||||
|
||||
status('Successfully added %d certificates in total' % len(bundle.certificates))
|
||||
|
||||
crt_bundle = bundle.create_bundle()
|
||||
|
||||
with open(ca_bundle_bin_file, 'wb') as f:
|
||||
f.write(crt_bundle)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except InputError as e:
|
||||
print(e)
|
||||
sys.exit(2)
|
67
components/mbedtls/esp_crt_bundle/include/esp_crt_bundle.h
Normal file
67
components/mbedtls/esp_crt_bundle/include/esp_crt_bundle.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
#ifndef _ESP_CRT_BUNDLE_H_
|
||||
#define _ESP_CRT_BUNDLE_H_
|
||||
|
||||
#include "mbedtls/ssl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Attach and enable use of a bundle for certificate verification
|
||||
*
|
||||
* Attach and enable use of a bundle for certificate verification through a verification callback.
|
||||
* If no specific bundle has been set through esp_crt_bundle_set() it will default to the
|
||||
* bundle defined in menuconfig and embedded in the binary.
|
||||
*
|
||||
* @param[in] conf The config struct for the SSL connection.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK if adding certificates was successful.
|
||||
* - Other if an error occured or an action must be taken by the calling process.
|
||||
*/
|
||||
esp_err_t esp_crt_bundle_attach(mbedtls_ssl_config *conf);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Disable and dealloc the certification bundle
|
||||
*
|
||||
* Removes the certificate verification callback and deallocates used resources
|
||||
*
|
||||
* @param[in] conf The config struct for the SSL connection.
|
||||
*/
|
||||
void esp_crt_bundle_detach(mbedtls_ssl_config *conf);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the default certificate bundle used for verification
|
||||
*
|
||||
* Overrides the default certificate bundle. In most use cases the bundle should be
|
||||
* set through menuconfig. The bundle needs to be sorted by subject name since binary search is
|
||||
* used to find certificates.
|
||||
*
|
||||
* @param[in] x509_bundle A pointer to the certificate bundle.
|
||||
*/
|
||||
void esp_crt_bundle_set(const uint8_t *x509_bundle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //_ESP_CRT_BUNDLE_H_
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,26 @@
|
|||
Entrust Root Certification Authority
|
||||
====================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
|
||||
BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
|
||||
b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
|
||||
A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
|
||||
MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
|
||||
MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
|
||||
Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
|
||||
dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
|
||||
A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
|
||||
Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
|
||||
j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
|
||||
rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
|
||||
DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
|
||||
MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
|
||||
hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
|
||||
A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
|
||||
Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
|
||||
v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
|
||||
W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
|
||||
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
|
||||
-----END CERTIFICATE-----
|
||||
|
Binary file not shown.
|
@ -0,0 +1,26 @@
|
|||
Entrust Root Certification Authority
|
||||
====================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
|
||||
BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
|
||||
b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
|
||||
A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
|
||||
MloXDTI2MTEyNzIwNTM0MFFwgFFxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
|
||||
MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
|
||||
Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
|
||||
dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
|
||||
A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
|
||||
Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
|
||||
j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
|
||||
rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
|
||||
DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
|
||||
MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
|
||||
hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBFFDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
|
||||
A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
|
||||
Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxR22IkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
|
||||
v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi447pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
|
||||
W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
|
||||
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
|
||||
-----END CERTIFICATE-----
|
||||
|
77
components/mbedtls/esp_crt_bundle/test_gen_crt_bundle/test_gen_crt_bundle.py
Executable file
77
components/mbedtls/esp_crt_bundle/test_gen_crt_bundle/test_gen_crt_bundle.py
Executable file
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
import gen_crt_bundle
|
||||
except ImportError:
|
||||
sys.path.append("..")
|
||||
import gen_crt_bundle
|
||||
|
||||
|
||||
idf_path = os.environ['IDF_PATH']
|
||||
ca_crts_path = idf_path + '/components/mbedtls/esp_crt_bundle/'
|
||||
test_crts_path = idf_path + '/components/mbedtls/esp_crt_bundle/test_gen_crt_bundle/'
|
||||
|
||||
ca_bundle_bin_file = 'x509_crt_bundle'
|
||||
|
||||
der_test_file = 'baltimore.der'
|
||||
pem_test_file = 'entrust.pem'
|
||||
verified_der_bundle = 'baltimore_crt_bundle'
|
||||
verified_pem_bundle = 'entrust_crt_bundle'
|
||||
invalid_test_file = 'invalid_crt.pem'
|
||||
ca_crts_all_file = 'cacrt_all.pem'
|
||||
|
||||
|
||||
class Py23TestCase(unittest.TestCase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Py23TestCase, self).__init__(*args, **kwargs)
|
||||
try:
|
||||
self.assertRaisesRegex
|
||||
except AttributeError:
|
||||
# assertRaisesRegexp is deprecated in Python3 but assertRaisesRegex doesn't exist in Python2
|
||||
# This fix is used in order to avoid using the alias from the six library
|
||||
self.assertRaisesRegex = self.assertRaisesRegexp
|
||||
|
||||
|
||||
class GenCrtBundleTests(Py23TestCase):
|
||||
|
||||
# Verify generation from der vs known certificate
|
||||
def test_gen_from_der(self):
|
||||
bundle = gen_crt_bundle.CertificateBundle()
|
||||
bundle.add_from_file(test_crts_path + der_test_file)
|
||||
|
||||
crt_bundle = bundle.create_bundle()
|
||||
|
||||
with open(test_crts_path + verified_der_bundle, 'rb') as f:
|
||||
verified_bundle = f.read()
|
||||
|
||||
self.assertEqual(crt_bundle, verified_bundle)
|
||||
|
||||
# Verify generation from pem vs known certificate
|
||||
def test_gen_from_pem(self):
|
||||
bundle = gen_crt_bundle.CertificateBundle()
|
||||
bundle.add_from_file(test_crts_path + pem_test_file)
|
||||
|
||||
crt_bundle = bundle.create_bundle()
|
||||
|
||||
with open(test_crts_path + verified_pem_bundle, 'rb') as f:
|
||||
verified_bundle = f.read()
|
||||
|
||||
self.assertEqual(crt_bundle, verified_bundle)
|
||||
|
||||
def test_invalid_crt_input(self):
|
||||
bundle = gen_crt_bundle.CertificateBundle()
|
||||
|
||||
with self.assertRaisesRegex(gen_crt_bundle.InputError, "Invalid certificate"):
|
||||
bundle.add_from_file(test_crts_path + invalid_test_file)
|
||||
|
||||
with self.assertRaisesRegex(gen_crt_bundle.InputError, "No certificate found"):
|
||||
bundle.add_from_pem("")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
|
@ -1,6 +1,8 @@
|
|||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES unity test_utils mbedtls libsodium)
|
||||
REQUIRES unity test_utils mbedtls libsodium
|
||||
EMBED_TXTFILES server_cert_chain.pem prvtkey.pem server_cert_bundle)
|
||||
|
||||
|
||||
idf_component_get_property(mbedtls mbedtls COMPONENT_LIB)
|
||||
target_compile_definitions(${mbedtls} INTERFACE "-DMBEDTLS_DEPRECATED_WARNING")
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
#
|
||||
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
||||
COMPONENT_EMBED_FILES := server_cert_chain.pem prvtkey.pem server_cert_bundle
|
27
components/mbedtls/test/prvtkey.pem
Normal file
27
components/mbedtls/test/prvtkey.pem
Normal file
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAra9Pvr1J4ltGfVdnOv4DVdYTL68UaIKu37r0TMRdBKn5gSKm
|
||||
nBnvDx4TyMfiaOyo6tADGZPbzYJ2r45Zmo8zoIiUwh9SWHkFghTl+jNp0+1QxRCH
|
||||
HyRak3ShIZvje+c0xDDgMIv41l62FCE86dNW0gUCC/KgRCInqzsKurbxZU2qjebI
|
||||
5TKDkOFJsmoaWPb3q2+wEztPpvjGlV33UVX3OK8bJtRKALFn3733E7g5F2qANjOu
|
||||
S+7jXDzqxw5HD+QZTTH2Kehh/hrV96WeXUVGnMWru2BtYWKD0Pdh7zGcXjP8oSf2
|
||||
FkVssh+0f9khI9Xz6KzdSIMVEeSrDXRKnyJnDQIDAQABAoIBAEtOCpZZvfIdvxdT
|
||||
URfb0Jhj5Be1onSZzLaGeavbK7V8+QgLfQ+LkwIL+WoBeGIj0i1VGTL6z79wBIOj
|
||||
hagk1K6S6WStbeecOU4oP3pW1lijuXRn8R4IhhkO5VoMG/q5yUATLPD/j1lq4Skj
|
||||
LCT5k9glgbiqbuB7qpVsWP+RmGJiLh3jBDrb1NrZLuDlXhXJO+AF69syxxiyvnFA
|
||||
s7aVHst2TPXgccA9Fh37GzxN4hratz6n0JAaMxpRdJaJF1sSQQznfrYfxnkwtE1y
|
||||
ZXS5XgeDjqv00mucZVVzNkhT9WeS0bXd0lblboK2z39cN2YDYrmfEr2HonbTozNj
|
||||
HPlBG2UCgYEA3zWj3kAFhpl6zrHvcIzaemDxi9pFam2wJLgzeSXuHBSZSazi//qm
|
||||
Dv7rgP38XsY6MeaqpLW687FZ6yiDg2OLLMc4ho7Rq6mOKjp3nTVHjO+LONfxgWul
|
||||
evhFIabW056jafecouUSvy/9nvrrA5QEJjaHxg0tREuaGiBBgMXTnd8CgYEAxzMr
|
||||
NkVHqIis5AzGUJOaF2uTqnXbnM+/+kEkJD6yNzFovsxkaebGQF9LA1s/qiDtZtlH
|
||||
QiXlDsWl/PrKmvxToBY3v3fJKeAMXValtAtX0h67RX6rJUqXBATT/AOcfdlWoaEt
|
||||
xTCRwi70xjVoSwnvE8CZAGDyHk3/cjRcOBe5QJMCgYAyy4ApGaSoRtEdrHxyvnsR
|
||||
knIlg1x8pc2J7ak5DpqrJTzk+UUHP8D+dKCfUC1YW//uTzHSHdEXl+qAi02yXrrT
|
||||
S9rfNC0exY0mqvuBeRh5SCIEo4/ABgE4hLsmt1L4AYfqm4C3yS2E+KTcwvksbUis
|
||||
cYhgV6tPeWzuORzu8xX/PQKBgG5puFv+jrel+l71jb7/8Xtlz5W+ehozNTAbh1Ln
|
||||
xZS+OFb5p/bjSaRIraWQoHtGgRBvAwZxRsOnXlgZEtBRaHDln8TrOn+Rhoj+DB79
|
||||
4pG/IwJkMa0b6RT7MB0SS12eaFxyoJIaV9CQgnCTDdn6CaCjMqt5EPsnNJ4y06Lr
|
||||
020tAoGASPmKKVhXJxfhzyzsY9Kk9o31MKISUqVDNimqVDOt8vPZw8m/2WXUVH9T
|
||||
DnuEGaBmpG22Gs1NxbktsYAUzBeRBpoQ/fNK55eaZorJLs3DfFK604lLClJlQsDd
|
||||
yfp4LcRNQGodV4Utl6mPOtOFa8nrVGMQn3+M3TK6QTNpjLe87OE=
|
||||
-----END RSA PRIVATE KEY-----
|
BIN
components/mbedtls/test/server_cert_bundle
Normal file
BIN
components/mbedtls/test/server_cert_bundle
Normal file
Binary file not shown.
63
components/mbedtls/test/server_cert_chain.pem
Normal file
63
components/mbedtls/test/server_cert_chain.pem
Normal file
|
@ -0,0 +1,63 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFODCCAyCgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCQ04x
|
||||
ETAPBgNVBAgMCFNoYW5naGFpMRIwEAYDVQQKDAlFc3ByZXNzaWYxJDAiBgNVBAMM
|
||||
G0VzcHJlc3NpZiBJbnRlcm1lZGlhdGUgdGVzdDAeFw0xOTEwMTEwMjU0MDdaFw0z
|
||||
MDA5MjMwMjU0MDdaMEgxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hhaTES
|
||||
MBAGA1UECgwJRXNwcmVzc2lmMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtr0++vUniW0Z9V2c6/gNV1hMvrxRogq7f
|
||||
uvRMxF0EqfmBIqacGe8PHhPIx+Jo7Kjq0AMZk9vNgnavjlmajzOgiJTCH1JYeQWC
|
||||
FOX6M2nT7VDFEIcfJFqTdKEhm+N75zTEMOAwi/jWXrYUITzp01bSBQIL8qBEIier
|
||||
Owq6tvFlTaqN5sjlMoOQ4UmyahpY9verb7ATO0+m+MaVXfdRVfc4rxsm1EoAsWff
|
||||
vfcTuDkXaoA2M65L7uNcPOrHDkcP5BlNMfYp6GH+GtX3pZ5dRUacxau7YG1hYoPQ
|
||||
92HvMZxeM/yhJ/YWRWyyH7R/2SEj1fPorN1IgxUR5KsNdEqfImcNAgMBAAGjggEY
|
||||
MIIBFDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAzBglghkgBhvhCAQ0E
|
||||
JhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRlMB0GA1UdDgQW
|
||||
BBR8LwbfGcYMVc++Ugdoc2XXYXUOBzB7BgNVHSMEdDBygBSZ45naA62T1+k4QIyt
|
||||
n2h1bWyGOKFWpFQwUjELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFNoYW5naGFpMRIw
|
||||
EAYDVQQKDAlFc3ByZXNzaWYxHDAaBgNVBAMME0VzcHJlc3NpZiBSb290IFRlc3SC
|
||||
AhAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG
|
||||
9w0BAQsFAAOCAgEAQ/9JU171woAvQlZ8gmYIOkyIfYQfKmhvw+2DoP+5r1+LOHtO
|
||||
frg9BshucqXlQ65yRWL8KaAIFKE4e/qBXnD/ZX8R8lR0aMKCgYVW6A1n0wWko/fU
|
||||
RNXt+sXr+fMX7h0HOC3mzWf2fZkR5B0jUSBQSVkXNt+jkjWOFIGzfHDKldgX5rVX
|
||||
vNmHwS5zRjbOvPaXrmNpV7wkQ/bRJbnFmbT5V6fwDFzdLzJami86eiT+C68d07/W
|
||||
We5htv20nYFYdwQJMWYlnGLPPaSE+n5m4QqsrlfR7uOgnuX3RfKHMsiWa7TxLA4g
|
||||
D9VZq88SQLshec/CIcYlSgnEfa9LxL2mKv386e9YWD2Oho/B33L5tdflihR5m1sd
|
||||
9xIgka9aXmHu8GEVaBRqzqtIReoa7KfmEQWYjqXH8YdDLMlMKl968Y7c779/SDxC
|
||||
1ibkanS1+2dPBYpoZldcnbH8w2dguk7luTuPlJxJph6NHGI7bbxL9z6yc5kJi2dS
|
||||
R4TNXI3UKZ5s7ZUPTv2nYMJIbyEzSjkxinLsr7rFLGlAIpAlRq1C6jmh1ArGA3H2
|
||||
jK0xYZcMN8Sz9gYV/zk/VTDMmiyZrYmZSxuhQFZCWaLN79z0pi5SefLW+1K6CzNj
|
||||
hah0wJEtzq492IQS0q3gH82iGM35Ffy+rtAWIsxrL/2wn+cOrPGvRdmR6J0=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFjjCCA3agAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwUjELMAkGA1UEBhMCQ04x
|
||||
ETAPBgNVBAgMCFNoYW5naGFpMRIwEAYDVQQKDAlFc3ByZXNzaWYxHDAaBgNVBAMM
|
||||
E0VzcHJlc3NpZiBSb290IFRlc3QwHhcNMTkxMDEwMTE1MDMyWhcNMjkxMDA3MTE1
|
||||
MDMyWjBaMQswCQYDVQQGEwJDTjERMA8GA1UECAwIU2hhbmdoYWkxEjAQBgNVBAoM
|
||||
CUVzcHJlc3NpZjEkMCIGA1UEAwwbRXNwcmVzc2lmIEludGVybWVkaWF0ZSB0ZXN0
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp7FJAjmfYsTlrcZPRJ3g
|
||||
+IiKW/Gm3q23X4xzdKUQopth3DPO9FDvnce9e7JwXa1WDF8CZqWkvKrJS0njEAUR
|
||||
JERLYSr28aVFUyodnfxp/+1B5aJFj7LHampLWXsnVCchHHZB0pYZ6KLrTUU73KKd
|
||||
WaJODtBrq5g9mNNZqVOOHljgr5r8AJefemsCs+LhGcqq8ZFWeZBwzF2YC0h+55hc
|
||||
7K5g0MnGnQHD3s5nuuSJ9Grz+NDvzESYjmZfTq56wXN6nQIi+4JYBpAx4y63n6NR
|
||||
0JPsSePDlnGC4KNmHOeF9nXMgvqEP1doLssKKWdPZub6VQHLTk5ILFr1JKaSPjgj
|
||||
4twKjCTzxN3dPmxY2KPq+tUtoXFxLxqrJk/HyBwiClxSlwhyAe9iZd3Lh0RFENEf
|
||||
jS6gdD7coLlkJzALLcUTp0VWFWpPT1MbgYMHnnOuKXjw6KWXz/iuxvOJO4ip3tRf
|
||||
ssuog/cMwmkxKC9oHfoIPBuafW42/os4aZwy/7fJgAFO/e1/n6T7T2qygNOKBvYq
|
||||
mS6SWm9OFhUJuUtPlUdvHiVDQx40S0a8Z2Rp4XcuBU0M8Toi3lkEwJX/l89Wsos+
|
||||
UOITA1B71HxkzHMZQNdXLfnV0tCtC6C3S8IktFm0Kfqa8ruXbjzkKg0I8Wfwyefv
|
||||
HTc7FRQpyYgXkVLt1ziA+4MCAwEAAaNmMGQwHQYDVR0OBBYEFJnjmdoDrZPX6ThA
|
||||
jK2faHVtbIY4MB8GA1UdIwQYMBaAFAqo9zQmHYRS1EF/1Fkb31gvGK9HMBIGA1Ud
|
||||
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IC
|
||||
AQBl+7vf7T6K7JhHQTFfOlUNoy4rdau6eAfoQE1wybUNIeuKLqhXfn7uLhYLE9Tq
|
||||
LAYcMN8M8F2MFt7nNifbAFRiXCRRes6tFyQkjqG4SijvGoMiCL3pVYEgrET2qIm8
|
||||
Rvupsh/u2UuGivy7XzJmMzU6HhTjF/Yfc6AiPkISzMqxLtbzCD+3TOxNe9nalsFv
|
||||
gmQsCYUqVZCwdThvHMWY7lC+KZ/f0X8gyVHZpx6K/K+MbMSTzZZa7VEjcJcEQZ8r
|
||||
+Br0e9X3EkGsKbnq/kVouMZGrZtbOXYNjoVStNdobNaJ1d379xbt8UgkvvSUhJoK
|
||||
Y4pZoO3nZZUHslLDuLNG6m2tk1SHL7NPNhJoAGwqFtLyrUaUaGa+uIXev4xA0Cby
|
||||
vUn+PXLKo9NcnDI38l/NxVhqWvKAwkWww/GDdic7GGfzJVSr+K4q3dxy3JW1nh4n
|
||||
gaGSeKrP0lgO5NqJswGFSrY05lx06HKxHRJRtLf8g+llrGrhApRVqjM9t0By7CgK
|
||||
E/EgGWG6MyGi2YLenFdFzFRgqEsKVn11XV+rkaC1NSu7l7QfTSiHrynBsYcCTnzD
|
||||
z4QxJvlLon22Jp1qCwFVKXAE9wc/ncENqiA6vbjP9pq5yps+XbO2frdSQcNwHFIR
|
||||
aaL+u5SAcyr3qV0I7Wghq0Xoo00DV/pm9K78tIamtsmNhw==
|
||||
-----END CERTIFICATE-----
|
312
components/mbedtls/test/test_esp_crt_bundle.c
Normal file
312
components/mbedtls/test/test_esp_crt_bundle.c
Normal file
|
@ -0,0 +1,312 @@
|
|||
/* SSL server using plain mbedTLS sockets
|
||||
*
|
||||
* Adapted from the ssl_server example in mbedtls.
|
||||
*
|
||||
* Original Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
||||
* Additions Copyright (C) Copyright 2019 Espressif Systems (Shanghai) PTE LTD, Apache 2.0 License.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/certs.h"
|
||||
#include "mbedtls/x509.h"
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls/net_sockets.h"
|
||||
#include "mbedtls/error.h"
|
||||
#include "mbedtls/debug.h"
|
||||
|
||||
#include "esp_crt_bundle.h"
|
||||
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
#define SERVER_ADDRESS "localhost"
|
||||
#define SERVER_PORT "4433"
|
||||
|
||||
extern const uint8_t server_cert_chain_pem_start[] asm("_binary_server_cert_chain_pem_start");
|
||||
extern const uint8_t server_cert_chain_pem_end[] asm("_binary_server_cert_chain_pem_end");
|
||||
|
||||
extern const uint8_t server_pk_start[] asm("_binary_prvtkey_pem_start");
|
||||
extern const uint8_t server_pk_end[] asm("_binary_prvtkey_pem_end");
|
||||
|
||||
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");
|
||||
|
||||
typedef struct {
|
||||
mbedtls_ssl_context ssl;
|
||||
mbedtls_net_context listen_fd;
|
||||
mbedtls_net_context client_fd;
|
||||
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
|
||||
mbedtls_ssl_config conf;
|
||||
mbedtls_x509_crt cert;
|
||||
mbedtls_pk_context pkey;
|
||||
|
||||
}mbedtls_endpoint_t;
|
||||
|
||||
static const char *TAG = "cert_bundle_test";
|
||||
|
||||
static volatile bool exit_flag;
|
||||
|
||||
esp_err_t endpoint_teardown(mbedtls_endpoint_t* endpoint);
|
||||
|
||||
esp_err_t server_setup(mbedtls_endpoint_t * server)
|
||||
{
|
||||
int ret;
|
||||
mbedtls_ssl_config_init( &server->conf );
|
||||
mbedtls_net_init( &server->listen_fd );
|
||||
mbedtls_net_init( &server->client_fd );
|
||||
mbedtls_ssl_init( &server->ssl );
|
||||
mbedtls_x509_crt_init( &server->cert );
|
||||
mbedtls_pk_init( &server->pkey );
|
||||
mbedtls_entropy_init( &server->entropy );
|
||||
mbedtls_ctr_drbg_init( &server->ctr_drbg );
|
||||
|
||||
ESP_LOGI(TAG, "Loading the server cert and key");
|
||||
ret = mbedtls_x509_crt_parse( &server->cert, server_cert_chain_pem_start,
|
||||
server_cert_chain_pem_end - server_cert_chain_pem_start);
|
||||
|
||||
if( ret != 0 ) {
|
||||
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned %d", ret );
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = mbedtls_pk_parse_key( &server->pkey, (const unsigned char *)server_pk_start ,
|
||||
server_pk_end - server_pk_start, NULL, 0 );
|
||||
if( ret != 0 ) {
|
||||
ESP_LOGE(TAG, "mbedtls_pk_parse_key returned %d", ret );
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Bind on https://%s:%s/", SERVER_ADDRESS, SERVER_PORT );
|
||||
if( ( ret = mbedtls_net_bind( &server->listen_fd, NULL, SERVER_PORT, MBEDTLS_NET_PROTO_TCP ) ) != 0 ) {
|
||||
ESP_LOGE(TAG, "mbedtls_net_bind returned %d", ret );
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Seeding the random number generator");
|
||||
if( ( ret = mbedtls_ctr_drbg_seed( &server->ctr_drbg, mbedtls_entropy_func, &server->entropy,
|
||||
NULL, 0) ) != 0 ) {
|
||||
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret );
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Setting up the SSL data");
|
||||
if( ( ret = mbedtls_ssl_config_defaults( &server->conf,
|
||||
MBEDTLS_SSL_IS_SERVER,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 )
|
||||
{
|
||||
ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret );
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_rng( &server->conf, mbedtls_ctr_drbg_random, &server->ctr_drbg );
|
||||
mbedtls_ssl_conf_ca_chain( &server->conf, server->cert.next, NULL );
|
||||
|
||||
if (( ret = mbedtls_ssl_conf_own_cert( &server->conf, &server->cert, &server->pkey ) ) != 0 )
|
||||
{
|
||||
ESP_LOGE(TAG, "mbedtls_ssl_conf_own_cert returned %d", ret );
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (( ret = mbedtls_ssl_setup( &server->ssl, &server->conf ) ) != 0 )
|
||||
{
|
||||
ESP_LOGE(TAG, "mbedtls_ssl_setup returned %d", ret );
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void server_task(void *pvParameters)
|
||||
{
|
||||
int ret;
|
||||
mbedtls_endpoint_t server;
|
||||
xSemaphoreHandle *sema = (xSemaphoreHandle *) pvParameters;
|
||||
|
||||
|
||||
if (server_setup(&server) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SSL server setup failed");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Waiting for a remote connection" );
|
||||
if( ( ret = mbedtls_net_accept( &server.listen_fd, &server.client_fd,
|
||||
NULL, 0, NULL ) ) != 0 ) {
|
||||
ESP_LOGE(TAG, "mbedtls_net_accept returned %d", ret );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mbedtls_ssl_set_bio( &server.ssl, &server.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL );
|
||||
|
||||
while(exit_flag == false) {
|
||||
mbedtls_ssl_handshake( &server.ssl );
|
||||
}
|
||||
ESP_LOGE(TAG, "Server shutdown");
|
||||
exit:
|
||||
endpoint_teardown(&server);
|
||||
xSemaphoreGive(*sema);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t endpoint_teardown(mbedtls_endpoint_t* endpoint)
|
||||
{
|
||||
mbedtls_net_free( &endpoint->client_fd );
|
||||
mbedtls_net_free( &endpoint->listen_fd );
|
||||
|
||||
mbedtls_x509_crt_free( &endpoint->cert );
|
||||
mbedtls_pk_free( &endpoint->pkey );
|
||||
mbedtls_ssl_free( &endpoint->ssl );
|
||||
mbedtls_ssl_config_free( &endpoint->conf );
|
||||
|
||||
mbedtls_ctr_drbg_free( &endpoint->ctr_drbg );
|
||||
mbedtls_entropy_free( &endpoint->entropy );
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t client_setup(mbedtls_endpoint_t* client)
|
||||
{
|
||||
int ret;
|
||||
mbedtls_ssl_config_init( &client->conf );
|
||||
mbedtls_net_init( &client->client_fd );
|
||||
mbedtls_ssl_init( &client->ssl );
|
||||
mbedtls_x509_crt_init( &client->cert );
|
||||
mbedtls_pk_init( &client->pkey );
|
||||
mbedtls_entropy_init( &client->entropy );
|
||||
mbedtls_ctr_drbg_init( &client->ctr_drbg );
|
||||
|
||||
ESP_LOGI(TAG, "Seeding the random number generator");
|
||||
if((ret = mbedtls_ctr_drbg_seed(&client->ctr_drbg, mbedtls_entropy_func, &client->entropy,
|
||||
NULL, 0)) != 0) {
|
||||
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Setting hostname for TLS session...");
|
||||
/* Hostname set here should match CN in server certificate */
|
||||
if((ret = mbedtls_ssl_set_hostname(&client->ssl, SERVER_ADDRESS)) != 0) {
|
||||
ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Setting up the SSL/TLS structure...");
|
||||
if((ret = mbedtls_ssl_config_defaults(&client->conf,
|
||||
MBEDTLS_SSL_IS_CLIENT,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
|
||||
ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
mbedtls_ssl_conf_rng(&client->conf, mbedtls_ctr_drbg_random, &client->ctr_drbg);
|
||||
|
||||
if ((ret = mbedtls_ssl_setup(&client->ssl, &client->conf)) != 0) {
|
||||
ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void client_task(void *pvParameters)
|
||||
{
|
||||
int ret;
|
||||
|
||||
xSemaphoreHandle *sema = (xSemaphoreHandle *) pvParameters;
|
||||
|
||||
mbedtls_endpoint_t client;
|
||||
|
||||
if(client_setup(&client) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SSL client setup failed");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Set the custom bundle which DOESN'T includes the server's root certificate (default bundle)
|
||||
esp_crt_bundle_attach(&client.conf);
|
||||
|
||||
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);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Connected.");
|
||||
mbedtls_ssl_set_bio(&client.ssl, &client.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
|
||||
|
||||
ESP_LOGI(TAG, "Performing the SSL/TLS handshake with bundle that is missing the server root certificate");
|
||||
ret = mbedtls_ssl_handshake(&client.ssl);
|
||||
|
||||
ESP_LOGI(TAG, "Verifying peer X.509 certificate for bundle ...");
|
||||
TEST_ASSERT(mbedtls_ssl_get_verify_result(&client.ssl) != 0);
|
||||
|
||||
// Reset session before new connection
|
||||
mbedtls_ssl_close_notify(&client.ssl);
|
||||
mbedtls_ssl_session_reset(&client.ssl);
|
||||
|
||||
// Set the custom bundle which includes the server's root certificate
|
||||
esp_crt_bundle_set(server_cert_bundle_start);
|
||||
|
||||
ESP_LOGI(TAG, "Performing the SSL/TLS handshake with bundle containing the server root certificate");
|
||||
ret = mbedtls_ssl_handshake(&client.ssl);
|
||||
|
||||
ESP_LOGI(TAG, "Verifying peer X.509 certificate ...");
|
||||
ret = mbedtls_ssl_get_verify_result(&client.ssl);
|
||||
TEST_ASSERT(ret == 0);
|
||||
|
||||
if(ret == 0) {
|
||||
ESP_LOGI(TAG, "Certificate validated");
|
||||
}
|
||||
|
||||
|
||||
exit_flag = true;
|
||||
|
||||
exit:
|
||||
mbedtls_ssl_close_notify(&client.ssl);
|
||||
mbedtls_ssl_session_reset(&client.ssl);
|
||||
esp_crt_bundle_detach(&client.conf);
|
||||
endpoint_teardown(&client);
|
||||
xSemaphoreGive(*sema);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("custom certificate bundle", "[mbedtls]")
|
||||
{
|
||||
test_case_uses_tcpip();
|
||||
|
||||
xSemaphoreHandle exit_sema = xSemaphoreCreateCounting(2, 0);
|
||||
|
||||
xTaskCreate(server_task, "server task", 8192, &exit_sema, 5, NULL);
|
||||
|
||||
// Wait for the server to start up
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
xTaskCreate(client_task, "https_get_task", 8192, &exit_sema, 5, NULL);
|
||||
|
||||
for(int i = 0; i < 2; i++) {
|
||||
if(!xSemaphoreTake(exit_sema, 10000/portTICK_PERIOD_MS)) {
|
||||
TEST_FAIL_MESSAGE("exit_sem not released by test task");
|
||||
}
|
||||
}
|
||||
vSemaphoreDelete(exit_sema);
|
||||
}
|
|
@ -154,6 +154,8 @@ INPUT = \
|
|||
## ESP Serial Slave Link
|
||||
$(IDF_PATH)/components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h \
|
||||
$(IDF_PATH)/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h \
|
||||
## ESP Certificate Bundle
|
||||
$(IDF_PATH)/components/mbedtls/esp_crt_bundle/include/esp_crt_bundle.h \
|
||||
##
|
||||
## Provisioning - API Reference
|
||||
##
|
||||
|
|
82
docs/en/api-reference/protocols/esp_crt_bundle.rst
Normal file
82
docs/en/api-reference/protocols/esp_crt_bundle.rst
Normal file
|
@ -0,0 +1,82 @@
|
|||
ESP x509 Certificate Bundle
|
||||
===========================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The ESP x509 Certificate Bundle API provides an easy way to include a bundle of custom x509 root certificates for TLS server verification.
|
||||
|
||||
.. note:: The bundle is currently not available when using WolfSSL.
|
||||
|
||||
The bundle comes with the complete list of root certificates from Mozilla’s NSS root certificate store. Using the gen_crt_bundle.py python utility the certificates’ subject name and public key are stored in a file and embedded in the {IDF_TARGET_NAME} binary.
|
||||
|
||||
When generating the bundle you may choose between:
|
||||
|
||||
* The full root certificate bundle from Mozilla, containing more than 130 certificates. The current bundle was updated Wed Jan 23 04:12:09 2019 GMT.
|
||||
* A pre-selected filter list of the name of the most commonly used root certificates, reducing the amount of certificates to around 35 while still having around 90 % coverage according to market share statistics.
|
||||
|
||||
In addition it is possible to specify a path to a certificate file or a directory containing certificates which then will be added to the generated bundle.
|
||||
|
||||
.. note:: Trusting all root certificates means the list will have to be updated if any of the certificates are retracted. This includes removing them from `cacrt_all.pem`.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Most configuration is done through menuconfig. Make and CMake will generate the bundle according to the configuration and embed it.
|
||||
|
||||
* :ref:`CONFIG_MBEDTLS_CERTIFICATE_BUNDLE`: automatically build and attach the bundle.
|
||||
* :ref:`CONFIG_MBEDTLS_DEFAULT_CERTIFICATE_BUNDLE`: decide which certificates to include from the complete root list.
|
||||
* :ref:`CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH`: specify the path of any additional certificates to embed in the bundle.
|
||||
|
||||
To enable the bundle when using ESP-TLS simply pass the function pointer to the bundle attach function:
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_tls_cfg_t cfg = {
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
};
|
||||
|
||||
This is done to avoid embedding the certificate bundle unless activated by the user.
|
||||
|
||||
If using mbedTLS directly then the bundle may be activated by directly calling the attach function during the setup process:
|
||||
|
||||
.. code:: c
|
||||
|
||||
mbedtls_ssl_config conf;
|
||||
mbedtls_ssl_config_init(&conf);
|
||||
|
||||
esp_crt_bundle_attach(&conf);
|
||||
|
||||
|
||||
Generating the List of Root Certificates
|
||||
----------------------------------------
|
||||
The list of root certificates comes from Mozilla's NSS root certificate store, which can be found `here <https://wiki.mozilla.org/CA/Included_Certificates>`_
|
||||
The list can be downloaded and created by running the script ``mk-ca-bundle.pl`` that is distributed as a part of `curl <https://github.com/curl/curl>`_.
|
||||
Another alternative would be to download the finished list directly from the curl website: `CA certificates extracted from Mozilla <https://curl.haxx.se/docs/caextract.html>`_
|
||||
|
||||
The common certificates bundle were made by selecting the authorities with a market share of more than 1 % from w3tech's `SSL Survey <https://w3techs.com/technologies/overview/ssl_certificate/all>`_.
|
||||
These authorities were then used to pick the names of the certificates for the filter list, `cmn_crt_authorities.csv`, from `this list <https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReportPEMCSV>`_ provided by Mozilla.
|
||||
|
||||
.. _updating_bundle:
|
||||
|
||||
Updating the Certificate Bundle
|
||||
-------------------------------
|
||||
|
||||
The bundle is embedded into the app and can be updated along with the app by an OTA update. If you want to include a more up-to-date bundle than the bundle currently included in IDF, then the certificate list can be downloaded from Mozilla as described in :ref:`updating_bundle`.
|
||||
|
||||
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
Simple HTTPS example that uses ESP-TLS to establish a secure socket connection using the certificate bundle with two custom certificates added for verification: :example:`protocols/https_x509_bundle`.
|
||||
|
||||
HTTPS example that uses ESP-TLS and the default bundle: :example:`protocols/https_request`.
|
||||
|
||||
HTTPS example that uses mbedTLS and the default bundle: :example:`protocols/https_mbedtls`.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_crt_bundle.inc
|
||||
|
|
@ -17,6 +17,7 @@ Application Protocols
|
|||
Modbus <modbus>
|
||||
Websocket Client <esp_websocket_client>
|
||||
:esp32: ESP Serial Slave Link <esp_serial_slave_link>
|
||||
Certificate Bundle <esp_crt_bundle>
|
||||
|
||||
Code examples for this API section are provided in the :example:`protocols` directory of ESP-IDF examples.
|
||||
|
||||
|
|
1
docs/zh_CN/api-reference/protocols/esp_crt_bundle.rst
Normal file
1
docs/zh_CN/api-reference/protocols/esp_crt_bundle.rst
Normal file
|
@ -0,0 +1 @@
|
|||
.. include:: ../../../en/api-reference/protocols/esp_crt_bundle.rst
|
|
@ -17,6 +17,7 @@
|
|||
Modbus slave <modbus>
|
||||
Local Control <esp_local_ctrl>
|
||||
:esp32: ESP Serial Slave Link <esp_serial_slave_link>
|
||||
Certificate Bundle <esp_crt_bundle>
|
||||
|
||||
此 API 部分的示例代码在 ESP-IDF 示例工程的 :example:`protocols` 目录下提供。
|
||||
|
||||
|
|
|
@ -2,5 +2,4 @@
|
|||
#
|
||||
# (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.)
|
||||
idf_component_register(SRCS "https_mbedtls_example_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES server_root_cert.pem)
|
||||
INCLUDE_DIRS ".")
|
|
@ -3,8 +3,3 @@
|
|||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
# embed files from the "certs" directory as binary data symbols
|
||||
# in the app
|
||||
COMPONENT_EMBED_TXTFILES := server_root_cert.pem
|
||||
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/error.h"
|
||||
#include "mbedtls/certs.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
|
||||
|
||||
/* Constants that aren't configurable in menuconfig */
|
||||
|
@ -61,19 +62,6 @@ static const char *REQUEST = "GET " WEB_URL " HTTP/1.0\r\n"
|
|||
"User-Agent: esp-idf/1.0 esp32\r\n"
|
||||
"\r\n";
|
||||
|
||||
/* Root cert for howsmyssl.com, taken from server_root_cert.pem
|
||||
|
||||
The PEM file was extracted from the output of this command:
|
||||
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
|
||||
|
||||
The CA root cert is the last cert given in the chain of certs.
|
||||
|
||||
To embed it in the app binary, the PEM file is named
|
||||
in the component.mk COMPONENT_EMBED_TXTFILES variable.
|
||||
*/
|
||||
extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
|
||||
extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end");
|
||||
|
||||
|
||||
static void https_get_task(void *pvParameters)
|
||||
{
|
||||
|
@ -102,14 +90,13 @@ static void https_get_task(void *pvParameters)
|
|||
abort();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Loading the CA root certificate...");
|
||||
ESP_LOGI(TAG, "Attaching the certificate bundle...");
|
||||
|
||||
ret = mbedtls_x509_crt_parse(&cacert, server_root_cert_pem_start,
|
||||
server_root_cert_pem_end-server_root_cert_pem_start);
|
||||
ret = esp_crt_bundle_attach(&conf);
|
||||
|
||||
if(ret < 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
|
||||
ESP_LOGE(TAG, "esp_crt_bundle_attach returned -0x%x\n\n", -ret);
|
||||
abort();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
|
||||
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
|
||||
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
|
||||
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
|
||||
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
|
||||
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
|
||||
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
|
||||
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
|
||||
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
|
||||
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
|
||||
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
|
||||
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
|
||||
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
|
||||
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
|
||||
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
|
||||
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
|
||||
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
|
||||
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
|
||||
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
|
||||
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
|
||||
-----END CERTIFICATE-----
|
|
@ -2,5 +2,4 @@
|
|||
#
|
||||
# (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.)
|
||||
idf_component_register(SRCS "https_request_example_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES server_root_cert.pem)
|
||||
INCLUDE_DIRS ".")
|
|
@ -3,8 +3,4 @@
|
|||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
# embed files from the "certs" directory as binary data symbols
|
||||
# in the app
|
||||
COMPONENT_EMBED_TXTFILES := server_root_cert.pem
|
||||
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "lwip/dns.h"
|
||||
|
||||
#include "esp_tls.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
|
||||
/* Constants that aren't configurable in menuconfig */
|
||||
#define WEB_SERVER "www.howsmyssl.com"
|
||||
|
@ -54,20 +55,6 @@ static const char *REQUEST = "GET " WEB_URL " HTTP/1.0\r\n"
|
|||
"User-Agent: esp-idf/1.0 esp32\r\n"
|
||||
"\r\n";
|
||||
|
||||
/* Root cert for howsmyssl.com, taken from server_root_cert.pem
|
||||
|
||||
The PEM file was extracted from the output of this command:
|
||||
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
|
||||
|
||||
The CA root cert is the last cert given in the chain of certs.
|
||||
|
||||
To embed it in the app binary, the PEM file is named
|
||||
in the component.mk COMPONENT_EMBED_TXTFILES variable.
|
||||
*/
|
||||
extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
|
||||
extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end");
|
||||
|
||||
|
||||
static void https_get_task(void *pvParameters)
|
||||
{
|
||||
char buf[512];
|
||||
|
@ -75,8 +62,7 @@ static void https_get_task(void *pvParameters)
|
|||
|
||||
while(1) {
|
||||
esp_tls_cfg_t cfg = {
|
||||
.cacert_buf = server_root_cert_pem_start,
|
||||
.cacert_bytes = server_root_cert_pem_end - server_root_cert_pem_start,
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
};
|
||||
|
||||
struct esp_tls *tls = esp_tls_conn_http_new(WEB_URL, &cfg);
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
|
||||
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
|
||||
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
|
||||
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
|
||||
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
|
||||
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
|
||||
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
|
||||
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
|
||||
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
|
||||
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
|
||||
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
|
||||
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
|
||||
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
|
||||
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
|
||||
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
|
||||
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
|
||||
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
|
||||
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
|
||||
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
|
||||
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
|
||||
-----END CERTIFICATE-----
|
10
examples/protocols/https_x509_bundle/CMakeLists.txt
Normal file
10
examples/protocols/https_x509_bundle/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(https_x509_bundle)
|
11
examples/protocols/https_x509_bundle/Makefile
Normal file
11
examples/protocols/https_x509_bundle/Makefile
Normal file
|
@ -0,0 +1,11 @@
|
|||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := https_x509_bundle
|
||||
|
||||
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
45
examples/protocols/https_x509_bundle/README.md
Normal file
45
examples/protocols/https_x509_bundle/README.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
# HTTPS x509 Bundle Example
|
||||
|
||||
This example shows how to use the ESP certificate bundle utility to embed a bundle of x509 certificates and use them to
|
||||
establish a simple HTTPS connection over a secure connection. The path of the certificates are specified using menuconfig.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
|
||||
## Example workflow
|
||||
- ESP TLS is initialized with the certificate bundle option enabled.
|
||||
- The application loops through the given URLs, establishing a secure TLS connection to all of them, verifying the server certificate included.
|
||||
|
||||
### Configure the project
|
||||
|
||||
* Open the project configuration menu (`idf.py menuconfig`)
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../README.md) for more details.
|
||||
* When using Make build system, set `Default serial port` under `Serial flasher config`.
|
||||
* If using a different folder than `certs` for storing certificates then update `Custom Certificate Bundle Path` under `Component config` - `mbedTLS` - `Certificate Bundle`
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
```
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (491) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
|
||||
I (4051) example_connect: Ethernet Link Up
|
||||
I (5971) tcpip_adapter: eth ip: 192.168.2.137, mask: 255.255.255.0, gw: 192.168.2.2
|
||||
I (5971) example_connect: Connected to Ethernet
|
||||
I (5971) example_connect: IPv4 address: 192.168.2.137
|
||||
I (5971) example_connect: IPv6 address: fe80:0000:0000:0000:bedd:c2ff:fed4:a92b
|
||||
I (5981) example: Connecting to 2 URLs
|
||||
I (8371) example: Connection established to https://www.howsmyssl.com/a/check
|
||||
I (11821) example: Connection established to https://espressif.com
|
||||
I (12821) example: Completed 2 connections
|
||||
I (12821) example: Starting over again...
|
||||
|
BIN
examples/protocols/https_x509_bundle/certs/DST_root.der
Normal file
BIN
examples/protocols/https_x509_bundle/certs/DST_root.der
Normal file
Binary file not shown.
|
@ -0,0 +1,25 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
|
||||
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
|
||||
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
|
||||
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
|
||||
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
|
||||
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
|
||||
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
|
||||
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
|
||||
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
|
||||
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
|
||||
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
|
||||
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
|
||||
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
|
||||
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
|
||||
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
|
||||
|
40
examples/protocols/https_x509_bundle/example_test.py
Normal file
40
examples/protocols/https_x509_bundle/example_test.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import IDF
|
||||
except ImportError:
|
||||
# this is a test case write with tiny-test-fw.
|
||||
# to run test cases outside tiny-test-fw,
|
||||
# we need to set environment variable `TEST_FW_PATH`,
|
||||
# then get and insert `TEST_FW_PATH` to sys path before import FW module
|
||||
test_fw_path = os.getenv("TEST_FW_PATH")
|
||||
if test_fw_path and test_fw_path not in sys.path:
|
||||
sys.path.insert(0, test_fw_path)
|
||||
import IDF
|
||||
|
||||
|
||||
@IDF.idf_example_test(env_tag="Example_WIFI", ignore=True)
|
||||
def test_examples_protocol_https_x509_bundle(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
1. join AP
|
||||
2. connect to multiple URLs
|
||||
3. send http request
|
||||
"""
|
||||
dut1 = env.get_dut("https_x509_bundle", "examples/protocols/https_x509_bundle")
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, "https_x509_bundle.bin")
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
IDF.log_performance("https_x509_bundle_bin_size", "{}KB".format(bin_size // 1024))
|
||||
IDF.check_performance("https_x509_bundle_bin_size", bin_size // 1024)
|
||||
# start test
|
||||
dut1.start_app()
|
||||
num_URLS = dut1.expect(re.compile(r"Connecting to (\d+) URLs"), timeout=30)
|
||||
dut1.expect(re.compile(r"Connection established to ([\s\S]*)"), timeout=30)
|
||||
dut1.expect("Completed {} connections".format(num_URLS[0]), timeout=60)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_https_x509_bundle()
|
4
examples/protocols/https_x509_bundle/main/CMakeLists.txt
Normal file
4
examples/protocols/https_x509_bundle/main/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
set(COMPONENT_SRCS "https_x509_bundle_example_main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
5
examples/protocols/https_x509_bundle/main/component.mk
Normal file
5
examples/protocols/https_x509_bundle/main/component.mk
Normal file
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/* HTTPS GET Example using plain mbedTLS sockets
|
||||
*
|
||||
* Contacts the howsmyssl.com API via TLS v1.2 and reads a JSON
|
||||
* response.
|
||||
*
|
||||
* Adapted from the ssl_client1 example in mbedtls.
|
||||
*
|
||||
* Original Copyright (C) 2006-2016, ARM Limited, All Rights Reserved, Apache 2.0 License.
|
||||
* Additions Copyright (C) Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD, Apache 2.0 License.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
#include "esp_tls.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
|
||||
#define MAX_URLS 2
|
||||
|
||||
static const char *web_urls[MAX_URLS] = {
|
||||
"https://www.howsmyssl.com/a/check",
|
||||
"https://espressif.com",
|
||||
};
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
static void https_get_task(void *pvParameters)
|
||||
{
|
||||
while (1) {
|
||||
int conn_count = 0;
|
||||
|
||||
ESP_LOGI(TAG, "Connecting to %d URLs", MAX_URLS);
|
||||
for (int i = 0; i < MAX_URLS; i++) {
|
||||
esp_tls_cfg_t cfg = {
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
};
|
||||
|
||||
struct esp_tls *tls = esp_tls_conn_http_new(web_urls[i], &cfg);
|
||||
|
||||
if (tls != NULL) {
|
||||
ESP_LOGI(TAG, "Connection established to %s", web_urls[i]);
|
||||
conn_count++;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Could not connect to %s", web_urls[i]);
|
||||
}
|
||||
|
||||
esp_tls_conn_delete(tls);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Completed %d connections", conn_count);
|
||||
ESP_LOGI(TAG, "Starting over again...");
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK( nvs_flash_init() );
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
xTaskCreate(&https_get_task, "https_get_task", 8192, NULL, 5, NULL);
|
||||
}
|
5
examples/protocols/https_x509_bundle/sdkconfig.defaults
Normal file
5
examples/protocols/https_x509_bundle/sdkconfig.defaults
Normal file
|
@ -0,0 +1,5 @@
|
|||
# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL is not set
|
||||
# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set
|
||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE=y
|
||||
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y
|
||||
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="certs"
|
|
@ -157,6 +157,14 @@ test_multi_heap_on_host:
|
|||
- cd components/heap/test_multi_heap_host
|
||||
- ./test_all_configs.sh
|
||||
|
||||
test_certificate_bundle_on_host:
|
||||
extends: .host_test_template
|
||||
tags:
|
||||
- build
|
||||
script:
|
||||
- cd components/mbedtls/esp_crt_bundle/test_gen_crt_bundle/
|
||||
- ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh ./test_gen_crt_bundle.py
|
||||
|
||||
test_confserver:
|
||||
extends: .host_test_template
|
||||
script:
|
||||
|
|
|
@ -6,6 +6,8 @@ components/espcoredump/espcoredump.py
|
|||
components/espcoredump/test/test_espcoredump.py
|
||||
components/espcoredump/test/test_espcoredump.sh
|
||||
components/heap/test_multi_heap_host/test_all_configs.sh
|
||||
components/mbedtls/esp_crt_bundle/gen_crt_bundle.py
|
||||
components/mbedtls/esp_crt_bundle/test_gen_crt_bundle/test_gen_crt_bundle.py
|
||||
components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py
|
||||
components/partition_table/gen_empty_partition.py
|
||||
components/partition_table/gen_esp32part.py
|
||||
|
|
Loading…
Reference in a new issue