This commit is contained in:
2024-07-16 21:11:16 -04:00
parent d258a70025
commit b90676fd13
615 changed files with 112854 additions and 542 deletions

View File

@@ -0,0 +1,6 @@
# The following 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)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp_secure_cert_app)

View File

@@ -0,0 +1,61 @@
# ESP Secure Certificate Application
The sample app demonstrates the use of APIs from *esp_secure_cert_mgr* to retrieve the contents of the *esp_secure_cert* partition. The example can also be used to verify the validity of the contents from the *esp_secure_cert* partition.
## Requirements
* The device must be pre-provisioned and have an *esp_secure_cert* partition.
## How to use the example
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
### Configure the project
* The *esp_secure_cert* partition needs to be generated and flashed first with help of [configure_esp_secure_cert.py](https://github.com/espressif/esp_secure_cert_mgr/blob/main/tools/configure_esp_secure_cert.py) script. See [tools/README.md](https://github.com/espressif/esp_secure_cert_mgr/blob/main/tools/README.md) for more details.
* Please ensure that appropriate type of esp_secure_cert partition has been set in your projects `partitions.csv` file. Please refer the "esp_secure_cert partition" section in the [component README](https://github.com/espressif/esp_secure_cert_mgr#readme) for more details.
### Build and Flash
Build the project and flash it to the board, then run the monitor tool to view the serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(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 (331) sample_app: Device Cert:
Length: 1233
-----BEGIN CERTIFICATE-----
.
.
-----END CERTIFICATE-----
I (441) sample_app: CA Cert:
Length: 1285
-----BEGIN CERTIFICATE-----
.
.
-----END CERTIFICATE-----
I (561) sample_app: Successfuly obtained ciphertext, ciphertext length is 1200
I (571) sample_app: Successfuly obtained initialization vector, iv length is 16
I (571) sample_app: RSA length is 2048
I (581) sample_app: Efuse key id 1
I (581) sample_app: Successfully obtained the ds context
I (831) sample_app: Ciphertext validated succcessfully
```
## Additional configurations for `pre_prov` partition
Few of the modules which were pre-provisioned initially had the name of the pre-provisioning partition as `pre_prov`. For the modules which have pre-provisioning partition of name `esp_secure_cert` this part can be ignored.
* For modules with `pre_prov` partition of type *cust_flash*, please update the line refering to `esp_secure_cert` partition in the partitions.csv with following:
```
pre_prov, 0x3F, , 0xD000, 0x6000,
```
* No change is necessary for `pre_prov` partition of type *nvs*.

View File

@@ -0,0 +1,4 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
)

View File

@@ -0,0 +1,257 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/* ESP Secure Cert App
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_secure_cert_read.h"
#include "esp_secure_cert_tlv_read.h"
#include "mbedtls/ssl.h"
#include "mbedtls/pk.h"
#include "mbedtls/x509.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "esp_idf_version.h"
#define TAG "esp_secure_cert_app"
#ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
static esp_err_t test_ciphertext_validity(esp_ds_data_ctx_t *ds_data, unsigned char *dev_cert, size_t dev_cert_len)
{
mbedtls_x509_crt crt;
mbedtls_x509_crt_init(&crt);
unsigned char *sig = NULL;
if (ds_data == NULL || dev_cert == NULL) {
return ESP_ERR_INVALID_ARG;
}
int ret = mbedtls_x509_crt_parse(&crt, dev_cert, dev_cert_len);
if (ret < 0) {
ESP_LOGE(TAG, "Parsing of device certificate failed, returned %02X", ret);
goto exit;
}
esp_err_t esp_ret = esp_ds_init_data_ctx(ds_data);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialze the DS context");
return esp_ret;
}
const size_t sig_len = 256;
uint32_t hash[8] = {[0 ... 7] = 0xAABBCCDD};
sig = (unsigned char *) calloc(1, 1000 * sizeof(char));
if (sig == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for signature");
goto exit;
}
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
ret = esp_ds_rsa_sign(NULL, NULL, NULL, 0, MBEDTLS_MD_SHA256, 0, (const unsigned char *) hash, sig);
#else
ret = esp_ds_rsa_sign(NULL, NULL, NULL, MBEDTLS_MD_SHA256, 0, (const unsigned char *) hash, sig);
#endif
if (ret != 0) {
ESP_LOGE(TAG, "Failed to sign the data with rsa key, returned %02X", ret);
goto exit;
}
esp_ds_release_ds_lock();
ret = mbedtls_pk_verify(&crt.pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, sig_len);
if (ret != 0) {
printf("\nFailed to verify the data\n");
goto exit;
}
free(sig);
return ESP_OK;
exit:
free(sig);
mbedtls_x509_crt_free(&crt);
printf("\nFailed to verify the ciphertext\n");
esp_ds_release_ds_lock();
return ESP_FAIL;
}
#else
static esp_err_t test_priv_key_validity(unsigned char* priv_key, size_t priv_key_len, unsigned char *dev_cert, size_t dev_cert_len)
{
static const char *pers = "Hello";
mbedtls_x509_crt crt;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_pk_context pk;
unsigned char *sig = NULL;
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_x509_crt_init(&crt);
mbedtls_entropy_init(&entropy);
mbedtls_pk_init(&pk);
esp_err_t esp_ret = ESP_FAIL;
if (priv_key == NULL || dev_cert == NULL) {
return ESP_ERR_INVALID_ARG;
}
int ret = mbedtls_x509_crt_parse(&crt, dev_cert, dev_cert_len);
if (ret != 0) {
ESP_LOGE(TAG, "Parsing of device certificate failed, returned %02X", ret);
esp_ret = ESP_FAIL;
goto exit;
} else {
ESP_LOGI(TAG, "Successfully parsed the certificate");
}
ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, strlen(pers));
if (ret != 0) {
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned -0x%04x", -ret );
esp_ret = ESP_FAIL;
goto exit;
}
#if (MBEDTLS_VERSION_NUMBER < 0x03000000)
ret = mbedtls_pk_parse_key(&pk, (const uint8_t *)priv_key, priv_key_len, NULL, 0);
#else
ret = mbedtls_pk_parse_key(&pk, (const uint8_t *)priv_key, priv_key_len, NULL, 0, mbedtls_ctr_drbg_random, &ctr_drbg);
#endif
if (ret != 0) {
ESP_LOGE(TAG, "Failed to parse the key");
esp_ret = ESP_FAIL;
goto exit;
} else {
ESP_LOGI(TAG, "Successfully parsed the key");
}
static uint32_t hash[8] = {[0 ... 7] = 0xAABBCCDD};
#define SIG_SIZE 1024
sig = (unsigned char*)calloc(1, SIG_SIZE * sizeof(char));
if (sig == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory");
esp_ret = ESP_FAIL;
goto exit;
}
size_t sig_len = 0;
#if (MBEDTLS_VERSION_NUMBER < 0x03000000)
ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, &sig_len, mbedtls_ctr_drbg_random, &ctr_drbg);
#else
ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, SIG_SIZE, &sig_len, mbedtls_ctr_drbg_random, &ctr_drbg);
#endif
if (ret != 0) {
ESP_LOGE(TAG, "Failed to sign the data");
esp_ret = ESP_FAIL;
goto exit;
} else {
ESP_LOGI(TAG, "Successfully signed the data");
}
ret = mbedtls_pk_verify(&crt.pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, sig_len);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to verify the signed data");
esp_ret = ESP_FAIL;
goto exit;
}
esp_ret = ESP_OK;
exit:
free(sig);
mbedtls_pk_free(&pk);
mbedtls_entropy_free(&entropy);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_x509_crt_free(&crt);
return esp_ret;
}
#endif
void app_main()
{
uint32_t len = 0;
char *addr = NULL;
esp_err_t esp_ret = ESP_FAIL;
esp_ret = esp_secure_cert_get_device_cert(&addr, &len);
if (esp_ret == ESP_OK) {
ESP_LOGI(TAG, "Device Cert: \nLength: %"PRIu32"\n%s", len, (char *)addr);
} else {
ESP_LOGE(TAG, "Failed to obtain flash address of device cert");
}
esp_ret = esp_secure_cert_get_ca_cert(&addr, &len);
if (esp_ret == ESP_OK) {
ESP_LOGI(TAG, "CA Cert: \nLength: %"PRIu32"\n%s", len, (char *)addr);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, addr, len, ESP_LOG_DEBUG);
} else {
ESP_LOGE(TAG, "Failed to obtain flash address of ca_cert");
}
#ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
esp_ret = esp_secure_cert_get_priv_key(&addr, &len);
if (esp_ret == ESP_OK) {
ESP_LOGI(TAG, "PEM KEY: \nLength: %"PRIu32"\n%s", len, (char *)addr);
} else {
ESP_LOGE(TAG, "Failed to obtain flash address of private_key");
}
uint32_t dev_cert_len = 0;
char *dev_cert_addr = NULL;
esp_ret = esp_secure_cert_get_device_cert(&dev_cert_addr, &dev_cert_len);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to obtain the dev cert flash address");
}
esp_ret = test_priv_key_validity((unsigned char *)addr, len, (unsigned char *)dev_cert_addr, dev_cert_len);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to validate the private key and device certificate");
}
#else
esp_ds_data_ctx_t *ds_data = NULL;
ds_data = esp_secure_cert_get_ds_ctx();
if (ds_data != NULL) {
ESP_LOGI(TAG, "Successfully obtained the ds context");
ESP_LOG_BUFFER_HEX_LEVEL(TAG, ds_data->esp_ds_data->c, ESP_DS_C_LEN, ESP_LOG_DEBUG);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, ds_data->esp_ds_data->iv, ESP_DS_IV_LEN, ESP_LOG_DEBUG);
ESP_LOGI(TAG, "The value of rsa length is %d", ds_data->rsa_length_bits);
ESP_LOGI(TAG, "The value of efuse key id is %d", ds_data->efuse_key_id);
} else {
ESP_LOGE(TAG, "Failed to obtain the ds context");
}
/* Read the dev_cert addr again */
esp_ret = esp_secure_cert_get_device_cert(&addr, &len);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to obtain the dev cert flash address");
}
esp_ret = test_ciphertext_validity(ds_data, (unsigned char *)addr, len);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to validate ciphertext");
} else {
ESP_LOGI(TAG, "Ciphertext validated succcessfully");
}
#endif
if (esp_ret == ESP_OK) {
ESP_LOGI(TAG, "Successfully obtained and verified the contents of esp_secure_cert partition");
} else {
ESP_LOGE(TAG, "Failed to obtain and verify the contents of the esp_secure_cert partition");
}
esp_secure_cert_tlv_config_t tlv_config = {};
tlv_config.type = ESP_SECURE_CERT_DEV_CERT_TLV;
tlv_config.subtype = ESP_SECURE_CERT_SUBTYPE_0;
esp_secure_cert_tlv_info_t tlv_info = {};
esp_ret = esp_secure_cert_get_tlv_info(&tlv_config, &tlv_info);
if (esp_ret == ESP_OK) {
ESP_LOGI(TAG, "Device Cert: \nLength: %"PRIu32"\n%s", tlv_info.length, tlv_info.data);
}
ESP_LOGI(TAG, "Printing a list of TLV entries");
esp_secure_cert_list_tlv_entries();
}

View File

@@ -0,0 +1,6 @@
version: "1.0.0"
description: ESP Secure Cert Manager Example
dependencies:
espressif/esp_secure_cert_mgr:
version: '^2.0.8'
override_path: '../../../' # three levels up, pointing the directory with the component itself

View File

@@ -0,0 +1,4 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
esp_secure_cert, 0x3F, , 0xD000, 0x2000, encrypted
factory, app, factory, 0x20000, 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 esp_secure_cert, 0x3F, , 0xD000, 0x2000, encrypted
4 factory, app, factory, 0x20000, 1M,

View File

@@ -0,0 +1,40 @@
# ESP Secure cert partition tests
This folder contains the test setup for testing the different flash formats supported by esp_secure_cert partition. The tests are based on the QEMU emulater.
## Requirements
* **QEMU emulator**:
The test is based on qemu and needs the qemu emulator support.
* For Linux Distributions, the executables to be downloaded from [qemu/releases](https://github.com/espressif/qemu/releases)
* For macOS you need to build the binaries for specific release locally on machine. see [Compiling Qemu](https://github.com/espressif/esp-toolchain-docs/blob/main/qemu/esp32/README.md#compiling-qemu) for more details.
* **Pytest**:
Install the pytest environment by executing following command
```pip install -r requirements.txt```
## Testing
The script [`build_qemu_images.sh`](./build_qemu_images.sh) can be used to generate the respective qemu images. These images are then provided to pytest framework for execution.
### Test the TLV support on esp32
The TLV support can be tested with following commands:
* ```./build_qemu_images.sh tlv```
* ``` cd ..```
* ``pytest --target esp32``
### Test the legacy flash format support on esp32
The TLV support can be tested with following commands:
* ```./build_qemu_images.sh legacy```
* ``` cd ..```
* ``pytest --target esp32``

View File

@@ -0,0 +1,82 @@
#!/bin/bash
ESP_SECURE_CERT_APP=$PWD/../
if [ -z "$1" ]; then
echo "Usage: build_qemu_image.sh image_type
image type can be tlv, legacy_format"
exit 1
fi
image_type=$1
# This option causes the script to fail if any command fails in the main body of the script
# Note: this does not apply to the internal functions
# We need to check the exit status of each command individually
set -e
save_defaults() {
cp sdkconfig.defaults sdkconfig_defaults_clean
}
restore_defaults() {
mv sdkconfig_defaults_clean sdkconfig.defaults
}
clean() {
rm -rf sdkconfig 2>&1
rm -rf build 2>&1
}
idf_path_verify() {
if [[ -z "$IDF_PATH" ]]; then
echo "IDF_PATH not set. Please set IDF_PATH "
exit 1
else
echo '--------####--------'
echo 'IDF_PATH & Branch'
echo $IDF_PATH;(cd $IDF_PATH;git branch --show-current)
echo '--------####--------'
fi
}
build_tlv_image() {
cd $ESP_SECURE_CERT_APP
save_defaults
clean
cat sdkconfig.ci.tlv >> sdkconfig.defaults
idf.py build
cd qemu_test
rm -rf qemu_test_images
mkdir qemu_test_images
./make-qemu-img.sh cust_flash_tlv/cust_flash_tlv.bin cust_flash_tlv/partition-table.bin ../ esp_secure_cert_app qemu_test_images/cust_flash_tlv_qemu.img
cd $ESP_SECURE_CERT_APP
restore_defaults
}
build_legacy_format_images() {
cd $ESP_SECURE_CERT_APP
save_defaults
clean
cat sdkconfig.ci.legacy >> sdkconfig.defaults
idf.py build
cd qemu_test
rm -rf qemu_test_images
mkdir qemu_test_images
./make-qemu-img.sh cust_flash/cust_flash.bin cust_flash/partition-table.bin ../ esp_secure_cert_app qemu_test_images/cust_flash_qemu.img
./make-qemu-img.sh cust_flash_legacy/cust_flash_legacy.bin cust_flash_legacy/partition-table.bin ../ esp_secure_cert_app qemu_test_images/cust_flash_legacy_qemu.img
./make-qemu-img.sh nvs/nvs.bin nvs/partition-table.bin ../ esp_secure_cert_app qemu_test_images/nvs_qemu.img
./make-qemu-img.sh nvs_legacy/nvs_legacy.bin nvs/partition-table.bin ../ esp_secure_cert_app qemu_test_images/nvs_legacy_qemu.img
./make-qemu-img.sh nvs_legacy/nvs_legacy.bin nvs_legacy/partition-table.bin ../ esp_secure_cert_app qemu_test_images/nvs_legacy_qemu_new_part_name.img
cd $ESP_SECURE_CERT_APP
restore_defaults
}
if [ $1 == "tlv" ]; then
# Build the TLV format image
build_tlv_image
fi
if [ $1 == "legacy_format" ]; then
# Build the TLV format image
build_legacy_format_images
fi

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -e
arg_esp_secucertimg=$1
arg_partition_table=$2
arg_projpath="$3"
arg_projname="$4"
arg_flashimg=$5
if [ -z "$1" -o -z "$2" -o -z "$3" ]; then
echo "Usage: make-qemu-img.sh esp_secure_cert_partition partition_table path_to_build_dir project_name image_name"
exit 1
fi
dd if=/dev/zero bs=1024 count=4096 of=${arg_flashimg}
dd if=${arg_projpath}/build/bootloader/bootloader.bin bs=1 seek=$((0x1000)) of=${arg_flashimg} conv=notrunc
dd if=${arg_esp_secucertimg} bs=1 seek=$((0xD000)) of=${arg_flashimg} conv=notrunc
dd if=${arg_partition_table} bs=1 seek=$((0xC000)) of=${arg_flashimg} conv=notrunc
dd if=${arg_projpath}/build/${arg_projname}.bin bs=1 seek=$((0x20000)) of=${arg_flashimg} conv=notrunc

View File

@@ -0,0 +1,3 @@
pytest_embedded
pytest_embedded_idf
pytest_embedded_qemu

View File

@@ -0,0 +1,2 @@
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS=y

View File

@@ -0,0 +1,3 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_OFFSET=0xc000
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"

View File

@@ -0,0 +1,24 @@
import pytest
import os
qemu_images = []
qemu_images_path = 'qemu_test/qemu_test_images'
# Use os.walk to traverse the directory and its subdirectories
for root, dirs, files in os.walk(qemu_images_path):
for file in files:
if file.endswith('.img'):
file_path = os.path.join(root, file)
qemu_images.append(file_path)
# Print the list of image filenames
print('The list entries are ------------')
for image in qemu_images:
print(image)
@pytest.mark.parametrize('qemu_image_path', qemu_images, indirect=True)
@pytest.mark.parametrize('skip_regenerate_image', ['y'], indirect=True)
def test_esp_secure_cert(dut):
dut.expect('Successfully obtained and verified the '
'contents of esp_secure_cert partition')

View File

@@ -0,0 +1,13 @@
[pytest]
addopts = --embedded-services idf,qemu -s
# log related
log_cli = True
log_cli_level = INFO
log_cli_format = %(asctime)s %(levelname)s %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_file = test.log
log_file_level = INFO
log_file_format = %(asctime)s %(levelname)s %(message)s
log_file_date_format = %Y-%m-%d %H:%M:%S