diff --git a/cmake/build_helpers.cmake b/cmake/build_helpers.cmake new file mode 100644 index 0000000..d216cde --- /dev/null +++ b/cmake/build_helpers.cmake @@ -0,0 +1,189 @@ +# +# Pico Keys SDK build helper functions +# + +function(picokeys_apply_strict_flags) + set(options) + set(oneValueArgs FILTER_REGEX) + set(multiValueArgs SOURCES) + cmake_parse_arguments(PKAS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT PKAS_SOURCES) + return() + endif() + + if (MSVC) + set(PICOKEYS_STRICT_FLAGS + -Wall + -Zc:strictStrings + -WX + ) + else () + set(PICOKEYS_STRICT_FLAGS + -pipe + -funsigned-char + -fstrict-aliasing + -fdiagnostics-color=auto + -Wextra + -Wchar-subscripts + -Wundef + -Wshadow + -Wcast-align + -Wwrite-strings + -Wunused + -Wuninitialized + -Wpointer-arith + -Wredundant-decls + -Winline + -Wformat + -Wformat-security + -Wswitch-enum + -Winit-self + -Wmissing-include-dirs + -Wempty-body + -Wmissing-prototypes + -Wstrict-prototypes + -Wold-style-definition + -Wbad-function-cast + -Wnested-externs + -Wmissing-declarations + -Werror + ) + endif() + + foreach(src IN LISTS PKAS_SOURCES) + if(PKAS_FILTER_REGEX) + if(NOT src MATCHES "${PKAS_FILTER_REGEX}") + continue() + endif() + endif() + set_property(SOURCE "${src}" APPEND PROPERTY COMPILE_OPTIONS ${PICOKEYS_STRICT_FLAGS}) + endforeach() +endfunction() + +function(picokeys_configure_host_target) + set(options) + set(oneValueArgs TARGET STRICT_FILTER_REGEX MACOS_APP_BUNDLE_ID MACOS_APP_DEVELOPMENT_TEAM) + set(multiValueArgs SOURCES INCLUDES) + cmake_parse_arguments(PKCHT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT PKCHT_TARGET OR ESP_PLATFORM) + return() + endif() + + target_sources(${PKCHT_TARGET} PUBLIC ${PKCHT_SOURCES}) + target_include_directories(${PKCHT_TARGET} PUBLIC ${PKCHT_INCLUDES}) + target_compile_options(${PKCHT_TARGET} PRIVATE -Wall) + + picokeys_apply_strict_flags( + SOURCES ${PKCHT_SOURCES} + FILTER_REGEX "${PKCHT_STRICT_FILTER_REGEX}" + ) + + if(NOT MSVC) + target_compile_options(${PKCHT_TARGET} PRIVATE -Werror) + + string(FIND ${CMAKE_C_COMPILER} ":" COMPILER_COLON) + if(${COMPILER_COLON} GREATER_EQUAL 0) + target_compile_options(${PKCHT_TARGET} PRIVATE -Wno-error=use-after-free) + endif() + endif() + + if(ENABLE_EMULATION) + if(NOT MSVC) + target_compile_options(${PKCHT_TARGET} PRIVATE -fdata-sections -ffunction-sections) + endif() + + if(APPLE) + target_link_options(${PKCHT_TARGET} PRIVATE -Wl,-dead_strip) + + if(MACOS_APP) + target_link_libraries(${PKCHT_TARGET} PRIVATE + "-framework Security" + "-framework CoreFoundation" + ) + if(CMAKE_GENERATOR STREQUAL "Xcode") + if("${PKCHT_MACOS_APP_DEVELOPMENT_TEAM}" STREQUAL "") + message(FATAL_ERROR "MACOS_APP=1 with Xcode requires MACOS_APP_DEVELOPMENT_TEAM") + endif() + target_compile_options(${PKCHT_TARGET} PRIVATE -Wno-missing-include-dirs) + set_target_properties(${PKCHT_TARGET} PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER "${PKCHT_MACOS_APP_BUNDLE_ID}" + MACOSX_BUNDLE_BUNDLE_NAME "${PKCHT_TARGET}" + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${PKCHT_MACOS_APP_BUNDLE_ID}" + XCODE_ATTRIBUTE_CODE_SIGN_STYLE "Automatic" + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${PKCHT_MACOS_APP_DEVELOPMENT_TEAM}" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Development" + XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/pico_novus.entitlements" + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES" + ) + endif() + endif() + + if(DEBUG_APDU) + target_compile_options(${PKCHT_TARGET} PRIVATE + -fsanitize=address + -g + -O1 + -fno-omit-frame-pointer + ) + target_link_options(${PKCHT_TARGET} PRIVATE + -fsanitize=address + -g + -O1 + -fno-omit-frame-pointer + ) + endif() + endif() + endif() +endfunction() + +function(picokeys_add_macos_app_xcode_driver) + set(options) + set(oneValueArgs TARGET BUNDLE_ID SIGN_IDENTITY DEVELOPMENT_TEAM) + cmake_parse_arguments(PKMXD "${options}" "${oneValueArgs}" "" ${ARGN}) + + if(NOT PKMXD_TARGET) + return() + endif() + if(NOT (APPLE AND ENABLE_EMULATION AND MACOS_APP AND NOT CMAKE_GENERATOR STREQUAL "Xcode" AND NOT MACOS_APP_XCODE_DRIVER)) + return() + endif() + if("${PKMXD_DEVELOPMENT_TEAM}" STREQUAL "") + message(FATAL_ERROR "MACOS_APP=1 requires MACOS_APP_DEVELOPMENT_TEAM") + endif() + + set(MACOS_APP_XCODE_BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build-macos-app-xcode") + set(MACOS_APP_XCODE_FORWARD_ARGS) + get_cmake_property(_cache_vars CACHE_VARIABLES) + foreach(_cv ${_cache_vars}) + if(_cv MATCHES "^(ENABLE_|DEBUG_APDU$|VIDPID$|USB_VID$|USB_PID$|USE_OPENSSL$|CMAKE_BUILD_TYPE$)") + get_property(_cv_type CACHE ${_cv} PROPERTY TYPE) + if(NOT _cv_type STREQUAL "INTERNAL") + set(_cv_val "${${_cv}}") + string(REPLACE ";" "\\;" _cv_val "${_cv_val}") + list(APPEND MACOS_APP_XCODE_FORWARD_ARGS "-D${_cv}:${_cv_type}=${_cv_val}") + endif() + endif() + endforeach() + + add_custom_target(${PKMXD_TARGET}_xcode ALL + COMMAND cmake -S "${CMAKE_CURRENT_SOURCE_DIR}" -B "${MACOS_APP_XCODE_BUILD_DIR}" -G Xcode + -DENABLE_EMULATION=1 + -DMACOS_APP=1 + -DMACOS_APP_XCODE_DRIVER=1 + -DMACOS_APP_BUNDLE_ID="${PKMXD_BUNDLE_ID}" + -DMACOS_APP_SIGN_IDENTITY="${PKMXD_SIGN_IDENTITY}" + -DMACOS_APP_DEVELOPMENT_TEAM="${PKMXD_DEVELOPMENT_TEAM}" + ${MACOS_APP_XCODE_FORWARD_ARGS} + COMMAND /bin/mkdir -p "${MACOS_APP_XCODE_BUILD_DIR}/Debug/include" + COMMAND /bin/mkdir -p "${MACOS_APP_XCODE_BUILD_DIR}/build/pico_novus.build/Debug/DerivedSources-normal/arm64" + COMMAND /bin/mkdir -p "${MACOS_APP_XCODE_BUILD_DIR}/build/pico_novus.build/Debug/DerivedSources/arm64" + COMMAND /bin/mkdir -p "${MACOS_APP_XCODE_BUILD_DIR}/build/pico_novus.build/Debug/DerivedSources" + COMMAND xcodebuild -allowProvisioningUpdates -project "${MACOS_APP_XCODE_BUILD_DIR}/${PKMXD_TARGET}.xcodeproj" -scheme "${PKMXD_TARGET}" -configuration Debug build + COMMENT "Building signed ${PKMXD_TARGET}.app via Xcode" + VERBATIM + ) +endfunction() + diff --git a/config/esp32/components/pico-keys-sdk/CMakeLists.txt b/config/esp32/components/pico-keys-sdk/CMakeLists.txt index 276e524..bbfd3f0 100755 --- a/config/esp32/components/pico-keys-sdk/CMakeLists.txt +++ b/config/esp32/components/pico-keys-sdk/CMakeLists.txt @@ -3,6 +3,7 @@ set(PICOKEYS_SDK_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../..) set(PICOKEYS_INCLUDE_DIRS ${PICOKEYS_SDK_DIR}/src ${PICOKEYS_SDK_DIR}/src/fs + ${PICOKEYS_SDK_DIR}/src/otp ${PICOKEYS_SDK_DIR}/src/rng ${PICOKEYS_SDK_DIR}/src/usb ${PICOKEYS_SDK_DIR}/src/led diff --git a/picokeys_sdk_import.cmake b/picokeys_sdk_import.cmake index 428b0db..e3231a0 100644 --- a/picokeys_sdk_import.cmake +++ b/picokeys_sdk_import.cmake @@ -80,6 +80,7 @@ if(NOT DEFINED ENABLE_EMULATION) set(ENABLE_EMULATION 0) endif() include(${CMAKE_CURRENT_LIST_DIR}/cmake/openssl.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/cmake/build_helpers.cmake) option(ENABLE_DELAYED_BOOT "Enable/disable delayed boot" OFF) configure_bool_option( @@ -233,14 +234,38 @@ if(ENABLE_PQC) ) endif() +if(ESP_PLATFORM) + list(APPEND PICOKEYS_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/otp/otp_esp32.c + ) +elseif(ENABLE_EMULATION) + if(APPLE) + list(APPEND PICOKEYS_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/otp/otp_macos.c + ) + else() + list(APPEND PICOKEYS_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/otp/otp_emulation.c + ) + endif() +elseif(PICO_RP2350) + list(APPEND PICOKEYS_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/otp/otp_rp2350.c + ) +elseif(PICO_RP2040) + list(APPEND PICOKEYS_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/otp/otp_rp2040.c + ) +endif() + list(APPEND PICOKEYS_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/main.c ${CMAKE_CURRENT_LIST_DIR}/src/usb/usb.c ${CMAKE_CURRENT_LIST_DIR}/src/fs/file.c ${CMAKE_CURRENT_LIST_DIR}/src/fs/flash.c ${CMAKE_CURRENT_LIST_DIR}/src/fs/low_flash.c - ${CMAKE_CURRENT_LIST_DIR}/src/fs/otp.c ${CMAKE_CURRENT_LIST_DIR}/src/fs/phy.c + ${CMAKE_CURRENT_LIST_DIR}/src/otp/otp.c ${CMAKE_CURRENT_LIST_DIR}/src/rng/random.c ${CMAKE_CURRENT_LIST_DIR}/src/rng/hwrng.c ${CMAKE_CURRENT_LIST_DIR}/src/eac.c @@ -284,6 +309,7 @@ list(APPEND INCLUDES ${CMAKE_CURRENT_LIST_DIR}/src/fs ${CMAKE_CURRENT_LIST_DIR}/src/rng ${CMAKE_CURRENT_LIST_DIR}/src/led + ${CMAKE_CURRENT_LIST_DIR}/src/otp ${CMAKE_CURRENT_LIST_DIR}/third-party/mbedtls/library ) set(SYSTEM_INCLUDES @@ -427,68 +453,6 @@ function(add_impl_library target) target_compile_definitions(${target} INTERFACE LIB_${TARGET_UPPER}=1) endfunction() -# Apply strict warning flags to a caller-provided source list. -# Usage: -# picokeys_apply_strict_flags(SOURCES ${SOURCES} FILTER_REGEX "/src/fido/") -function(picokeys_apply_strict_flags) - set(options) - set(oneValueArgs FILTER_REGEX) - set(multiValueArgs SOURCES) - cmake_parse_arguments(PKAS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if(NOT PKAS_SOURCES) - return() - endif() - - if (MSVC) - set(PICOKEYS_STRICT_FLAGS - -Wall - -Zc:strictStrings - -WX - ) - else () - set(PICOKEYS_STRICT_FLAGS - -pipe - -funsigned-char - -fstrict-aliasing - -fdiagnostics-color=auto - -Wextra - -Wchar-subscripts - -Wundef - -Wshadow - -Wcast-align - -Wwrite-strings - -Wunused - -Wuninitialized - -Wpointer-arith - -Wredundant-decls - -Winline - -Wformat - -Wformat-security - -Wswitch-enum - -Winit-self - -Wmissing-include-dirs - -Wempty-body - -Wmissing-prototypes - -Wstrict-prototypes - -Wold-style-definition - -Wbad-function-cast - -Wnested-externs - -Wmissing-declarations - -Werror - ) - endif() - - foreach(src IN LISTS PKAS_SOURCES) - if(PKAS_FILTER_REGEX) - if(NOT src MATCHES "${PKAS_FILTER_REGEX}") - continue() - endif() - endif() - set_property(SOURCE "${src}" APPEND PROPERTY COMPILE_OPTIONS ${PICOKEYS_STRICT_FLAGS}) - endforeach() -endfunction() - if(USB_ITF_HID) list(APPEND PICOKEYS_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/usb/hid/hid.c @@ -507,7 +471,6 @@ if(USB_ITF_CCID) ) endif() - if(NOT MSVC) add_compile_options("-fmacro-prefix-map=${CMAKE_CURRENT_LIST_DIR}/=") endif() @@ -546,9 +509,8 @@ else() endif() if(MSVC) - set( - CMAKE_C_FLAGS - "${CMAKE_C_FLAGS} -wd4820 -wd4255 -wd5045 -wd4706 -wd4061 -wd5105 -wd4141 -wd4200" + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} + -wd4820 -wd4255 -wd5045 -wd4706 -wd4061 -wd5105 -wd4141 -wd4200" ) add_compile_definitions( diff --git a/src/fs/otp.c b/src/fs/otp.c deleted file mode 100644 index a507cfa..0000000 --- a/src/fs/otp.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). - * Copyright (c) 2022 Pol Henarejos. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - - #include -#include "picokeys.h" -#include "otp.h" - -#ifdef PICO_RP2350 -#include "pico/bootrom.h" -#include "hardware/structs/otp.h" -#include "hardware/regs/otp_data.h" -#endif -#include "random.h" -#include "mbedtls/ecdsa.h" -#ifndef _MSC_VER -#include -#endif - -#ifdef PICO_RP2350 - -static bool is_empty_buffer(const uint8_t *buffer, uint16_t buffer_len) { - for (int i = 0; i < buffer_len; i++) { - if (buffer[i] != 0x00) { - return false; - } - } - return true; -} - -static int otp_write_data_mode(uint16_t row, const uint8_t *data, uint16_t len, bool is_ecc) { - otp_cmd_t cmd = { .flags = row | (is_ecc ? OTP_CMD_ECC_BITS : 0) | OTP_CMD_WRITE_BITS }; - uint32_t ret = rom_func_otp_access((uint8_t *)data, len, cmd); - if (ret) { - printf("OTP Write failed with error: %ld\n", ret); - } - return ret; -} - -int otp_write_data(uint16_t row, const uint8_t *data, uint16_t len) { - return otp_write_data_mode(row, data, len, true); -} - -int otp_write_data_raw(uint16_t row, const uint8_t *data, uint16_t len) { - return otp_write_data_mode(row, data, len, false); -} - -const uint8_t* otp_buffer(uint16_t row) { - volatile uint32_t *p = ((uint32_t *)(OTP_DATA_BASE + (row*2))); - return (const uint8_t *)p; -} - -const uint8_t* otp_buffer_raw(uint16_t row) { - volatile uint32_t *p = ((uint32_t *)(OTP_DATA_RAW_BASE + (row*4))); - return (const uint8_t *)p; -} - -bool is_empty_otp_buffer(uint16_t row, uint16_t len) { - return is_empty_buffer(otp_buffer_raw(row), len * 2); -} - -static bool is_otp_locked_page(uint8_t page) { - volatile uint32_t *p = ((uint32_t *)(OTP_DATA_BASE + ((OTP_DATA_PAGE0_LOCK0_ROW + page*2)*2))); - return ((p[0] & 0xFFFF0000) == 0x3C3C0000 && (p[1] & 0xFF) == 0x3C); -} - -static void otp_lock_page(uint8_t page) { - if (!is_otp_locked_page(page)) { - alignas(4) uint32_t value = 0x3c3c3c; - otp_write_data_raw(OTP_DATA_PAGE0_LOCK0_ROW + page*2 + 1, (uint8_t *)&value, sizeof(value)); - } - - otp_hw->sw_lock[page] = 0b1100; -} -#endif - -#ifdef ESP_PLATFORM - -uint8_t _otp_key_1[32] = {0}; -uint8_t _otp_key_2[32] = {0}; - -static const uint8_t esp_secure_boot_digest[32] = { - 0x0c, 0x1e, 0xce, 0xf3, 0xb4, 0x8f, 0x4a, 0x81, - 0x45, 0x6c, 0x85, 0x39, 0x15, 0xcc, 0x05, 0x36, - 0xbe, 0x23, 0x24, 0xee, 0xac, 0x8e, 0x3b, 0xb5, - 0x77, 0x6f, 0x2d, 0xb9, 0x62, 0x38, 0x75, 0x6a -}; - -static esp_efuse_purpose_t esp_secure_boot_purpose(uint8_t digest_idx) { - switch (digest_idx) { - case 0: return ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0; - case 1: return ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1; - case 2: return ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2; - default: return ESP_EFUSE_KEY_PURPOSE_MAX; - } -} - -static bool esp_find_secure_boot_block(uint8_t digest_idx, esp_efuse_block_t *out_block) { - esp_efuse_purpose_t purpose = esp_secure_boot_purpose(digest_idx); - if (purpose == ESP_EFUSE_KEY_PURPOSE_MAX) { - return false; - } - for (esp_efuse_block_t blk = EFUSE_BLK_KEY0; blk < EFUSE_BLK_KEY_MAX; blk++) { - if (esp_efuse_get_key_purpose(blk) == purpose) { - if (out_block) { - *out_block = blk; - } - return true; - } - } - return false; -} - -static esp_err_t esp_provision_secure_boot_digest(uint8_t digest_idx, esp_efuse_block_t *out_block) { - esp_efuse_purpose_t purpose = esp_secure_boot_purpose(digest_idx); - if (purpose == ESP_EFUSE_KEY_PURPOSE_MAX) { - return ESP_ERR_INVALID_ARG; - } - - esp_efuse_block_t block = EFUSE_BLK_KEY_MAX; - if (esp_find_secure_boot_block(digest_idx, &block)) { - const esp_efuse_desc_t **key_desc = esp_efuse_get_key(block); - if (!key_desc) { - return ESP_FAIL; - } - uint8_t existing[32] = {0}; - esp_err_t err = esp_efuse_read_field_blob(key_desc, existing, sizeof(existing) * 8); - if (err != ESP_OK) { - return err; - } - if (memcmp(existing, esp_secure_boot_digest, sizeof(existing)) != 0) { - return ESP_ERR_INVALID_STATE; - } - if (out_block) { - *out_block = block; - } - return ESP_OK; - } - - block = esp_efuse_find_unused_key_block(); - if (block == EFUSE_BLK_KEY_MAX) { - return ESP_ERR_NOT_FOUND; - } - - esp_err_t err = esp_efuse_batch_write_begin(); - if (err != ESP_OK) { - return err; - } - - err = esp_efuse_set_key_purpose(block, purpose); - if (err == ESP_OK) { - const esp_efuse_desc_t **key_desc = esp_efuse_get_key(block); - if (!key_desc) { - err = ESP_FAIL; - } else { - err = esp_efuse_write_field_blob(key_desc, esp_secure_boot_digest, sizeof(esp_secure_boot_digest) * 8); - } - } - - if (err == ESP_OK) { - err = esp_efuse_batch_write_commit(); - } else { - esp_efuse_batch_write_cancel(); - } - - if (err == ESP_OK && out_block) { - *out_block = block; - } - return err; -} - -static esp_err_t esp_disable_debug_interfaces(void) { - esp_err_t err = ESP_OK; - -#ifdef ESP_EFUSE_SOFT_DIS_JTAG - err = esp_efuse_write_field_bit(ESP_EFUSE_SOFT_DIS_JTAG); - if (err != ESP_OK) { - return err; - } -#endif - -#ifdef ESP_EFUSE_HARD_DIS_JTAG - err = esp_efuse_write_field_bit(ESP_EFUSE_HARD_DIS_JTAG); - if (err != ESP_OK) { - return err; - } -#endif - -#ifdef ESP_EFUSE_DIS_USB_JTAG - err = esp_efuse_write_field_bit(ESP_EFUSE_DIS_USB_JTAG); - if (err != ESP_OK) { - return err; - } -#endif - -#ifdef ESP_EFUSE_DIS_USB_SERIAL_JTAG - err = esp_efuse_write_field_bit(ESP_EFUSE_DIS_USB_SERIAL_JTAG); - if (err != ESP_OK) { - return err; - } -#endif - -#ifdef ESP_EFUSE_DIS_PAD_JTAG - err = esp_efuse_write_field_bit(ESP_EFUSE_DIS_PAD_JTAG); - if (err != ESP_OK) { - return err; - } -#endif - - return err; -} - -esp_err_t read_key_from_efuse(esp_efuse_block_t block, uint8_t *key, size_t key_len) { - const esp_efuse_desc_t **key_desc = esp_efuse_get_key(block); - - if (!key_desc) { - return ESP_FAIL; - } - - return esp_efuse_read_field_blob(key_desc, key, key_len * 8); -} - -#endif - -const uint8_t *otp_key_1 = NULL; -const uint8_t *otp_key_2 = NULL; - -#ifdef PICO_RP2350 -typedef int otp_ret_t; -#define OTP_WRITE(ROW, DATA, LEN) otp_write_data(ROW, DATA, LEN) -#define OTP_READ(ROW, PTR) do { PTR = otp_buffer(ROW); } while(0) -#define OTP_EMTPY(ROW, LEN) is_empty_otp_buffer(ROW, LEN) -#elif defined(ESP_PLATFORM) -typedef esp_err_t otp_ret_t; -#define OTP_WRITE(ROW, DATA, LEN) esp_efuse_write_key(ROW, ESP_EFUSE_KEY_PURPOSE_USER, DATA, LEN) -#define OTP_READ(ROW, PTR) do { \ - esp_err_t ret = read_key_from_efuse(ROW, _##PTR, sizeof(_##PTR)); \ - if (ret != ESP_OK) { printf("Error reading OTP key 1 [%d]\n", ret); } \ - PTR = _##PTR; } while(0) -#define OTP_EMTPY(ROW, LEN) esp_efuse_key_block_unused(ROW) -#endif - -#ifndef SECURE_BOOT_BOOTKEY_INDEX -#define SECURE_BOOT_BOOTKEY_INDEX 0 -#endif -#ifndef PICOKEYS_REQUIRE_SECURE_BOOT_BEFORE_LOCK -#define PICOKEYS_REQUIRE_SECURE_BOOT_BEFORE_LOCK 1 -#endif - -bool otp_is_secure_boot_enabled(uint8_t *bootkey) { - (void)bootkey; -#ifdef PICO_RP2350 - const uint8_t *crit1 = otp_buffer(OTP_DATA_CRIT1_ROW); - if ((crit1[0] & (1 << OTP_DATA_CRIT1_SECURE_BOOT_ENABLE_LSB)) == 0) { - return false; - } - alignas(2) uint8_t BOOTKEY[32] = { - 0xE1, 0xD1, 0x6B, 0xA7, 0x64, 0xAB, 0xD7, 0x12, - 0xD4, 0xEF, 0x6E, 0x3E, 0xDD, 0x74, 0x4E, 0xD5, - 0x63, 0x8C, 0x26, 0x0B, 0x77, 0x1C, 0xF9, 0x81, - 0x51, 0x11, 0x0B, 0xAF, 0xAC, 0x9B, 0xC8, 0x71 - }; - uint8_t bootkey_idx = 0; - for (; bootkey_idx < 6; bootkey_idx++) { - const uint8_t *bootkey_row = otp_buffer(OTP_DATA_BOOTKEY0_0_ROW + 0x10 * bootkey_idx); - if (memcmp(bootkey_row, BOOTKEY, sizeof(BOOTKEY)) == 0) { - break; - } - } - if (bootkey_idx == 6) { - return false; - } - const uint8_t *boot_flags1 = otp_buffer(OTP_DATA_BOOT_FLAGS1_ROW); - if ((boot_flags1[0] & (1 << (bootkey_idx + OTP_DATA_BOOT_FLAGS1_KEY_VALID_LSB))) == 0) { - return false; - } - if (bootkey) { - *bootkey = bootkey_idx; - } - return true; -#elif defined(ESP_PLATFORM) - if (!esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_EN)) { - return false; - } - - uint8_t preferred = SECURE_BOOT_BOOTKEY_INDEX; - if (preferred <= 2 && esp_find_secure_boot_block(preferred, NULL) - && !esp_efuse_get_digest_revoke(preferred)) { - if (bootkey) { - *bootkey = preferred; - } - return true; - } - - for (uint8_t idx = 0; idx <= 2; idx++) { - if (esp_find_secure_boot_block(idx, NULL) && !esp_efuse_get_digest_revoke(idx)) { - if (bootkey) { - *bootkey = idx; - } - return true; - } - } -#endif - return false; -} - -bool otp_is_secure_boot_locked(void) { - uint8_t bootkey_idx = 0xFF; - if (otp_is_secure_boot_enabled(&bootkey_idx) == false) { - return false; - } -#ifdef PICO_RP2350 - const uint8_t *boot_flags1 = otp_buffer_raw(OTP_DATA_BOOT_FLAGS1_ROW); - if ((boot_flags1[1] & ((OTP_DATA_BOOT_FLAGS1_KEY_INVALID_BITS >> OTP_DATA_BOOT_FLAGS1_KEY_INVALID_LSB) & (~(1 << bootkey_idx)))) != ((OTP_DATA_BOOT_FLAGS1_KEY_INVALID_BITS >> OTP_DATA_BOOT_FLAGS1_KEY_INVALID_LSB) & (~(1 << bootkey_idx)))) { - return false; - } - const uint8_t *crit1 = otp_buffer_raw(OTP_DATA_CRIT1_ROW); - if ((crit1[0] & (1 << OTP_DATA_CRIT1_DEBUG_DISABLE_LSB)) == 0 - || (crit1[0] & (1 << OTP_DATA_CRIT1_GLITCH_DETECTOR_ENABLE_LSB)) == 0 - || ((crit1[0] & (3 << OTP_DATA_CRIT1_GLITCH_DETECTOR_SENS_LSB)) != (3 << OTP_DATA_CRIT1_GLITCH_DETECTOR_SENS_LSB))) { - return false; - } - return bootkey_idx != 0xFF; -#elif defined(ESP_PLATFORM) - for (uint8_t idx = 0; idx <= 2; idx++) { - if (idx == bootkey_idx) { - continue; - } - if (!esp_efuse_get_digest_revoke(idx)) { - return false; - } - } - return true; -#endif - return false; -} - -int otp_enable_secure_boot(uint8_t bootkey, bool secure_lock) { - int ret = 0; -#ifdef PICO_RP2350 - alignas(2) uint8_t BOOTKEY[] = "\xe1\xd1\x6b\xa7\x64\xab\xd7\x12\xd4\xef\x6e\x3e\xdd\x74\x4e\xd5\x63\x8c\x26\xb\x77\x1c\xf9\x81\x51\x11\xb\xaf\xac\x9b\xc8\x71"; - if (is_empty_otp_buffer(OTP_DATA_BOOTKEY0_0_ROW + 0x10*bootkey, 32)) { - PICOKEYS_CHECK(otp_write_data(OTP_DATA_BOOTKEY0_0_ROW + 0x10*bootkey, BOOTKEY, sizeof(BOOTKEY))); - } - - const uint8_t *boot_flags1 = otp_buffer_raw(OTP_DATA_BOOT_FLAGS1_ROW); - alignas(4) uint8_t flagsb1[] = { boot_flags1[0] | (1 << (bootkey + OTP_DATA_BOOT_FLAGS1_KEY_VALID_LSB)), boot_flags1[1], boot_flags1[2], 0x00 }; - if (secure_lock) { - flagsb1[1] |= ((OTP_DATA_BOOT_FLAGS1_KEY_INVALID_BITS >> OTP_DATA_BOOT_FLAGS1_KEY_INVALID_LSB) & (~(1 << bootkey))); - } - - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_BOOT_FLAGS1_ROW, flagsb1, sizeof(flagsb1))); - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_BOOT_FLAGS1_R1_ROW, flagsb1, sizeof(flagsb1))); - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_BOOT_FLAGS1_R2_ROW, flagsb1, sizeof(flagsb1))); - - const uint8_t *crit1 = otp_buffer_raw(OTP_DATA_CRIT1_ROW); - alignas(4) uint8_t flagsc1[] = { crit1[0] | (1 << OTP_DATA_CRIT1_SECURE_BOOT_ENABLE_LSB), crit1[1], crit1[2], 0x00 }; - if (secure_lock) { - flagsc1[0] |= (1 << OTP_DATA_CRIT1_DEBUG_DISABLE_LSB); - flagsc1[0] |= (1 << OTP_DATA_CRIT1_GLITCH_DETECTOR_ENABLE_LSB); - flagsc1[0] |= (3 << OTP_DATA_CRIT1_GLITCH_DETECTOR_SENS_LSB); - } - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_ROW, flagsc1, sizeof(flagsc1))); - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R1_ROW, flagsc1, sizeof(flagsc1))); - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R2_ROW, flagsc1, sizeof(flagsc1))); - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R3_ROW, flagsc1, sizeof(flagsc1))); - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R4_ROW, flagsc1, sizeof(flagsc1))); - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R5_ROW, flagsc1, sizeof(flagsc1))); - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R6_ROW, flagsc1, sizeof(flagsc1))); - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R7_ROW, flagsc1, sizeof(flagsc1))); - - if (secure_lock) { - const uint8_t *page1 = otp_buffer_raw(OTP_DATA_PAGE1_LOCK1_ROW); - uint8_t page1v = page1[0] | (OTP_DATA_PAGE1_LOCK1_LOCK_BL_VALUE_READ_ONLY << OTP_DATA_PAGE1_LOCK1_LOCK_BL_LSB); - alignas(4) uint8_t flagsp1[] = { page1v, page1v, page1v, 0x00 }; - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_PAGE1_LOCK1_ROW, flagsp1, sizeof(flagsp1))); - const uint8_t *page2 = otp_buffer_raw(OTP_DATA_PAGE2_LOCK1_ROW); - uint8_t page2v = page2[0] | (OTP_DATA_PAGE2_LOCK1_LOCK_BL_VALUE_READ_ONLY << OTP_DATA_PAGE2_LOCK1_LOCK_BL_LSB); - alignas(4) uint8_t flagsp2[] = { page2v, page2v, page2v, 0x00 }; - PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_PAGE2_LOCK1_ROW, flagsp2, sizeof(flagsp2))); - } -#elif defined(ESP_PLATFORM) - if (bootkey > 2) { - return ESP_ERR_INVALID_ARG; - } - - if (secure_lock && PICOKEYS_REQUIRE_SECURE_BOOT_BEFORE_LOCK) { - if (!esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_EN)) { - printf("Secure lock requires SECURE_BOOT_EN already set. Enable secure boot first.\n"); - return ESP_ERR_INVALID_STATE; - } - } - - esp_efuse_block_t key_block = EFUSE_BLK_KEY_MAX; - esp_err_t err = esp_provision_secure_boot_digest(bootkey, &key_block); - if (err != ESP_OK) { - printf("Error provisioning secure boot digest %u [%d]\n", bootkey, err); - return err; - } - - if (!esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_EN)) { - err = esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_EN); - if (err != ESP_OK) { - printf("Error enabling secure boot [%d]\n", err); - return err; - } - } - - if (secure_lock) { - for (uint8_t idx = 0; idx <= 2; idx++) { - if (idx == bootkey) { - continue; - } - err = esp_efuse_set_digest_revoke(idx); - if (err != ESP_OK) { - printf("Error revoking secure boot digest %u [%d]\n", idx, err); - return err; - } - } - - err = esp_efuse_set_key_dis_write(key_block); - if (err != ESP_OK) { - printf("Error setting secure boot key block read only [%d]\n", err); - return err; - } - err = esp_efuse_set_keypurpose_dis_write(key_block); - if (err != ESP_OK) { - printf("Error setting secure boot key purpose read only [%d]\n", err); - return err; - } - - /* // Not sure if it allows future upgrades if ROM download mode is disabled, so leaving it enabled for now - err = esp_efuse_disable_rom_download_mode(); - if (err != ESP_OK) { - printf("Error disabling ROM download mode [%d]\n", err); - return err; - } - */ - - err = esp_disable_debug_interfaces(); - if (err != ESP_OK) { - printf("Error disabling JTAG interfaces [%d]\n", err); - return err; - } - } -#else - (void)bootkey; - (void)secure_lock; -#endif // PICO_RP2350 - goto err; - err: - if (ret != PICOKEYS_OK) { - return ret; - } - return PICOKEYS_OK; -} - -#ifdef PICO_RP2350 -static void otp_invalidate_key(uint16_t row, uint16_t len) { - if (!is_empty_otp_buffer(row, len)) { - uint8_t *inval = (uint8_t *)calloc(len * 2, sizeof(uint8_t)); - if (inval) { - memset(inval, 0xFF, len * 2); - otp_write_data_raw(row, inval, len * 2); - mbedtls_platform_zeroize(inval, len * 2); - free(inval); - } - } -} - -static otp_ret_t otp_chaff(uint16_t row, uint16_t len) { - const uint8_t *raw = otp_buffer_raw(row); - uint8_t *chaff = (uint8_t *)calloc(len * 2, sizeof(uint8_t)); - if (chaff) { - memcpy(chaff, raw, len * 2); - for (int i = 0; i < len * 2; i++) { - chaff[i] ^= 0xFF; - } - otp_ret_t ret = otp_write_data_raw(row + 32, chaff, len * 2); - mbedtls_platform_zeroize(chaff, len * 2); - free(chaff); - return ret; - } - return BOOTROM_ERROR_INVALID_STATE; -} - -static otp_ret_t otp_migrate_key(uint16_t new_row, uint16_t old_row, uint16_t len) { - if (is_empty_otp_buffer(new_row, len) && !is_empty_otp_buffer(old_row, len)) { - const uint8_t *key = otp_buffer(old_row); - uint8_t *new_key = (uint8_t *)calloc(len, sizeof(uint8_t)); - if (new_key) { - memcpy(new_key, key, len); - otp_ret_t ret = otp_write_data(new_row, new_key, len); - if (ret == BOOTROM_OK) { - otp_chaff(new_row, len); - otp_invalidate_key(old_row, 32); - } - mbedtls_platform_zeroize(new_key, len); - free(new_key); - return ret; - } - } - return BOOTROM_ERROR_INVALID_STATE; -} - -static void otp_migrate_chaff(void) { - otp_migrate_key(OTP_MKEK_ROW, OTP_OLD_MKEK_ROW, 32); - otp_migrate_key(OTP_DEVK_ROW, OTP_OLD_DEVK_ROW, 32); - otp_lock_page(OTP_MKEK_ROW >> 6); -} -#endif - -void otp_init_files(void) { - -#ifdef PICO_RP2350 - otp_migrate_chaff(); -#endif - -#if defined(PICO_RP2350) || defined(ESP_PLATFORM) - otp_ret_t ret = 0; - uint16_t write_otp[2] = {0xFFFF, 0xFFFF}; - if (OTP_EMTPY(OTP_KEY_1, 32)) { - uint8_t mkek[32] = {0}; - random_fill_buffer(mkek, sizeof(mkek)); - ret = OTP_WRITE(OTP_KEY_1, mkek, sizeof(mkek)); - if (ret != 0) { - printf("Error writing OTP key 1 [%d]\n", ret); - } -#ifdef PICO_RP2350 - otp_chaff(OTP_KEY_1, 32); -#endif - mbedtls_platform_zeroize(mkek, sizeof(mkek)); - write_otp[0] = OTP_KEY_1; - } - OTP_READ(OTP_KEY_1, otp_key_1); - - if (OTP_EMTPY(OTP_KEY_2, 32)) { - mbedtls_ecdsa_context ecdsa; - size_t olen = 0; - uint8_t pkey[MBEDTLS_ECP_MAX_BYTES]; - while (olen != 32) { - mbedtls_ecdsa_init(&ecdsa); - mbedtls_ecp_group_id ec_id = MBEDTLS_ECP_DP_SECP256K1; - mbedtls_ecdsa_genkey(&ecdsa, ec_id, random_fill_iterator, NULL); - mbedtls_ecp_write_key_ext(&ecdsa, &olen, pkey, sizeof(pkey)); - mbedtls_ecdsa_free(&ecdsa); - } - ret = OTP_WRITE(OTP_KEY_2, pkey, olen); - if (ret != 0) { - printf("Error writing OTP key 2 [%d]\n", ret); - } - mbedtls_platform_zeroize(pkey, sizeof(pkey)); -#ifdef PICO_RP2350 - otp_chaff(OTP_KEY_2, 32); -#endif - write_otp[1] = OTP_KEY_2; - } - OTP_READ(OTP_KEY_2, otp_key_2); - - for (size_t i = 0; i < sizeof(write_otp) / sizeof(write_otp[0]); i++) { - if (write_otp[i] != 0xFFFF) { -#if defined(PICO_RP2350) - otp_lock_page(write_otp[i] >> 6); -#elif defined(ESP_PLATFORM) - ret = esp_efuse_set_key_dis_write(write_otp[i]); - if (ret != ESP_OK) { - printf("Error setting OTP key %d to read only [%d]\n", i, ret); - } - ret = esp_efuse_set_keypurpose_dis_write(write_otp[i]); - if (ret != ESP_OK) { - printf("Error setting OTP key %d purpose to read only [%d]\n", i, ret); - } -#endif - } - } -#elif defined(ENABLE_EMULATION) - static uint8_t _otp1[32] = {0}, _otp2[32] = {0}; - memset(_otp1, 0xAC, sizeof(_otp1)); - memset(_otp2, 0xBE, sizeof(_otp2)); - otp_key_1 = _otp1; - otp_key_2 = _otp2; -#endif -} diff --git a/src/main.c b/src/main.c index 59b19dd..fe7b339 100644 --- a/src/main.c +++ b/src/main.c @@ -159,7 +159,7 @@ int main(void) { random_init(); - otp_init_files(); + otp_init(); low_flash_init(); diff --git a/src/otp/otp.c b/src/otp/otp.c new file mode 100644 index 0000000..d465192 --- /dev/null +++ b/src/otp/otp.c @@ -0,0 +1,39 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "otp.h" +#include "otp_platform.h" +#include + +const uint8_t *otp_key_1 = NULL; +const uint8_t *otp_key_2 = NULL; + +int otp_enable_secure_boot(uint8_t bootkey, bool secure_lock) { + return otp_platform_enable_secure_boot(bootkey, secure_lock); +} + +bool otp_is_secure_boot_enabled(uint8_t *bootkey) { + return otp_platform_is_secure_boot_enabled(bootkey); +} + +bool otp_is_secure_boot_locked(void) { + return otp_platform_is_secure_boot_locked(); +} + +void otp_init(void) { + otp_platform_init(&otp_key_1, &otp_key_2); +} diff --git a/src/fs/otp.h b/src/otp/otp.h similarity index 58% rename from src/fs/otp.h rename to src/otp/otp.h index 71b8bd8..9e0bd6a 100644 --- a/src/fs/otp.h +++ b/src/otp/otp.h @@ -15,43 +15,19 @@ * along with this program. If not, see . */ - #ifndef _OTP_H_ #define _OTP_H_ -#ifdef PICO_RP2350 - -#define OTP_OLD_MKEK_ROW 0xEF0 -#define OTP_OLD_DEVK_ROW 0xED0 -#define OTP_MKEK_ROW 0xE90 -#define OTP_DEVK_ROW 0xE80 - -#define OTP_KEY_1 OTP_MKEK_ROW -#define OTP_KEY_2 OTP_DEVK_ROW - -extern const uint8_t* otp_buffer(uint16_t row); -extern const uint8_t* otp_buffer_raw(uint16_t row); -extern bool is_empty_otp_buffer(uint16_t row, uint16_t len); -extern int otp_write_data(uint16_t row, const uint8_t *data, uint16_t len); -extern int otp_write_data_raw(uint16_t row, const uint8_t *data, uint16_t len); - -#elif defined(ESP_PLATFORM) - -#include "esp_efuse.h" -#include "esp_efuse_table.h" - -#define OTP_KEY_1 EFUSE_BLK_KEY3 -#define OTP_KEY_2 EFUSE_BLK_KEY4 - -#endif +#include +#include extern int otp_enable_secure_boot(uint8_t bootkey, bool secure_lock); -extern void otp_init_files(void); - -extern const uint8_t *otp_key_1; -extern const uint8_t *otp_key_2; +extern void otp_init(void); extern bool otp_is_secure_boot_enabled(uint8_t *bootkey); extern bool otp_is_secure_boot_locked(void); +extern const uint8_t *otp_key_1; +extern const uint8_t *otp_key_2; + #endif // _OTP_H_ diff --git a/src/otp/otp_emulation.c b/src/otp/otp_emulation.c new file mode 100644 index 0000000..4fcf896 --- /dev/null +++ b/src/otp/otp_emulation.c @@ -0,0 +1,47 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include + +#include "picokeys.h" +#include "otp_platform.h" + +int otp_platform_enable_secure_boot(uint8_t bootkey, bool secure_lock) { + (void)bootkey; + (void)secure_lock; + return PICOKEYS_OK; +} + +bool otp_platform_is_secure_boot_enabled(uint8_t *bootkey) { + (void)bootkey; + return false; +} + +bool otp_platform_is_secure_boot_locked(void) { + return false; +} + +void otp_platform_init(const uint8_t **otp_key_1_out, const uint8_t **otp_key_2_out) { + static uint8_t _otp1[32] = {0}; + static uint8_t _otp2[32] = {0}; + + memset(_otp1, 0xAC, sizeof(_otp1)); + memset(_otp2, 0xBE, sizeof(_otp2)); + + *otp_key_1_out = _otp1; + *otp_key_2_out = _otp2; +} diff --git a/src/otp/otp_esp32.c b/src/otp/otp_esp32.c new file mode 100644 index 0000000..ddc7b49 --- /dev/null +++ b/src/otp/otp_esp32.c @@ -0,0 +1,341 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include "picokeys.h" +#include "otp.h" +#include "otp_platform.h" + +#include "random.h" +#include "mbedtls/ecdsa.h" + +#include "esp_efuse.h" +#include "esp_efuse_table.h" + +#define OTP_KEY_1 EFUSE_BLK_KEY3 +#define OTP_KEY_2 EFUSE_BLK_KEY4 + +uint8_t _otp_key_1[32] = {0}; +uint8_t _otp_key_2[32] = {0}; + +static const uint8_t esp_secure_boot_digest[32] = { + 0x0c, 0x1e, 0xce, 0xf3, 0xb4, 0x8f, 0x4a, 0x81, + 0x45, 0x6c, 0x85, 0x39, 0x15, 0xcc, 0x05, 0x36, + 0xbe, 0x23, 0x24, 0xee, 0xac, 0x8e, 0x3b, 0xb5, + 0x77, 0x6f, 0x2d, 0xb9, 0x62, 0x38, 0x75, 0x6a +}; + +#ifndef SECURE_BOOT_BOOTKEY_INDEX +#define SECURE_BOOT_BOOTKEY_INDEX 0 +#endif +#ifndef PICOKEYS_REQUIRE_SECURE_BOOT_BEFORE_LOCK +#define PICOKEYS_REQUIRE_SECURE_BOOT_BEFORE_LOCK 1 +#endif + +static esp_efuse_purpose_t esp_secure_boot_purpose(uint8_t digest_idx) { + switch (digest_idx) { + case 0: return ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0; + case 1: return ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1; + case 2: return ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2; + default: return ESP_EFUSE_KEY_PURPOSE_MAX; + } +} + +static bool esp_find_secure_boot_block(uint8_t digest_idx, esp_efuse_block_t *out_block) { + esp_efuse_purpose_t purpose = esp_secure_boot_purpose(digest_idx); + if (purpose == ESP_EFUSE_KEY_PURPOSE_MAX) { + return false; + } + for (esp_efuse_block_t blk = EFUSE_BLK_KEY0; blk < EFUSE_BLK_KEY_MAX; blk++) { + if (esp_efuse_get_key_purpose(blk) == purpose) { + if (out_block) { + *out_block = blk; + } + return true; + } + } + return false; +} + +static esp_err_t esp_provision_secure_boot_digest(uint8_t digest_idx, esp_efuse_block_t *out_block) { + esp_efuse_purpose_t purpose = esp_secure_boot_purpose(digest_idx); + if (purpose == ESP_EFUSE_KEY_PURPOSE_MAX) { + return ESP_ERR_INVALID_ARG; + } + + esp_efuse_block_t block = EFUSE_BLK_KEY_MAX; + if (esp_find_secure_boot_block(digest_idx, &block)) { + const esp_efuse_desc_t **key_desc = esp_efuse_get_key(block); + if (!key_desc) { + return ESP_FAIL; + } + uint8_t existing[32] = {0}; + esp_err_t err = esp_efuse_read_field_blob(key_desc, existing, sizeof(existing) * 8); + if (err != ESP_OK) { + return err; + } + if (memcmp(existing, esp_secure_boot_digest, sizeof(existing)) != 0) { + return ESP_ERR_INVALID_STATE; + } + if (out_block) { + *out_block = block; + } + return ESP_OK; + } + + block = esp_efuse_find_unused_key_block(); + if (block == EFUSE_BLK_KEY_MAX) { + return ESP_ERR_NOT_FOUND; + } + + esp_err_t err = esp_efuse_batch_write_begin(); + if (err != ESP_OK) { + return err; + } + + err = esp_efuse_set_key_purpose(block, purpose); + if (err == ESP_OK) { + const esp_efuse_desc_t **key_desc = esp_efuse_get_key(block); + if (!key_desc) { + err = ESP_FAIL; + } else { + err = esp_efuse_write_field_blob(key_desc, esp_secure_boot_digest, sizeof(esp_secure_boot_digest) * 8); + } + } + + if (err == ESP_OK) { + err = esp_efuse_batch_write_commit(); + } else { + esp_efuse_batch_write_cancel(); + } + + if (err == ESP_OK && out_block) { + *out_block = block; + } + return err; +} + +static esp_err_t esp_disable_debug_interfaces(void) { + esp_err_t err = ESP_OK; + +#ifdef ESP_EFUSE_SOFT_DIS_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_SOFT_DIS_JTAG); + if (err != ESP_OK) { + return err; + } +#endif +#ifdef ESP_EFUSE_HARD_DIS_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_HARD_DIS_JTAG); + if (err != ESP_OK) { + return err; + } +#endif +#ifdef ESP_EFUSE_DIS_USB_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_DIS_USB_JTAG); + if (err != ESP_OK) { + return err; + } +#endif +#ifdef ESP_EFUSE_DIS_USB_SERIAL_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_DIS_USB_SERIAL_JTAG); + if (err != ESP_OK) { + return err; + } +#endif +#ifdef ESP_EFUSE_DIS_PAD_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_DIS_PAD_JTAG); + if (err != ESP_OK) { + return err; + } +#endif + + return err; +} + +static esp_err_t read_key_from_efuse(esp_efuse_block_t block, uint8_t *key, size_t key_len) { + const esp_efuse_desc_t **key_desc = esp_efuse_get_key(block); + if (!key_desc) { + return ESP_FAIL; + } + return esp_efuse_read_field_blob(key_desc, key, key_len * 8); +} + +bool otp_platform_is_secure_boot_enabled(uint8_t *bootkey) { + if (!esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_EN)) { + return false; + } + + uint8_t preferred = SECURE_BOOT_BOOTKEY_INDEX; + if (preferred <= 2 && esp_find_secure_boot_block(preferred, NULL) + && !esp_efuse_get_digest_revoke(preferred)) { + if (bootkey) { + *bootkey = preferred; + } + return true; + } + + for (uint8_t idx = 0; idx <= 2; idx++) { + if (esp_find_secure_boot_block(idx, NULL) && !esp_efuse_get_digest_revoke(idx)) { + if (bootkey) { + *bootkey = idx; + } + return true; + } + } + return false; +} + +bool otp_platform_is_secure_boot_locked(void) { + uint8_t bootkey_idx = 0xFF; + if (!otp_platform_is_secure_boot_enabled(&bootkey_idx)) { + return false; + } + + for (uint8_t idx = 0; idx <= 2; idx++) { + if (idx == bootkey_idx) { + continue; + } + if (!esp_efuse_get_digest_revoke(idx)) { + return false; + } + } + return true; +} + +int otp_platform_enable_secure_boot(uint8_t bootkey, bool secure_lock) { + if (bootkey > 2) { + return ESP_ERR_INVALID_ARG; + } + + if (secure_lock && PICOKEYS_REQUIRE_SECURE_BOOT_BEFORE_LOCK && !esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_EN)) { + printf("Secure lock requires SECURE_BOOT_EN already set. Enable secure boot first.\n"); + return ESP_ERR_INVALID_STATE; + } + + esp_efuse_block_t key_block = EFUSE_BLK_KEY_MAX; + esp_err_t err = esp_provision_secure_boot_digest(bootkey, &key_block); + if (err != ESP_OK) { + printf("Error provisioning secure boot digest %u [%d]\n", bootkey, err); + return err; + } + + if (!esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_EN)) { + err = esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_EN); + if (err != ESP_OK) { + printf("Error enabling secure boot [%d]\n", err); + return err; + } + } + + if (secure_lock) { + for (uint8_t idx = 0; idx <= 2; idx++) { + if (idx == bootkey) { + continue; + } + err = esp_efuse_set_digest_revoke(idx); + if (err != ESP_OK) { + printf("Error revoking secure boot digest %u [%d]\n", idx, err); + return err; + } + } + + err = esp_efuse_set_key_dis_write(key_block); + if (err != ESP_OK) { + printf("Error setting secure boot key block read only [%d]\n", err); + return err; + } + err = esp_efuse_set_keypurpose_dis_write(key_block); + if (err != ESP_OK) { + printf("Error setting secure boot key purpose read only [%d]\n", err); + return err; + } + + /* // Not sure if it allows future upgrades if ROM download mode is disabled, so leaving it enabled for now + err = esp_efuse_disable_rom_download_mode(); + if (err != ESP_OK) { + printf("Error disabling ROM download mode [%d]\n", err); + return err; + } + */ + + err = esp_disable_debug_interfaces(); + if (err != ESP_OK) { + printf("Error disabling JTAG interfaces [%d]\n", err); + return err; + } + } + + return PICOKEYS_OK; +} + +void otp_platform_init(const uint8_t **otp_key_1_out, const uint8_t **otp_key_2_out) { + esp_err_t ret = 0; + uint16_t write_otp[2] = {0xFFFF, 0xFFFF}; + + if (esp_efuse_key_block_unused(OTP_KEY_1)) { + uint8_t mkek[32] = {0}; + random_fill_buffer(mkek, sizeof(mkek)); + ret = esp_efuse_write_key(OTP_KEY_1, ESP_EFUSE_KEY_PURPOSE_USER, mkek, sizeof(mkek)); + if (ret != 0) { + printf("Error writing OTP key 1 [%d]\n", ret); + } + mbedtls_platform_zeroize(mkek, sizeof(mkek)); + write_otp[0] = OTP_KEY_1; + } + ret = read_key_from_efuse(OTP_KEY_1, _otp_key_1, sizeof(_otp_key_1)); + if (ret != ESP_OK) { + printf("Error reading OTP key 1 [%d]\n", ret); + } + *otp_key_1_out = _otp_key_1; + + if (esp_efuse_key_block_unused(OTP_KEY_2)) { + mbedtls_ecdsa_context ecdsa; + size_t olen = 0; + uint8_t pkey[MBEDTLS_ECP_MAX_BYTES]; + while (olen != 32) { + mbedtls_ecdsa_init(&ecdsa); + mbedtls_ecp_group_id ec_id = MBEDTLS_ECP_DP_SECP256K1; + mbedtls_ecdsa_genkey(&ecdsa, ec_id, random_fill_iterator, NULL); + mbedtls_ecp_write_key_ext(&ecdsa, &olen, pkey, sizeof(pkey)); + mbedtls_ecdsa_free(&ecdsa); + } + ret = esp_efuse_write_key(OTP_KEY_2, ESP_EFUSE_KEY_PURPOSE_USER, pkey, olen); + if (ret != 0) { + printf("Error writing OTP key 2 [%d]\n", ret); + } + mbedtls_platform_zeroize(pkey, sizeof(pkey)); + write_otp[1] = OTP_KEY_2; + } + ret = read_key_from_efuse(OTP_KEY_2, _otp_key_2, sizeof(_otp_key_2)); + if (ret != ESP_OK) { + printf("Error reading OTP key 2 [%d]\n", ret); + } + *otp_key_2_out = _otp_key_2; + + for (size_t i = 0; i < sizeof(write_otp) / sizeof(write_otp[0]); i++) { + if (write_otp[i] != 0xFFFF) { + ret = esp_efuse_set_key_dis_write(write_otp[i]); + if (ret != ESP_OK) { + printf("Error setting OTP key %d to read only [%d]\n", i, ret); + } + ret = esp_efuse_set_keypurpose_dis_write(write_otp[i]); + if (ret != ESP_OK) { + printf("Error setting OTP key %d purpose to read only [%d]\n", i, ret); + } + } + } +} diff --git a/src/otp/otp_macos.c b/src/otp/otp_macos.c new file mode 100644 index 0000000..63890da --- /dev/null +++ b/src/otp/otp_macos.c @@ -0,0 +1,210 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "picokeys.h" +#include "otp_platform.h" + +#if defined(MACOS_APP) && MACOS_APP +#include +#include +#include +#include + +#include "mbedtls/sha256.h" + +#define CF_SAFE_RELEASE(x) do { if ((x) != NULL) CFRelease(x); } while (0) +#define SE_KEY_TAG "com.picokeys.novus.se.key" + +static int sec_err(const char *ctx, OSStatus st) { + fprintf(stderr, "[macos-se] %s failed: OSStatus=%d\n", ctx, (int)st); + return -1; +} + +static int macos_se_vault_load_or_create_key2(uint8_t out_key32[32]) { + int rc = -1; + SecKeyRef private_key = NULL; + SecKeyRef public_key = NULL; + CFDataRef tag = NULL; + CFDictionaryRef find_query = NULL; + CFDictionaryRef priv_attrs = NULL; + CFNumberRef key_size_num = NULL; + CFDictionaryRef attrs = NULL; + CFDataRef pub_data = NULL; + CFErrorRef err = NULL; + OSStatus st; + + tag = CFDataCreate(NULL, (const UInt8 *)SE_KEY_TAG, (CFIndex)strlen(SE_KEY_TAG)); + if (!tag) { + fprintf(stderr, "[macos-se] CFDataCreate(tag) failed\n"); + goto cleanup; + } + + const void *find_keys[] = { + kSecClass, + kSecAttrApplicationTag, + kSecAttrKeyType, + kSecReturnRef + }; + const void *find_vals[] = { + kSecClassKey, + tag, + kSecAttrKeyTypeECSECPrimeRandom, + kCFBooleanTrue + }; + find_query = CFDictionaryCreate(NULL, + find_keys, + find_vals, + 4, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!find_query) { + fprintf(stderr, "[macos-se] CFDictionaryCreate(find_query) failed\n"); + goto cleanup; + } + + st = SecItemCopyMatching(find_query, (CFTypeRef *)&private_key); + if (st == errSecItemNotFound) { + const void *priv_keys[] = { kSecAttrIsPermanent, kSecAttrApplicationTag }; + const void *priv_vals[] = { kCFBooleanTrue, tag }; + priv_attrs = CFDictionaryCreate(NULL, + priv_keys, + priv_vals, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!priv_attrs) { + fprintf(stderr, "[macos-se] CFDictionaryCreate(priv_attrs) failed\n"); + goto cleanup; + } + + int key_size = 256; + key_size_num = CFNumberCreate(NULL, kCFNumberIntType, &key_size); + if (!key_size_num) { + fprintf(stderr, "[macos-se] CFNumberCreate(key_size) failed\n"); + goto cleanup; + } + + const void *keys[] = { + kSecAttrKeyType, + kSecAttrKeySizeInBits, + kSecAttrTokenID, + kSecPrivateKeyAttrs + }; + const void *vals[] = { + kSecAttrKeyTypeECSECPrimeRandom, + key_size_num, + kSecAttrTokenIDSecureEnclave, + priv_attrs + }; + attrs = CFDictionaryCreate(NULL, + keys, + vals, + 4, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!attrs) { + fprintf(stderr, "[macos-se] CFDictionaryCreate(attrs) failed\n"); + goto cleanup; + } + + private_key = SecKeyCreateRandomKey(attrs, &err); + if (!private_key) { + fprintf(stderr, "[macos-se] SecKeyCreateRandomKey failed\n"); + if (err) { + CFShow(err); + } + goto cleanup; + } + printf("[macos-se] Created Secure Enclave key '%s'\n", SE_KEY_TAG); + } else if (st != errSecSuccess) { + sec_err("SecItemCopyMatching", st); + goto cleanup; + } else { + printf("[macos-se] Using existing Secure Enclave key '%s'\n", SE_KEY_TAG); + } + + public_key = SecKeyCopyPublicKey(private_key); + if (!public_key) { + fprintf(stderr, "[macos-se] SecKeyCopyPublicKey failed\n"); + goto cleanup; + } + + pub_data = SecKeyCopyExternalRepresentation(public_key, &err); + if (!pub_data) { + fprintf(stderr, "[macos-se] SecKeyCopyExternalRepresentation failed\n"); + if (err) { + CFShow(err); + } + goto cleanup; + } + + mbedtls_sha256(CFDataGetBytePtr(pub_data), + (size_t)CFDataGetLength(pub_data), + out_key32, + 0); + if (!memcmp(out_key32, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 32)) { + out_key32[31] = 1; + } + rc = 0; + +cleanup: + if (err) { + CFRelease(err); + } + CF_SAFE_RELEASE(pub_data); + CF_SAFE_RELEASE(public_key); + CF_SAFE_RELEASE(attrs); + CF_SAFE_RELEASE(key_size_num); + CF_SAFE_RELEASE(priv_attrs); + CF_SAFE_RELEASE(find_query); + CF_SAFE_RELEASE(tag); + CF_SAFE_RELEASE(private_key); + return rc; +} +#endif + +int otp_platform_enable_secure_boot(uint8_t bootkey, bool secure_lock) { + (void)bootkey; + (void)secure_lock; + return PICOKEYS_OK; +} + +bool otp_platform_is_secure_boot_enabled(uint8_t *bootkey) { + (void)bootkey; + return true; +} + +bool otp_platform_is_secure_boot_locked(void) { + return false; +} + +void otp_platform_init(const uint8_t **otp_key_1_out, const uint8_t **otp_key_2_out) { + static uint8_t _otp1[32] = {0}; + static uint8_t _otp2[32] = {0}; + + memset(_otp1, 0xAC, sizeof(_otp1)); + memset(_otp2, 0xBE, sizeof(_otp2)); + +#if defined(MACOS_APP) && MACOS_APP + if (macos_se_vault_load_or_create_key2(_otp2) != 0) { + printf("Warning: failed to load MACOS_APP Secure Enclave key; using dummy otp_key_2\n"); + } +#endif + *otp_key_1_out = _otp1; + *otp_key_2_out = _otp2; +} diff --git a/src/otp/otp_platform.h b/src/otp/otp_platform.h new file mode 100644 index 0000000..965b330 --- /dev/null +++ b/src/otp/otp_platform.h @@ -0,0 +1,29 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _OTP_PLATFORM_H_ +#define _OTP_PLATFORM_H_ + +#include +#include + +int otp_platform_enable_secure_boot(uint8_t bootkey, bool secure_lock); +bool otp_platform_is_secure_boot_enabled(uint8_t *bootkey); +bool otp_platform_is_secure_boot_locked(void); +void otp_platform_init(const uint8_t **otp_key_1_out, const uint8_t **otp_key_2_out); + +#endif diff --git a/src/otp/otp_rp2040.c b/src/otp/otp_rp2040.c new file mode 100644 index 0000000..70aeea1 --- /dev/null +++ b/src/otp/otp_rp2040.c @@ -0,0 +1,47 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include + +#include "picokeys.h" +#include "otp_platform.h" + +int otp_platform_enable_secure_boot(uint8_t bootkey, bool secure_lock) { + (void)bootkey; + (void)secure_lock; + return PICOKEYS_WRONG_DATA; +} + +bool otp_platform_is_secure_boot_enabled(uint8_t *bootkey) { + (void)bootkey; + return false; +} + +bool otp_platform_is_secure_boot_locked(void) { + return false; +} + +void otp_platform_init(const uint8_t **otp_key_1_out, const uint8_t **otp_key_2_out) { + static uint8_t _otp1[32] = {0}; + static uint8_t _otp2[32] = {0}; + + memset(_otp1, 0xDF, sizeof(_otp1)); + memset(_otp2, 0x93, sizeof(_otp2)); + + *otp_key_1_out = _otp1; + *otp_key_2_out = _otp2; +} diff --git a/src/otp/otp_rp2350.c b/src/otp/otp_rp2350.c new file mode 100644 index 0000000..8b9f37a --- /dev/null +++ b/src/otp/otp_rp2350.c @@ -0,0 +1,294 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "picokeys.h" +#include "otp.h" +#include "otp_platform.h" + +#include "pico/bootrom.h" +#include "hardware/structs/otp.h" +#include "hardware/regs/otp_data.h" + +#include "random.h" +#include "mbedtls/ecdsa.h" + +#define OTP_OLD_MKEK_ROW 0xEF0 +#define OTP_OLD_DEVK_ROW 0xED0 +#define OTP_MKEK_ROW 0xE90 +#define OTP_DEVK_ROW 0xE80 + +#define OTP_KEY_1 OTP_MKEK_ROW +#define OTP_KEY_2 OTP_DEVK_ROW + + +static bool is_empty_buffer(const uint8_t *buffer, uint16_t buffer_len) { + for (int i = 0; i < buffer_len; i++) { + if (buffer[i] != 0x00) { + return false; + } + } + return true; +} + +static int otp_write_data_mode(uint16_t row, const uint8_t *data, uint16_t len, bool is_ecc) { + otp_cmd_t cmd = { .flags = row | (is_ecc ? OTP_CMD_ECC_BITS : 0) | OTP_CMD_WRITE_BITS }; + uint32_t ret = rom_func_otp_access((uint8_t *)data, len, cmd); + if (ret) { + printf("OTP Write failed with error: %ld\n", ret); + } + return ret; +} + +static int otp_write_data(uint16_t row, const uint8_t *data, uint16_t len) { + return otp_write_data_mode(row, data, len, true); +} + +static int otp_write_data_raw(uint16_t row, const uint8_t *data, uint16_t len) { + return otp_write_data_mode(row, data, len, false); +} + +static const uint8_t* otp_buffer(uint16_t row) { + volatile uint32_t *p = ((uint32_t *)(OTP_DATA_BASE + (row*2))); + return (const uint8_t *)p; +} + +static const uint8_t* otp_buffer_raw(uint16_t row) { + volatile uint32_t *p = ((uint32_t *)(OTP_DATA_RAW_BASE + (row*4))); + return (const uint8_t *)p; +} + +static bool is_empty_otp_buffer(uint16_t row, uint16_t len) { + return is_empty_buffer(otp_buffer_raw(row), len * 2); +} + +static bool is_otp_locked_page(uint8_t page) { + volatile uint32_t *p = ((uint32_t *)(OTP_DATA_BASE + ((OTP_DATA_PAGE0_LOCK0_ROW + page*2)*2))); + return ((p[0] & 0xFFFF0000) == 0x3C3C0000 && (p[1] & 0xFF) == 0x3C); +} + +static void otp_lock_page(uint8_t page) { + if (!is_otp_locked_page(page)) { + alignas(4) uint32_t value = 0x3c3c3c; + otp_write_data_raw(OTP_DATA_PAGE0_LOCK0_ROW + page*2 + 1, (uint8_t *)&value, sizeof(value)); + } + + otp_hw->sw_lock[page] = 0b1100; +} + +static void otp_invalidate_key(uint16_t row, uint16_t len) { + if (!is_empty_otp_buffer(row, len)) { + uint8_t *inval = (uint8_t *)calloc(len * 2, sizeof(uint8_t)); + if (inval) { + memset(inval, 0xFF, len * 2); + otp_write_data_raw(row, inval, len * 2); + mbedtls_platform_zeroize(inval, len * 2); + free(inval); + } + } +} + +static int otp_chaff(uint16_t row, uint16_t len) { + const uint8_t *raw = otp_buffer_raw(row); + uint8_t *chaff = (uint8_t *)calloc(len * 2, sizeof(uint8_t)); + if (chaff) { + memcpy(chaff, raw, len * 2); + for (int i = 0; i < len * 2; i++) { + chaff[i] ^= 0xFF; + } + int ret = otp_write_data_raw(row + 32, chaff, len * 2); + mbedtls_platform_zeroize(chaff, len * 2); + free(chaff); + return ret; + } + return BOOTROM_ERROR_INVALID_STATE; +} + +static int otp_migrate_key(uint16_t new_row, uint16_t old_row, uint16_t len) { + if (is_empty_otp_buffer(new_row, len) && !is_empty_otp_buffer(old_row, len)) { + const uint8_t *key = otp_buffer(old_row); + uint8_t *new_key = (uint8_t *)calloc(len, sizeof(uint8_t)); + if (new_key) { + memcpy(new_key, key, len); + int ret = otp_write_data(new_row, new_key, len); + if (ret == BOOTROM_OK) { + otp_chaff(new_row, len); + otp_invalidate_key(old_row, 32); + } + mbedtls_platform_zeroize(new_key, len); + free(new_key); + return ret; + } + } + return BOOTROM_ERROR_INVALID_STATE; +} + +static void otp_migrate_chaff(void) { + otp_migrate_key(OTP_MKEK_ROW, OTP_OLD_MKEK_ROW, 32); + otp_migrate_key(OTP_DEVK_ROW, OTP_OLD_DEVK_ROW, 32); + otp_lock_page(OTP_MKEK_ROW >> 6); +} + +bool otp_platform_is_secure_boot_enabled(uint8_t *bootkey) { + const uint8_t *crit1 = otp_buffer(OTP_DATA_CRIT1_ROW); + if ((crit1[0] & (1 << OTP_DATA_CRIT1_SECURE_BOOT_ENABLE_LSB)) == 0) { + return false; + } + alignas(2) uint8_t BOOTKEY[32] = { + 0xE1, 0xD1, 0x6B, 0xA7, 0x64, 0xAB, 0xD7, 0x12, + 0xD4, 0xEF, 0x6E, 0x3E, 0xDD, 0x74, 0x4E, 0xD5, + 0x63, 0x8C, 0x26, 0x0B, 0x77, 0x1C, 0xF9, 0x81, + 0x51, 0x11, 0x0B, 0xAF, 0xAC, 0x9B, 0xC8, 0x71 + }; + uint8_t bootkey_idx = 0; + for (; bootkey_idx < 6; bootkey_idx++) { + const uint8_t *bootkey_row = otp_buffer(OTP_DATA_BOOTKEY0_0_ROW + 0x10 * bootkey_idx); + if (memcmp(bootkey_row, BOOTKEY, sizeof(BOOTKEY)) == 0) { + break; + } + } + if (bootkey_idx == 6) { + return false; + } + const uint8_t *boot_flags1 = otp_buffer(OTP_DATA_BOOT_FLAGS1_ROW); + if ((boot_flags1[0] & (1 << (bootkey_idx + OTP_DATA_BOOT_FLAGS1_KEY_VALID_LSB))) == 0) { + return false; + } + if (bootkey) { + *bootkey = bootkey_idx; + } + return true; +} + +bool otp_platform_is_secure_boot_locked(void) { + uint8_t bootkey_idx = 0xFF; + if (otp_platform_is_secure_boot_enabled(&bootkey_idx) == false) { + return false; + } + const uint8_t *boot_flags1 = otp_buffer_raw(OTP_DATA_BOOT_FLAGS1_ROW); + if ((boot_flags1[1] & ((OTP_DATA_BOOT_FLAGS1_KEY_INVALID_BITS >> OTP_DATA_BOOT_FLAGS1_KEY_INVALID_LSB) & (~(1 << bootkey_idx)))) != + ((OTP_DATA_BOOT_FLAGS1_KEY_INVALID_BITS >> OTP_DATA_BOOT_FLAGS1_KEY_INVALID_LSB) & (~(1 << bootkey_idx)))) { + return false; + } + const uint8_t *crit1 = otp_buffer_raw(OTP_DATA_CRIT1_ROW); + if ((crit1[0] & (1 << OTP_DATA_CRIT1_DEBUG_DISABLE_LSB)) == 0 + || (crit1[0] & (1 << OTP_DATA_CRIT1_GLITCH_DETECTOR_ENABLE_LSB)) == 0 + || ((crit1[0] & (3 << OTP_DATA_CRIT1_GLITCH_DETECTOR_SENS_LSB)) != (3 << OTP_DATA_CRIT1_GLITCH_DETECTOR_SENS_LSB))) { + return false; + } + return bootkey_idx != 0xFF; +} + +int otp_platform_enable_secure_boot(uint8_t bootkey, bool secure_lock) { + int ret = 0; + + alignas(2) uint8_t BOOTKEY[] = "\xe1\xd1\x6b\xa7\x64\xab\xd7\x12\xd4\xef\x6e\x3e\xdd\x74\x4e\xd5\x63\x8c\x26\xb\x77\x1c\xf9\x81\x51\x11\xb\xaf\xac\x9b\xc8\x71"; + if (is_empty_otp_buffer(OTP_DATA_BOOTKEY0_0_ROW + 0x10*bootkey, 32)) { + PICOKEYS_CHECK(otp_write_data(OTP_DATA_BOOTKEY0_0_ROW + 0x10*bootkey, BOOTKEY, sizeof(BOOTKEY))); + } + + const uint8_t *boot_flags1 = otp_buffer_raw(OTP_DATA_BOOT_FLAGS1_ROW); + alignas(4) uint8_t flagsb1[] = { boot_flags1[0] | (1 << (bootkey + OTP_DATA_BOOT_FLAGS1_KEY_VALID_LSB)), boot_flags1[1], boot_flags1[2], 0x00 }; + if (secure_lock) { + flagsb1[1] |= ((OTP_DATA_BOOT_FLAGS1_KEY_INVALID_BITS >> OTP_DATA_BOOT_FLAGS1_KEY_INVALID_LSB) & (~(1 << bootkey))); + } + + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_BOOT_FLAGS1_ROW, flagsb1, sizeof(flagsb1))); + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_BOOT_FLAGS1_R1_ROW, flagsb1, sizeof(flagsb1))); + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_BOOT_FLAGS1_R2_ROW, flagsb1, sizeof(flagsb1))); + + const uint8_t *crit1 = otp_buffer_raw(OTP_DATA_CRIT1_ROW); + alignas(4) uint8_t flagsc1[] = { crit1[0] | (1 << OTP_DATA_CRIT1_SECURE_BOOT_ENABLE_LSB), crit1[1], crit1[2], 0x00 }; + if (secure_lock) { + flagsc1[0] |= (1 << OTP_DATA_CRIT1_DEBUG_DISABLE_LSB); + flagsc1[0] |= (1 << OTP_DATA_CRIT1_GLITCH_DETECTOR_ENABLE_LSB); + flagsc1[0] |= (3 << OTP_DATA_CRIT1_GLITCH_DETECTOR_SENS_LSB); + } + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_ROW, flagsc1, sizeof(flagsc1))); + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R1_ROW, flagsc1, sizeof(flagsc1))); + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R2_ROW, flagsc1, sizeof(flagsc1))); + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R3_ROW, flagsc1, sizeof(flagsc1))); + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R4_ROW, flagsc1, sizeof(flagsc1))); + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R5_ROW, flagsc1, sizeof(flagsc1))); + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R6_ROW, flagsc1, sizeof(flagsc1))); + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_CRIT1_R7_ROW, flagsc1, sizeof(flagsc1))); + + if (secure_lock) { + const uint8_t *page1 = otp_buffer_raw(OTP_DATA_PAGE1_LOCK1_ROW); + uint8_t page1v = page1[0] | (OTP_DATA_PAGE1_LOCK1_LOCK_BL_VALUE_READ_ONLY << OTP_DATA_PAGE1_LOCK1_LOCK_BL_LSB); + alignas(4) uint8_t flagsp1[] = { page1v, page1v, page1v, 0x00 }; + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_PAGE1_LOCK1_ROW, flagsp1, sizeof(flagsp1))); + const uint8_t *page2 = otp_buffer_raw(OTP_DATA_PAGE2_LOCK1_ROW); + uint8_t page2v = page2[0] | (OTP_DATA_PAGE2_LOCK1_LOCK_BL_VALUE_READ_ONLY << OTP_DATA_PAGE2_LOCK1_LOCK_BL_LSB); + alignas(4) uint8_t flagsp2[] = { page2v, page2v, page2v, 0x00 }; + PICOKEYS_CHECK(otp_write_data_raw(OTP_DATA_PAGE2_LOCK1_ROW, flagsp2, sizeof(flagsp2))); + } + + goto err; +err: + if (ret != PICOKEYS_OK) { + return ret; + } + return PICOKEYS_OK; +} + +void otp_platform_init(const uint8_t **otp_key_1_out, const uint8_t **otp_key_2_out) { + otp_migrate_chaff(); + + int ret = 0; + uint16_t write_otp[2] = {0xFFFF, 0xFFFF}; + if (is_empty_otp_buffer(OTP_KEY_1, 32)) { + uint8_t mkek[32] = {0}; + random_fill_buffer(mkek, sizeof(mkek)); + ret = otp_write_data(OTP_KEY_1, mkek, sizeof(mkek)); + if (ret != 0) { + printf("Error writing OTP key 1 [%d]\n", ret); + } + otp_chaff(OTP_KEY_1, 32); + mbedtls_platform_zeroize(mkek, sizeof(mkek)); + write_otp[0] = OTP_KEY_1; + } + *otp_key_1_out = otp_buffer(OTP_KEY_1); + + if (is_empty_otp_buffer(OTP_KEY_2, 32)) { + mbedtls_ecdsa_context ecdsa; + size_t olen = 0; + uint8_t pkey[MBEDTLS_ECP_MAX_BYTES]; + while (olen != 32) { + mbedtls_ecdsa_init(&ecdsa); + mbedtls_ecp_group_id ec_id = MBEDTLS_ECP_DP_SECP256K1; + mbedtls_ecdsa_genkey(&ecdsa, ec_id, random_fill_iterator, NULL); + mbedtls_ecp_write_key_ext(&ecdsa, &olen, pkey, sizeof(pkey)); + mbedtls_ecdsa_free(&ecdsa); + } + ret = otp_write_data(OTP_KEY_2, pkey, olen); + if (ret != 0) { + printf("Error writing OTP key 2 [%d]\n", ret); + } + mbedtls_platform_zeroize(pkey, sizeof(pkey)); + otp_chaff(OTP_KEY_2, 32); + write_otp[1] = OTP_KEY_2; + } + *otp_key_2_out = otp_buffer(OTP_KEY_2); + + for (size_t i = 0; i < sizeof(write_otp) / sizeof(write_otp[0]); i++) { + if (write_otp[i] != 0xFFFF) { + otp_lock_page(write_otp[i] >> 6); + } + } +}