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);
+ }
+ }
+}