├── .github └── workflows │ ├── release_pypi.yml │ └── upload_component.yml ├── .gitignore ├── .gitlab-ci.yml ├── .pre-commit-config.yaml ├── CMakeLists.txt ├── Changelog.md ├── Kconfig ├── LICENSE ├── README.md ├── ci └── build_test.sh ├── docs ├── _static │ └── tlv_format.png └── format.md ├── examples ├── esp_secure_cert_app │ ├── CMakeLists.txt │ ├── README.md │ ├── main │ │ ├── CMakeLists.txt │ │ ├── app_main.c │ │ └── idf_component.yml │ ├── partitions.csv │ ├── qemu_test │ │ ├── README.md │ │ ├── build_qemu_images.sh │ │ ├── cust_flash │ │ │ ├── cust_flash.bin │ │ │ └── partition-table.bin │ │ ├── cust_flash_legacy │ │ │ ├── cust_flash_legacy.bin │ │ │ └── partition-table.bin │ │ ├── cust_flash_tlv │ │ │ ├── cust_flash_tlv.bin │ │ │ └── partition-table.bin │ │ ├── make-qemu-img.sh │ │ ├── nvs │ │ │ ├── nvs.bin │ │ │ └── partition-table.bin │ │ ├── nvs_legacy │ │ │ ├── nvs_legacy.bin │ │ │ └── partition-table.bin │ │ └── requirements.txt │ ├── sdkconfig.ci.legacy │ ├── sdkconfig.ci.tlv │ ├── sdkconfig.defaults │ └── test_esp_secure_cert.py └── pytest.ini ├── idf_component.yml ├── include ├── esp_secure_cert_crypto.h ├── esp_secure_cert_read.h ├── esp_secure_cert_tlv_config.h └── esp_secure_cert_tlv_read.h ├── private_include ├── esp_secure_cert_config.h └── esp_secure_cert_tlv_private.h ├── srcs ├── esp_secure_cert_crypto.c ├── esp_secure_cert_read.c └── esp_secure_cert_tlv_read.c └── tools ├── LICENSE ├── MANIFEST.in ├── README.md ├── __init__.py ├── configure_esp_secure_cert.py ├── esp_secure_cert ├── __init__.py ├── configure_ds.py ├── custflash_format.py ├── efuse_helper.py ├── esp_secure_cert_helper.py ├── nvs_format.py └── tlv_format.py ├── requirements.txt └── setup.py /.github/workflows/release_pypi.yml: -------------------------------------------------------------------------------- 1 | name: PyPI release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build_and_upload: 10 | runs-on: ubuntu-22.04 # 22.04 is the last version with Python 3.7 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Set up Python 3.7 16 | uses: actions/setup-python@v2 17 | with: 18 | python-version: '3.7' 19 | 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip build setuptools twine 23 | - name: Build and upload esp-secure-cert-tool ${{ github.event.release.tag_name }} 24 | env: 25 | TWINE_USERNAME: __token__ 26 | TWINE_PASSWORD: ${{ secrets.PYPI_PROJECT_TOKEN }} 27 | working-directory: ./tools 28 | run: | 29 | PUBLISHED_VERSION=$(curl https://pypi.org/pypi/esp-secure-cert-tool/json 2>/dev/null | jq -r '.info.version') 30 | CURRENT_VERSION=$(python setup.py --version 2>/dev/null) 31 | if [ "$PUBLISHED_VERSION" == "$CURRENT_VERSION" ]; then 32 | echo "Version ${PUBLISHED_VERSION} already published, skipping..." 33 | exit 0 34 | else 35 | echo "Packaging and publishing new esp-secure-cert-tool version: ${CURRENT_VERSION}" 36 | python setup.py sdist 37 | tar -ztvf dist/* 38 | twine upload dist/* 39 | fi 40 | -------------------------------------------------------------------------------- /.github/workflows/upload_component.yml: -------------------------------------------------------------------------------- 1 | name: Push esp_secure_cert_mgr to Espressif Component Service 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | upload_components: 10 | runs-on: ubuntu-22.04 # 22.04 is the last version with Python 3.7 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: Upload esp_secure_cert_mgr to component service 15 | uses: espressif/upload-components-ci-action@v1 16 | with: 17 | name: esp_secure_cert_mgr 18 | namespace: "espressif" 19 | api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | tools/esp_secure_cert_data/* 3 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - qemu_test 3 | - build 4 | 5 | variables: 6 | BATCH_BUILD: "1" 7 | V: "0" 8 | MAKEFLAGS: "-j5 --no-keep-going" 9 | GIT_SUBMODULE_STRATEGY: recursive 10 | QEMU_IMAGE: ${CI_DOCKER_REGISTRY}/qemu-v5.2:2-20230522 11 | 12 | # before each job, we need to check if this job is filtered by bot stage/job filter 13 | .apply_bot_filter: &apply_bot_filter 14 | python $APPLY_BOT_FILTER_SCRIPT || exit 0 15 | 16 | .setup_env: 17 | before_script: &setup_env 18 | # apply bot filter in before script 19 | - *apply_bot_filter 20 | # add gitlab ssh key 21 | - mkdir -p ~/.ssh 22 | - chmod 700 ~/.ssh 23 | - echo -n $GITLAB_KEY > ~/.ssh/id_rsa_base64 24 | - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa 25 | - chmod 600 ~/.ssh/id_rsa 26 | - echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config 27 | - git --version 28 | - git submodule update --init --recursive 29 | 30 | .build_idf_template: 31 | stage: build 32 | before_script: *setup_env 33 | image: espressif/idf:release-v5.0 34 | tags: 35 | - build 36 | parallel: 3 37 | variables: 38 | PEDANTIC_FLAGS: "-Werror -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function" 39 | EXTRA_CFLAGS: "${PEDANTIC_FLAGS}" 40 | EXTRA_CXXFLAGS: "${PEDANTIC_FLAGS}" 41 | script: 42 | - export EXTRA_CFLAGS=$EXTRA_CFLAGS 43 | - export EXTRA_CXXFLAGS=$EXTRA_CXXFLAGS 44 | - cd ci 45 | - ./build_test.sh $IDF_TARGET $CI_NODE_INDEX || exit 1 46 | 47 | .build_idf_template_release_v4.4: 48 | extends: .build_idf_template 49 | image: espressif/idf:release-v4.4 50 | 51 | .build_idf_template_release_v4.3: 52 | extends: .build_idf_template 53 | image: espressif/idf:release-v4.3 54 | 55 | build_demo_esp32: 56 | extends: .build_idf_template 57 | variables: 58 | IDF_TARGET: esp32 59 | 60 | qemu_test_tlv_esp32: 61 | stage: qemu_test 62 | image: $QEMU_IMAGE 63 | tags: 64 | - qemu_test 65 | script: 66 | - export EXTRA_CFLAGS=$EXTRA_CFLAGS 67 | - export EXTRA_CXXFLAGS=$EXTRA_CXXFLAGS 68 | - export ROOT_PATH=$PWD 69 | - git clone --recursive --single-branch -b v5.2-dev https://github.com/espressif/esp-idf --shallow-submodules --depth 1 /opt/esp-idf 70 | - cd /opt/esp-idf 71 | - bash install.sh 72 | - . ./export.sh 73 | - cd $ROOT_PATH/examples/esp_secure_cert_app/qemu_test 74 | - ./build_qemu_images.sh tlv 75 | - cd ../ 76 | - pytest --target esp32 77 | - cd qemu_test 78 | - ./build_qemu_images.sh legacy_format 79 | - cd ../ 80 | - pytest --target esp32 81 | 82 | build_demo_esp32s2: 83 | extends: .build_idf_template 84 | variables: 85 | IDF_TARGET: esp32s2 86 | 87 | build_demo_esp32s3: 88 | extends: .build_idf_template 89 | variables: 90 | IDF_TARGET: esp32s3 91 | 92 | build_demo_esp32c3: 93 | extends: .build_idf_template 94 | variables: 95 | IDF_TARGET: esp32c3 96 | 97 | build_demo_esp32_v4.4: 98 | extends: .build_idf_template_release_v4.4 99 | variables: 100 | IDF_TARGET: esp32 101 | 102 | build_demo_esp32s3_v4.4: 103 | extends: .build_idf_template_release_v4.4 104 | variables: 105 | IDF_TARGET: esp32s3 106 | 107 | build_demo_esp32c3_v4.4: 108 | extends: .build_idf_template_release_v4.4 109 | variables: 110 | IDF_TARGET: esp32c3 111 | 112 | build_demo_esp32_v4.3: 113 | extends: .build_idf_template_release_v4.3 114 | variables: 115 | IDF_TARGET: esp32 116 | 117 | build_demo_esp32c3_v4.3: 118 | extends: .build_idf_template_release_v4.3 119 | variables: 120 | IDF_TARGET: esp32c3 121 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Contains the pre-commit hooks for the repository 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v3.3.0 5 | hooks: 6 | - id: trailing-whitespace 7 | exclude: '.+\.(md|rst)' 8 | - id: end-of-file-fixer 9 | - id: check-executables-have-shebangs 10 | - id: mixed-line-ending 11 | args: ['-f=lf'] 12 | - repo: https://github.com/PyCQA/flake8 13 | rev: 5.0.4 14 | hooks: 15 | - id: flake8 16 | args: ['--tee', '--benchmark'] 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(srcs "srcs/esp_secure_cert_tlv_read.c" "srcs/esp_secure_cert_crypto.c") 2 | 3 | if(CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS) 4 | list(APPEND srcs "srcs/esp_secure_cert_read.c") 5 | endif() 6 | 7 | set(priv reqs "") 8 | idf_build_get_property(build_components BUILD_COMPONENTS) 9 | if(esp_partition IN_LIST build_components) 10 | list(APPEND priv_reqs esp_partition) 11 | endif() 12 | 13 | idf_component_register(SRCS ${srcs} 14 | INCLUDE_DIRS "include" 15 | PRIV_INCLUDE_DIRS "private_include" 16 | REQUIRES spi_flash mbedtls nvs_flash efuse 17 | PRIV_REQUIRES ${priv_reqs}) 18 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # This file contains the list of changes across different versions 2 | 3 | ## v2.5.0 4 | * Assigned 2 project specific entries for Matter project in the list of TLV entries 5 | 6 | ## v2.4.1 7 | * Added a new API `esp_secure_cert_get_tlv_info` for obtaining TLV information 8 | * Added `esp_secure_cert_free_tlv_info` API for freeing TLV information. 9 | * Added `esp_secure_cert_iterate_to_next_tlv` API for iterating the TLV entries 10 | * Updated the API documentation for available `esp_secure_cert_get_*` APIs 11 | 12 | ## v2.4.0 (yanked) 13 | * Added support for multiple entries of the same type by adding a new field called subtype. 14 | * Fixed API for obtaining CA cert for the legacy flash formats (9b091ee) 15 | 16 | ### Yank explanation 17 | This version was later yanked due to following reason. 18 | 19 | * The API `esp_secure_cert_tlv_get_addr` which was made public in this version has incorrect documentation and the respective free API was not present. 20 | * The changes in this version also modifiy the behaviour of existing APIs to obtain the TLV entry of latest subtype. While no current users shall be affected, this may cause inconsistency in the available API usage going forward. 21 | 22 | Please note that the yanked version does not affect any of existing users. The yanking is done due to future API usage considerations and to avoid any possible inconsistencies. 23 | 24 | ## v2.3.1 25 | * Make esp_secure_cert_get_key_type API available for DS peripheral case as well. 26 | 27 | ## v2.3.0 28 | * Added support to obtain the priv key type 29 | * Added support for getting the efuse key id for priv key 30 | 31 | ## v2.2.0 32 | * tools: Support DER encoded private keys when creating secure cert partition on host 33 | ### Breaking changes in v2.2.0 34 | * Updated the order of arguments for esp_pbkdf2_hmac_sha256 API to match it with corresponding mbedTLS API 35 | 36 | ## v2.1.0 37 | * Added support for HMAC based ECDSA key derivation with PBKDF2-HMAC-SHA256 38 | * Fixed build failure when example is setup through component manager 39 | 40 | ## v2.0.8 41 | * Fix for supporting IDF v4.3 42 | 43 | ## v2.0.7 44 | * Updated documentation regarding TLV format 45 | * Fixed priv_key free API when HMAC based encryption scheme is enabled. 46 | 47 | ## v2.0.6 48 | * Added HMAC based encryption scheme to protect private key data 49 | * Added support for private key validation in the esp_secure_cert_app 50 | * Added support of configurable esp_secure_cert partition offset in for configure_esp_secure_cert.py utility 51 | 52 | ## v2.0.5 53 | * Fixed targets in Kconfig to reflect DS Peripheral compatibility 54 | 55 | ## v2.0.4 56 | * Add implementation of `esp_secure_cert_free_*` APIs for TLV configuration. 57 | 58 | ## v2.0.3 59 | * Added C linkage so that C++ code can find the definitions for secure cert APIs. 60 | * Minor documentation fixes. 61 | 62 | ## v2.0.2 63 | * Updated reference to the new esp_partition component (IDFv5.0) 64 | 65 | ## v2.0.1 66 | * Added fixes for build failures with `-Wstrict-prototypes` CFLAG. 67 | * Added fix for build failure with toolchain change in IDFv4.x and IDFv5.x 68 | 69 | ## v2.0.0 70 | * Added esp-secure-cert-tool to PyPi. 71 | * Restructure esp-secure-cert-tool 72 | ### Breaking changes in v2.0.0 73 | * Added the support for TLV format for storing data in esp_secure_cert partition. 74 | * Make the TLV `cust_flash_tlv` as the default flash format. 75 | * Marked all the supported flash formats before TLV as legacy: `cust_flash`, `nvs`. 76 | * esp_secure_cert_app: Updated the partition table for the example 77 | 78 | ## v1.0.3 79 | * esp_secure_cert API now Dynamically identify the type of partitionand access the data accordingly 80 | * esp_secure_cert_app: Enable support for target esp32 81 | * Added tests based on qemu 82 | * Added priv_key functionality to the configure_esp_secure_cert.py script. 83 | ### Breaking changes in v1.0.3 84 | * Removed all the configuration options related to selecting the type of `esp_secure_cert` partition 85 | * Remove `esp_secure_cert_get_*_addr` API, the contents can now be obtained through `esp_secure_cert_get_*` API. 86 | * Remove APIs to obain the contents of the DS contexts e.g. efuse key id, ciphertext, iv etc. The contents can be accesed from inside the DS context which can be obtained through respective API. 87 | * Breaking change in the `esp_secure_cert_get_*` API: 88 | The API now accepts `char **buffer` instead of `char *buffer`. It will allocate the required memory dynamically and directly if necessary and provide the respective pointer. 89 | -------------------------------------------------------------------------------- /Kconfig: -------------------------------------------------------------------------------- 1 | menu "ESP Secure Cert Manager" 2 | 3 | config ESP_SECURE_CERT_DS_PERIPHERAL 4 | bool "Enable DS peripheral support" 5 | default y 6 | depends on !IDF_TARGET_ESP32 && !IDF_TARGET_ESP32C2 7 | help 8 | Enable the DS peripheral support. Not supported on esp32 and esp32c2. 9 | 10 | config ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS 11 | bool "Enable support for legacy formats" 12 | default n 13 | help 14 | This option enables support for the legacy formats along with 15 | the current format in the esp_secure_cert component. 16 | The current format is 17 | cust_flash_tlv 18 | The legacy formats are as follows: 19 | cust_flash 20 | nvs 21 | 22 | endmenu # ESP Secure Cert Manager 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP Secure Certificate Manager 2 | 3 | The *esp_secure_cert_mgr* provides a simplified interface to access the PKI credentials of a device pre-provisioned with the 4 | Espressif Provisioning Service. It provides the set of APIs that are required to access the contents of 5 | the `esp_secure_cert` partition. 6 | A demo example has also been provided with the `esp_secure_cert_mgr`, more details can be found out 7 | in the [example README](https://github.com/espressif/esp_secure_cert_mgr/blob/main/examples/esp_secure_cert_app/README.md) 8 | 9 | ## Usage Guidelines 10 | 11 | ### 1) Include `esp_secure_cert_mgr` in your project 12 | There are two ways to include `esp_secure_cert_mgr` in your project: 13 | 14 | i) Add `esp_secure_cert_mgr` to your project with help of IDF component manager: 15 | * The component is hosted at https://components.espressif.com/component/espressif/esp_secure_cert_mgr. Please use the same link to obtain the latest available version of the component along with the instructions on how to add it to your project. 16 | * Additional details about using a component through IDF component manager can be found [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html#using-with-a-project) 17 | 18 | ii) Add `esp_secure_cert_mgr` as an extra component in your project. 19 | 20 | * Download `esp_secure_cert_mgr` with: 21 | ``` 22 | git clone https://github.com/espressif/esp_secure_cert_mgr.git 23 | ``` 24 | * Include `esp_secure_cert_mgr` in `ESP-IDF` with setting `EXTRA_COMPONENT_DIRS` in CMakeLists.txt/Makefile of your project.For reference see [Optional Project Variables](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html#optional-project-variables) 25 | 26 | ### 2) Use the public API provided by `esp_secure_cert_mgr` in your project 27 | * The file [esp_secure_cert_read.h](https://github.com/espressif/esp_secure_cert_mgr/blob/main/include/esp_secure_cert_read.h) contains the public APIs provided by the `esp_secure_cert_mgr`. Please include the file in your project to make use of the available APIs. The file also contains more details about the available APIs. 28 | 29 | ## What is Pre-Provisioning? 30 | 31 | With the Espressif Pre-Provisioning Service, the ESP modules are pre-provisioned with an encrypted RSA private key and respective X509 public certificate before they are shipped out to you. The PKI credentials can then be registered with the cloud service to establish a secure TLS channel for communication. With the pre-provisioning taking place in the factory, it provides a hassle-free PKI infrastructure to the Makers. You may use this repository to set up your test modules to validate that your firmware works with the pre-provisioned modules that you ordered through Espressif's pre-provisioning service. 32 | 33 | ## ESP Secure Cert Partition 34 | 35 | When a device is pre-provisioned that means the PKI credentials are generated for the device. The PKI credentials are then stored in a partition named 36 | *esp_secure_cert*. 37 | 38 | The `esp_secure_cert` partition can be generated on host with help of [configure_esp_secure_cert.py](https://github.com/espressif/esp_secure_cert_mgr/blob/main/tools/configure_esp_secure_cert.py) utility, more details about the utility can be found in the [tools/README](https://github.com/espressif/esp_secure_cert_mgr/tree/main/tools#readme). 39 | 40 | For esp devices that support DS peripheral, the pre-provisioning is done by leveraging the security benefit of the DS peripheral. In that case, all of the data which is present in the *esp_secure_cert* partition is completely secure. 41 | 42 | When the device is pre-provisioned with help of the DS peripheral then by default the partition primarily contains the following data: 43 | 1) Device certificate: It is the public key/ certificate for the device's private key. It is used in TLS authentication. 44 | 2) CA certificate: This is the certificate of the CA which is used to sign the device cert. 45 | 3) Ciphertext: This is the encrypted private key of the device. The ciphertext is encrypted using the DS peripheral, thus it is completely safe to store on the flash. 46 | 47 | As listed above, the data only contains the public certificates and the encrypted private key and hence it is completely secure in itself. There is no need to further encrypt this data with any additional security algorithm. 48 | 49 | ### Partition Format 50 | 51 | The *esp_secure_cert* partition uses TLV format by default. Please take a look at the [format document](https://github.com/espressif/esp_secure_cert_mgr/tree/main/docs/format.md) for more details. 52 | -------------------------------------------------------------------------------- /ci/build_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ESP_SECURE_CERT_APP=$PWD/../examples/esp_secure_cert_app 3 | 4 | # This option causes the script to fail if any command fails in the main body of the script 5 | # Note: this does not apply to the internal functions 6 | # We need to check the exit status of each command individually 7 | set -e 8 | 9 | clean() { 10 | rm -r sdkconfig 2>&1 11 | rm -r build 2>&1 12 | } 13 | 14 | idf_path_verify() { 15 | if [[ -z "$IDF_PATH" ]]; then 16 | echo "IDF_PATH not set. Please set IDF_PATH " 17 | exit 1 18 | else 19 | echo '--------####--------' 20 | echo 'IDF_PATH & Branch' 21 | echo $IDF_PATH;(cd $IDF_PATH;git branch --show-current) 22 | echo '--------####--------' 23 | fi 24 | } 25 | 26 | # Build the default config with ds support enabled 27 | # $1 = IDF_TARGET 28 | build_ds_config() { 29 | idf_target=$1 30 | cd $ESP_SECURE_CERT_APP 31 | clean 32 | idf.py set-target $idf_target || exit $? 33 | idf.py build || exit $? 34 | clean 35 | } 36 | 37 | # Build the firmware with the config option for legacy flash formats enabled 38 | # $1 = IDF_TARGET 39 | build_ds_config_legacy_flash_format() { 40 | idf_target=$1 41 | cd $ESP_SECURE_CERT_APP 42 | clean 43 | echo "CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS=y" >> sdkconfig.defaults 44 | idf.py set-target $idf_target || exit $? 45 | idf.py build || exit $? 46 | clean 47 | } 48 | 49 | # Build the firmware with ds support disabled 50 | build_no_ds_config() { 51 | idf_target=$1 52 | if [[ $idf_target == "esp32" ]]; then 53 | echo "No ds config is same as default config for esp32" 54 | echo "Exiting" 55 | exit 0 56 | fi 57 | cd $ESP_SECURE_CERT_APP 58 | clean 59 | echo "CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=n" >> sdkconfig.defaults 60 | idf.py set-target $idf_target || exit $? 61 | idf.py build || exit $? 62 | clean 63 | } 64 | 65 | # $1 = IDF_TARGET 66 | # $2 = CI_PARALLEL_COUNT 67 | # This function assigns tests based on the parallel count value 68 | assign_test() { 69 | idf_target=$1 70 | ci_parallel_count=$2 71 | if [[ $ci_parallel_count == "1" ]]; then 72 | echo "Building the default config" 73 | build_ds_config $idf_target || exit 1 74 | fi 75 | if [[ $ci_parallel_count == "2" ]]; then 76 | echo "Building the legacy flash format config" 77 | build_ds_config_legacy_flash_format $idf_target || exit 1 78 | fi 79 | if [[ $ci_parallel_count == "3" ]]; then 80 | echo "Building with ds support disabled" 81 | build_no_ds_config $idf_target || exit 1 82 | fi 83 | } 84 | 85 | IDF_TARGET=$1 86 | CI_PARALLEL_COUNT=$2 87 | echo "Building for target "$IDF_TARGET 88 | echo "CI Parallel count: "$CI_PARALLEL_COUNT 89 | idf_path_verify || exit 1 90 | assign_test $IDF_TARGET $CI_PARALLEL_COUNT 91 | -------------------------------------------------------------------------------- /docs/_static/tlv_format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/docs/_static/tlv_format.png -------------------------------------------------------------------------------- /docs/format.md: -------------------------------------------------------------------------------- 1 | # `esp_secure_cert` partition format 2 | 3 | ## TLV format 4 | 5 | The `esp_secure_cert` partition uses TLV format by default. TLV format has been decided based on its simplicity and efficiency. We use customized headers specifically designed for the `esp_secure_cert` partition. The `esp_secure_cert` partition is supposed to be a READ-only partition which can contain the data which is bound to the device and is not usually updated e.g. Device private key, Device certificate, CA certificate. Currently `esp_secure_cert_mgr` component does not support modification of the `esp_secure_cert` partition. 6 | 7 | The TLV format used in the esp_secure_cert partition is as follows: 8 | 9 | ![](_static/tlv_format.png) 10 | 11 | * TLV header: It contains the information regarding the data such as the type of the data and the length of the data. For more details about the TLV, please take a look at [tlv_config.h](https://github.com/espressif/esp_secure_cert_mgr/tree/main/private_include/esp_secure_cert_tlv_config.h). 12 | 13 | * i) TLV flags field - flags byte that identifies different characteristics for the TLV 14 | * ii) TLV type: Currently the TLV format supports only a list of pre-defined types. The latest list for pre-defined types can be found at [tlv_config.h](https://github.com/espressif/esp_secure_cert_mgr/tree/main/private_include/esp_secure_cert_tlv_config.h). Additional custom types are also provided in order to allow storing custom data in TLV format. The custom data types can be typecasted to appropriate types after reading the TLV. 15 | * iii) TLV length - the length of the data field in the TLV. 16 | * TLV footer: It contains the crc32 of the data and header field. 17 | * TLV format Padding - In TLV format a padding is added automatically between the end offset of data and TLV footer. The padding is added in order to make the data field a multiple of 16 bytes which is the minimum alignment required for flash encrypted writes. 18 | 19 | 20 | ### Partition table entry 21 | 22 | * For TLV format the `partitions.csv` file for the project should contain the following line which enables it to identify the `esp_secure_cert` partition: 23 | 24 | ``` 25 | # Name, Type, SubType, Offset, Size, Flags 26 | esp_secure_cert, 0x3F, , 0xD000, 0x2000, encrypted 27 | ``` 28 | 29 | Please note that, TLV format uses compact data representation and hence partition size is kept as 8KiB. 30 | 31 | > Note: The TLV read API expects that a padding of appropriate size is added to data to make it size as a multiple of 16 bytes, the partition generation utility i.e. [configure_esp_secure_cert.py](https://github.com/espressif/esp_secure_cert_mgr/blob/main/tools/configure_esp_secure_cert.py) takes care of this internally while generating the partition. 32 | 33 | When flash encryption is enabled for the device it is imporatant to encrypt the `esp_secure_cert` partition as well. Adding the encrypted flag in the partition table as done above can ensure that this is done. When flash encryption is not enabled this flag shall be ignored. 34 | 35 | ## TLV storage algorithms 36 | 37 | The `esp_secure_cert_mgr` component also supports following algorithms for TLV. In this case the TLV data is not stored directly but is stored by using one of the following algorithms. The information about the algorithms is a part of the TLV itself. 38 | 39 | - HMAC based AES-GCM encryption: 40 | In this case the first key stored on the device with purpose `HMAC_UP` is used to derive an AES key and the salt. 41 | This key is used to encrypt the TLV contents. 42 | This algorithm is suitable where the platform encryption is not enabled. 43 | 44 | - HMAC based ECDSA key derivation: 45 | In this case the ECDSA key is derived using following two values 46 | - Salt: This is the salt stored in a different tlv with type `ESP_SECURE_CERT_HMAC_ECDSA_KEY_SALT` 47 | - HMAC key: This is the first key stored on the device with purpose `HMAC_UP`. 48 | Both of these entities are used as input for the pbkdf2 algorithm to generate the ECDSA key. 49 | 50 | Please ensure that appropriate TLV flags are enabled to take care of the underlying security configuration for the given TLV entry. On top of these security configuration, if platform flash encryption is enabled then the secure cert partition contents shall be stored in an encrypted manner on the external flash storage 51 | 52 | ## Legacy formats for `esp_secure_cert` partition 53 | 54 | `esp_secure_cert` partition also supports two legacy flash formats. 55 | The support for these can be enabled through following menuconfig option: 56 | * `Component config > ESP Secure Cert Manager -> Enable support for legacy formats` 57 | 58 | 1) *cust_flash*: In this case, the partition is a custom flash partition. The data is directly stored over the flash. 59 | * In this case the `partitions.csv` file for the project should contain the following line which enables it to identify the `esp_secure_cert` partition. 60 | 61 | ``` 62 | # Name, Type, SubType, Offset, Size, Flags 63 | esp_secure_cert, 0x3F, , 0xD000, 0x6000, encrypted 64 | ``` 65 | When flash encryption is enabled the behaviour is same as mentioned above for the TLV format. 66 | 67 | 2) *nvs partition*: In this case, the partition is of the `nvs` type. The `nvs_flash` abstraction layer from the ESP-IDF is used to store and then retreive the contents of the `esp_secure_cert` partition. 68 | 69 | * In this case the `partitions.csv` file for the project should contain the following line which enables it to identify the `esp_secure_cert` partition. 70 | 71 | ``` 72 | # Name, Type, SubType, Offset, Size, Flags 73 | esp_secure_cert, data, nvs, 0xD000, 0x6000, 74 | ``` 75 | Currently the nvs encryption option is not supported for the `esp_secure_cert` partition. 76 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(esp_secure_cert_app) 7 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/README.md: -------------------------------------------------------------------------------- 1 | # ESP Secure Certificate Application 2 | 3 | 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. 4 | 5 | ## Requirements 6 | * The device must be pre-provisioned and have an *esp_secure_cert* partition. 7 | 8 | ## How to use the example 9 | Before project configuration and build, be sure to set the correct chip target using `idf.py set-target `. 10 | ### Configure the project 11 | 12 | * 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. 13 | 14 | * 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. 15 | 16 | ### Build and Flash 17 | 18 | Build the project and flash it to the board, then run the monitor tool to view the serial output: 19 | 20 | ``` 21 | idf.py -p PORT flash monitor 22 | ``` 23 | 24 | (Replace PORT with the name of the serial port to use.) 25 | 26 | (To exit the serial monitor, type ``Ctrl-]``.) 27 | 28 | See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. 29 | 30 | ### Example Output 31 | ``` 32 | I (331) sample_app: Device Cert: 33 | Length: 1233 34 | -----BEGIN CERTIFICATE----- 35 | . 36 | . 37 | -----END CERTIFICATE----- 38 | 39 | I (441) sample_app: CA Cert: 40 | Length: 1285 41 | -----BEGIN CERTIFICATE----- 42 | . 43 | . 44 | -----END CERTIFICATE----- 45 | 46 | I (561) sample_app: Successfuly obtained ciphertext, ciphertext length is 1200 47 | I (571) sample_app: Successfuly obtained initialization vector, iv length is 16 48 | I (571) sample_app: RSA length is 2048 49 | I (581) sample_app: Efuse key id 1 50 | I (581) sample_app: Successfully obtained the ds context 51 | I (831) sample_app: Ciphertext validated succcessfully 52 | ``` 53 | 54 | ## Additional configurations for `pre_prov` partition 55 | 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. 56 | 57 | * 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: 58 | ``` 59 | pre_prov, 0x3F, , 0xD000, 0x6000, 60 | ``` 61 | * No change is necessary for `pre_prov` partition of type *nvs*. 62 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRC_DIRS "." 3 | INCLUDE_DIRS "." 4 | ) 5 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/main/app_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | /* ESP Secure Cert App 8 | 9 | This example code is in the Public Domain (or CC0 licensed, at your option.) 10 | 11 | Unless required by applicable law or agreed to in writing, this 12 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 13 | CONDITIONS OF ANY KIND, either express or implied. 14 | */ 15 | #include 16 | #include 17 | #include "esp_log.h" 18 | #include "soc/soc_caps.h" 19 | #include "esp_secure_cert_read.h" 20 | #include "esp_secure_cert_tlv_read.h" 21 | 22 | #include "mbedtls/ssl.h" 23 | #include "mbedtls/pk.h" 24 | #include "mbedtls/x509.h" 25 | #include "mbedtls/entropy.h" 26 | #include "mbedtls/ctr_drbg.h" 27 | #include "mbedtls/error.h" 28 | #include "esp_idf_version.h" 29 | 30 | #if SOC_ECDSA_SUPPORTED 31 | #include "ecdsa/ecdsa_alt.h" 32 | #include "esp_efuse.h" 33 | #endif 34 | #define TAG "esp_secure_cert_app" 35 | 36 | 37 | #ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 38 | static esp_err_t test_ciphertext_validity(esp_ds_data_ctx_t *ds_data, unsigned char *dev_cert, size_t dev_cert_len) 39 | { 40 | mbedtls_x509_crt crt; 41 | mbedtls_x509_crt_init(&crt); 42 | unsigned char *sig = NULL; 43 | 44 | if (ds_data == NULL || dev_cert == NULL) { 45 | return ESP_ERR_INVALID_ARG; 46 | } 47 | 48 | int ret = mbedtls_x509_crt_parse(&crt, dev_cert, dev_cert_len); 49 | if (ret < 0) { 50 | ESP_LOGE(TAG, "Parsing of device certificate failed, returned %02X", ret); 51 | goto exit; 52 | } 53 | 54 | esp_err_t esp_ret = esp_ds_init_data_ctx(ds_data); 55 | if (esp_ret != ESP_OK) { 56 | ESP_LOGE(TAG, "Failed to initialze the DS context"); 57 | return esp_ret; 58 | } 59 | 60 | const size_t sig_len = 256; 61 | uint32_t hash[8] = {[0 ... 7] = 0xAABBCCDD}; 62 | 63 | sig = (unsigned char *) calloc(1, 1000 * sizeof(char)); 64 | if (sig == NULL) { 65 | ESP_LOGE(TAG, "Failed to allocate memory for signature"); 66 | goto exit; 67 | } 68 | 69 | #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) 70 | ret = esp_ds_rsa_sign(NULL, NULL, NULL, 0, MBEDTLS_MD_SHA256, 0, (const unsigned char *) hash, sig); 71 | #else 72 | ret = esp_ds_rsa_sign(NULL, NULL, NULL, MBEDTLS_MD_SHA256, 0, (const unsigned char *) hash, sig); 73 | #endif 74 | if (ret != 0) { 75 | ESP_LOGE(TAG, "Failed to sign the data with rsa key, returned %02X", ret); 76 | goto exit; 77 | } 78 | esp_ds_release_ds_lock(); 79 | 80 | ret = mbedtls_pk_verify(&crt.pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, sig_len); 81 | if (ret != 0) { 82 | printf("\nFailed to verify the data\n"); 83 | goto exit; 84 | } 85 | free(sig); 86 | return ESP_OK; 87 | exit: 88 | free(sig); 89 | mbedtls_x509_crt_free(&crt); 90 | printf("\nFailed to verify the ciphertext\n"); 91 | esp_ds_release_ds_lock(); 92 | return ESP_FAIL; 93 | } 94 | #else 95 | 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) 96 | { 97 | static const char *pers = "Hello"; 98 | mbedtls_x509_crt crt; 99 | mbedtls_entropy_context entropy; 100 | mbedtls_ctr_drbg_context ctr_drbg; 101 | mbedtls_pk_context pk; 102 | unsigned char *sig = NULL; 103 | 104 | mbedtls_ctr_drbg_init(&ctr_drbg); 105 | mbedtls_x509_crt_init(&crt); 106 | mbedtls_entropy_init(&entropy); 107 | mbedtls_pk_init(&pk); 108 | esp_err_t esp_ret = ESP_FAIL; 109 | if (priv_key == NULL || dev_cert == NULL) { 110 | return ESP_ERR_INVALID_ARG; 111 | } 112 | 113 | int ret = mbedtls_x509_crt_parse(&crt, dev_cert, dev_cert_len); 114 | if (ret != 0) { 115 | ESP_LOGE(TAG, "Parsing of device certificate failed, returned %02X", ret); 116 | esp_ret = ESP_FAIL; 117 | goto exit; 118 | } else { 119 | ESP_LOGI(TAG, "Successfully parsed the certificate"); 120 | } 121 | ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, strlen(pers)); 122 | if (ret != 0) { 123 | ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned -0x%04x", -ret ); 124 | esp_ret = ESP_FAIL; 125 | goto exit; 126 | } 127 | 128 | esp_secure_cert_key_type_t key_type = ESP_SECURE_CERT_DEFAULT_FORMAT_KEY; 129 | #ifndef CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS 130 | esp_ret = esp_secure_cert_get_priv_key_type(&key_type); 131 | if (esp_ret != ESP_OK) { 132 | ESP_LOGE(TAG, "Failed to obtain the priv key type"); 133 | goto exit; 134 | } 135 | #endif 136 | if (key_type == ESP_SECURE_CERT_ECDSA_PERIPHERAL_KEY) { 137 | #if SOC_ECDSA_SUPPORTED 138 | ESP_LOGI(TAG, "Setting up the ECDSA key from eFuse"); 139 | uint8_t efuse_block_id; 140 | esp_ret = esp_secure_cert_get_priv_key_efuse_id(&efuse_block_id); 141 | if (esp_ret != ESP_OK) { 142 | ESP_LOGE(TAG, "Failed to obtain efuse key id"); 143 | goto exit; 144 | } 145 | ESP_LOGI(TAG, "Using key from eFuse block %d for ECDSA key", efuse_block_id); 146 | esp_ecdsa_pk_conf_t pk_conf = { 147 | .grp_id = MBEDTLS_ECP_DP_SECP256R1, 148 | .efuse_block = efuse_block_id, 149 | }; 150 | if (esp_ecdsa_set_pk_context(&pk, &pk_conf) != 0) { 151 | ESP_LOGE(TAG, "Failed to set ECDSA context"); 152 | esp_ret = ESP_FAIL; 153 | goto exit; 154 | } 155 | ESP_LOGI(TAG, "Successfully set ECDSA key context"); 156 | #endif 157 | } else { 158 | #if (MBEDTLS_VERSION_NUMBER < 0x03000000) 159 | ret = mbedtls_pk_parse_key(&pk, (const uint8_t *)priv_key, priv_key_len, NULL, 0); 160 | #else 161 | ret = mbedtls_pk_parse_key(&pk, (const uint8_t *)priv_key, priv_key_len, NULL, 0, mbedtls_ctr_drbg_random, &ctr_drbg); 162 | #endif 163 | if (ret != 0) { 164 | ESP_LOGE(TAG, "Failed to parse the key"); 165 | esp_ret = ESP_FAIL; 166 | goto exit; 167 | } else { 168 | ESP_LOGI(TAG, "Successfully parsed the key"); 169 | } 170 | } 171 | 172 | static uint32_t hash[8] = {[0 ... 7] = 0xAABBCCDD}; 173 | #define SIG_SIZE 1024 174 | sig = (unsigned char*)calloc(1, SIG_SIZE * sizeof(char)); 175 | if (sig == NULL) { 176 | ESP_LOGE(TAG, "Failed to allocate memory"); 177 | esp_ret = ESP_FAIL; 178 | goto exit; 179 | } 180 | size_t sig_len = 0; 181 | #if (MBEDTLS_VERSION_NUMBER < 0x03000000) 182 | ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, &sig_len, mbedtls_ctr_drbg_random, &ctr_drbg); 183 | #else 184 | ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, SIG_SIZE, &sig_len, mbedtls_ctr_drbg_random, &ctr_drbg); 185 | #endif 186 | if (ret != 0) { 187 | ESP_LOGE(TAG, "Failed to sign the data"); 188 | esp_ret = ESP_FAIL; 189 | goto exit; 190 | } else { 191 | ESP_LOGI(TAG, "Successfully signed the data"); 192 | } 193 | 194 | ret = mbedtls_pk_verify(&crt.pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, sig_len); 195 | if (ret != 0) { 196 | ESP_LOGE(TAG, "Failed to verify the signed data"); 197 | esp_ret = ESP_FAIL; 198 | goto exit; 199 | } 200 | esp_ret = ESP_OK; 201 | exit: 202 | free(sig); 203 | mbedtls_pk_free(&pk); 204 | mbedtls_entropy_free(&entropy); 205 | mbedtls_ctr_drbg_free(&ctr_drbg); 206 | mbedtls_x509_crt_free(&crt); 207 | return esp_ret; 208 | } 209 | #endif 210 | 211 | void app_main() 212 | { 213 | uint32_t len = 0; 214 | char *addr = NULL; 215 | esp_err_t esp_ret = ESP_FAIL; 216 | 217 | esp_ret = esp_secure_cert_get_device_cert(&addr, &len); 218 | if (esp_ret == ESP_OK) { 219 | ESP_LOGI(TAG, "Device Cert: \nLength: %"PRIu32"\n%s", len, (char *)addr); 220 | } else { 221 | ESP_LOGE(TAG, "Failed to obtain flash address of device cert"); 222 | } 223 | 224 | esp_ret = esp_secure_cert_get_ca_cert(&addr, &len); 225 | if (esp_ret == ESP_OK) { 226 | ESP_LOGI(TAG, "CA Cert: \nLength: %"PRIu32"\n%s", len, (char *)addr); 227 | ESP_LOG_BUFFER_HEX_LEVEL(TAG, addr, len, ESP_LOG_DEBUG); 228 | } else { 229 | ESP_LOGE(TAG, "Failed to obtain flash address of ca_cert"); 230 | } 231 | 232 | #ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 233 | esp_ret = esp_secure_cert_get_priv_key(&addr, &len); 234 | if (esp_ret == ESP_OK) { 235 | ESP_LOGI(TAG, "PEM KEY: \nLength: %"PRIu32"\n%s", len, (char *)addr); 236 | } else { 237 | ESP_LOGE(TAG, "Failed to obtain flash address of private_key"); 238 | } 239 | uint32_t dev_cert_len = 0; 240 | char *dev_cert_addr = NULL; 241 | esp_ret = esp_secure_cert_get_device_cert(&dev_cert_addr, &dev_cert_len); 242 | if (esp_ret != ESP_OK) { 243 | ESP_LOGE(TAG, "Failed to obtain the dev cert flash address"); 244 | } 245 | 246 | esp_ret = test_priv_key_validity((unsigned char *)addr, len, (unsigned char *)dev_cert_addr, dev_cert_len); 247 | if (esp_ret != ESP_OK) { 248 | ESP_LOGE(TAG, "Failed to validate the private key and device certificate"); 249 | } 250 | #else 251 | esp_ds_data_ctx_t *ds_data = NULL; 252 | ds_data = esp_secure_cert_get_ds_ctx(); 253 | if (ds_data != NULL) { 254 | ESP_LOGI(TAG, "Successfully obtained the ds context"); 255 | ESP_LOG_BUFFER_HEX_LEVEL(TAG, ds_data->esp_ds_data->c, ESP_DS_C_LEN, ESP_LOG_DEBUG); 256 | ESP_LOG_BUFFER_HEX_LEVEL(TAG, ds_data->esp_ds_data->iv, ESP_DS_IV_LEN, ESP_LOG_DEBUG); 257 | ESP_LOGI(TAG, "The value of rsa length is %d", ds_data->rsa_length_bits); 258 | ESP_LOGI(TAG, "The value of efuse key id is %d", ds_data->efuse_key_id); 259 | } else { 260 | ESP_LOGE(TAG, "Failed to obtain the ds context"); 261 | } 262 | 263 | /* Read the dev_cert addr again */ 264 | esp_ret = esp_secure_cert_get_device_cert(&addr, &len); 265 | if (esp_ret != ESP_OK) { 266 | ESP_LOGE(TAG, "Failed to obtain the dev cert flash address"); 267 | } 268 | 269 | esp_ret = test_ciphertext_validity(ds_data, (unsigned char *)addr, len); 270 | if (esp_ret != ESP_OK) { 271 | ESP_LOGE(TAG, "Failed to validate ciphertext"); 272 | } else { 273 | ESP_LOGI(TAG, "Ciphertext validated succcessfully"); 274 | } 275 | #endif 276 | if (esp_ret == ESP_OK) { 277 | ESP_LOGI(TAG, "Successfully obtained and verified the contents of esp_secure_cert partition"); 278 | } else { 279 | ESP_LOGE(TAG, "Failed to obtain and verify the contents of the esp_secure_cert partition"); 280 | } 281 | #ifndef CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS 282 | esp_secure_cert_tlv_config_t tlv_config = {}; 283 | tlv_config.type = ESP_SECURE_CERT_DEV_CERT_TLV; 284 | tlv_config.subtype = ESP_SECURE_CERT_SUBTYPE_0; 285 | esp_secure_cert_tlv_info_t tlv_info = {}; 286 | esp_ret = esp_secure_cert_get_tlv_info(&tlv_config, &tlv_info); 287 | if (esp_ret == ESP_OK) { 288 | ESP_LOGI(TAG, "Device Cert: \nLength: %"PRIu32"\n%s", tlv_info.length, tlv_info.data); 289 | } 290 | 291 | ESP_LOGI(TAG, "Printing a list of TLV entries"); 292 | esp_secure_cert_list_tlv_entries(); 293 | #endif 294 | 295 | } 296 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | version: "1.0.0" 2 | description: ESP Secure Cert Manager Example 3 | dependencies: 4 | espressif/esp_secure_cert_mgr: 5 | version: '^2.0.8' 6 | override_path: '../../../' # three levels up, pointing the directory with the component itself 7 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/partitions.csv: -------------------------------------------------------------------------------- 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, 5 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/README.md: -------------------------------------------------------------------------------- 1 | # ESP Secure cert partition tests 2 | 3 | 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. 4 | 5 | ## Requirements 6 | 7 | * **QEMU emulator**: 8 | 9 | The test is based on qemu and needs the qemu emulator support. 10 | 11 | * For Linux Distributions, the executables to be downloaded from [qemu/releases](https://github.com/espressif/qemu/releases) 12 | 13 | * 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. 14 | 15 | * **Pytest**: 16 | Install the pytest environment by executing following command 17 | 18 | ```pip install -r requirements.txt``` 19 | 20 | ## Testing 21 | 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. 22 | 23 | 24 | ### Test the TLV support on esp32 25 | The TLV support can be tested with following commands: 26 | 27 | * ```./build_qemu_images.sh tlv``` 28 | 29 | * ``` cd ..``` 30 | 31 | * ``pytest --target esp32`` 32 | 33 | ### Test the legacy flash format support on esp32 34 | The TLV support can be tested with following commands: 35 | 36 | * ```./build_qemu_images.sh legacy``` 37 | 38 | * ``` cd ..``` 39 | 40 | * ``pytest --target esp32`` 41 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/build_qemu_images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ESP_SECURE_CERT_APP=$PWD/../ 3 | 4 | if [ -z "$1" ]; then 5 | echo "Usage: build_qemu_image.sh image_type 6 | image type can be tlv, legacy_format" 7 | exit 1 8 | fi 9 | 10 | image_type=$1 11 | 12 | # This option causes the script to fail if any command fails in the main body of the script 13 | # Note: this does not apply to the internal functions 14 | # We need to check the exit status of each command individually 15 | set -e 16 | 17 | save_defaults() { 18 | cp sdkconfig.defaults sdkconfig_defaults_clean 19 | } 20 | 21 | restore_defaults() { 22 | mv sdkconfig_defaults_clean sdkconfig.defaults 23 | } 24 | 25 | clean() { 26 | rm -rf sdkconfig 2>&1 27 | rm -rf build 2>&1 28 | } 29 | 30 | idf_path_verify() { 31 | if [[ -z "$IDF_PATH" ]]; then 32 | echo "IDF_PATH not set. Please set IDF_PATH " 33 | exit 1 34 | else 35 | echo '--------####--------' 36 | echo 'IDF_PATH & Branch' 37 | echo $IDF_PATH;(cd $IDF_PATH;git branch --show-current) 38 | echo '--------####--------' 39 | fi 40 | } 41 | 42 | build_tlv_image() { 43 | cd $ESP_SECURE_CERT_APP 44 | save_defaults 45 | clean 46 | cat sdkconfig.ci.tlv >> sdkconfig.defaults 47 | idf.py build 48 | cd qemu_test 49 | rm -rf qemu_test_images 50 | mkdir qemu_test_images 51 | ./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 52 | cd $ESP_SECURE_CERT_APP 53 | restore_defaults 54 | } 55 | 56 | build_legacy_format_images() { 57 | cd $ESP_SECURE_CERT_APP 58 | save_defaults 59 | clean 60 | cat sdkconfig.ci.legacy >> sdkconfig.defaults 61 | idf.py build 62 | cd qemu_test 63 | rm -rf qemu_test_images 64 | mkdir qemu_test_images 65 | ./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 66 | ./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 67 | ./make-qemu-img.sh nvs/nvs.bin nvs/partition-table.bin ../ esp_secure_cert_app qemu_test_images/nvs_qemu.img 68 | ./make-qemu-img.sh nvs_legacy/nvs_legacy.bin nvs/partition-table.bin ../ esp_secure_cert_app qemu_test_images/nvs_legacy_qemu.img 69 | ./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 70 | cd $ESP_SECURE_CERT_APP 71 | restore_defaults 72 | } 73 | 74 | if [ $1 == "tlv" ]; then 75 | # Build the TLV format image 76 | build_tlv_image 77 | fi 78 | 79 | if [ $1 == "legacy_format" ]; then 80 | # Build the TLV format image 81 | build_legacy_format_images 82 | fi 83 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/cust_flash/cust_flash.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/cust_flash/cust_flash.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/cust_flash/partition-table.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/cust_flash/partition-table.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/cust_flash_legacy/cust_flash_legacy.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/cust_flash_legacy/cust_flash_legacy.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/cust_flash_legacy/partition-table.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/cust_flash_legacy/partition-table.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/cust_flash_tlv/cust_flash_tlv.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/cust_flash_tlv/cust_flash_tlv.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/cust_flash_tlv/partition-table.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/cust_flash_tlv/partition-table.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/make-qemu-img.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | arg_esp_secucertimg=$1 4 | arg_partition_table=$2 5 | arg_projpath="$3" 6 | arg_projname="$4" 7 | arg_flashimg=$5 8 | 9 | if [ -z "$1" -o -z "$2" -o -z "$3" ]; then 10 | echo "Usage: make-qemu-img.sh esp_secure_cert_partition partition_table path_to_build_dir project_name image_name" 11 | exit 1 12 | fi 13 | 14 | dd if=/dev/zero bs=1024 count=4096 of=${arg_flashimg} 15 | dd if=${arg_projpath}/build/bootloader/bootloader.bin bs=1 seek=$((0x1000)) of=${arg_flashimg} conv=notrunc 16 | dd if=${arg_esp_secucertimg} bs=1 seek=$((0xD000)) of=${arg_flashimg} conv=notrunc 17 | dd if=${arg_partition_table} bs=1 seek=$((0xC000)) of=${arg_flashimg} conv=notrunc 18 | dd if=${arg_projpath}/build/${arg_projname}.bin bs=1 seek=$((0x20000)) of=${arg_flashimg} conv=notrunc 19 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/nvs/nvs.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/nvs/nvs.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/nvs/partition-table.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/nvs/partition-table.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/nvs_legacy/nvs_legacy.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/nvs_legacy/nvs_legacy.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/nvs_legacy/partition-table.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/examples/esp_secure_cert_app/qemu_test/nvs_legacy/partition-table.bin -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/qemu_test/requirements.txt: -------------------------------------------------------------------------------- 1 | pytest_embedded 2 | pytest_embedded_idf 3 | pytest_embedded_qemu 4 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/sdkconfig.ci.legacy: -------------------------------------------------------------------------------- 1 | CONFIG_ESP_TASK_WDT_EN=n 2 | CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS=y 3 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/sdkconfig.ci.tlv: -------------------------------------------------------------------------------- 1 | CONFIG_ESP_TASK_WDT_EN=n 2 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | CONFIG_PARTITION_TABLE_CUSTOM=y 2 | CONFIG_PARTITION_TABLE_OFFSET=0xc000 3 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 4 | -------------------------------------------------------------------------------- /examples/esp_secure_cert_app/test_esp_secure_cert.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import os 3 | 4 | qemu_images = [] 5 | qemu_images_path = 'qemu_test/qemu_test_images' 6 | 7 | # Use os.walk to traverse the directory and its subdirectories 8 | for root, dirs, files in os.walk(qemu_images_path): 9 | for file in files: 10 | if file.endswith('.img'): 11 | file_path = os.path.join(root, file) 12 | qemu_images.append(file_path) 13 | 14 | # Print the list of image filenames 15 | print('The list entries are ------------') 16 | for image in qemu_images: 17 | print(image) 18 | 19 | 20 | @pytest.mark.parametrize('qemu_image_path', qemu_images, indirect=True) 21 | @pytest.mark.parametrize('skip_regenerate_image', ['y'], indirect=True) 22 | def test_esp_secure_cert(dut): 23 | dut.expect('Successfully obtained and verified the ' 24 | 'contents of esp_secure_cert partition') 25 | -------------------------------------------------------------------------------- /examples/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = --embedded-services idf,qemu -s 3 | 4 | # log related 5 | log_cli = True 6 | log_cli_level = INFO 7 | log_cli_format = %(asctime)s %(levelname)s %(message)s 8 | log_cli_date_format = %Y-%m-%d %H:%M:%S 9 | 10 | log_file = test.log 11 | log_file_level = INFO 12 | log_file_format = %(asctime)s %(levelname)s %(message)s 13 | log_file_date_format = %Y-%m-%d %H:%M:%S 14 | -------------------------------------------------------------------------------- /idf_component.yml: -------------------------------------------------------------------------------- 1 | version: "2.5.0" 2 | description: "ESP Secure Cert Manager" 3 | url: https://github.com/espressif/esp_secure_cert_mgr 4 | dependencies: 5 | idf: 6 | version: ">=4.3" 7 | -------------------------------------------------------------------------------- /include/esp_secure_cert_crypto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #pragma once 7 | #include "esp_err.h" 8 | #include "soc/soc_caps.h" 9 | 10 | #ifdef SOC_HMAC_SUPPORTED 11 | #include "esp_hmac.h" 12 | /* 13 | * @info 14 | * PBKDF2_hmac- password based key derivation function based on HMAC. 15 | * The API acts as a password based key derivation function by using the hardware HMAC peripheral present on the SoC. The hmac key shall be used from the efuse key block provided as the hmac_key_id. The API makes use of the hardware HMAC peripheral and hardware SHA peripheral present on the SoC. The SHA operation is limited to SHA256. 16 | * @input 17 | * hmac_key_id The efuse_key_id in which the HMAC key has already been burned. 18 | * This key should be read and write protected for its protection. That way it can only be accessed by the hardware HMAC peripheral. 19 | * salt The buffer containing the salt value 20 | * salt_len The length of the salt in bytes 21 | * key_length The expected length of the derived key. 22 | * iteration_count The count for which the internal cryptographic operation shall be repeated. 23 | * output The pointer to the buffer in which the derived key shall be stored. It must of a writable buffer of size key_length bytes 24 | * 25 | */ 26 | int esp_pbkdf2_hmac_sha256(hmac_key_id_t hmac_key_id, const unsigned char *salt, size_t salt_len, 27 | size_t iteration_count, size_t key_length, unsigned char *output); 28 | #endif 29 | -------------------------------------------------------------------------------- /include/esp_secure_cert_read.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #pragma once 7 | #include "esp_err.h" 8 | 9 | #include "soc/soc_caps.h" 10 | #ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 11 | #include "rsa_sign_alt.h" 12 | #endif 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | /** 20 | @brief Enumeration of ESP Secure Certificate key types. 21 | */ 22 | typedef enum key_type { 23 | ESP_SECURE_CERT_INVALID_KEY = -1, /* Invalid key */ 24 | ESP_SECURE_CERT_DEFAULT_FORMAT_KEY, /* Key type for the key in default format */ 25 | ESP_SECURE_CERT_HMAC_ENCRYPTED_KEY, /* Encrypted key type */ 26 | ESP_SECURE_CERT_HMAC_DERIVED_ECDSA_KEY, /* HMAC-derived ECDSA key type. */ 27 | ESP_SECURE_CERT_ECDSA_PERIPHERAL_KEY, /* ECDSA peripheral key type. */ 28 | } esp_secure_cert_key_type_t; 29 | 30 | /* @info 31 | * Init the esp_secure_cert nvs partition 32 | * 33 | * @return 34 | * - ESP_OK On success 35 | * - ESP_FAIL/other relevant esp error code 36 | * On failure 37 | */ 38 | esp_err_t esp_secure_cert_init_nvs_partition(void); 39 | 40 | /* @info 41 | * Get the device cert from the esp_secure_cert partition 42 | * 43 | * @note 44 | * If your esp_secure_cert partition is of type NVS, the API will dynamically allocate 45 | * the required memory to store the device cert and return the pointer. 46 | * The pointer can be freed in this case (NVS) using respective free API 47 | * 48 | * In case of cust_flash partition, a read only flash pointer shall be returned here. 49 | * 50 | * A respective call to the esp_secure_cert_free_device_cert() should be made to free any memory (if allocated) 51 | * 52 | * IMPORTANT: This API shall provide only the first entry of type Device cert (ESP_SECURE_CERT_DEV_CERT_TLV) present in the esp_secure_cert partition with subtype set as 0. 53 | * If you have multiple entries of the given type with different subtypes then please use the generic API esp_secure_cert_get_tlv_info with the appropriate type and subtype. 54 | * The type in this case shall be ESP_SECURE_CERT_DEV_CERT_TLV 55 | * and the subtype shall be the index of the device cert that needs to be obtained. 56 | * 57 | * @params 58 | * - buffer(out) This value shall be filled with the device cert address 59 | * on successful completion 60 | * - len(out) This value shall be filled with the length of the device cert 61 | * If your esp_secure_cert partition is of type NVS, the API will dynamically allocate 62 | * the required memory to store the device cert 63 | * 64 | * In case of cust_flash partition, a read only flash pointer shall be returned here. 65 | * @return 66 | * - ESP_OK On success 67 | * - ESP_FAIL/other relevant esp error code 68 | * On failure 69 | */ 70 | esp_err_t esp_secure_cert_get_device_cert(char **buffer, uint32_t *len); 71 | 72 | /* 73 | * Free any internally allocated resources for the device cert. 74 | * @note 75 | * This API would free the memory if it is dynamically allocated 76 | * 77 | * @params 78 | * - buffer(in) The data pointer 79 | * This pointer should be the same one which has been obtained 80 | * through "esp_secure_cert_get_device_cert" API. 81 | */ 82 | esp_err_t esp_secure_cert_free_device_cert(char *buffer); 83 | 84 | /* @info 85 | * Get the ca cert from the esp_secure_cert partition 86 | * 87 | * @note 88 | * The API shall dynamically allocate the memory if required. 89 | * The dynamic allocation of memory shall be required in following cases: 90 | * 1) partition type is NVS 91 | * 2) HMAC encryption is enabled for the API needs to be called 92 | * 93 | * The esp_secure_cert_free_ca_cert API needs to be called in order to free the memory. 94 | * The API shall only free the memory if it has been dynamically allocated. 95 | * 96 | * IMPORTANT: This API shall provide only the first entry of type CA cert (ESP_SECURE_CERT_CA_CERT_TLV) present in the esp_secure_cert partition subtype set as 0. 97 | * If you have multiple entries of the given type with different subtypes then please use the generic API esp_secure_cert_get_tlv_info with the appropriate type and subtype. 98 | * The type in this case shall be ESP_SECURE_CERT_CA_CERT_TLV 99 | * and the subtype shall be the index of the device cert that needs to be obtained. 100 | * 101 | * @params 102 | * - buffer(out) This value shall be filled with the ca cert address 103 | * on successful completion 104 | * - len(out) This value shall be filled with the length of the ca cert 105 | * If your esp_secure_cert partition is of type NVS, the API will dynamically allocate 106 | * the required memory to store the ca cert 107 | * 108 | * In case of cust_flash partition, a read only flash pointer shall be returned here. 109 | * @return 110 | * - ESP_OK On success 111 | * - ESP_FAIL/other relevant esp error code 112 | * On failure 113 | */ 114 | esp_err_t esp_secure_cert_get_ca_cert(char **buffer, uint32_t *len); 115 | 116 | /* 117 | * Free any internally allocated resources for the ca cert. 118 | * @note 119 | * This API would free the memory if it is dynamically allocated 120 | * 121 | * @params 122 | * - buffer(in) The data pointer 123 | * This pointer should be the same one which 124 | * has been obtained through "esp_secure_cert_get_ca_cert" API. 125 | */ 126 | esp_err_t esp_secure_cert_free_ca_cert(char *buffer); 127 | 128 | #ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 129 | /* @info 130 | * Get the private key from the esp_secure_cert partition 131 | * 132 | * @note 133 | * The API shall dynamically allocate the memory if required. 134 | * The dynamic allocation of memory shall be required in following cases: 135 | * 1) partition type is NVS 136 | * 2) HMAC encryption is enabled for the API needs to be called 137 | * 138 | * The esp_secure_cert_free_priv_key API needs to be called in order to free the memory. 139 | * The API shall only free the memory if it has been dynamically allocated. 140 | * 141 | * IMPORTANT: This API shall provide only the first entry of type private key (ESP_SECURE_CERT_PRIV_KEY_TLV) present in the esp_secure_cert partition with subtype set as 0. 142 | * If you have multiple entries of the given type with different subtypes then please use the generic API esp_secure_cert_get_tlv_info with the appropriate type and subtype. 143 | * The type in this case shall be ESP_SECURE_CERT_PRIV_KEY_TLV 144 | * and the subtype shall be the index of the device cert that needs to be obtained. 145 | * 146 | * @params 147 | * - buffer(out) This value shall be filled with the private key address 148 | * on successful completion 149 | * - len(out) This value shall be filled with the length of the private key 150 | * 151 | * 152 | * @return 153 | * - ESP_OK On success 154 | * - ESP_FAIL/other relevant esp error code 155 | * On failure 156 | */ 157 | esp_err_t esp_secure_cert_get_priv_key(char **buffer, uint32_t *len); 158 | 159 | /* 160 | * Free any internally allocated resources for the priv key. 161 | * @note 162 | * This API would free the memory if it is dynamically allocated 163 | * 164 | * @params 165 | * - buffer(in) The data pointer 166 | * This pointer should be the same one which 167 | * has been obtained through "esp_secure_cert_get_priv_key" API. 168 | */ 169 | esp_err_t esp_secure_cert_free_priv_key(char *buffer); 170 | 171 | #else /* !CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */ 172 | /* @info 173 | * This function returns the flash esp_ds_context which can then be 174 | * directly provided to an esp-tls connection through its config structure. 175 | * The memory for the context is dynamically allocated. 176 | * @note 177 | * This shall generate the DS context only for the 178 | * TLV entry with subtype 0 (First TLV entry for DS context) 179 | * Internally this API assumes that the TLV entries with 180 | * type ESP_SECURE_CERT_DS_CTX_TLV and ESP_SECURE_CERT_DS_DATA_TLV and subtype 0 181 | * are present. 182 | * A call to esp_secure_cert_free_ds_ctx() should be made 183 | * to free the allocated memory 184 | * 185 | * @return 186 | * - ds_ctx The pointer to the DS context, On success 187 | * - NULL On failure 188 | */ 189 | esp_ds_data_ctx_t *esp_secure_cert_get_ds_ctx(void); 190 | 191 | /* 192 | * @info 193 | * Free the DS context 194 | */ 195 | void esp_secure_cert_free_ds_ctx(esp_ds_data_ctx_t *ds_ctx); 196 | #endif /* CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */ 197 | 198 | #ifndef CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS 199 | 200 | /* @info 201 | * Get the private key type from the esp_secure_cert partition 202 | * 203 | * @note 204 | * The API is only supported for the TLV format 205 | * This API shall only provide information for the private key with subtype set to ESP_SECURE_CERT_TLV_SUBTYPE_0 (first entry) 206 | * 207 | * @params 208 | * - priv_key_type(out) Pointer to store the obtained key type 209 | * @return 210 | * - ESP_OK On success 211 | * - ESP_FAIL/other relevant esp error code 212 | * On failure 213 | */ 214 | esp_err_t esp_secure_cert_get_priv_key_type(esp_secure_cert_key_type_t *priv_key_type); 215 | 216 | /* @info 217 | * Get the efuse block id in which the private key is stored. 218 | * @note 219 | * The API is only supported for the TLV format. 220 | * For now only ECDSA type of private key can be stored in the efuse block 221 | * This API shall only provide information for the private key with subtype set to ESP_SECURE_CERT_TLV_SUBTYPE_0 (first entry) 222 | * 223 | * @params 224 | * - efuse_block_id(out) Pointer to store the obtained efuse block id 225 | * @return 226 | * - ESP_OK On success 227 | * - ESP_FAIL/other relevant esp error code 228 | * On failure 229 | */ 230 | esp_err_t esp_secure_cert_get_priv_key_efuse_id(uint8_t *efuse_block_id); 231 | #endif 232 | 233 | #ifdef __cplusplus 234 | } 235 | #endif 236 | -------------------------------------------------------------------------------- /include/esp_secure_cert_tlv_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "esp_bit_defs.h" 10 | #include "sdkconfig.h" 11 | #include "soc/soc_caps.h" 12 | #ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 13 | #include "esp_ds.h" 14 | #endif 15 | 16 | /* 17 | * Plase note that no two TLV structures of the same type 18 | * can be stored in the esp_secure_cert partition at one time. 19 | */ 20 | typedef enum esp_secure_cert_tlv_type { 21 | ESP_SECURE_CERT_CA_CERT_TLV = 0, 22 | ESP_SECURE_CERT_DEV_CERT_TLV, 23 | ESP_SECURE_CERT_PRIV_KEY_TLV, 24 | ESP_SECURE_CERT_DS_DATA_TLV, 25 | ESP_SECURE_CERT_DS_CONTEXT_TLV, 26 | ESP_SECURE_CERT_HMAC_ECDSA_KEY_SALT, 27 | ESP_SECURE_CERT_TLV_SEC_CFG, 28 | // Any new tlv types should be added above this 29 | ESP_SECURE_CERT_TLV_END = 50, 30 | //Custom data types 31 | //that can be defined by the user 32 | ESP_SECURE_CERT_USER_DATA_1 = 51, 33 | ESP_SECURE_CERT_USER_DATA_2 = 52, 34 | ESP_SECURE_CERT_USER_DATA_3 = 53, 35 | ESP_SECURE_CERT_USER_DATA_4 = 54, 36 | ESP_SECURE_CERT_USER_DATA_5 = 54, 37 | /* The following TLV entries are reserved for project-specific 38 | * purposes and should not be utilized by the application */ 39 | ESP_SECURE_CERT_MATTER_TLV_1 = 201, 40 | ESP_SECURE_CERT_MATTER_TLV_2 = 202, 41 | ESP_SECURE_CERT_TLV_MAX = 254, /* Max TLV entry identifier (should not be assigned to a TLV entry) */ 42 | ESP_SECURE_CERT_TLV_INVALID = 255, /* Invalid TLV type */ 43 | } esp_secure_cert_tlv_type_t; 44 | 45 | typedef enum esp_secure_cert_tlv_subtype { 46 | ESP_SECURE_CERT_SUBTYPE_0 = 0, 47 | ESP_SECURE_CERT_SUBTYPE_1 = 1, 48 | ESP_SECURE_CERT_SUBTYPE_2 = 2, 49 | ESP_SECURE_CERT_SUBTYPE_3 = 3, 50 | ESP_SECURE_CERT_SUBTYPE_4 = 4, 51 | ESP_SECURE_CERT_SUBTYPE_5 = 5, 52 | ESP_SECURE_CERT_SUBTYPE_6 = 6, 53 | ESP_SECURE_CERT_SUBTYPE_7 = 7, 54 | ESP_SECURE_CERT_SUBTYPE_8 = 8, 55 | ESP_SECURE_CERT_SUBTYPE_9 = 9, 56 | ESP_SECURE_CERT_SUBTYPE_10 = 10, 57 | ESP_SECURE_CERT_SUBTYPE_11 = 11, 58 | ESP_SECURE_CERT_SUBTYPE_12 = 12, 59 | ESP_SECURE_CERT_SUBTYPE_13 = 13, 60 | ESP_SECURE_CERT_SUBTYPE_14 = 14, 61 | ESP_SECURE_CERT_SUBTYPE_15 = 15, 62 | ESP_SECURE_CERT_SUBTYPE_16 = 16, 63 | ESP_SECURE_CERT_SUBTYPE_MAX = 254, /* Max Subtype entry identifier (should not be assigned to a TLV entry) */ 64 | ESP_SECURE_CERT_SUBTYPE_INVALID = 255, /* Invalid TLV subtype */ 65 | } esp_secure_cert_tlv_subtype_t; 66 | -------------------------------------------------------------------------------- /include/esp_secure_cert_tlv_read.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #pragma once 7 | #include "esp_err.h" 8 | 9 | #include "esp_secure_cert_tlv_config.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | /* 17 | * TLV config struct 18 | */ 19 | typedef struct tlv_config { 20 | esp_secure_cert_tlv_type_t type; /* TLV type */ 21 | esp_secure_cert_tlv_subtype_t subtype; /* TLV subtype */ 22 | } esp_secure_cert_tlv_config_t; 23 | 24 | /* 25 | * TLV info struct 26 | */ 27 | typedef struct tlv_info { 28 | esp_secure_cert_tlv_type_t type; /* Type of the TLV */ 29 | esp_secure_cert_tlv_subtype_t subtype; /* Subtype of the TLV */ 30 | char *data; /* Pointer to the buffer containting TLV data */ 31 | uint32_t length; /* TLV data length */ 32 | uint8_t flags; 33 | } esp_secure_cert_tlv_info_t; 34 | 35 | /* 36 | * TLV iterator struct 37 | */ 38 | typedef struct tlv_iterator { 39 | void *iterator; /* Opaque TLV iterator */ 40 | } esp_secure_cert_tlv_iterator_t; 41 | 42 | /* 43 | * Get the TLV information for given TLV configuration 44 | * 45 | * @note 46 | * TLV Algorithms: 47 | * If the TLV data is stored with some additional encryption then it first needs to be decrypted and the decrypted data is 48 | * stored in a dynamically allocated buffer. This API automatically decrypts any encryption applied to the TLV by supported algorithms. 49 | * For this the API may look for TLV entries of other types which store necessary information, these TLV entries must be of the same subtype as of the subtype field in the config struct. 50 | * Please see documentation regarding supported TLV storage algorithms in the TLV documentation. 51 | * A call to the esp_secure_cert_free_tlv_info() should be made to free any memory allocated while populating the tlv information object. 52 | * This API also validates the crc of the respective tlv before returning the offset. 53 | * 54 | * If tlv type in the config struct is set to ESP_SECURE_CERT_TLV_END then the address returned shall be the end address of current tlv formatted data and the length returned shall be the total length of the valid TLV entries. 55 | * @input 56 | * tlv_config Pointer to a readable struct of type esp_secure_cert_tlv_config_t. 57 | * The contents of the struct must be already filled by the caller, 58 | * This information shall be used to find the appropriate TLV entry. 59 | * 60 | * tlv_info Pointer to a writable struct of type esp_secure_cert_tlv_info_t, 61 | * If TLV entry defined by tlv_config is found then the TLV information shall be populated in this struct. 62 | * @return 63 | * 64 | * - ESP_OK On success 65 | * - ESP_FAIL/other relevant esp error code 66 | * On failure 67 | */ 68 | esp_err_t esp_secure_cert_get_tlv_info(esp_secure_cert_tlv_config_t *tlv_config, esp_secure_cert_tlv_info_t *tlv_info); 69 | 70 | /* 71 | * Free the memory allocated while populating the tlv_info object 72 | * @note 73 | * Please note this does not free the tlv_info struct itself but only the memory allocated internally while populating this struct. 74 | */ 75 | esp_err_t esp_secure_cert_free_tlv_info(esp_secure_cert_tlv_info_t *tlv_info); 76 | 77 | /* 78 | * Iterate to the next valid TLV entry 79 | * @note 80 | * To obtain the first TLV entry, the tlv_iterator structure must be zero initialized 81 | * @input 82 | * tlv_iterator Pointer to a readable struct of type esp_secure_cert_tlv_iterator_t 83 | * 84 | * @return 85 | * ESP_OK On success 86 | * The iterator location shall be moved to point to the next TLV entry. 87 | * ESP_FAIL/other relevant error codes 88 | * On failure 89 | */ 90 | esp_err_t esp_secure_cert_iterate_to_next_tlv(esp_secure_cert_tlv_iterator_t *tlv_iterator); 91 | 92 | /* 93 | * Get the TLV information from a valid iterator location 94 | * 95 | * @note 96 | * A call to the esp_secure_cert_free_tlv_info() should be made to free any memory allocated while populating the tlv information object. 97 | * 98 | * @input 99 | * tlv_config Pointer to a readable struct of type esp_secure_cert_tlv_iterator_t. 100 | * The iterator must be set to point to a valid TLV, 101 | * by a previous call to esp_secure_cert_iterate_to_next_tlv();. 102 | * 103 | * tlv_info Pointer to a writable struct of type esp_secure_cert_tlv_info_t 104 | * If TLV entry pointed by the iterator is valid then the TLV information shall be populated in this struct. 105 | * @return 106 | * ESP_OK On success 107 | * The tlv_info object shall be populated with information of the TLV pointed by the iterator 108 | * ESP_FAIL/other relevant error codes 109 | * On failure 110 | */ 111 | esp_err_t esp_secure_cert_get_tlv_info_from_iterator(esp_secure_cert_tlv_iterator_t *tlv_iterator, esp_secure_cert_tlv_info_t *tlv_info); 112 | 113 | /* 114 | * List TLV entries 115 | * 116 | * This API serially traverses through all of the available 117 | * TLV entries in the esp_secure_cert partition and logs 118 | * brief information about each TLV entry. 119 | */ 120 | void esp_secure_cert_list_tlv_entries(void); 121 | 122 | #ifdef __cplusplus 123 | } 124 | #endif 125 | -------------------------------------------------------------------------------- /private_include/esp_secure_cert_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | #include 9 | 10 | #ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 11 | #include "esp_ds.h" 12 | #endif 13 | 14 | #define ESP_SECURE_CERT_PKEY_MAGIC_BYTE 0xC1 /* Magic byte of the generated private key */ 15 | #define ESP_SECURE_CERT_DEV_CERT_MAGIC_BYTE 0xC2 /* Magic byte of the generated device certificate */ 16 | #define ESP_SECURE_CERT_CA_CERT_MAGIC_BYTE 0xC3 /* Magic byte of the CA certificate */ 17 | 18 | /* NVS Config */ 19 | 20 | #define ESP_SECURE_CERT_NVS_PARTITION_NAME "esp_secure_cert" /* Name of the nvs pre prov partition */ 21 | #define ESP_SECURE_CERT_NVS_KEYS_PARTITION "esp_secure_cert_keys" 22 | 23 | #define ESP_SECURE_CERT_NVS_LEGACY_PARTITION_NAME "pre_prov" 24 | #define ESP_SECURE_CERT_NVS_LEGACY_KEYS_PARTITION "pre_prov_keys" 25 | 26 | #define ESP_SECURE_CERT_PRIV_KEY "priv_key" 27 | #define ESP_SECURE_CERT_DEV_CERT "dev_cert" 28 | #define ESP_SECURE_CERT_CA_CERT "ca_cert" 29 | #define ESP_SECURE_CERT_NAMESPACE "esp_secure_cert" 30 | #define ESP_SECURE_CERT_LEGACY_NAMESPACE "pre_prov" 31 | 32 | 33 | #define ESP_SECURE_CERT_CIPHERTEXT "cipher_c" 34 | #define ESP_SECURE_CERT_RSA_LEN "rsa_len" 35 | #define ESP_SECURE_CERT_EFUSE_KEY_ID "ds_key_id" 36 | #define ESP_SECURE_CERT_IV "iv" 37 | 38 | #define ESP_SECURE_CERT_METADATA_SIZE 64 /* 32 bytes are reserved for the metadata (Must be a multiple of 32)*/ 39 | 40 | #define ESP_SECURE_CERT_DEV_CERT_SIZE 2048 41 | #define ESP_SECURE_CERT_CA_CERT_SIZE 4096 42 | #ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 43 | #define ESP_SECURE_CERT_PRIV_KEY_SIZE 4096 44 | #else 45 | #define ESP_SECURE_CERT_CIPHERTEXT_SIZE (ESP_DS_C_LEN + 16) 46 | #define ESP_SECURE_CERT_IV_SIZE (ESP_DS_IV_LEN + 16) 47 | #endif 48 | 49 | #define ESP_SECURE_CERT_METADATA_OFFSET 0 /* 32 bytes are reserved for the metadata (Must be a multiple of 32)*/ 50 | #define ESP_SECURE_CERT_DEV_CERT_OFFSET (ESP_SECURE_CERT_METADATA_OFFSET + ESP_SECURE_CERT_METADATA_SIZE) 51 | #define ESP_SECURE_CERT_CA_CERT_OFFSET (ESP_SECURE_CERT_DEV_CERT_OFFSET + ESP_SECURE_CERT_DEV_CERT_SIZE) 52 | #ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 53 | #define ESP_SECURE_CERT_PRIV_KEY_OFFSET (ESP_SECURE_CERT_CA_CERT_OFFSET + ESP_SECURE_CERT_CA_CERT_SIZE) 54 | #define ESP_SECURE_CERT_MAX_SIZE (ESP_SECURE_CERT_PRIV_KEY_OFFSET + ESP_SECURE_CERT_PRIV_KEY_SIZE) 55 | #else 56 | #define ESP_SECURE_CERT_CIPHERTEXT_OFFSET (ESP_SECURE_CERT_CA_CERT_OFFSET + ESP_SECURE_CERT_CA_CERT_SIZE) 57 | #define ESP_SECURE_CERT_IV_OFFSET (ESP_SECURE_CERT_CIPHERTEXT_OFFSET + ESP_SECURE_CERT_CIPHERTEXT_SIZE) 58 | #define ESP_SECURE_CERT_MAX_SIZE (ESP_SECURE_CERT_IV_OFFSET + ESP_SECURE_CERT_IV_SIZE) 59 | #endif 60 | 61 | #define ESP_SECURE_CERT_CUST_FLASH_PARTITION_TYPE 0x3F /* Custom partition type */ 62 | #define ESP_SECURE_CERT_NVS_PARTITION_TYPE 0x01 /* Data type partition */ 63 | #define ESP_SECURE_CERT_PARTITION_NAME "esp_secure_cert" /* Name of the custom pre prov partition */ 64 | #define ESP_SECURE_CERT_LEGACY_PARTITION_NAME "pre_prov" 65 | 66 | #define ESP_SECURE_CERT_METADATA_MAGIC_WORD 0x12345678 67 | 68 | 69 | 70 | typedef struct { 71 | uint32_t dev_cert_crc; /* CRC of the dev cert data */ 72 | uint16_t dev_cert_len; /* The actual length of the device cert */ 73 | uint32_t ca_cert_crc; /* CRC of the ca cert data */ 74 | uint16_t ca_cert_len; /* The actual length of the ca cert [The length before the 32 byte alignment] */ 75 | #ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 76 | uint32_t priv_key_crc; /* CRC of the priv key data */ 77 | uint16_t priv_key_len; /* The actual length of the private key */ 78 | #else 79 | uint32_t ciphertext_crc; /* CRC of the ciphertext data */ 80 | uint16_t ciphertext_len; /* The actual length of the ciphertext */ 81 | uint32_t iv_crc; /* CRC of the iv data */ 82 | uint16_t iv_len; /* The actual length of iv*/ 83 | uint16_t rsa_length; /* Length of the RSA private key that is encrypted as ciphertext */ 84 | uint8_t efuse_key_id; /* The efuse key block id which holds the HMAC key used to encrypt the ciphertext */ 85 | #endif 86 | uint32_t magic_word; /* The magic word which shall identify the valid metadata when read from flash */ 87 | } esp_secure_cert_metadata; 88 | -------------------------------------------------------------------------------- /private_include/esp_secure_cert_tlv_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #pragma once 7 | #include "esp_secure_cert_config.h" 8 | #include "esp_secure_cert_tlv_config.h" 9 | 10 | #ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 11 | #include "rsa_sign_alt.h" 12 | #endif 13 | #include "soc/soc_caps.h" 14 | 15 | #define ESP_SECURE_CERT_TLV_PARTITION_TYPE 0x3F /* Custom partition type */ 16 | #define ESP_SECURE_CERT_TLV_PARTITION_NAME "esp_secure_cert" /* Name of the custom esp_secure_cert partition */ 17 | #define ESP_SECURE_CERT_TLV_MAGIC 0xBA5EBA11 18 | 19 | #define ESP_SECURE_CERT_HMAC_KEY_ID (0) /* The hmac_key_id value that shall be used for HMAC based ecdsa key generation */ 20 | #define ESP_SECURE_CERT_DERIVED_ECDSA_KEY_SIZE (32) /* The key size in bytes of the derived ecdsa key */ 21 | #define ESP_SECURE_CERT_KEY_DERIVATION_ITERATION_COUNT (2048) /* The iteration count for ecdsa key derivation */ 22 | 23 | 24 | 25 | /** 26 | * Flags 8 bits 27 | * Used bits: 28 | * bit7(MSB) & bit6 - hmac_based_encryption 29 | * 0b10 - (i.e. 2 in decimal) the data in the block needs to be 30 | * decrypted first using the HMAC based encryption scheme 31 | * before sending out 32 | * 0b01 - (i.e. 1 in decimal) the hmac based ecdsa 33 | * private key generation is enabled. Generate the private key internally using the hardware HMAC peripheral. 34 | * 35 | * bit5 & bit4 & bit3 - TLV key flags 36 | * 0b001 - (i.e. 1 in decimal) The ecdsa key is stored in an eFuse key block 37 | * 38 | * In this case all the flags are mutually exclusive. 39 | * Ununsed bits: 40 | * . 41 | * . 42 | * bit0 (LSB) 43 | */ 44 | 45 | #define ESP_SECURE_CERT_TLV_FLAG_HMAC_ENCRYPTION (2 << 6) 46 | #define ESP_SECURE_CERT_TLV_FLAG_HMAC_ECDSA_KEY_DERIVATION (1 << 6) 47 | #define ESP_SECURE_CERT_TLV_FLAG_KEY_ECDSA_PERIPHERAL (1 << 3) 48 | #define ESP_SECURE_CERT_TLV_KEY_FLAGS_BIT_MASK (BIT5 | BIT4 | BIT3) 49 | 50 | #define ESP_SECURE_CERT_IS_TLV_ENCRYPTED(flags) \ 51 | ((flags & (BIT7 | BIT6)) == ESP_SECURE_CERT_TLV_FLAG_HMAC_ENCRYPTION) 52 | 53 | #define ESP_SECURE_CERT_HMAC_ECDSA_KEY_DERIVATION(flags) \ 54 | ((flags & (BIT7 | BIT6)) == ESP_SECURE_CERT_TLV_FLAG_HMAC_ECDSA_KEY_DERIVATION) 55 | 56 | #define ESP_SECURE_CERT_KEY_ECDSA_PERIPHERAL(flags) \ 57 | ((flags & ESP_SECURE_CERT_TLV_KEY_FLAGS_BIT_MASK) == ESP_SECURE_CERT_TLV_FLAG_KEY_ECDSA_PERIPHERAL) 58 | 59 | /* secure cert partition of cust_flash type in this case is of 8 KB size, 60 | * out of which 3-3.1 KB size is utilized. 61 | */ 62 | 63 | /* 64 | * Header for each tlv 65 | */ 66 | typedef struct esp_secure_cert_tlv_header { 67 | uint32_t magic; 68 | uint8_t flags; /* flags byte that identifies different characteristics for the TLV */ 69 | uint8_t reserved[3]; /* Reserved bytes for future use, the value currently should be 0x0 */ 70 | uint8_t type; /* Type of tlv structure, this shall be typecasted 71 | to esp_secure_cert_tlv_type_t for further use */ 72 | uint8_t subtype; /* Subtype of TLV structure, this acts as an index for the type */ 73 | uint16_t length; /* Length of the data */ 74 | uint8_t value[0]; /* Actual data in form of byte array */ 75 | } __attribute__((packed)) esp_secure_cert_tlv_header_t; 76 | 77 | /* 78 | * Footer for each tlv 79 | */ 80 | typedef struct esp_secure_cert_tlv_footer { 81 | uint32_t crc; /* crc of the data */ 82 | } esp_secure_cert_tlv_footer_t; 83 | 84 | _Static_assert(sizeof(esp_secure_cert_tlv_header_t) == 12, "TLV header size should be 12 bytes"); 85 | 86 | _Static_assert(sizeof(esp_secure_cert_tlv_footer_t) == 4, "TLV footer size should be 4 bytes"); 87 | 88 | /* 89 | * Note: 90 | * 91 | * The data stored in a cust flash partition should be as follows: 92 | * 93 | * tlv_header1 -> data_1 -> tlv_footer1 -> tlv_header2... 94 | * 95 | */ 96 | 97 | typedef struct esp_secure_cert_tlv_sec_cfg { 98 | uint8_t priv_key_efuse_id; /* eFuse block id in which the private key is stored (efuse_key_block_id + ESP_EFUSE_BLK_KEY0)*/ 99 | uint8_t reserved[39]; /* Reserving 39 bytes for future use */ 100 | } __attribute__((packed)) esp_secure_cert_tlv_sec_cfg_t; 101 | 102 | _Static_assert(sizeof(esp_secure_cert_tlv_sec_cfg_t) == 40, "TLV sec cfg size should be 40 bytes"); 103 | 104 | /* 105 | * Map the entire esp_secure_cert partition 106 | * and return the virtual address. 107 | * 108 | * @note 109 | * The mapping is done only once and function shall 110 | * simply return same address in case of successive calls. 111 | **/ 112 | const void *esp_secure_cert_get_mapped_addr(void); 113 | 114 | /* 115 | * Find the offset of tlv structure of given type in the esp_secure_cert partition 116 | * 117 | * Note: This API also validates the crc of the respective tlv before returning the offset 118 | * @input 119 | * esp_secure_cert_addr Memory mapped address of the esp_secure_cert partition 120 | * type Type of the tlv structure. 121 | * for calculating current crc for esp_secure_cert 122 | * 123 | * tlv_address Void pointer to store tlv address 124 | * 125 | */ 126 | esp_err_t esp_secure_cert_find_tlv(const void *esp_secure_cert_addr, esp_secure_cert_tlv_type_t type, uint8_t subtype, void **tlv_address); 127 | 128 | /* 129 | * Get the flash address of the data of a TLV entry 130 | * 131 | * Note: This API also validates the crc of the respective tlv before returning the offset. The offset is not the physical address but the address where it is mapped in the memory space. 132 | * @input 133 | * type Type of the TLV entry 134 | * subtype Subtype of the TLV entry (index) 135 | * buffer Pointer to the buffer to store the data address 136 | * len Pointer to store the length of the data 137 | * 138 | * Note: If tlv type = ESP_SECURE_CERT_TLV_END then the address returned shall be the end address of current tlv formatted data. 139 | * If tlv subtype = ESP_SECURE_CERT_SUBTYPE_MAX then the the address of tlv of given type and highest subtype found shall be returned. 140 | * @return 141 | * 142 | * - ESP_OK On success 143 | * - ESP_FAIL/other relevant esp error code 144 | * On failure 145 | */ 146 | esp_err_t esp_secure_cert_tlv_get_addr(esp_secure_cert_tlv_type_t type, esp_secure_cert_tlv_subtype_t subtype, char **buffer, uint32_t *len); 147 | 148 | /* 149 | * Identify if esp_secure_cert partition of type TLV is present. 150 | * @return 151 | * - 1 on if the partition is identified as TLV 152 | * - 0 on failure 153 | */ 154 | bool esp_secure_cert_is_tlv_partition(void); 155 | 156 | #ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 157 | /* @info 158 | * This function returns the flash esp_ds_context which can then be 159 | * directly provided to an esp-tls connection through its config structure. 160 | * The memory for the context is dynamically allocated. 161 | * The internal structures are however directly accessed from flash. 162 | * e.g. esp_ds_data 163 | * 164 | * @params 165 | * - ds_ctx The pointer to the DS context 166 | * @return 167 | * - ESP_OK On success 168 | * - ESP_FAIL/other relevant esp error code 169 | * On failure 170 | */ 171 | esp_ds_data_ctx_t *esp_secure_cert_tlv_get_ds_ctx(void); 172 | 173 | /* 174 | *@info 175 | * Free the ds context 176 | */ 177 | void esp_secure_cert_tlv_free_ds_ctx(esp_ds_data_ctx_t *ds_ctx); 178 | #endif 179 | 180 | #if SOC_HMAC_SUPPORTED 181 | #define HMAC_ENCRYPTION_MESSAGE_LEN (32) 182 | #define HMAC_ENCRYPTION_IV_LEN (16) 183 | #define HMAC_ENCRYPTION_TAG_LEN (16) 184 | #define HMAC_ENCRYPTION_AES_GCM_KEY_LEN (32) 185 | 186 | /* 187 | * @info 188 | * Calculate the IV for the hmac based encryption 189 | * iv The pointer to the buffer to which IV should be written 190 | * The buffer must be a writable buffer of size HMAC_ENCRYPTION_IV_LEN 191 | */ 192 | esp_err_t esp_secure_cert_calculate_hmac_encryption_iv(uint8_t *iv); 193 | 194 | /* 195 | * @info 196 | * Calculate the IV for the hmac based encryption 197 | * aes_key The pointer to the buffer to which IV should be written 198 | * The buffer must be a writable 199 | * buffer of size HMAC_ENCRYPTION_AES_GCM_KEY_LEN 200 | */ 201 | esp_err_t esp_secure_cert_calculate_hmac_encryption_key(uint8_t *aes_key); 202 | #endif 203 | -------------------------------------------------------------------------------- /srcs/esp_secure_cert_crypto.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #include 7 | #include "esp_heap_caps.h" 8 | #include "esp_err.h" 9 | #include "esp_log.h" 10 | #include "soc/soc_caps.h" 11 | 12 | #if SOC_HMAC_SUPPORTED 13 | static const char *TAG = "esp_secure_cert_crypto"; 14 | 15 | #include "esp_hmac.h" 16 | #define SHA256_MD_SIZE 32 17 | int esp_pbkdf2_hmac_sha256(hmac_key_id_t hmac_key_id, const unsigned char *salt, size_t salt_len, 18 | size_t iteration_count, size_t key_length, unsigned char *output) 19 | { 20 | int ret = -1; 21 | int j; 22 | unsigned int i; 23 | unsigned char md1[SHA256_MD_SIZE] = {}; 24 | unsigned char work[SHA256_MD_SIZE] = {}; 25 | // Considering that we only have SHA256, fixing the MD_SIZE to 32 bytes 26 | const size_t MD_SIZE = SHA256_MD_SIZE; 27 | size_t use_len; 28 | unsigned char *out_p = output; 29 | unsigned char counter[4] = {}; 30 | 31 | counter[3] = 1; 32 | uint8_t *hmac_input; 33 | esp_err_t esp_ret = ESP_FAIL; 34 | hmac_input = (uint8_t *) heap_caps_calloc(1, salt_len + sizeof(counter) + 1, MALLOC_CAP_INTERNAL); 35 | if (hmac_input == NULL) { 36 | ESP_LOGE(TAG, "Failed to allocate memory for hmac input"); 37 | return -1; 38 | } 39 | 40 | while (key_length) { 41 | // U1 ends up in work 42 | size_t hmac_input_len = 0; 43 | memcpy(hmac_input, salt, salt_len); 44 | hmac_input_len = hmac_input_len + salt_len; 45 | memcpy(hmac_input + salt_len, counter, sizeof(counter)); 46 | hmac_input_len = hmac_input_len + sizeof(counter); 47 | esp_ret = esp_hmac_calculate(hmac_key_id, hmac_input, hmac_input_len, work); 48 | if (esp_ret != ESP_OK) { 49 | ESP_LOGE(TAG, "Could not calculate the HMAC value, returned %04X", esp_ret); 50 | ret = -1; 51 | goto cleanup; 52 | } 53 | 54 | memcpy(md1, work, MD_SIZE); 55 | 56 | for (i = 1; i < iteration_count; i++) { 57 | // U2 ends up in md1 58 | esp_ret = esp_hmac_calculate(hmac_key_id, md1, MD_SIZE, md1); 59 | if (esp_ret != ESP_OK) { 60 | ESP_LOGE(TAG, "Could not calculate the HMAC value, returned %04X", esp_ret); 61 | ret = -1; 62 | goto cleanup; 63 | } 64 | // U1 xor U2 65 | // 66 | for (j = 0; j < MD_SIZE; j++) { 67 | work[j] ^= md1[j]; 68 | } 69 | } 70 | 71 | use_len = (key_length < MD_SIZE) ? key_length : MD_SIZE; 72 | memcpy(out_p, work, use_len); 73 | 74 | key_length -= (uint32_t) use_len; 75 | out_p += use_len; 76 | 77 | for (i = 4; i > 0; i--) { 78 | if (++counter[i - 1] != 0) { 79 | break; 80 | } 81 | } 82 | } 83 | //Success 84 | ret = 0; 85 | cleanup: 86 | 87 | /* Zeroise buffers to clear sensitive data from memory. */ 88 | free(hmac_input); 89 | memset(work, 0, SHA256_MD_SIZE); 90 | memset(md1, 0, SHA256_MD_SIZE); 91 | return ret; 92 | } 93 | #endif 94 | -------------------------------------------------------------------------------- /srcs/esp_secure_cert_read.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include "esp_log.h" 10 | #include "esp_err.h" 11 | #include "esp_partition.h" 12 | #include "esp_crc.h" 13 | #include "nvs_flash.h" 14 | #include "esp_secure_cert_read.h" 15 | #include "esp_secure_cert_config.h" 16 | #include "esp_secure_cert_tlv_config.h" 17 | #include "esp_secure_cert_tlv_private.h" 18 | #include "esp_heap_caps.h" 19 | 20 | #if __has_include("esp_idf_version.h") 21 | #include "esp_idf_version.h" 22 | #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) 23 | #include "spi_flash_mmap.h" 24 | #endif 25 | #endif 26 | 27 | static const char *TAG = "esp_secure_cert"; 28 | 29 | typedef enum partition_format { 30 | ESP_SECURE_CERT_PF_INVALID = -1, 31 | ESP_SECURE_CERT_PF_CUST_FLASH, 32 | ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY, 33 | ESP_SECURE_CERT_PF_NVS, 34 | ESP_SECURE_CERT_PF_TLV, 35 | } esp_secure_cert_part_fmt_t; 36 | 37 | static esp_secure_cert_part_fmt_t current_partition_format = ESP_SECURE_CERT_PF_INVALID; 38 | static const char *nvs_partition_name; 39 | static const char *nvs_namespace_name; 40 | 41 | // In case of esp_secure_cert nvs namespace is same as nvs partition name 42 | 43 | #define NVS_STR 1 44 | #define NVS_BLOB 2 45 | #define NVS_U8 3 46 | #define NVS_U16 4 47 | 48 | /* API to find the esp_secure_cert partition 49 | * @params 50 | * partition_type The type of partition to find 51 | * partition_name The name of the partition to find 52 | * partition_format The partition_format of the partition, 53 | * The global variable current_partition_format shall 54 | * be set to this value if find partition operation 55 | * was successful 56 | */ 57 | static const esp_partition_t *esp_secure_cert_find_partition(esp_partition_type_t partition_type, const char *partition_name, esp_secure_cert_part_fmt_t format) 58 | { 59 | esp_partition_iterator_t it = esp_partition_find(partition_type, ESP_PARTITION_SUBTYPE_ANY, partition_name); 60 | if (it != NULL) { 61 | current_partition_format = format; 62 | const esp_partition_t *part = esp_partition_get(it); 63 | if (part == NULL) { 64 | ESP_LOGE(TAG, "Failed to get partition"); 65 | return NULL; 66 | } else { 67 | return part; 68 | } 69 | } 70 | // if it reaches here that means it is NULL 71 | return NULL; 72 | } 73 | 74 | static const esp_partition_t *esp_secure_cert_get_partition(void) 75 | { 76 | static const esp_partition_t *part; 77 | if (part != NULL) { 78 | return part; 79 | } 80 | 81 | // This switch case statement is added for increasing readability 82 | // The actual behaviour is a simple fall through 83 | current_partition_format = ESP_SECURE_CERT_PF_TLV; 84 | 85 | switch (current_partition_format) { 86 | case ESP_SECURE_CERT_PF_TLV: 87 | part = esp_secure_cert_find_partition(ESP_SECURE_CERT_CUST_FLASH_PARTITION_TYPE, ESP_SECURE_CERT_TLV_PARTITION_NAME, ESP_SECURE_CERT_PF_TLV); 88 | if (part != NULL) { 89 | if (esp_secure_cert_is_tlv_partition()) { 90 | ESP_LOGI(TAG, "Pre-provisioned partition information:"); 91 | ESP_LOGI(TAG, "partition format: %s, partition name: %s", "CUST_FLASH_TLV", ESP_SECURE_CERT_PARTITION_NAME); 92 | return part; 93 | } 94 | } 95 | // fall through 96 | case ESP_SECURE_CERT_PF_CUST_FLASH: 97 | part = esp_secure_cert_find_partition(ESP_SECURE_CERT_CUST_FLASH_PARTITION_TYPE, ESP_SECURE_CERT_PARTITION_NAME, ESP_SECURE_CERT_PF_CUST_FLASH); 98 | if (part != NULL) { 99 | ESP_LOGI(TAG, "Pre-provisioned partition information:"); 100 | ESP_LOGI(TAG, "partition format: %s, partition name: %s", "CUST_FLASH", ESP_SECURE_CERT_PARTITION_NAME); 101 | return part; 102 | } 103 | 104 | // fall through 105 | case ESP_SECURE_CERT_PF_NVS: 106 | nvs_partition_name = ESP_SECURE_CERT_PARTITION_NAME; 107 | nvs_namespace_name = ESP_SECURE_CERT_PARTITION_NAME; 108 | part = esp_secure_cert_find_partition(ESP_SECURE_CERT_NVS_PARTITION_TYPE, nvs_partition_name, ESP_SECURE_CERT_PF_NVS); 109 | 110 | if (part != NULL) { 111 | if (nvs_flash_init_partition(nvs_partition_name) != ESP_OK) { 112 | ESP_LOGE(TAG, "NVS partition %s was found but inititlization failed", nvs_partition_name); 113 | return NULL; 114 | } 115 | ESP_LOGD(TAG, "Partition found, current format is NVS."); 116 | ESP_LOGD(TAG, "NVS partition %s is inititalized", nvs_partition_name); 117 | esp_err_t err; 118 | nvs_handle_t handle; 119 | 120 | err = nvs_open_from_partition(nvs_partition_name, nvs_namespace_name, NVS_READONLY, &handle); 121 | if (err == ESP_OK) { 122 | ESP_LOGI(TAG, "Pre-provisioned partition information:"); 123 | ESP_LOGI(TAG, "partition format: %s, partition name: %s, partition namespace: %s", "NVS", nvs_partition_name, nvs_namespace_name); 124 | nvs_close(handle); 125 | return part; 126 | } 127 | 128 | nvs_namespace_name = ESP_SECURE_CERT_LEGACY_PARTITION_NAME; 129 | err = nvs_open_from_partition(nvs_partition_name, nvs_namespace_name, NVS_READONLY, &handle); 130 | nvs_close(handle); 131 | if (err == ESP_OK) { 132 | ESP_LOGI(TAG, "Pre-provisioned partition information:"); 133 | ESP_LOGI(TAG, "partition format: %s, partition name: %s, partition namespace: %s", "NVS", nvs_partition_name, nvs_namespace_name); 134 | return part; 135 | } else { 136 | ESP_LOGE(TAG, "Failed to open nvs partition %s with namespace: %s", nvs_partition_name, nvs_namespace_name); 137 | return NULL; 138 | } 139 | } 140 | 141 | esp_err_t err; 142 | nvs_handle_t handle; 143 | nvs_partition_name = ESP_SECURE_CERT_LEGACY_PARTITION_NAME; 144 | nvs_namespace_name = ESP_SECURE_CERT_LEGACY_PARTITION_NAME; 145 | part = esp_secure_cert_find_partition(ESP_SECURE_CERT_NVS_PARTITION_TYPE, nvs_partition_name, ESP_SECURE_CERT_PF_NVS); 146 | if (part != NULL) { 147 | if (nvs_flash_init_partition(nvs_partition_name) != ESP_OK) { 148 | ESP_LOGE(TAG, "NVS partition %s was found but inititlization failed", nvs_partition_name); 149 | return NULL; 150 | } 151 | err = nvs_open_from_partition(nvs_partition_name, nvs_namespace_name, NVS_READONLY, &handle); 152 | nvs_close(handle); 153 | if (err == ESP_OK) { 154 | ESP_LOGI(TAG, "Pre-provisioned partition information:"); 155 | ESP_LOGI(TAG, "partition format: %s, partition name: %s, partition namespace: %s", "NVS", nvs_partition_name, nvs_namespace_name); 156 | return part; 157 | } else { 158 | ESP_LOGE(TAG, "Failed to open nvs partition %s with namespace: %s", nvs_partition_name, nvs_namespace_name); 159 | return NULL; 160 | } 161 | } 162 | 163 | // fall through 164 | case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY: 165 | part = esp_secure_cert_find_partition(ESP_SECURE_CERT_CUST_FLASH_PARTITION_TYPE, ESP_SECURE_CERT_LEGACY_PARTITION_NAME, ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY); 166 | if (part != NULL) { 167 | ESP_LOGI(TAG, "Partition found, current format is cust flash legacy."); 168 | ESP_LOGI(TAG, "Current partition format: %s, partition name: %s", "CUST_FLASH_LEGACY", ESP_SECURE_CERT_LEGACY_PARTITION_NAME); 169 | return part; 170 | } 171 | 172 | // fall through 173 | case ESP_SECURE_CERT_PF_INVALID: 174 | default: 175 | current_partition_format = ESP_SECURE_CERT_PF_INVALID; 176 | ESP_LOGE(TAG, "Failed to get esp_secure_cert partition"); 177 | return NULL; 178 | } 179 | return NULL; 180 | } 181 | 182 | static void esp_secure_cert_get_partition_format(void) 183 | { 184 | if (current_partition_format != ESP_SECURE_CERT_PF_INVALID) { 185 | return; 186 | } 187 | 188 | const esp_partition_t *part = esp_secure_cert_get_partition(); 189 | if (part == NULL) { 190 | ESP_LOGE(TAG, "Failed to obtain the current partition and partition format"); 191 | current_partition_format = ESP_SECURE_CERT_PF_INVALID; 192 | } 193 | return; 194 | } 195 | 196 | static int nvs_get(const char *name_space, const char *key, char *value, size_t *len, size_t type) 197 | { 198 | esp_err_t err; 199 | nvs_handle_t handle; 200 | 201 | err = nvs_open_from_partition(nvs_partition_name, name_space, NVS_READONLY, &handle); 202 | if (err != ESP_OK) { 203 | ESP_LOGE(TAG, "Could not open NVS handle (0x%x)!", err); 204 | return err; 205 | } 206 | 207 | switch (type) { 208 | case NVS_STR: 209 | err = nvs_get_str(handle, key, value, len); 210 | break; 211 | case NVS_BLOB: 212 | err = nvs_get_blob(handle, key, value, len); 213 | break; 214 | case NVS_U8: 215 | err = nvs_get_u8(handle, key, (uint8_t *)value); 216 | break; 217 | case NVS_U16: 218 | err = nvs_get_u16(handle, key, (uint16_t *)value); 219 | break; 220 | default: 221 | ESP_LOGE(TAG, "Invalid type of NVS data provided"); 222 | err = ESP_ERR_INVALID_ARG; 223 | } 224 | 225 | if (err != ESP_OK) { 226 | ESP_LOGE(TAG, "Error (0x%02X) reading NVS data!", err); 227 | return err; 228 | } 229 | 230 | nvs_close(handle); 231 | return err; 232 | } 233 | 234 | esp_err_t esp_secure_cert_init_nvs_partition(void) 235 | { 236 | const esp_partition_t *part = esp_secure_cert_get_partition(); 237 | if (part == NULL) { 238 | ESP_LOGE(TAG, "Could not get partition."); 239 | return ESP_FAIL; 240 | } 241 | 242 | if (nvs_flash_init_partition(ESP_SECURE_CERT_PARTITION_NAME) == ESP_OK) { 243 | return ESP_OK; 244 | } else { 245 | return nvs_flash_init_partition(ESP_SECURE_CERT_LEGACY_PARTITION_NAME); 246 | } 247 | } 248 | 249 | static esp_err_t esp_secure_cert_read_raw_flash(const esp_partition_t *partition, uint32_t src_offset, void *dst, uint32_t size) 250 | { 251 | /* Encrypted partitions need to be read via a cache mapping */ 252 | const void *buf; 253 | spi_flash_mmap_handle_t handle; 254 | esp_err_t err; 255 | 256 | err = esp_partition_mmap(partition, src_offset, size, SPI_FLASH_MMAP_DATA, &buf, &handle); 257 | if (err != ESP_OK) { 258 | return err; 259 | } 260 | memcpy(dst, buf, size); 261 | spi_flash_munmap(handle); 262 | return ESP_OK; 263 | } 264 | 265 | const void *esp_secure_cert_mmap(const esp_partition_t *partition, uint32_t src_offset, uint32_t size) 266 | { 267 | /* Encrypted partitions need to be read via a cache mapping */ 268 | const void *buf; 269 | spi_flash_mmap_handle_t handle; 270 | esp_err_t err; 271 | 272 | err = esp_partition_mmap(partition, src_offset, size, SPI_FLASH_MMAP_DATA, &buf, &handle); 273 | if (err != ESP_OK) { 274 | return NULL; 275 | } 276 | return buf; 277 | } 278 | 279 | static esp_err_t esp_secure_cert_read_metadata(esp_secure_cert_metadata *metadata, size_t offset, const esp_partition_t *part, uint32_t *data_len, uint32_t *data_crc) 280 | { 281 | esp_err_t err; 282 | err = esp_secure_cert_read_raw_flash(part, ESP_SECURE_CERT_METADATA_OFFSET, metadata, sizeof(esp_secure_cert_metadata)); 283 | if (err != ESP_OK) { 284 | ESP_LOGE(TAG, "Could not read metadata."); 285 | return ESP_FAIL; 286 | } 287 | 288 | if (metadata->magic_word != ESP_SECURE_CERT_METADATA_MAGIC_WORD) { 289 | ESP_LOGE(TAG, "Metadata magic word does not match"); 290 | return ESP_FAIL; 291 | } 292 | 293 | switch (offset) { 294 | case ESP_SECURE_CERT_METADATA_OFFSET: 295 | *data_len = sizeof(metadata); 296 | break; 297 | case ESP_SECURE_CERT_DEV_CERT_OFFSET: 298 | if (current_partition_format == ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY) { 299 | /* In the legacy format the order of dev_cert_len, dev_cert_crc have been interchanged with ca_cert_len and ca_cert_crc */ 300 | /* Returning the appropriate values here */ 301 | *data_len = metadata->ca_cert_len; 302 | *data_crc = metadata->ca_cert_crc; 303 | } else { 304 | *data_len = metadata->dev_cert_len; 305 | *data_crc = metadata->dev_cert_crc; 306 | } 307 | break; 308 | case ESP_SECURE_CERT_CA_CERT_OFFSET: 309 | if (current_partition_format == ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY) { 310 | /* In the legacy format the order of dev_cert_len, dev_cert_crc have been interchanged with ca_cert_len and ca_cert_crc */ 311 | /* Returning the appropriate values here */ 312 | *data_len = metadata->dev_cert_len; 313 | *data_crc = metadata->dev_cert_crc; 314 | } else { 315 | *data_len = metadata->ca_cert_len; 316 | *data_crc = metadata->ca_cert_crc; 317 | } 318 | break; 319 | #ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 320 | case ESP_SECURE_CERT_PRIV_KEY_OFFSET: 321 | *data_len = metadata->priv_key_len; 322 | *data_crc = metadata->priv_key_crc; 323 | break; 324 | #else /* !CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */ 325 | case ESP_SECURE_CERT_CIPHERTEXT_OFFSET: 326 | *data_len = metadata->ciphertext_len; 327 | *data_crc = metadata->ciphertext_crc; 328 | break; 329 | case ESP_SECURE_CERT_IV_OFFSET: 330 | *data_len = metadata->iv_len; 331 | *data_crc = metadata->iv_crc; 332 | break; 333 | #endif /* CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */ 334 | default: 335 | err = ESP_ERR_INVALID_ARG; 336 | ESP_LOGE(TAG, "Invalid offset value given"); 337 | } 338 | 339 | return err; 340 | } 341 | 342 | static esp_err_t esp_secure_cert_get_addr(size_t offset, char **buffer, uint32_t *len) 343 | { 344 | esp_err_t err; 345 | static esp_secure_cert_metadata metadata; 346 | uint32_t data_len = 0; 347 | uint32_t data_crc = 0; 348 | 349 | const esp_partition_t *part = esp_secure_cert_get_partition(); 350 | if (part == NULL) { 351 | ESP_LOGE(TAG, "Could not get partition."); 352 | return ESP_FAIL; 353 | } 354 | 355 | err = esp_secure_cert_read_metadata(&metadata, offset, part, &data_len, &data_crc); 356 | if (err != ESP_OK) { 357 | ESP_LOGE(TAG, "Error in reading the metadata"); 358 | return err; 359 | } 360 | 361 | *len = data_len; 362 | *buffer = (char *)esp_secure_cert_mmap(part, offset, *len); 363 | if (buffer == NULL) { 364 | return ESP_FAIL; 365 | } 366 | 367 | uint32_t read_crc = esp_crc32_le(UINT32_MAX, (const uint8_t * )*buffer, data_len); 368 | if (read_crc != data_crc) { 369 | ESP_LOGE(TAG, "Data has been tampered"); 370 | return ESP_FAIL; 371 | } 372 | return ESP_OK; 373 | } 374 | 375 | esp_err_t esp_secure_cert_get_device_cert(char **buffer, uint32_t *len) 376 | { 377 | // This API sets the global variable current_partition_format 378 | esp_secure_cert_get_partition_format(); 379 | esp_err_t ret; 380 | switch (current_partition_format) { 381 | case ESP_SECURE_CERT_PF_TLV: 382 | return esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_DEV_CERT_TLV, ESP_SECURE_CERT_SUBTYPE_MAX, buffer, len); 383 | 384 | case ESP_SECURE_CERT_PF_CUST_FLASH: 385 | case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY: 386 | return esp_secure_cert_get_addr(ESP_SECURE_CERT_DEV_CERT_OFFSET, buffer, len);; 387 | 388 | case ESP_SECURE_CERT_PF_NVS: 389 | ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_DEV_CERT, NULL, (size_t *)len, NVS_STR); 390 | if (ret != ESP_OK) { 391 | ESP_LOGE(TAG, "Failed to get device cert length from nvs"); 392 | return ret; 393 | } 394 | 395 | *buffer = (char *)calloc(1, *len + 1); 396 | if (*buffer == NULL) { 397 | ESP_LOGE(TAG, "Not enough memory for device cert buffer"); 398 | return ESP_ERR_NO_MEM; 399 | } 400 | 401 | return nvs_get(nvs_namespace_name, ESP_SECURE_CERT_DEV_CERT, *buffer, (size_t *)len, NVS_STR); 402 | 403 | case ESP_SECURE_CERT_PF_INVALID: 404 | default: 405 | ESP_LOGE(TAG, "Invalid flash format"); 406 | return ESP_FAIL; 407 | } 408 | 409 | } 410 | 411 | esp_err_t esp_secure_cert_free_device_cert(char *buffer) 412 | { 413 | switch(current_partition_format) { 414 | // fall through 415 | case ESP_SECURE_CERT_PF_TLV: 416 | case ESP_SECURE_CERT_PF_CUST_FLASH: 417 | case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY: 418 | return ESP_OK; 419 | break; 420 | 421 | case ESP_SECURE_CERT_PF_NVS: 422 | free(buffer); 423 | return ESP_OK; 424 | break; 425 | case ESP_SECURE_CERT_PF_INVALID: 426 | default: 427 | return ESP_FAIL; 428 | } 429 | } 430 | 431 | esp_err_t esp_secure_cert_get_ca_cert(char **buffer, uint32_t *len) 432 | { 433 | // This API sets the global variable current_partition_format 434 | esp_secure_cert_get_partition_format(); 435 | esp_err_t ret; 436 | 437 | switch (current_partition_format) { 438 | case ESP_SECURE_CERT_PF_TLV: 439 | return esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_CA_CERT_TLV, ESP_SECURE_CERT_SUBTYPE_MAX, buffer, len); 440 | 441 | case ESP_SECURE_CERT_PF_CUST_FLASH: 442 | case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY: 443 | return esp_secure_cert_get_addr(ESP_SECURE_CERT_CA_CERT_OFFSET, buffer, len); 444 | 445 | case ESP_SECURE_CERT_PF_NVS: 446 | ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_CA_CERT, NULL, (size_t *)len, NVS_STR); 447 | if (ret != ESP_OK) { 448 | ESP_LOGE(TAG, "Failed to get ca cert length from nvs"); 449 | return ret; 450 | } 451 | 452 | *buffer = (char *)calloc(1, *len + 1); 453 | if (*buffer == NULL) { 454 | ESP_LOGE(TAG, "Not enough memory for ca cert buffer"); 455 | return ESP_ERR_NO_MEM; 456 | } 457 | return nvs_get(nvs_namespace_name, ESP_SECURE_CERT_CA_CERT, *buffer, (size_t *)len, NVS_STR); 458 | 459 | case ESP_SECURE_CERT_PF_INVALID: 460 | default: 461 | ESP_LOGE(TAG, "Invalid flash format"); 462 | return ESP_FAIL; 463 | } 464 | 465 | } 466 | 467 | esp_err_t esp_secure_cert_free_ca_cert(char *buffer) 468 | { 469 | switch(current_partition_format) { 470 | // fall through 471 | case ESP_SECURE_CERT_PF_TLV: 472 | case ESP_SECURE_CERT_PF_CUST_FLASH: 473 | case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY: 474 | return ESP_OK; 475 | break; 476 | 477 | case ESP_SECURE_CERT_PF_NVS: 478 | free(buffer); 479 | return ESP_OK; 480 | break; 481 | case ESP_SECURE_CERT_PF_INVALID: 482 | default: 483 | return ESP_FAIL; 484 | } 485 | } 486 | 487 | #ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 488 | esp_err_t esp_secure_cert_get_priv_key(char **buffer, uint32_t *len) 489 | { 490 | 491 | // This API sets the global variable current_partition_format 492 | esp_secure_cert_get_partition_format(); 493 | esp_err_t ret; 494 | 495 | switch (current_partition_format) { 496 | case ESP_SECURE_CERT_PF_TLV: 497 | return esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_PRIV_KEY_TLV, ESP_SECURE_CERT_SUBTYPE_MAX, buffer, len); 498 | case ESP_SECURE_CERT_PF_CUST_FLASH: 499 | case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY: 500 | return esp_secure_cert_get_addr(ESP_SECURE_CERT_PRIV_KEY_OFFSET, buffer, len);; 501 | 502 | case ESP_SECURE_CERT_PF_NVS: 503 | ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_PRIV_KEY, NULL, (size_t *)len, NVS_STR); 504 | if (ret != ESP_OK) { 505 | ESP_LOGE(TAG, "Failed to get priv key length from nvs"); 506 | return ret; 507 | } 508 | 509 | *buffer = (char *)calloc(1, *len + 1); 510 | if (*buffer == NULL) { 511 | ESP_LOGE(TAG, "Not enough memory for priv key buffer"); 512 | return ESP_ERR_NO_MEM; 513 | } 514 | 515 | return nvs_get(nvs_namespace_name, ESP_SECURE_CERT_PRIV_KEY, *buffer, (size_t *)len, NVS_STR); 516 | 517 | case ESP_SECURE_CERT_PF_INVALID: 518 | default: 519 | ESP_LOGE(TAG, "Invalid flash format"); 520 | return ESP_FAIL; 521 | } 522 | 523 | } 524 | 525 | esp_err_t esp_secure_cert_free_priv_key(char *buffer) 526 | { 527 | switch(current_partition_format) { 528 | // fall through 529 | case ESP_SECURE_CERT_PF_TLV: 530 | case ESP_SECURE_CERT_PF_CUST_FLASH: 531 | case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY: 532 | return ESP_OK; 533 | break; 534 | 535 | case ESP_SECURE_CERT_PF_NVS: 536 | free(buffer); 537 | return ESP_OK; 538 | break; 539 | case ESP_SECURE_CERT_PF_INVALID: 540 | default: 541 | return ESP_FAIL; 542 | } 543 | } 544 | #endif 545 | 546 | #ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL 547 | static esp_err_t esp_secure_cert_read(size_t offset, unsigned char *buffer, uint32_t *len) 548 | { 549 | esp_err_t err; 550 | static esp_secure_cert_metadata metadata; 551 | uint32_t data_len = 0; 552 | uint32_t data_crc = 0; 553 | 554 | const esp_partition_t *part = esp_secure_cert_get_partition(); 555 | if (part == NULL) { 556 | ESP_LOGE(TAG, "Could not get partition."); 557 | return ESP_FAIL; 558 | } 559 | 560 | err = esp_secure_cert_read_metadata(&metadata, offset, part, &data_len, &data_crc); 561 | if (err != ESP_OK) { 562 | ESP_LOGE(TAG, "Error in reading the metadata"); 563 | return err; 564 | } 565 | 566 | if (buffer == NULL) { 567 | *len = data_len; 568 | return ESP_OK; 569 | } 570 | 571 | if (*len < data_len) { 572 | ESP_LOGE(TAG, "Insufficient length of buffer. buffer size: %"PRIu32", required: %"PRIu32"", *len, data_len); 573 | return ESP_FAIL; 574 | } 575 | 576 | /* If the requested offset belongs to the medatada, return the already read metadata */ 577 | if (offset == ESP_SECURE_CERT_METADATA_OFFSET) { 578 | memcpy(buffer, &metadata, sizeof(metadata)); 579 | return ESP_OK; 580 | } 581 | 582 | err = esp_secure_cert_read_raw_flash(part, offset, buffer, data_len); 583 | if (err != ESP_OK) { 584 | ESP_LOGE(TAG, "Could not read data."); 585 | return ESP_FAIL; 586 | } 587 | 588 | uint32_t read_crc = esp_crc32_le(UINT32_MAX, (const uint8_t * )buffer, data_len); 589 | if (read_crc != data_crc) { 590 | ESP_LOGE(TAG, "Data has been tampered"); 591 | return ESP_FAIL; 592 | } 593 | 594 | return ESP_OK; 595 | } 596 | 597 | esp_ds_data_ctx_t *esp_secure_cert_get_ds_ctx(void) 598 | { 599 | esp_secure_cert_get_partition_format(); 600 | if (current_partition_format == ESP_SECURE_CERT_PF_TLV) { 601 | return esp_secure_cert_tlv_get_ds_ctx(); 602 | } 603 | esp_err_t esp_ret; 604 | esp_ds_data_ctx_t *ds_data_ctx; 605 | uint32_t len = 0; 606 | ds_data_ctx = (esp_ds_data_ctx_t *)heap_caps_malloc(sizeof(esp_ds_data_ctx_t), MALLOC_CAP_INTERNAL); 607 | if (ds_data_ctx == NULL) { 608 | ESP_LOGE(TAG, "Error in allocating memory for esp_ds_data_context"); 609 | goto exit; 610 | } 611 | 612 | ds_data_ctx->esp_ds_data = (esp_ds_data_t *)heap_caps_calloc(1, sizeof(esp_ds_data_t), MALLOC_CAP_INTERNAL); 613 | if (ds_data_ctx->esp_ds_data == NULL) { 614 | ESP_LOGE(TAG, "Could not allocate memory for DS data handle "); 615 | goto exit; 616 | } 617 | 618 | // This API sets the global variable current_partition_format 619 | 620 | if (current_partition_format == ESP_SECURE_CERT_PF_CUST_FLASH || current_partition_format == ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY) { 621 | char *buffer; 622 | len = 0; 623 | esp_ret = esp_secure_cert_get_addr(ESP_SECURE_CERT_CIPHERTEXT_OFFSET, &buffer, &len); 624 | if (esp_ret != ESP_OK) { 625 | ESP_LOGE(TAG, "Error in reading ciphertext"); 626 | goto exit; 627 | } 628 | 629 | memcpy((void *)ds_data_ctx->esp_ds_data->c, buffer, len); 630 | 631 | len = 0; 632 | esp_ret = esp_secure_cert_get_addr(ESP_SECURE_CERT_IV_OFFSET, &buffer, &len); 633 | if (esp_ret != ESP_OK) { 634 | ESP_LOGE(TAG, "Error in reading initialization vector"); 635 | goto exit; 636 | } 637 | 638 | memcpy((void *)ds_data_ctx->esp_ds_data->iv, buffer, len); 639 | unsigned char metadata[ESP_SECURE_CERT_METADATA_SIZE] = {}; 640 | len = sizeof(metadata); 641 | esp_err_t err = esp_secure_cert_read(ESP_SECURE_CERT_METADATA_OFFSET, metadata, &len); 642 | if (err != ESP_OK) { 643 | ESP_LOGE(TAG, "Error in reading metadata"); 644 | goto exit; 645 | } 646 | ds_data_ctx->rsa_length_bits = ((esp_secure_cert_metadata *)metadata)->rsa_length; 647 | ds_data_ctx->efuse_key_id = ((esp_secure_cert_metadata *)metadata)->efuse_key_id; 648 | return ds_data_ctx; 649 | 650 | } else if (current_partition_format == ESP_SECURE_CERT_PF_NVS) { 651 | len = ESP_DS_C_LEN; 652 | esp_ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_CIPHERTEXT, (char *) ds_data_ctx->esp_ds_data->c, (size_t *) &len, NVS_BLOB); 653 | if (esp_ret != ESP_OK) { 654 | ESP_LOGE(TAG, "Error in reading ciphertext"); 655 | goto exit; 656 | } 657 | 658 | len = ESP_DS_IV_LEN; 659 | esp_ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_IV, (char *)ds_data_ctx->esp_ds_data->iv, (size_t *)&len, NVS_BLOB); 660 | if (esp_ret != ESP_OK) { 661 | ESP_LOGE(TAG, "Error in reading initialization vector"); 662 | goto exit; 663 | } 664 | 665 | esp_ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_EFUSE_KEY_ID, (void *)&ds_data_ctx->efuse_key_id, 0, NVS_U8); 666 | if (esp_ret != ESP_OK) { 667 | ESP_LOGE(TAG, "Error in reading efuse key id"); 668 | goto exit; 669 | } 670 | 671 | esp_ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_RSA_LEN, (void *)&ds_data_ctx->rsa_length_bits, 0, NVS_U16); 672 | if (esp_ret != ESP_OK) { 673 | ESP_LOGE(TAG, "Error in reading rsa key length"); 674 | goto exit; 675 | } 676 | return ds_data_ctx; 677 | } 678 | 679 | exit: 680 | if (ds_data_ctx != NULL) { 681 | free(ds_data_ctx->esp_ds_data); 682 | } 683 | free(ds_data_ctx); 684 | return NULL; 685 | } 686 | 687 | void esp_secure_cert_free_ds_ctx(esp_ds_data_ctx_t *ds_ctx) 688 | { 689 | esp_secure_cert_get_partition_format(); 690 | if (current_partition_format == ESP_SECURE_CERT_PF_TLV) { 691 | esp_secure_cert_tlv_free_ds_ctx(ds_ctx); 692 | } 693 | 694 | if (ds_ctx != NULL) { 695 | free(ds_ctx->esp_ds_data); 696 | } 697 | free(ds_ctx); 698 | } 699 | #endif 700 | -------------------------------------------------------------------------------- /tools/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /tools/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # esp_secure_cert Configuration Tool 2 | > WARNING: This tool is to be used only for development purpose. It does not enable any kind security feature for the protection of the sensitive data in the `esp_secure_cert` partition. 3 | 4 | The script [configure_esp_secure_cert.py](https://github.com/espressif/esp_secure_cert_mgr/blob/main/tools/configure_esp_secure_cert.py) is used for configuring the ESP platform with PKI credentials into the esp_secure_cert partition which shall reside on its flash storage. 5 | It also configures the DS peripheral on the ESP32-S2/ESP32-S3/ESP32-C3 SoC. The steps in the script are based on technical details of certain operations in the Digital Signature calculation, which can be found in the Digital Signature Section of [ESP32-S2 TRM](https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf). 6 | 7 | The script generates a partition named `esp_secure_cert` on host machine, that contains the parameters required by the DS peripheral. The `configure_esp_secure_cert.py` utility automatically flashes the partition to the ESP platform which has been connected. 8 | 9 | # Installation 10 | 11 | ``` 12 | pip install esp-secure-cert-tool 13 | ``` 14 | 15 | # Configuration 16 | 17 | 1) Generate root ca and key pair: 18 | ``` 19 | openssl req -newkey rsa:2048 -nodes -keyout prvtkey.pem -x509 -days 3650 -out cacert.pem -subj "/CN=Test CA" 20 | ``` 21 | 22 | 2) Generate client private key: 23 | ``` 24 | openssl genrsa -out client.key 25 | ``` 26 | 27 | 3) Generate device cert: 28 | ``` 29 | openssl req -out client.csr -key client.key -new 30 | openssl x509 -req -days 365 -in client.csr -CA cacert.pem -CAkey prvtkey.pem -sha256 -CAcreateserial -out client.crt 31 | ``` 32 | 33 | # Generate `esp_secure_cert` partition 34 | Following commands can be used to configure the DS peripheral and generate the `esp_secure_cert` partition. 35 | The script can generate `cust_flash` as well as `nvs` type of `esp_secure_cert` partition. Please refer [upper level README](../README.md) for more details about type of partitions. 36 | 37 | * When configuring the DS peripheral, by default the configuration script does not enable the read protection for the efuse key block in which the DS key is programmed. This is done for allowing flexibility while using the script for development purpose. 38 | 39 | * Please remove the `--configure_ds` argument from these commands if use of the DS peripheral is disabled in the menu config. 40 | > **WARNING**: This is not recommended for production purpose as the private key shall be stored as plaintext. 41 | 42 | ## Generate `esp_secure_cert` partition of type `cust_flash_tlv`: 43 | 44 | This command shall generate a binary partition containing the PKI credentials stored in the TLV format and flash it at the default offset of `0xD000`. 45 | 46 | ``` 47 | configure_esp_secure_cert.py -p /* Serial port */ --keep_ds_data_on_host --efuse_key_id 1 --ca-cert cacert.pem --device-cert client.crt --private-key client.key --target_chip /* target chip */ --secure_cert_type cust_flash_tlv --configure_ds 48 | ``` 49 | 50 | * When [Flash Encryption](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html) is enabled for the device, the option ``--skip_flash`` (explained below) can be used to prevent the flashing opereation and only save the `esp_secure_cert.bin` on the host machine. It can then be flashed on the target using below command: 51 | 52 | ```esptool.py -p /* Serial Port*/ write_flash 0xD000 esp_secure_cert.bin --encrypt``` 53 | 54 | More details regarding [esptool.py](https://docs.espressif.com/projects/esptool/en/latest/esp32/esptool/index.html#esptool-py) utility can be found [here](https://docs.espressif.com/projects/esptool/en/latest/esp32/esptool/index.html). 55 | 56 | Note: This is only applicable for [Development mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html#flash-enc-development-mode) of Flash Encryption. 57 | 58 | ## Legacy partition formats: 59 | 60 | 1. Generate `esp_secure_cert` partition of type `cust_flash`: 61 | 62 | ``` 63 | configure_esp_secure_cert.py -p /* Serial port */ --keep_ds_data_on_host --efuse_key_id 1 --ca-cert cacert.pem --device-cert client.crt --private-key client.key --target_chip /* target chip */ --secure_cert_type cust_flash --configure_ds 64 | ``` 65 | 66 | 2. Generate `esp_secure_cert` partition of type `nvs`: 67 | ``` 68 | configure_esp_secure_cert.py -p /* Serial port */ --keep_ds_data_on_host --efuse_key_id 1 --ca-cert cacert.pem --device-cert client.crt --private-key client.key --target_chip /* target chip */ --secure_cert_type nvs --configure_ds 69 | ``` 70 | 71 | ## Additional options for the utility 72 | The following options can be provided additional to the main arguments given above. 73 | 74 | ### Set the `esp_secure_cert` partition offset 75 | By default the `esp_secure_cert` partition shall be flashed at an offset of `0xD000` by the utility. 76 | In order to flash the `esp_secure_cert` partition at a different offset, the following argument can be provided additionally to the configure_esp_secure_cert.py script. 77 | 78 | ``` 79 | configure_esp_secure_cert.py --sec_cert_part_offset /* offset value in hex e.g. 0xD000 */ 80 | ``` 81 | ### Skip automatic flashing of `esp_secure_cert` partition 82 | 83 | By default the `esp_secure_cert` partition shall be flashed automatically at the offset value provided to `sec_cert_part_offset` argument. 84 | In order to stop the flashing process the following argument can be provided additionally to the configure_esp_secure_cert.py script 85 | 86 | ``` 87 | configure_esp_secure_cert.py --skip_flash 88 | ``` 89 | 90 | ### Help section 91 | The additional options supported by the utility can be found at `configure_esp_secure_cert.py --help`. 92 | -------------------------------------------------------------------------------- /tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/tools/__init__.py -------------------------------------------------------------------------------- /tools/configure_esp_secure_cert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD 3 | # SPDX-License-Identifier: Apache-2.0 4 | import argparse 5 | import os 6 | import subprocess 7 | import sys 8 | from esp_secure_cert import nvs_format, custflash_format 9 | from esp_secure_cert import configure_ds, tlv_format 10 | from esp_secure_cert.efuse_helper import ( 11 | log_efuse_summary, 12 | configure_efuse_key_block, 13 | ) 14 | 15 | # Check python version is proper or not to avoid script failure 16 | assert sys.version_info >= (3, 6, 0), 'Python version too low.' 17 | 18 | esp_secure_cert_data_dir = 'esp_secure_cert_data' 19 | # hmac_key_file is generated when HMAC_KEY is calculated, 20 | # it is used when burning HMAC_KEY to efuse 21 | hmac_key_file = os.path.join(esp_secure_cert_data_dir, 'hmac_key.bin') 22 | ecdsa_key_file = os.path.join(esp_secure_cert_data_dir, 'ecdsa_key.bin') 23 | # csv and bin filenames are default filenames 24 | # for nvs partition files created with this script 25 | csv_filename = os.path.join(esp_secure_cert_data_dir, 'esp_secure_cert.csv') 26 | bin_filename = os.path.join(esp_secure_cert_data_dir, 'esp_secure_cert.bin') 27 | # Targets supported by the script 28 | supported_targets = {'esp32', 'esp32s2', 'esp32c3', 'esp32s3', 29 | 'esp32c6', 'esp32h2', 'esp32p4'} 30 | 31 | 32 | # Flash esp_secure_cert partition after its generation 33 | # 34 | # @info 35 | # The partition shall be flashed at the offset provided 36 | # for the --sec_cert_part_offset option 37 | def flash_esp_secure_cert_partition(idf_target, 38 | port, sec_cert_part_offset, 39 | flash_filename): 40 | print('Flashing the esp_secure_cert partition at {0} offset' 41 | .format(sec_cert_part_offset)) 42 | print('Note: You can skip this step by providing --skip_flash argument') 43 | flash_command = f"esptool.py --chip {idf_target} " + \ 44 | f"-p {port} write_flash " + \ 45 | f" {sec_cert_part_offset} {flash_filename}" 46 | try: 47 | flash_command_output = subprocess.check_output( 48 | flash_command, 49 | shell=True 50 | ) 51 | print(flash_command_output.decode('utf-8')) 52 | except subprocess.CalledProcessError as e: 53 | print(e.output.decode("utf-8")) 54 | print('ERROR: Failed to execute the flash command') 55 | sys.exit(-1) 56 | 57 | 58 | def cleanup(args): 59 | if args.keep_ds_data is False: 60 | if os.path.exists(hmac_key_file): 61 | os.remove(hmac_key_file) 62 | if os.path.exists(csv_filename): 63 | os.remove(csv_filename) 64 | 65 | 66 | def main(): 67 | parser = argparse.ArgumentParser(description=''' 68 | The python utility helps to configure and provision 69 | the device with PKI credentials, to generate the esp_secure_cert partition. 70 | The utility also configures the DS peripheral on the SoC if available. 71 | ''') 72 | 73 | parser.add_argument( 74 | '--private-key', 75 | dest='privkey', 76 | default='client.key', 77 | metavar='relative/path/to/client-priv-key', 78 | help='relative path to client private key') 79 | 80 | parser.add_argument( 81 | '--pwd', '--password', 82 | dest='priv_key_pass', 83 | metavar='[password]', 84 | help='the password associated with the private key') 85 | 86 | parser.add_argument( 87 | '--device-cert', 88 | dest='device_cert', 89 | default='client.crt', 90 | metavar='relative/path/to/device-cert', 91 | help='relative path to device/client certificate ' 92 | '(which contains the public part of the client private key) ') 93 | 94 | parser.add_argument( 95 | '--ca-cert', 96 | dest='ca_cert', 97 | default='ca.crt', 98 | metavar='relative/path/to/ca-cert', 99 | help='relative path to ca certificate which ' 100 | 'has been used to sign the client certificate') 101 | 102 | parser.add_argument( 103 | '--target_chip', 104 | dest='target_chip', type=str, 105 | choices=supported_targets, 106 | default='esp32c3', 107 | metavar='target chip', 108 | help='The target chip e.g. esp32s2, s3, c3') 109 | 110 | parser.add_argument( 111 | '--summary', 112 | dest='summary', action='store_true', 113 | help='Provide this option to print efuse summary of the chip') 114 | 115 | parser.add_argument( 116 | '--secure_cert_type', 117 | dest='sec_cert_type', type=str, 118 | choices={'cust_flash_tlv', 'cust_flash', 'nvs'}, 119 | default='cust_flash_tlv', 120 | metavar='type of secure_cert partition', 121 | help='The type of esp_secure_cert partition. ' 122 | 'Can be \"cust_flash_tlv\" or \"cust_flash\" or \"nvs\". ' 123 | 'Please note that \"cust_flash\" and \"nvs\" are legacy formats.') 124 | 125 | parser.add_argument( 126 | '--configure_ds', 127 | dest='configure_ds', action='store_true', 128 | help='Provide this option to configure the DS peripheral.') 129 | 130 | parser.add_argument( 131 | '--skip_flash', 132 | dest='skip_flash', action='store_true', 133 | help='Provide this option to skip flashing the' 134 | ' esp_secure_cert partition at the value' 135 | ' provided to sec_cert_part_offset option') 136 | 137 | parser.add_argument( 138 | '--efuse_key_id', 139 | dest='efuse_key_id', type=int, choices=range(0, 6), 140 | metavar='[key_id] ', 141 | default=1, 142 | help='Provide the efuse key_id which ' 143 | 'contains/will contain HMAC_KEY, default is 1') 144 | 145 | parser.add_argument( 146 | '--efuse_key_file', 147 | help='eFuse key file which contains the ' 148 | 'key that shall be burned in ' 149 | 'the eFuse (e.g. HMAC key, ECDSA key)', 150 | metavar='[/path/to/efuse key file]') 151 | 152 | parser.add_argument( 153 | '--port', '-p', 154 | dest='port', 155 | metavar='[port]', 156 | required=True, 157 | help='UART com port to which the ESP device is connected') 158 | 159 | parser.add_argument( 160 | '--keep_ds_data_on_host', '-keep_ds_data', 161 | dest='keep_ds_data', action='store_true', 162 | help='Keep encrypted private key data and key ' 163 | 'on host machine for testing purpose') 164 | 165 | parser.add_argument( 166 | '--sec_cert_part_offset', 167 | dest='sec_cert_part_offset', 168 | default='0xD000', 169 | help='The flash offset of esp_secure_cert partition' 170 | ' Hex value must be given e.g. 0xD000') 171 | 172 | parser.add_argument( 173 | '--priv_key_algo', 174 | help='Signing algorithm used by the private key ' 175 | ', e.g. RSA 2048, ECDSA 256', 176 | nargs=2, required=True, 177 | metavar='[sign algorithm, key size]') 178 | 179 | args = parser.parse_args() 180 | 181 | idf_target = args.target_chip 182 | if idf_target not in supported_targets: 183 | if idf_target is not None: 184 | print('ERROR: The script does not support ' 185 | 'the target {}'.format(idf_target)) 186 | sys.exit(-1) 187 | idf_target = str(idf_target) 188 | 189 | if args.summary is not False: 190 | log_efuse_summary(idf_target, args.port) 191 | sys.exit(0) 192 | 193 | if (os.path.exists(args.privkey) is False): 194 | print('ERROR: The provided private key file does not exist') 195 | sys.exit(-1) 196 | 197 | if (os.path.exists(args.device_cert) is False): 198 | print('ERROR: The provided client cert file does not exist') 199 | sys.exit(-1) 200 | 201 | if (os.path.exists(esp_secure_cert_data_dir) is False): 202 | os.makedirs(esp_secure_cert_data_dir) 203 | 204 | # Provide CA cert path only if it exists 205 | ca_cert = None 206 | if (os.path.exists(args.ca_cert) is True): 207 | ca_cert = os.path.abspath(args.ca_cert) 208 | 209 | c = None 210 | iv = None 211 | key_size = None 212 | 213 | if args.configure_ds is not False: 214 | if args.priv_key_algo[0] == 'RSA': 215 | efuse_key_file = args.efuse_key_file 216 | hmac_key = configure_ds.configure_efuse_for_rsa(idf_target, args.port, hmac_key_file, efuse_key_file, args.priv_key_algo[1], args.privkey, args.priv_key_pass, args.efuse_key_id) 217 | # Calculate the encrypted private key data along 218 | # with all other parameters 219 | c, iv, key_size = configure_ds.calculate_rsa_ds_params(args.privkey, # type: ignore # noqa: E501 220 | args.priv_key_pass, # type: ignore # noqa: E501 221 | hmac_key, 222 | idf_target) 223 | elif args.priv_key_algo[0] == 'ECDSA': 224 | efuse_key_file = args.efuse_key_file 225 | configure_ds.configure_efuse_for_ecdsa(idf_target, args.port, ecdsa_key_file, efuse_key_file, esp_secure_cert_data_dir, args.priv_key_algo[1], args.privkey, args.priv_key_pass, args.efuse_key_id) 226 | else: 227 | raise ValueError('Invalid priv key algorithm ' 228 | f'{args.priv_key_algo[0]}') 229 | 230 | else: 231 | print('--configure_ds option not set. ' 232 | 'Configuring without use of DS peripheral.') 233 | print('WARNING: Not Secure.\n' 234 | 'the private shall be stored as plaintext') 235 | 236 | if args.sec_cert_type == 'cust_flash_tlv': 237 | key_type = tlv_format.tlv_priv_key_type_t.ESP_SECURE_CERT_DEFAULT_FORMAT_KEY # type: ignore # noqa: E501 238 | tlv_priv_key = tlv_format.tlv_priv_key_t(key_type, 239 | args.privkey, 240 | args.priv_key_pass) 241 | 242 | if args.configure_ds is not False: 243 | if args.priv_key_algo[0] == 'RSA': 244 | tlv_priv_key.key_type = tlv_format.tlv_priv_key_type_t.ESP_SECURE_CERT_RSA_DS_PERIPHERAL_KEY # type: ignore # noqa: E501 245 | tlv_priv_key.ciphertext = c 246 | tlv_priv_key.iv = iv 247 | tlv_priv_key.efuse_key_id = args.efuse_key_id 248 | tlv_priv_key.priv_key_len = key_size 249 | 250 | tlv_format.generate_partition_ds(tlv_priv_key, 251 | args.device_cert, 252 | ca_cert, idf_target, 253 | bin_filename) 254 | if args.priv_key_algo[0] == 'ECDSA': 255 | tlv_priv_key.key_type = tlv_format.tlv_priv_key_type_t.ESP_SECURE_CERT_ECDSA_PERIPHERAL_KEY # type: ignore # noqa: E501 256 | print('Generating ECDSA partition') 257 | tlv_priv_key.efuse_key_id = args.efuse_key_id 258 | priv_key_len = int(args.priv_key_algo[1], 10) 259 | tlv_priv_key.priv_key_len = priv_key_len 260 | tlv_format.generate_partition_ds(tlv_priv_key, 261 | args.device_cert, 262 | ca_cert, idf_target, 263 | bin_filename) 264 | else: 265 | tlv_format.generate_partition_no_ds(tlv_priv_key, 266 | args.device_cert, 267 | ca_cert, idf_target, 268 | bin_filename) 269 | elif args.sec_cert_type == 'cust_flash': 270 | if args.configure_ds is not False: 271 | custflash_format.generate_partition_ds(c, iv, args.efuse_key_id, 272 | key_size, args.device_cert, 273 | ca_cert, idf_target, 274 | bin_filename) 275 | else: 276 | custflash_format.generate_partition_no_ds(args.device_cert, 277 | ca_cert, 278 | args.privkey, 279 | args.priv_key_pass, 280 | idf_target, bin_filename) 281 | elif args.sec_cert_type == 'nvs': 282 | # Generate csv file for the DS data and generate an NVS partition. 283 | if args.configure_ds is not False: 284 | nvs_format.generate_csv_file_ds(c, iv, args.efuse_key_id, 285 | key_size, args.device_cert, 286 | ca_cert, csv_filename) 287 | else: 288 | nvs_format.generate_csv_file_no_ds(args.device_cert, ca_cert, 289 | args.privkey, 290 | args.priv_key_pass, 291 | csv_filename) 292 | nvs_format.generate_partition(csv_filename, bin_filename) 293 | 294 | if args.skip_flash is False: 295 | flash_esp_secure_cert_partition(idf_target, 296 | args.port, 297 | args.sec_cert_part_offset, 298 | bin_filename) 299 | 300 | cleanup(args) 301 | 302 | 303 | if __name__ == '__main__': 304 | main() 305 | -------------------------------------------------------------------------------- /tools/esp_secure_cert/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp_secure_cert_mgr/8fa6f109a3cda6f52194d0f302eab7868a696285/tools/esp_secure_cert/__init__.py -------------------------------------------------------------------------------- /tools/esp_secure_cert/configure_ds.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import hmac 3 | import os 4 | import struct 5 | import sys 6 | from cryptography.hazmat.backends import default_backend 7 | from cryptography.hazmat.primitives.asymmetric import rsa, ec 8 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 9 | from cryptography.utils import int_to_bytes 10 | from esp_secure_cert.esp_secure_cert_helper import load_private_key 11 | from esp_secure_cert.efuse_helper import ( 12 | log_efuse_summary, 13 | configure_efuse_key_block, 14 | ) 15 | from esp_secure_cert.esp_secure_cert_helper import convert_der_key_to_pem 16 | 17 | supported_targets_rsa_ds = ['esp32s2', 'esp32s3', 'esp32c3', 18 | 'esp32c6', 'esp32h2', 'esp32p4'] 19 | supported_key_size_rsa = {'esp32s2': [1024, 2048, 3072, 4096], 20 | 'esp32c3': [1024, 2048, 3072], 21 | 'esp32s3': [1024, 2048, 3072, 4096], 22 | 'esp32c6': [1024, 2048, 3072], 23 | 'esp32h2': [1024, 2048, 3072], 24 | 'esp32p4': [1024, 2048, 3072, 4096]} 25 | 26 | supported_targets_ecdsa = ['esp32h2', 'esp32p4'] 27 | supported_key_size_ecdsa = {'esp32h2': [256], 28 | 'esp32p4': [256]} 29 | 30 | def number_as_bytes(number, pad_bits=None): 31 | """ 32 | Given a number, format as a little endian array of bytes 33 | """ 34 | result = int_to_bytes(number)[::-1] 35 | while pad_bits is not None and len(result) < (pad_bits // 8): 36 | result += b'\x00' 37 | return result 38 | 39 | 40 | # @return 41 | # c : ciphertext_c 42 | # iv : initialization vector 43 | # key_size : key size of the RSA private key in bytes. 44 | # @input 45 | # privkey : path to the RSA private key 46 | # priv_key_pass : path to the RSA privaete key password 47 | # hmac_key : HMAC key value ( to calculate DS params) 48 | # idf_target : The target chip for the script (e.g. esp32c3) 49 | # @info 50 | # The function calculates the encrypted private key parameters. 51 | # Consult the DS documentation (available for the ESP32-S2) 52 | # in the esp-idf programming guide for more details 53 | # about the variables and calculations. 54 | def calculate_rsa_ds_params(privkey, priv_key_pass, hmac_key, idf_target): 55 | private_key_data = load_private_key(privkey, priv_key_pass) 56 | private_key = private_key_data["key_instance"] 57 | if not isinstance(private_key, rsa.RSAPrivateKey): 58 | print('ERROR: Only RSA private keys are supported') 59 | sys.exit(-1) 60 | if hmac_key is None: 61 | print('ERROR: hmac_key cannot be None') 62 | sys.exit(-2) 63 | 64 | priv_numbers = private_key.private_numbers() 65 | pub_numbers = private_key.public_key().public_numbers() 66 | Y = priv_numbers.d 67 | M = pub_numbers.n 68 | key_size = private_key.key_size 69 | if key_size not in supported_key_size_rsa[idf_target]: 70 | print('ERROR: Private key size {0} not supported for the target {1},' 71 | '\nthe supported key sizes are {2}' 72 | .format(key_size, idf_target, 73 | str(supported_key_size_rsa[idf_target]))) 74 | sys.exit(-1) 75 | 76 | iv = os.urandom(16) 77 | 78 | rr = 1 << (key_size * 2) 79 | rinv = rr % pub_numbers.n 80 | mprime = - rsa._modinv(M, 1 << 32) 81 | mprime &= 0xFFFFFFFF 82 | length = key_size // 32 - 1 83 | 84 | # get max supported key size for the respective target 85 | max_len = max(supported_key_size_rsa[idf_target]) 86 | aes_key = hmac.HMAC(hmac_key, b'\xFF' * 32, hashlib.sha256).digest() 87 | 88 | md_in = number_as_bytes(Y, max_len) + \ 89 | number_as_bytes(M, max_len) + \ 90 | number_as_bytes(rinv, max_len) + \ 91 | struct.pack('= v4.6, Please make sure the ' 231 | 'requirement is satisfied') 232 | raise 233 | finally: 234 | if os.path.exists(temp_ecdsa_key_file): 235 | os.remove(temp_ecdsa_key_file) 236 | -------------------------------------------------------------------------------- /tools/esp_secure_cert/custflash_format.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import struct 3 | import zlib 4 | from esp_secure_cert.esp_secure_cert_helper import load_private_key, load_certificate 5 | from cryptography.hazmat.primitives import serialization 6 | from cryptography.hazmat.backends import default_backend 7 | 8 | # size is calculated as actual size + 16 (offset) 9 | ciphertext_size = {'esp32s2': 1600, 'esp32s3': 1600, 'esp32c3': 1216} 10 | 11 | 12 | # @info 13 | # This function generates the cust_flash partition of 14 | # the encrypted private key parameters when DS is enabled. 15 | def generate_partition_ds(c, iv, hmac_key_id, key_size, 16 | device_cert, ca_cert, 17 | idf_target, op_file): 18 | # Following offsets have been calculated with help of 19 | # esp_secure_cert_config.h 20 | 21 | METADATA_OFFSET = 0 22 | DEV_CERT_OFFSET = METADATA_OFFSET + 64 23 | CA_CERT_OFFSET = DEV_CERT_OFFSET + 2048 24 | CIPHERTEXT_OFFSET = CA_CERT_OFFSET + 4096 25 | IV_OFFSET = CIPHERTEXT_OFFSET + ciphertext_size[idf_target] 26 | 27 | # cust_flash partition is of size 0x6000 i.e. 24576 28 | with open(op_file, 'wb') as output_file: 29 | output_file_data = bytearray(b'\xff' * 24576) 30 | metadata = b'\x00' 31 | dev_cert_data = load_certificate(device_cert) 32 | 33 | # Write dev cert at specific address 34 | if dev_cert_data["encoding"] == serialization.Encoding.PEM.value: 35 | dev_cert = dev_cert_data["bytes"] + b'\0' 36 | else: 37 | dev_cert = dev_cert_data["bytes"] 38 | 39 | output_file_data[DEV_CERT_OFFSET: DEV_CERT_OFFSET 40 | + len(dev_cert)] = dev_cert 41 | # The following line packs the dev_cert_crc 42 | # and dev_cet_len into the metadata in little endian format 43 | # The value `0xffffffff` corresponds to the 44 | # starting value used at the time of calculation 45 | metadata = struct.pack(' dict: 9 | """ 10 | Executes an 'espefuse' command to obtain 11 | eFuse summary in JSON format. 12 | 13 | Args: 14 | idf_target (str): ESP-IDF target to build for. 15 | port (str): Serial port to communicate with the device. 16 | 17 | Returns: 18 | dict: A dictionary containing eFuse summary data in JSON format. 19 | 20 | Raises: 21 | subprocess.CalledProcessError: If the 'espefuse' command exits 22 | with a non-zero status. 23 | json.JSONDecodeError: If the eFuse summary output 24 | cannot be parsed as JSON. 25 | """ 26 | efuse_summary = None 27 | try: 28 | efuse_summary = subprocess.check_output( 29 | f"espefuse.py --chip {idf_target} -p {port} summary --format json", 30 | shell=True 31 | ) 32 | except subprocess.CalledProcessError as e: 33 | print(e.output.decode("utf-8")) 34 | sys.exit(-1) 35 | 36 | efuse_summary = efuse_summary.decode("utf-8") 37 | # Remove everything before actual json data 38 | # from efuse_summary command output. 39 | efuse_summary = efuse_summary[efuse_summary.find("{"):] 40 | try: 41 | efuse_summary_json = json.loads(efuse_summary) 42 | return efuse_summary_json 43 | except json.JSONDecodeError: 44 | raise json.JSONDecodeError("Failed to parse the " 45 | "eFuse summary JSON output") 46 | 47 | 48 | def log_efuse_summary(idf_target: str, port: str) -> None: 49 | """ 50 | Prints the efuse summary on console by executing the `espefuse.py` script. 51 | 52 | Args: 53 | idf_target (str): IDF target chip. 54 | port (str): Serial port to use for communication with the chip. 55 | 56 | Returns: 57 | None 58 | 59 | Raises: 60 | OSError: If there is an issue executing the `espefuse.py` script. 61 | """ 62 | try: 63 | os.system(f"espefuse.py --chip {idf_target} -p {port} summary") 64 | except OSError: 65 | raise OSError("Unable to execute `espefuse.py` script") 66 | 67 | 68 | def efuse_burn_key(idf_target: str, port: str, 69 | efuse_key_file: str, efuse_key_id: int, 70 | efuse_purpose: str): 71 | """ 72 | Burns a key to the efuse using the "espefuse.py" script. 73 | 74 | Args: 75 | idf_target (str): Target chip of the ESP-IDF build. 76 | port (str): Serial port to use. 77 | efuse_key_file (str): Path to the key file. 78 | efuse_key_id (int): The eFuse key id to use for burning the key. 79 | efuse_purpose (str): The purpose to be set for the eFuse key block 80 | 81 | Raises: 82 | FileNotFoundError: If the key file cannot be found or read. 83 | """ 84 | # In case of a development (default) usecase 85 | # we dont enable the read protection. 86 | key_block_status = '--no-read-protect --show-sensitive-info' 87 | 88 | print('WARNING:Efuse key block shall not be read ' 89 | 'protected in development mode (default)\n') 90 | 91 | if not os.path.isfile(efuse_key_file): 92 | raise FileNotFoundError(f"Key file not found: {efuse_key_file}") 93 | try: 94 | op = os.system(f'espefuse.py --chip {idf_target} -p {port} burn_key ' 95 | f'BLOCK_KEY{efuse_key_id} {efuse_key_file} ' 96 | f'{efuse_purpose} {key_block_status}') 97 | except OSError: 98 | print('Failed to burn the eFuse key') 99 | raise 100 | 101 | if (op != 0): 102 | raise RuntimeError('Failed to burn efuse key') 103 | 104 | 105 | def configure_efuse_key_block(idf_target: str, port: str, 106 | efuse_key_file: str, efuse_key_id: int, 107 | efuse_purpose: str) -> Union[bytes, None]: 108 | """ 109 | Configures the provided efuse key_block. 110 | 111 | If the provided efuse key_block is empty the function burns the key 112 | read from the keyfile into the efuse 113 | If the key_block already contains a key the function reads 114 | the key from the efuse key_block and returns the key read 115 | 116 | Args: 117 | idf_target (str): Target chip of the ESP-IDF build. 118 | port (str): Serial port to use. 119 | efuse_key_file (str): Path to the key file. 120 | efuse_key_id (int): The eFuse key id to use for burning the key. 121 | efuse_purpose (str): The purpose to be set for the eFuse key block. 122 | 123 | Returns: 124 | bytes: 256-bit key written in the given 125 | key_block (efuse_key_id) if successful. 126 | If key is already burned and is readable 127 | then this API returns the same key 128 | None: If the operation fails. 129 | """ 130 | efuse_summary_json = get_efuse_summary_json(idf_target, port) 131 | key_blk = 'BLOCK_KEY' + str(efuse_key_id) 132 | key_purpose = 'KEY_PURPOSE_' + str(efuse_key_id) 133 | 134 | kb_writeable = efuse_summary_json[key_blk]['writeable'] 135 | kb_readable = efuse_summary_json[key_blk]['readable'] 136 | efuse_key_read = None 137 | 138 | # If the efuse key block is writable (empty) then generate and write 139 | # the new efuse key and check again 140 | # If the efuse key block is not writable (already contains a key) 141 | # then check if it is readable 142 | if kb_writeable is True: 143 | print(f'Provided key block (KEY BLOCK {efuse_key_id}) is writable\n' 144 | f'Generating a new key and burning it in the efuse..\n') 145 | 146 | if not os.path.exists(efuse_key_file): 147 | raise FileNotFoundError('Key file not present') 148 | 149 | # Burn efuse key 150 | efuse_burn_key(idf_target, port, efuse_key_file, 151 | efuse_key_id, efuse_purpose) 152 | 153 | new_efuse_summary_json = get_efuse_summary_json(idf_target, port) 154 | 155 | if (new_efuse_summary_json[key_purpose]['value'] 156 | != efuse_purpose): 157 | raise RuntimeError(f'ERROR: Failed to verify the key purpose ' 158 | f'of the key block{efuse_key_id})') 159 | else: 160 | # If the efuse key block is readable, then read the key from 161 | # efuse block and provide it as the return argument 162 | # If the efuse key block is not readable or it has key 163 | # purpose set to a different value than given efuse_purpose 164 | # then we cannot use it for DS operation 165 | if kb_readable is True: 166 | if (efuse_summary_json[key_purpose]['value'] == 167 | efuse_purpose): 168 | efuse_key_read = efuse_summary_json[key_blk]['value'] 169 | efuse_key_read = bytes.fromhex(efuse_key_read) 170 | if (efuse_purpose == 'ECDSA_KEY'): 171 | 172 | # Convert hex value to bytes object 173 | original_bytes = efuse_key_read 174 | # Reverse the byte order from little endian to big endian 175 | reversed_bytes = original_bytes[::-1] 176 | reversed_hex_value = reversed_bytes.hex() 177 | reversed_number = int(reversed_hex_value, 16) 178 | key = load_private_key(efuse_key_file, None) 179 | private_value = key["key_instance"].private_numbers().private_value # type: ignore # noqa: E501 180 | if (reversed_number != private_value): 181 | raise RuntimeError('The private key given does not ' 182 | 'match with the one burned in the ' 183 | 'efuse, Please burn the key in a ' 184 | 'different key block') 185 | 186 | print('Using the same ECDSA key burned ' 187 | f'in the efuse {key_blk}') 188 | 189 | if (efuse_purpose == 'HMAC_DOWN_DIGITAL_SIGNATURE' 190 | or efuse_purpose == 'HMAC_UP'): 191 | 192 | with open(efuse_key_file, 'wb') as hmac_key_file: 193 | hmac_key_file.write(efuse_key_read) 194 | 195 | print('Using the same hmac key burned ' 196 | f'in efuse {key_blk}') 197 | 198 | else: 199 | print(f'ERROR: Provided efuse key block' 200 | f'((KEY BLOCK {efuse_key_id})) ' 201 | f'contains a key with key purpose different ' 202 | f'than {efuse_purpose}, ' 203 | f'\nplease execute the script again with ' 204 | f'a different value of the efuse key id.') 205 | raise RuntimeError('ERROR: key block already used') 206 | else: 207 | print(f'ERROR: Provided efuse key block (KEY BLOCK {efuse_key_id})' 208 | f' is not readable and writeable, ' 209 | f'\nplease execute the script again ' 210 | f'with a different value of the efuse key id.') 211 | raise RuntimeError('ERROR: Key block already used') 212 | -------------------------------------------------------------------------------- /tools/esp_secure_cert/esp_secure_cert_helper.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from cryptography.hazmat.primitives import serialization 3 | from cryptography.hazmat.backends import default_backend 4 | from cryptography.x509 import ( 5 | load_pem_x509_certificate, 6 | load_der_x509_certificate 7 | ) 8 | 9 | 10 | def load_private_key(key_file_path: str, 11 | password: str = None) -> Dict[str, str]: 12 | """ 13 | Load a private key from a file in either PEM or DER format. 14 | 15 | Args: 16 | key_file_path (str): Path to the private key file. 17 | password (str): Password to decrypt the private key, 18 | if it is encrypted. 19 | Returns: 20 | Dict[str, str]: A dictionary with the `"encoding"` and `"bytes"` keys. 21 | The `"encoding"` key holds a value 22 | of type `str` (a member of the `serialization.Encoding` enum) 23 | and the `"bytes"` key holds a value of type `bytes`. 24 | 25 | Raises: 26 | FileNotFoundError: If the private key file cannot be found or read. 27 | ValueError: If the private key file is not in PEM or DER format. 28 | 29 | """ 30 | result = {} 31 | 32 | try: 33 | with open(key_file_path, "rb") as key_file: 34 | key = key_file.read() 35 | except FileNotFoundError: 36 | raise FileNotFoundError(f"Key file not found: {key_file_path}") 37 | 38 | try: 39 | # Attempt to load the key as a PEM-encoded private key 40 | private_key = serialization.load_pem_private_key( 41 | key, 42 | password=password, 43 | backend=default_backend()) 44 | 45 | result["encoding"] = serialization.Encoding.PEM.value 46 | key_encoding = serialization.Encoding.PEM 47 | key_enc_alg = serialization.NoEncryption() 48 | priv_key_format = serialization.PrivateFormat.TraditionalOpenSSL 49 | 50 | result["bytes"] = private_key.private_bytes( 51 | encoding=key_encoding, 52 | format=priv_key_format, 53 | encryption_algorithm=key_enc_alg 54 | ) 55 | result["key_instance"] = private_key 56 | return result 57 | except ValueError: 58 | pass 59 | 60 | try: 61 | private_key = serialization.load_der_private_key( 62 | key, 63 | password=password, 64 | backend=default_backend() 65 | ) 66 | result["encoding"] = serialization.Encoding.DER.value 67 | key_encoding = serialization.Encoding.DER 68 | priv_key_format = serialization.PrivateFormat.TraditionalOpenSSL 69 | key_enc_alg = serialization.NoEncryption() 70 | result["bytes"] = private_key.private_bytes( 71 | encoding=key_encoding, 72 | format=priv_key_format, 73 | encryption_algorithm=key_enc_alg 74 | ) 75 | result["key_instance"] = private_key 76 | return result 77 | except ValueError: 78 | raise ValueError("Unsupported key encoding format," 79 | " Please provide PEM or DER encoded key") 80 | 81 | 82 | def convert_der_key_to_pem(key_file_path: str, password: str = None) -> bytes: 83 | """ 84 | Convert a key from DER format to PEM format, or return the PEM key as-is. 85 | """ 86 | with open(key_file_path, 'rb') as key_file: 87 | key_data = key_file.read() 88 | 89 | try: 90 | # First, try to load the key as a PEM-encoded private key 91 | private_key = serialization.load_pem_private_key( 92 | key_data, 93 | password=password, 94 | backend=default_backend() 95 | ) 96 | # If successful, return the original PEM data 97 | return key_data 98 | except ValueError: 99 | pass # If it fails, it might be a DER key, so continue 100 | 101 | try: 102 | # Attempt to load the key as a DER-encoded private key 103 | private_key = serialization.load_der_private_key( 104 | key_data, 105 | password=password, 106 | backend=default_backend() 107 | ) 108 | except ValueError: 109 | raise ValueError("Unsupported key encoding format. " 110 | "Please provide a PEM or DER encoded key.") 111 | 112 | # Convert the DER key to PEM format 113 | pem_key = private_key.private_bytes( 114 | encoding=serialization.Encoding.PEM, 115 | format=serialization.PrivateFormat.TraditionalOpenSSL, 116 | encryption_algorithm=serialization.NoEncryption() 117 | ) 118 | 119 | return pem_key 120 | 121 | 122 | def load_certificate(cert_file_path: str) -> Dict[str, str]: 123 | """ 124 | Load a certificate from a file in either PEM or DER format. 125 | 126 | Args: 127 | cert_file_path (str): The path to the certificate file. 128 | 129 | Returns: 130 | Dict[str, str]: A dictionary with the `"encoding"` and `"bytes"` keys. 131 | The `"encoding"` key holds a value of 132 | type `str` (a member of the `serialization.Encoding enum) 133 | and the `"bytes"` key holds a value of type `bytes`. 134 | 135 | Raises: 136 | FileNotFoundError: If the certificate file cannot be found or read. 137 | ValueError: If the certificate file is not in PEM or DER format. 138 | """ 139 | result = {} 140 | 141 | try: 142 | with open(cert_file_path, "rb") as cert_file: 143 | cert_data = cert_file.read() 144 | except FileNotFoundError: 145 | raise FileNotFoundError(f"Cert file not found: {cert_file_path}") 146 | 147 | try: 148 | cert = load_pem_x509_certificate(cert_data, backend=default_backend()) 149 | result["encoding"] = serialization.Encoding.PEM.value 150 | cert_encoding = serialization.Encoding.PEM 151 | result["bytes"] = cert.public_bytes(encoding=cert_encoding) 152 | result["cert_instance"] = cert 153 | return result 154 | except ValueError: 155 | pass 156 | 157 | try: 158 | cert = load_der_x509_certificate(cert_data, backend=default_backend()) 159 | result["encoding"] = serialization.Encoding.DER.value 160 | cert_encoding = serialization.Encoding.DER 161 | result["bytes"] = cert.public_bytes(encoding=cert_encoding) 162 | result["cert_instance"] = cert 163 | return result 164 | except ValueError: 165 | raise ValueError("Unsupported certificate encoding format," 166 | "Please provide PEM or DER encoded certificate") 167 | -------------------------------------------------------------------------------- /tools/esp_secure_cert/nvs_format.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import esp_idf_nvs_partition_gen.nvs_partition_gen as nvs_gen 4 | 5 | # @info 6 | # Generate a custom csv file of encrypted private key parameters 7 | # when DS is enabled. 8 | # The csv file is required by the nvs_partition_generator 9 | # utility to create the nvs partition. 10 | def generate_csv_file_ds(c, iv, hmac_key_id, key_size, 11 | device_cert, ca_cert, csv_file): 12 | 13 | with open(csv_file, 'wt', encoding='utf8') as f: 14 | f.write('# This is a generated csv file containing ' 15 | 'required parameters for the Digital Signature operation\n') 16 | f.write('key,type,encoding,value\nesp_secure_cert,namespace,,\n') 17 | 18 | if ca_cert is not None: 19 | f.write('ca_cert,file,string,{}\n'.format(ca_cert)) 20 | f.write('cipher_c,data,hex2bin,{}\n'.format(c.hex())) 21 | f.write('dev_cert,file,string,{}\n'.format(device_cert)) 22 | f.write('rsa_len,data,u16,{}\n'.format(key_size)) 23 | f.write('ds_key_id,data,u8,{}\n'.format(hmac_key_id)) 24 | f.write('iv,data,hex2bin,{}\n'.format(iv.hex())) 25 | 26 | 27 | # @info 28 | # Generate a custom csv file of encrypted private key parameters 29 | # when DS is disabled. 30 | # The csv file is required by the nvs_partition_generator utility 31 | # to create the nvs partition. 32 | def generate_csv_file_no_ds(device_cert, ca_cert, priv_key, 33 | priv_key_pass, csv_file): 34 | 35 | with open(csv_file, 'wt', encoding='utf8') as f: 36 | f.write('# This is a generated csv file containing required ' 37 | 'parameters for the Digital Signature operation\n') 38 | f.write('key,type,encoding,value\nesp_secure_cert,namespace,,\n') 39 | 40 | if ca_cert is not None: 41 | f.write('ca_cert,file,string,{}\n'.format(ca_cert)) 42 | f.write('dev_cert,file,string,{}\n'.format(device_cert)) 43 | 44 | if priv_key_pass is not None: 45 | print('Private key is going to be written' 46 | 'in password encrypted format.') 47 | print('If you want to write ptivate key in plaintext,' 48 | ' Please remove the password') 49 | 50 | f.write('priv_key,file,string,{}\n'.format(priv_key)) 51 | 52 | 53 | class DefineArgs(object): 54 | def __init__(self, attributes): 55 | for key, value in attributes.items(): 56 | self.__setattr__(key, value) 57 | 58 | 59 | # @info 60 | # This function uses the nvs_partition_generater utility 61 | # to generate the nvs partition of the encrypted private key parameters. 62 | def generate_partition(input_filename, output_filename): 63 | 64 | nvs_args = DefineArgs({ 65 | 'input': input_filename, 66 | 'outdir': os.getcwd(), 67 | 'output': output_filename, 68 | 'size': hex(0x3000), 69 | 'version': 2, 70 | 'keyfile': None, 71 | }) 72 | 73 | nvs_gen.generate(nvs_args, is_encr_enabled=False, encr_key=None) 74 | -------------------------------------------------------------------------------- /tools/esp_secure_cert/tlv_format.py: -------------------------------------------------------------------------------- 1 | import enum 2 | import struct 3 | import zlib 4 | from esp_secure_cert.esp_secure_cert_helper import ( 5 | load_private_key, 6 | load_certificate 7 | ) 8 | from cryptography.hazmat.primitives import serialization 9 | 10 | 11 | class tlv_type_t(enum.IntEnum): 12 | CA_CERT = 0 13 | DEV_CERT = 1 14 | PRIV_KEY = 2 15 | DS_DATA = 3 16 | DS_CONTEXT = 4 17 | ECDSA_KEY_SALT = 5 18 | SEC_CFG = 6 19 | TLV_END = 50 20 | USER_DATA_1 = 51 21 | USER_DATA_2 = 52 22 | USER_DATA_3 = 53 23 | USER_DATA_4 = 54 24 | USER_DATA_5 = 55 25 | 26 | 27 | class tlv_priv_key_type_t(enum.IntEnum): 28 | ESP_SECURE_CERT_INVALID_KEY = -1 29 | ESP_SECURE_CERT_DEFAULT_FORMAT_KEY = 0 30 | ESP_SECURE_CERT_HMAC_ENCRYPTED_KEY = 1 31 | ESP_SECURE_CERT_HMAC_DERIVED_ECDSA_KEY = 2 32 | ESP_SECURE_CERT_ECDSA_PERIPHERAL_KEY = 3 33 | ESP_SECURE_CERT_RSA_DS_PERIPHERAL_KEY = 4 34 | 35 | 36 | class tlv_priv_key_t: 37 | def __init__(self, key_type: tlv_priv_key_type_t, 38 | key_path, key_pass): 39 | self.key_type = key_type 40 | self.key_path = key_path 41 | self.key_pass = key_pass 42 | self.efuse_key_id = -1 43 | self.salt = None 44 | self.ciphertext = None 45 | self.iv = None 46 | self.priv_key_len = 0 47 | self.priv_key_format = None 48 | 49 | 50 | # This is the minimum required flash address alignment to write 51 | # to an encrypted partition on esp device 52 | MIN_ALIGNMENT_REQUIRED = 16 53 | 54 | 55 | def _get_tlv_header_key_info_byte(key_type): 56 | ''' 57 | Set Nth bit to one in the given number 58 | @input 59 | flags Input Number 60 | N The number of bit to be set to 1 61 | ''' 62 | def _set_bit(flags, N): 63 | return flags | (1 << N) 64 | flags = 0 65 | if key_type == tlv_priv_key_type_t.ESP_SECURE_CERT_HMAC_ENCRYPTED_KEY: 66 | flags = _set_bit(flags, 7) # i.e. 2 << 6 67 | 68 | if key_type == tlv_priv_key_type_t.ESP_SECURE_CERT_HMAC_DERIVED_ECDSA_KEY: 69 | flags = _set_bit(flags, 6) 70 | 71 | if key_type == tlv_priv_key_type_t.ESP_SECURE_CERT_ECDSA_PERIPHERAL_KEY: 72 | flags = _set_bit(flags, 3) 73 | 74 | return hex(flags) 75 | 76 | 77 | def prepare_tlv(tlv_type, tlv_type_info, data, data_len): 78 | # Add the magic at start ( unsigned int ) 79 | tlv_header = struct.pack('=0.1.6,<1.0.0 4 | -------------------------------------------------------------------------------- /tools/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | import sys 6 | 7 | 8 | def get_install_requires(): 9 | with open('requirements.txt') as f: 10 | required = f.read().splitlines() 11 | return required 12 | 13 | 14 | try: 15 | from setuptools import find_packages, setup 16 | except ImportError: 17 | print( 18 | "Package setuptools is missing from your Python installation. " 19 | "Please see the installation section in the esp-secure-cert-tool " 20 | "documentation for instructions on how to install it." 21 | ) 22 | exit(1) 23 | 24 | VERSION = "2.1.5" 25 | 26 | long_description = """ 27 | ==================== 28 | esp-secure-cert-tool 29 | ==================== 30 | The python utility helps to configure and provision the device with 31 | PKI credentials to generate the esp_secure_cert partition. 32 | The utility also configures the DS peripheral on the SoC if available. 33 | 34 | The `esp-secure-cert-tool` utility is `hosted on 35 | github `_. 37 | 38 | Documentation 39 | ------------- 40 | Visit online `esp-secure-cert-tool 41 | documentation `_ \ 43 | or run ``configure_esp_secure_cert.py -h``. 44 | 45 | License 46 | ------- 47 | The License for the project can be found 48 | `here `_ 50 | """ 51 | 52 | setup( 53 | name="esp-secure-cert-tool", 54 | version=VERSION, 55 | description="A python utility which helps to configure and provision" 56 | "the ESP platform with PKI credentials " 57 | "into the esp_secure_cert partition", 58 | long_description=long_description, 59 | long_description_content_type='text/x-rst', 60 | url="https://github.com/espressif/" 61 | "esp_secure_cert_mgr/blob/main/tools", 62 | project_urls={ 63 | "Documentation": "https://github.com/espressif/" 64 | "esp_secure_cert_mgr/blob/main/tools/README.md", 65 | "Source": "https://github.com/espressif/esp_secure_cert_mgr/" 66 | "blob/main/tools/configure_esp_secure_cert.py", 67 | }, 68 | author="Espressif Systems", 69 | author_email="", 70 | license="Apache-2.0", 71 | classifiers=[ 72 | "License :: OSI Approved :: Apache Software License", 73 | "Intended Audience :: Developers", 74 | "Natural Language :: English", 75 | "Operating System :: POSIX", 76 | "Operating System :: Microsoft :: Windows", 77 | "Operating System :: MacOS :: MacOS X", 78 | "Topic :: Software Development :: Embedded Systems", 79 | "Programming Language :: Python :: 3.7", 80 | "Programming Language :: Python :: 3.8", 81 | "Programming Language :: Python :: 3.9", 82 | "Programming Language :: Python :: 3.10", 83 | ], 84 | python_requires=">=3.7", 85 | setup_requires=(["wheel"] if "bdist_wheel" in sys.argv else []), 86 | install_requires=get_install_requires(), 87 | include_package_data=True, 88 | packages=find_packages(), 89 | scripts=["configure_esp_secure_cert.py"], 90 | ) 91 | --------------------------------------------------------------------------------