From bc12015011069a600777d3d896b23e627af710b7 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 14 Jan 2025 15:25:17 +0000 Subject: [PATCH 01/80] Use 4-way key shares for AES private keys The privateaes.bin key file is now 4x256bit numbers (A,B,C,D), and the AES key X is A^B^C^D --- README.md | 3 ++- bintool/bintool.cpp | 4 ++-- bintool/bintool.h | 4 ++-- bintool/mbedtls_wrapper.c | 2 +- bintool/mbedtls_wrapper.h | 16 +++++++++++++++- main.cpp | 16 +++++++++++----- 6 files changed, 33 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b594878a..bc013250 100644 --- a/README.md +++ b/README.md @@ -586,7 +586,8 @@ The encrypted binary will have the following structure: - Padding to ensure the encrypted length is a multiple of 4 words - Signature metadata block -The AES key must be provided as a .bin file of the 256 bit AES key to be used for encryption. +The AES key must be provided as a .bin file containing a 4-way share of the 256 bit AES key to be used for encryption. +Eg if the 256 bit key is X, the bin file should contain the 256 bit numbers A B C D where X = A ^ B ^ C ^ D. ```text $ picotool help encrypt diff --git a/bintool/bintool.cpp b/bintool/bintool.cpp index 19debf6c..e19dec4b 100644 --- a/bintool/bintool.cpp +++ b/bintool/bintool.cpp @@ -855,7 +855,7 @@ void verify_block(std::vector bin, uint32_t storage_addr, uint32_t runt } -int encrypt(elf_file *elf, block *new_block, const private_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign) { +int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign) { std::vector to_enc = get_lm_hash_data(elf, new_block); @@ -961,7 +961,7 @@ int encrypt(elf_file *elf, block *new_block, const private_t aes_key, const publ } -std::vector encrypt(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const private_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign) { +std::vector encrypt(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign) { std::random_device rand{}; assert(rand.max() - rand.min() >= 256); diff --git a/bintool/bintool.h b/bintool/bintool.h index 2c574cde..a83f6095 100644 --- a/bintool/bintool.h +++ b/bintool/bintool.h @@ -25,7 +25,7 @@ std::unique_ptr find_first_block(elf_file *elf); block place_new_block(elf_file *elf, std::unique_ptr &first_block); #if HAS_MBEDTLS int hash_andor_sign(elf_file *elf, block *new_block, const public_t public_key, const private_t private_key, bool hash_value, bool sign, bool clear_sram = false); - int encrypt(elf_file *elf, block *new_block, const private_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign); + int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign); #endif // Bins @@ -37,6 +37,6 @@ block place_new_block(std::vector &bin, uint32_t storage_addr, std::uni uint32_t calc_checksum(std::vector bin); #if HAS_MBEDTLS std::vector hash_andor_sign(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const public_t public_key, const private_t private_key, bool hash_value, bool sign, bool clear_sram = false); - std::vector encrypt(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const private_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign); + std::vector encrypt(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign); void verify_block(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *block, verified_t &hash_verified, verified_t &sig_verified, get_more_bin_cb more_cb = nullptr); #endif diff --git a/bintool/mbedtls_wrapper.c b/bintool/mbedtls_wrapper.c index 0fcf083f..b00bb7a1 100644 --- a/bintool/mbedtls_wrapper.c +++ b/bintool/mbedtls_wrapper.c @@ -40,7 +40,7 @@ void mb_sha256_buffer(const uint8_t *data, size_t len, message_digest_t *digest_ mbedtls_sha256(data, len, digest_out->bytes, 0); } -void mb_aes256_buffer(const uint8_t *data, size_t len, uint8_t *data_out, const private_t *key, iv_t *iv) { +void mb_aes256_buffer(const uint8_t *data, size_t len, uint8_t *data_out, const aes_key_t *key, iv_t *iv) { mbedtls_aes_context aes; assert(len % 16 == 0); diff --git a/bintool/mbedtls_wrapper.h b/bintool/mbedtls_wrapper.h index c96909cc..c21999d7 100644 --- a/bintool/mbedtls_wrapper.h +++ b/bintool/mbedtls_wrapper.h @@ -40,11 +40,25 @@ typedef struct iv { uint8_t bytes[16]; } iv_t; /**< Convenience typedef */ +typedef struct aes_key_share { + union { + struct { + /** A 4-way share of the 256-bit value. */ + uint8_t bytes_a[32]; + uint8_t bytes_b[32]; + uint8_t bytes_c[32]; + uint8_t bytes_d[32]; + }; + uint8_t bytes[128]; + }; +} aes_key_share_t; /**< Convenience typedef */ + typedef signature_t public_t; typedef message_digest_t private_t; +typedef message_digest_t aes_key_t; void mb_sha256_buffer(const uint8_t *data, size_t len, message_digest_t *digest_out); -void mb_aes256_buffer(const uint8_t *data, size_t len, uint8_t *data_out, const private_t *key, iv_t *iv); +void mb_aes256_buffer(const uint8_t *data, size_t len, uint8_t *data_out, const aes_key_t *key, iv_t *iv); void mb_sign_sha256(const uint8_t *entropy, size_t entropy_size, const message_digest_t *m, const public_t *p, const private_t *d, signature_t *out); uint32_t mb_verify_signature_secp256k1( diff --git a/main.cpp b/main.cpp index 8b1576be..7b58194e 100644 --- a/main.cpp +++ b/main.cpp @@ -795,7 +795,7 @@ struct encrypt_command : public cmd { hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)" ).force_expand_help(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + - named_typed_file_selection_x("aes_key", 2, "bin") % "AES Key" + + named_typed_file_selection_x("aes_key", 2, "bin") % "AES Key Share" + optional_typed_file_selection_x("signing_key", 3, "pem") % "Signing Key file" ); } @@ -4800,7 +4800,7 @@ bool encrypt_command::execute(device_map &devices) { } if (get_file_type_idx(2) != filetype::bin) { - fail(ERROR_ARGS, "Can only read AES key from BIN file"); + fail(ERROR_ARGS, "Can only read AES key share from BIN file"); } if (settings.seal.sign && settings.filenames[3].empty()) { @@ -4813,10 +4813,16 @@ bool encrypt_command::execute(device_map &devices) { auto aes_file = get_file_idx(ios::in|ios::binary, 2); - - private_t aes_key; - aes_file->read((char*)aes_key.bytes, sizeof(aes_key.bytes)); + aes_file->exceptions(std::iostream::failbit | std::iostream::badbit); + + // Key is stored as a 4-way share, ie X = A ^ B ^ C ^ D + aes_key_share_t aes_key_share; + aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); + aes_key_t aes_key; + for (int i=0; i < sizeof(aes_key); i++) { + aes_key.bytes[i] = aes_key_share.bytes_a[i] ^ aes_key_share.bytes_b[i] ^ aes_key_share.bytes_c[i] ^ aes_key_share.bytes_d[i]; + } private_t private_key = {}; public_t public_key = {}; From 78f27fb23218b3b2fcb223656236754322036bca Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 15 Jan 2025 14:00:15 +0000 Subject: [PATCH 02/80] Remove check that ELF segments are between metadata blocks This is not required, as you can still load data outside of the region between the metadata blocks which contain the binary - for example, loading code into scratch memory. --- bintool/bintool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bintool/bintool.cpp b/bintool/bintool.cpp index e19dec4b..ae7ff520 100644 --- a/bintool/bintool.cpp +++ b/bintool/bintool.cpp @@ -625,7 +625,7 @@ std::vector get_lm_hash_data(elf_file *elf, block *new_block, bool clea if (data.size() != seg->physical_size()) { fail(ERROR_INCOMPATIBLE, "Elf segment physical size (%" PRIx32 ") does not match data size in file (%zx)", seg->physical_size(), data.size()); } - if (seg->physical_size() && seg->physical_address() < new_block->physical_addr) { + if (seg->physical_size()) { std::copy(data.begin(), data.end(), std::back_inserter(to_hash)); DEBUG_LOG("HASH %08x + %08x\n", (int)seg->physical_address(), (int)seg->physical_size()); entries.push_back( From d1088ff6c6216f43186b1419b71309e603a70862 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 15 Jan 2025 14:20:07 +0000 Subject: [PATCH 03/80] Fix overlapping memory ranges issue for encrypted binaries --- main.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 7b58194e..2bb1bc93 100644 --- a/main.cpp +++ b/main.cpp @@ -245,6 +245,10 @@ template struct range_map { } } + void empty() { + m.clear(); + } + pair get(uint32_t p) { auto f = m.upper_bound(p); if (f == m.end()) { @@ -2784,7 +2788,13 @@ void build_rmap_load_map(std::shared_ptrload_map, range_mapentries.size(); i++) { auto e = load_map->entries[i]; if (e.storage_address != 0) { - rmap.insert(range(e.runtime_address, e.runtime_address + e.size), e.storage_address); + try { + rmap.insert(range(e.runtime_address, e.runtime_address + e.size), e.storage_address); + } catch (command_failure&) { + // Overlapping memory ranges are permitted in a load_map, so empty the range map and then add the entry + rmap.empty(); + rmap.insert(range(e.runtime_address, e.runtime_address + e.size), e.storage_address); + } } } } From a65daa741374f15d33dd7f2159020fef8341c005 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 20 Jan 2025 11:17:37 +0000 Subject: [PATCH 04/80] Update share as word-wise --- bintool/mbedtls_wrapper.h | 18 ++++++++++-------- main.cpp | 9 ++++++--- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/bintool/mbedtls_wrapper.h b/bintool/mbedtls_wrapper.h index c21999d7..f3c7101b 100644 --- a/bintool/mbedtls_wrapper.h +++ b/bintool/mbedtls_wrapper.h @@ -40,22 +40,24 @@ typedef struct iv { uint8_t bytes[16]; } iv_t; /**< Convenience typedef */ +typedef struct aes_key { + /** An array 32 bytes key data. */ + union { + uint8_t bytes[32]; + uint32_t words[8]; + }; +} aes_key_t; /**< Convenience typedef */ + typedef struct aes_key_share { + /** An array 128 bytes key data, 1 word from each share at a time. */ union { - struct { - /** A 4-way share of the 256-bit value. */ - uint8_t bytes_a[32]; - uint8_t bytes_b[32]; - uint8_t bytes_c[32]; - uint8_t bytes_d[32]; - }; uint8_t bytes[128]; + uint32_t words[32]; }; } aes_key_share_t; /**< Convenience typedef */ typedef signature_t public_t; typedef message_digest_t private_t; -typedef message_digest_t aes_key_t; void mb_sha256_buffer(const uint8_t *data, size_t len, message_digest_t *digest_out); void mb_aes256_buffer(const uint8_t *data, size_t len, uint8_t *data_out, const aes_key_t *key, iv_t *iv); diff --git a/main.cpp b/main.cpp index 2bb1bc93..68173555 100644 --- a/main.cpp +++ b/main.cpp @@ -4825,13 +4825,16 @@ bool encrypt_command::execute(device_map &devices) { auto aes_file = get_file_idx(ios::in|ios::binary, 2); aes_file->exceptions(std::iostream::failbit | std::iostream::badbit); - // Key is stored as a 4-way share, ie X = A ^ B ^ C ^ D aes_key_share_t aes_key_share; aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); aes_key_t aes_key; - for (int i=0; i < sizeof(aes_key); i++) { - aes_key.bytes[i] = aes_key_share.bytes_a[i] ^ aes_key_share.bytes_b[i] ^ aes_key_share.bytes_c[i] ^ aes_key_share.bytes_d[i]; + // Key is stored as a 4-way share of each word, ie X[0] = A[0] ^ B[0] ^ C[0] ^ D[0], stored as A[0], B[0], C[0], D[0] + for (int i=0; i < count_of(aes_key.words); i++) { + aes_key.words[i] = aes_key_share.words[i*4] + ^ aes_key_share.words[i*4 + 1] + ^ aes_key_share.words[i*4 + 2] + ^ aes_key_share.words[i*4 + 3]; } private_t private_key = {}; From 4eac4572b1dfa35c054b5b1d33b0a6df28f4bfa2 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 20 Jan 2025 14:05:24 +0000 Subject: [PATCH 05/80] Add enc_bootloader binary You can now use `picotool encrypt --embed ...` to create a self-decrypting binary, using enc_bootloader --- BUILD.bazel | 22 + CMakeLists.txt | 44 + bintool/bintool.cpp | 15 +- bintool/bintool.h | 1 + enc_bootloader.cpp | 39 + enc_bootloader.h | 12 + enc_bootloader/BUILD.bazel | 18 + enc_bootloader/CMakeLists.txt | 55 + enc_bootloader/aes.S | 1726 +++++++++++++++++++++++++++++ enc_bootloader/config.h | 70 ++ enc_bootloader/enc_bootloader.c | 127 +++ enc_bootloader/enc_bootloader.elf | Bin 0 -> 58340 bytes main.cpp | 285 +++-- 13 files changed, 2309 insertions(+), 105 deletions(-) create mode 100644 enc_bootloader.cpp create mode 100644 enc_bootloader.h create mode 100644 enc_bootloader/BUILD.bazel create mode 100644 enc_bootloader/CMakeLists.txt create mode 100644 enc_bootloader/aes.S create mode 100644 enc_bootloader/config.h create mode 100644 enc_bootloader/enc_bootloader.c create mode 100755 enc_bootloader/enc_bootloader.elf diff --git a/BUILD.bazel b/BUILD.bazel index d120f500..267a2d40 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -17,6 +17,13 @@ picotool_binary_data_header( out = "xip_ram_perms_elf.h", ) +# TODO: Make it possible to build the prebuilt from source. +picotool_binary_data_header( + name = "enc_bootloader_elf", + src = "//enc_bootloader:enc_bootloader_prebuilt", + out = "enc_bootloader_elf.h", +) + # TODO: Make it possible to build the prebuilt from source. picotool_binary_data_header( name = "flash_id_bin", @@ -37,6 +44,19 @@ cc_library( ], ) +cc_library( + name = "enc_bootloader", + srcs = ["enc_bootloader.cpp"], + hdrs = [ + "enc_bootloader.h", + "enc_bootloader_elf.h", + ], + deps = [ + "//bazel:data_locs", + "//lib/whereami", + ], +) + filegroup( name = "data_locs_header", srcs = ["data_locs.h"], @@ -62,6 +82,7 @@ cc_binary( "otp.h", "rp2350.rom.h", "xip_ram_perms.cpp", + "enc_bootloader.cpp", ] + select({ # MSVC can't handle long strings, so use this manually generated # header instead. @@ -97,6 +118,7 @@ cc_binary( }), deps = [ ":xip_ram_perms", + ":enc_bootloader", "//bazel:data_locs", "//bintool", "//elf", diff --git a/CMakeLists.txt b/CMakeLists.txt index 14e0dcdf..1b9e21e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,33 @@ if (NOT PICOTOOL_NO_LIBUSB) DEPENDS xip_ram_perms ) + # compile enc_bootloader.elf + if (NOT DEFINED USE_PRECOMPILED) + set(USE_PRECOMPILED true) + endif() + ExternalProject_Add(enc_bootloader + PREFIX enc_bootloader + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/enc_bootloader + BINARY_DIR ${CMAKE_BINARY_DIR}/enc_bootloader + CMAKE_ARGS + "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}" + "-DPICO_SDK_PATH:FILEPATH=${PICO_SDK_PATH}" + "-DUSE_PRECOMPILED:BOOL=${USE_PRECOMPILED}" + "-DPICO_DEBUG_INFO_IN_RELEASE=OFF" + BUILD_ALWAYS 1 # todo remove this + INSTALL_COMMAND "" + ) + + set(ENC_BOOTLOADER_ELF ${CMAKE_BINARY_DIR}/enc_bootloader/enc_bootloader.elf) + add_executable(enc_bootloader_elf IMPORTED) + add_dependencies(enc_bootloader_elf enc_bootloader) + set_property(TARGET enc_bootloader_elf PROPERTY IMPORTED_LOCATION ${ENC_BOOTLOADER_ELF}) + # copy enc_bootloader.elf into build directory + add_custom_command(TARGET enc_bootloader + COMMAND ${CMAKE_COMMAND} -E copy ${ENC_BOOTLOADER_ELF} ${CMAKE_BINARY_DIR}/enc_bootloader.elf + DEPENDS enc_bootloader + ) + # compile flash_id ExternalProject_Add(flash_id PREFIX picoboot_flash_id @@ -172,6 +199,7 @@ endif() add_custom_target(binary_data DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h ${CMAKE_CURRENT_BINARY_DIR}/xip_ram_perms_elf.h + ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h ${CMAKE_CURRENT_BINARY_DIR}/flash_id_bin.h) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h COMMAND ${CMAKE_COMMAND} @@ -188,6 +216,14 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xip_ram_perms_elf.h DEPENDS xip_ram_perms COMMENT "Configuring xip_ram_perms_elf.h" VERBATIM) +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h + COMMAND ${CMAKE_COMMAND} + -D BINARY_FILE=${ENC_BOOTLOADER_ELF} + -D OUTPUT_NAME=enc_bootloader_elf + -P ${CMAKE_CURRENT_LIST_DIR}/cmake/binh.cmake + DEPENDS enc_bootloader + COMMENT "Configuring enc_bootloader_elf.h" + VERBATIM) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/flash_id_bin.h COMMAND ${CMAKE_COMMAND} -D BINARY_FILE=${FLASH_ID_BIN} @@ -233,8 +269,10 @@ target_include_directories(regs_headers INTERFACE ${PICO_SDK_PATH}/src/rp2350/ha # Main picotool executable add_executable(picotool data_locs.cpp + enc_bootloader.cpp ${OTP_EXE} main.cpp) +add_dependencies(picotool enc_bootloader_elf) if (NOT PICOTOOL_NO_LIBUSB) target_sources(picotool PRIVATE xip_ram_perms.cpp) add_dependencies(picotool generate_otp_header xip_ram_perms_elf binary_data) @@ -328,6 +366,12 @@ install(FILES DESTINATION ${INSTALL_CONFIGDIR} ) +#Install enc_bootloader.elf +install(FILES +${ENC_BOOTLOADER_ELF} +DESTINATION ${INSTALL_DATADIR} +) + if (NOT PICOTOOL_NO_LIBUSB) if (NOT PICOTOOL_CODE_OTP) #Install the otp json diff --git a/bintool/bintool.cpp b/bintool/bintool.cpp index ae7ff520..13c66c5a 100644 --- a/bintool/bintool.cpp +++ b/bintool/bintool.cpp @@ -855,8 +855,7 @@ void verify_block(std::vector bin, uint32_t storage_addr, uint32_t runt } -int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign) { - +void encrypt_guts(elf_file *elf, block *new_block, const aes_key_t aes_key, std::vector &iv_data, std::vector &enc_data) { std::vector to_enc = get_lm_hash_data(elf, new_block); std::random_device rand{}; @@ -872,13 +871,21 @@ int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const publ e = rand(); } - std::vector iv_data(iv.bytes, iv.bytes + sizeof(iv.bytes)); + iv_data.resize(sizeof(iv.bytes)); + memcpy(iv_data.data(), iv.bytes, sizeof(iv.bytes)); - std::vector enc_data; enc_data.resize(to_enc.size()); aes256_buffer(to_enc.data(), to_enc.size(), enc_data.data(), &aes_key, &iv); +} + +int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign) { + + std::vector iv_data; + std::vector enc_data; + encrypt_guts(elf, new_block, aes_key, iv_data, enc_data); + unsigned int i=0; for(const auto &seg : sorted_segs(elf)) { if (!seg->is_load()) continue; diff --git a/bintool/bintool.h b/bintool/bintool.h index a83f6095..d7db52cf 100644 --- a/bintool/bintool.h +++ b/bintool/bintool.h @@ -25,6 +25,7 @@ std::unique_ptr find_first_block(elf_file *elf); block place_new_block(elf_file *elf, std::unique_ptr &first_block); #if HAS_MBEDTLS int hash_andor_sign(elf_file *elf, block *new_block, const public_t public_key, const private_t private_key, bool hash_value, bool sign, bool clear_sram = false); + void encrypt_guts(elf_file *elf, block *new_block, const aes_key_t aes_key, std::vector &iv_data, std::vector &enc_data); int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign); #endif diff --git a/enc_bootloader.cpp b/enc_bootloader.cpp new file mode 100644 index 00000000..21b39333 --- /dev/null +++ b/enc_bootloader.cpp @@ -0,0 +1,39 @@ + +#include +#include +#include +#include +#include + +#include "enc_bootloader.h" +#include "enc_bootloader_elf.h" + +#include "data_locs.h" + +#include "whereami++.h" + + +std::shared_ptr get_enc_bootloader() { + // search same directory as executable + whereami::whereami_path_t executablePath = whereami::getExecutablePath(); + std::string local_loc = executablePath.dirname() + "/"; + if (std::find(data_locs.begin(), data_locs.end(), local_loc) == data_locs.end()) { + data_locs.insert(data_locs.begin(), local_loc); + } + + for (auto loc : data_locs) { + std::string filename = loc + "enc_bootloader.elf"; + std::ifstream i(filename); + if (i.good()) { + printf("Picking file %s\n", filename.c_str()); + auto file = std::make_shared(filename, std::ios::in|std::ios::binary); + return file; + } + } + + // fall back to embedded enc_bootloader.elf file + printf("Could not find enc_bootloader.elf file - using embedded binary\n"); + auto tmp = std::make_shared(); + tmp->write(reinterpret_cast(enc_bootloader_elf), enc_bootloader_elf_SIZE); + return tmp; +} diff --git a/enc_bootloader.h b/enc_bootloader.h new file mode 100644 index 00000000..dc942634 --- /dev/null +++ b/enc_bootloader.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include +#include + +std::shared_ptr get_enc_bootloader(); diff --git a/enc_bootloader/BUILD.bazel b/enc_bootloader/BUILD.bazel new file mode 100644 index 00000000..bf46b104 --- /dev/null +++ b/enc_bootloader/BUILD.bazel @@ -0,0 +1,18 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "enc_bootloader_prebuilt", + srcs = ["enc_bootloader.elf"], +) + +# TODO: Make this work. +cc_library( + name = "enc_bootloader", + srcs = ["enc_bootloader.c"], + tags = ["manual"], + deps = [ + "//:enc_bootloader", + "@pico-sdk//src/rp2_common/pico_stdlib", + "@pico-sdk//src/rp2_common/pico_rand", + ], +) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt new file mode 100644 index 00000000..1ef39fd4 --- /dev/null +++ b/enc_bootloader/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.12) + +if (NOT USE_PRECOMPILED) + set(PICO_PLATFORM rp2350-arm-s) + + set(PICO_NO_PICOTOOL 1) + + # If the user set these environment variables to influence the picotool + # build, unset them here so that they do not influence the pico-sdk + # build. This is especially required for flags that are not supported + # by arm-none-eabi compilers. + unset(ENV{CFLAGS}) + unset(ENV{CXXFLAGS}) + unset(ENV{LDFLAGS}) + + # Pull in SDK (must be before project) + include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) + + project(enc_bootloader C CXX ASM) + set(CMAKE_C_STANDARD 11) + set(CMAKE_CXX_STANDARD 17) + + if (PICO_SDK_VERSION_STRING VERSION_LESS "2.0.0") + message(FATAL_ERROR "Raspberry Pi Pico SDK version 2.0.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") + endif() + + # Initialize the SDK + pico_sdk_init() + + # Encrypted Bootloader + add_executable(enc_bootloader + enc_bootloader.c + aes.S + ) + + target_link_libraries(enc_bootloader + pico_stdlib + pico_rand + ) + + pico_set_binary_type(enc_bootloader no_flash) + # create linker script to run from 0x20070000 + file(READ ${PICO_LINKER_SCRIPT_PATH}/memmap_no_flash.ld LINKER_SCRIPT) + string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x20078000, LENGTH = 32k" LINKER_SCRIPT "${LINKER_SCRIPT}") + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/memmap_enc_bootloader.ld "${LINKER_SCRIPT}") + pico_set_linker_script(enc_bootloader ${CMAKE_CURRENT_BINARY_DIR}/memmap_enc_bootloader.ld) +else() + project(enc_bootloader C CXX ASM) + message("Using precompiled enc_bootloader.elf") + configure_file(${CMAKE_CURRENT_LIST_DIR}/enc_bootloader.elf ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader.elf COPYONLY) + # Use manually specified variables + set(NULL ${CMAKE_MAKE_PROGRAM}) + set(NULL ${PICO_SDK_PATH}) + set(NULL ${PICO_DEBUG_INFO_IN_RELEASE}) +endif() diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S new file mode 100644 index 00000000..d51605a4 --- /dev/null +++ b/enc_bootloader/aes.S @@ -0,0 +1,1726 @@ +.syntax unified +.cpu cortex-m33 +.thumb + +#include "config.h" +#include "hardware/platform_defs.h" +#include "hardware/regs/addressmap.h" +#include "hardware/regs/sha256.h" +#include "hardware/rcp.h" + +.global gen_lut_sbox +.global ctr_crypt_s +.global remap +.global gen_rand_sha +.global init_key + +.global rkey_s +.global lut_a,lut_a_map +.global lut_b,lut_b_map +.global rstate_sha,rstate_lfsr + +@ RCP macros + +#define CTAG0 0x2a +#define CTAG1 0x2b +#define CTAG2 0x2c +#define CTAG3 0x2d @ not used +#define CTAG4 0x2e +#define CTAG5 0x30 +#define CTAG6 0x31 +#define CTAG7 0x32 +#define CTAG8 0x33 +#define CTAG9 0x34 +#define CTAG10 0x35 @ not used +#define CTAG11 0x36 +#define CTAG12 0x37 +#define CTAG13 0x38 +#define CTAG14 0x39 +#define CTAG15 0x3a +#define CTAG16 0x3b +#define CTAG17 0x3c +#define CTAG18 0x3d @ not used + +.macro SET_COUNT n +.if RC_COUNT +.if RC_JITTER + rcp_count_set \n +.else + rcp_count_set_nodelay \n +.endif +.endif +.endm + +.macro CHK_COUNT n +.if RC_COUNT +.if RC_JITTER + rcp_count_check \n +.else + rcp_count_check_nodelay \n +.endif +.endif +.endm + +.macro GET_CANARY rx,tag +.if RC_CANARY +.if RC_JITTER + rcp_canary_get \rx,\tag +.else + rcp_canary_get_nodelay \rx,\tag +.endif +.endif +.endm + +.macro CHK_CANARY rx,tag +.if RC_CANARY +.if RC_JITTER + rcp_canary_check \rx,\tag +.else + rcp_canary_check_nodelay \rx,\tag +.endif +.endif +.endm + +.macro GET_CANARY_NJ rx,tag @ with no jitter even if you ask for it (for situations where it would otherwise slow things down a lot) +.if RC_CANARY + rcp_canary_get_nodelay \rx,\tag +.endif +.endm + +.macro CHK_CANARY_NJ rx,tag @ with no jitter even if you ask for it +.if RC_CANARY + rcp_canary_check_nodelay \rx,\tag +.endif +.endm + +.macro clear03 offset=0 + getchaffaddress r0,\offset + ldmia r0,{r0-r3} +.endm + +.macro clear03_preserve_r3 offset=0 + getchaffaddress r0,\offset + ldmia r0!,{r1-r2} + ldmia r0!,{r1-r2} +.endm + +.macro clear01 offset=0 + getchaffaddress r0,\offset + ldmia r0,{r0,r1} +.endm + +@ Put workspace in the second scratch area +@ The "a"=allocatable attribute (and possibly the %progbits attribute) are necessary to store the murmur3 constants, +@ otherwise they may end up silently replaced with 0 or 0xffffffff +.section .scratch_y.aes,"a",%progbits + +@ chaff has to be at the start of scratch_y = 0x20081000 because this is assumed by the following macro, getchaffaddress +@ (It seems ADR does not work, nor is it possible to assert that chaff==0x20081000) +@ getchaffaddress is used by clear03 and clear01 and other sensitive cases which require the first load to be a random one +@ chaff has to be 0 mod 16 for other reasons +.macro getchaffaddress rx,offset=0 +@ ldr \rx,=(chaff+\offset) + mov \rx,#(0x1000+\offset) + movt \rx,#0x2008 +.endm +chaff: +.space 48 + +@ Regardless of configuration, the code uses a single 256-entry LUT, +@ which is a simple S-box table. +@ The LUT is represented as two shares, lut_a and lut_b, +@ whose values must be EORed. Furthermore, the contents of each share are +@ scambled according to a 4-byte "map". The map comprises two bytes that +@ are EORed into the addressing of the share, and two bytes that are +@ EORed into the data read back from the share. Performing a lookup +@ of a value x involves computing +@ lut_a[x ^ a₀ ^ a₁] ^ c₀ ^ c₁ ^ lut_b[x ^ b₀ ^ b₁] ^ d₀ ^ d₁ +@ where a₀, a₁, c₀ and c₁ are the "map" of the lut_a share and +@ b₀, b₁, d₀ and d₁ are the "map" of the lut_b share. +@ In practice the result of a lookup is itself represented in two +@ shares, namely +@ lut_a[x ^ a₀ ^ a₁] ^ c₀ ^ d₀ and +@ lut_b[x ^ b₀ ^ b₁] ^ c₁ ^ d₁ +.balign 16 +lut_a: @ LUT share A (must be 0 mod 16 so that init_key_sbox knows how to mask the lookup) +.space 256 +lut_a_map: @ the current scrambling of lut_a; not particularly secret since it can be deduced from the contents of lut_a and lut_b +.space 4 +.space 4 @ align to 8 mod 16 +lut_b: @ LUT share B (must be 8 mod 16 so that init_key_sbox knows how to mask the lookup) +.space 256 +lut_b_map: +.space 4 +.space 4 @ align to multiple of 8 +rkey_s: @ round key shares: 600 bytes = 15 rounds * 2 shares * (4+1) words + @ every fourth word has a word that is used as a vperm count, and also as a spacer to misalign the shares mod 16 +.space 600 +rkey4way: @ scratch area for init_key; could overlap this with other scratch space if need to save space +.space 128 +.if CT_BPERM +bperm_rand: @ 32 half words that define the oblivious permutation of blocks +.space 64 +.endif +.balign 16 +rstate_sha: @ 128-bit SHA random state, to be initialised to TRNG bytes; zeroth byte must be initialised to zero +.space 16 +rstate_lfsr: @ 32-bit LFSR random state and constant used to step it (initialised by C program) +.space 8 +.balign 16 +permscratch: @ Must be 0 mod 16; 16 bytes of scratch space to store permutation(s) +perm16: +.space 16 +@ Scratch space of 32 bytes used both by init_key_sbox and map_sbox_s +.balign 16 +fourway: @ Must be 0 mod 16 +shareA: @ 0 mod 16 +.space 20 @ Only need 16 bytes, but choosing shareB!=shareA mod 16 +shareB: @ 4 mod 16 +.space 20 +shareC: @ 8 mod 16 +.space 4 +statevperm: @ 12 mod 16 +.space 4 @ vperm state rotation: only last two bits are operational; other bits random +RKshareC: +.space 4 +.balign 16 + +.if CT_BPERM +.balign 16 +murmur3_constants: @ Five constants used in murmur3_32 hash +.word 0xcc9e2d51 +.word 0x1b873593 +.word 0xe6546b64 +.word 0x85ebca6b +.word 0xc2b2ae35 +.endif + +@ Put main code in first scratch area +.section .scratch_x.aes,"ax",%progbits + +.if GEN_RAND_SHA +@ we need SHA256_SUM0_OFFSET==8 (see note below) +.if SHA256_SUM0_OFFSET!=8 +.err +.endif + +@ Return single random word in r0 +@ Preserves r1-r13 +.balign 4 +gen_rand_sha: + push {r14} + GET_CANARY_NJ r14,CTAG1 + push {r1-r3,r14} + bl gen_rand_sha_nonpres + pop {r1-r3,r14} + CHK_CANARY_NJ r14,CTAG1 + pop {r15} + +@ Return single random word in r0 +@ Trashes r1-r3 +.balign 4 +gen_rand_sha_nonpres: + ldr r0,=SHA256_BASE + ldr r2,=rstate_sha + ldrb r1,[r2] @ get word counter from bottom byte of rstate_sha[] (offset into SUM registers) + subs r3,r1,#4 @ decrement it to previous SUM register + ble 1f @ if the offset was 4 or less we have run out of SUM register values + ldr r0,[r0,r1] @ read value from SUM register: note that this relies on SHA256_SUM0_OFFSET==8 + strb r3,[r2] @ save updated SUM register offset in bottom byte of rstate_sha[] + bx r14 +1: + movs r3,#SHA256_SUM6_OFFSET+1 + strb r3,[r2] @ reset word counter: the +1 is compensated for later + movw r1,#(1<>30, vpermB=Bptr[4]>>30, and +@ roundkey shareA(i) = Aptr[i+vpermA mod 4] ror ((i+vpermA mod 4)^th byte of Aptr[4]) +@ roundkey shareB(i) = Bptr[i+vpermB mod 4] ror ((i+vpermB mod 4)^th byte of Bptr[4])+16 +.balign 4 +.thumb_func +ref_roundkey_shares_s: + mov r11,#15 @ there are 15 expanded keys +ref_roundkey_shares_s_test: @ entry point for test code to do fewer than 15 rounds + ldr r4,=rkey_s + loadlfsr + steplfsr @ r0=change in RKshareC + adr r2,RKshareCchange + str r0,[r2] + ldr r3,=RKshareC + ldr r5,[r3] + eors r5,r5,r0 + str r5,[r3] + @ r0=lfsr_state, r1=lfsr_const, r4=roundkey_ptr, r11=roundcounter + +ref_roundkey_shares_s_loop: + ldmia r4!,{r5-r8,r10} @ r5-r8 = rkey shareA, r10=X_A=vperm+rotations of rkey shareA + + ldr r12,[r4,#16] @ r12 = X_B=vperm+rotations of rkey shareB + mov r2,r12,lsr#30 @ r2 = vpermB + sub r9,r2,r10,lsr#30 @ r9 = vpermB - vpermA (|junk) + mov r2,r9,lsl#3 @ r2 = 8*(vpermB - vpermA) mod 32 + mov r12,r12,ror r2 + usub8 r12,r10,r12 @ r12 = rotsA - (rotsB ror r2) + + @ r2,r3,r10=workspace, r0=lfsr_state, r1=lfsr_const, r4=roundkeyB_ptr, r5-r8=roundkeyA, r9=vpermdiff, r10=rotsA, r11=roundcounter, r12=rotdiff + steplfsr; eors r5,r5,r0; ands r9,r9,#3; ldr r3,[r4,r9,lsl#2]; ror r2,r0,r12; eors r3,r3,r2,ror#16; mov r12,r12,ror#8; str r3,[r4,r9,lsl#2]; adds r9,r9,#1 + steplfsr; eors r6,r6,r0; ands r9,r9,#3; ldr r3,[r4,r9,lsl#2]; ror r2,r0,r12; eors r3,r3,r2,ror#16; mov r12,r12,ror#8; str r3,[r4,r9,lsl#2]; adds r9,r9,#1 + steplfsr; eors r7,r7,r0; ands r9,r9,#3; ldr r3,[r4,r9,lsl#2]; ror r2,r0,r12; eors r3,r3,r2,ror#16; mov r12,r12,ror#8; str r3,[r4,r9,lsl#2]; adds r9,r9,#1 + steplfsr; eors r8,r8,r0; ands r9,r9,#3; ldr r3,[r4,r9,lsl#2]; ror r2,r0,r12; eors r3,r3,r2,ror#16; str r3,[r4,r9,lsl#2] + + ldr r3,RKshareCchange + movs r2,#0 + usub8 r10,r2,r10 + ror r2,r3,r10; mov r10,r10,ror#8; eors r5,r5,r2 + ror r2,r3,r10; mov r10,r10,ror#8; eors r6,r6,r2 + ror r2,r3,r10; mov r10,r10,ror#8; eors r7,r7,r2 + ror r2,r3,r10; eors r8,r8,r2 + + subs r4,r4,#20 + stmia r4,{r5-r8} + adds r4,r4,#40 + subs r11,r11,#1 + + bne ref_roundkey_shares_s_loop + ldr r2,=rstate_lfsr @ restore rstate_lfsr + savelfsr @ Save lfsr_state + clear03 24 +ref_roundkey_shares_s_exit: + bx r14 + .balign 4 +RKshareCchange: + .space 4 + +.balign 4 +.thumb_func +@ Rotates roundkey vperms and RK_ROR rotations by random amounts +@ Trashes r0-r10 +@ If i = word number 0..3, +@ Aptr=memory word pointer to block of 20 bytes containing H&V-rotated share A roundkey (similarly B), then +@ vpermA=Aptr[4]>>30, vpermB=Bptr[4]>>30, and +@ roundkey shareA(i) = Aptr[i+vpermA mod 4] ror ((i+vpermA mod 4)^th byte of Aptr[4]) +@ roundkey shareB(i) = Bptr[i+vpermB mod 4] ror ((i+vpermB mod 4)^th byte of Bptr[4])+16 +ref_roundkey_hvperms_s: + movs r7,#30 +ref_roundkey_hvperms_s_test: @ entry point for test code to do fewer than 30 key shares + GET_CANARY r10,CTAG9 + push {r10,r14} + ldr r10,=rkey_s +ref_roundkey_hvperms_s_loop: + bl gen_rand_lfsr_nonpres @ r0=new vperm high|rotations + ldmia r10,{r2-r5,r9} @ r2-r5=roundkey share A/B, r9=old vperm high|rotations + str r0,[r10,#16] + mov r8,r0,lsr#30 @ r8=new vperm low + sub r6,r8,r9,lsr#30 @ r6=(new vperm low)-(old vperm low) | junk + mov r8,r6,lsl#3 @ r8=8*((new vperm low)-(old vperm low)) mod 32 + mov r0,r0,ror r8 + usub8 r0,r9,r0 @ i^th byte of r0 = (i^th byte of old rotations) - ((i+newvperm-oldvperm)^th byte of new rotations) + movs r2,r2,ror r0; ands r6,r6,#3; str r2,[r10,r6,lsl#2]; movs r0,r0,ror#8; adds r6,r6,#1 + movs r3,r3,ror r0; ands r6,r6,#3; str r3,[r10,r6,lsl#2]; movs r0,r0,ror#8; adds r6,r6,#1 + movs r4,r4,ror r0; ands r6,r6,#3; str r4,[r10,r6,lsl#2]; movs r0,r0,ror#8; adds r6,r6,#1 + movs r5,r5,ror r0; ands r6,r6,#3; str r5,[r10,r6,lsl#2] + adds r10,r10,#20 + subs r7,r7,#1 + bne ref_roundkey_hvperms_s_loop + clear03 28 +ref_roundkey_hvperms_s_exit: @ label exit point to be to able to specify to analysis code + pop {r10,r14} + CHK_CANARY r10,CTAG9 + bx r14 + +.else + +@ "refresh" shares of rkeys by random eor into both shares of each word, and also randomise the single word RKshareC +@ Trashes r0-r11 +.balign 4 +.thumb_func +ref_roundkey_shares_s: + mov r11,#15 @ there are 15 expanded keys +ref_roundkey_shares_s_test: @ entry point for test code to do fewer than 15 rounds + GET_CANARY r4,CTAG8 + push {r4,r14} + ldr r4,=rkey_s + loadlfsr + steplfsr @ r0=change in RKshareC + ldr r3,=RKshareC + ldr r5,[r3] + eors r5,r5,r0 + str r5,[r3] + mov r10,r0 +ref_roundkey_shares_s_loop: + ldmia r4!,{r5-r9} @ r5-r8 = rkey shareA with vperm r9 + + @ clear03: would need to do this with, say r2,r3,r12 (reloading r2 later) + + ldr r3,[r4,#16] @ rkey shareB has a vperm of r10>>30 + movs r3,r3,lsr#30 + sub r9,r3,r9,lsr#30 @ r9 = vperm_B - vperm_A (|junk) + @ r3,r12=workspace, r0=lfsr_state, r1=lfsr_const, r2=rstate_lfsr, r4=roundkeyB_ptr, r5-r8=roundkeyA, r9=vpermdiff, r10=RKshareCchange, r11=roundcounter + + steplfsr; eors r5,r5,r0; and r9,r9,#3; eors r5,r5,r10; ldr r3,[r4,r9,lsl#2]; eors r3,r3,r0,ror#16; str r3,[r4,r9,lsl#2]; adds r9,r9,#1 + steplfsr; eors r6,r6,r0; and r9,r9,#3; eors r6,r6,r10; ldr r3,[r4,r9,lsl#2]; eors r3,r3,r0,ror#16; str r3,[r4,r9,lsl#2]; adds r9,r9,#1 + steplfsr; eors r7,r7,r0; and r9,r9,#3; eors r7,r7,r10; ldr r3,[r4,r9,lsl#2]; eors r3,r3,r0,ror#16; str r3,[r4,r9,lsl#2]; adds r9,r9,#1 + steplfsr; eors r8,r8,r0; and r9,r9,#3; eors r8,r8,r10; ldr r3,[r4,r9,lsl#2]; eors r3,r3,r0,ror#16; str r3,[r4,r9,lsl#2] + + subs r4,r4,#20 + stmia r4,{r5-r8} + adds r4,r4,#40 + subs r11,r11,#1 + + @ clear03: would need to do this with, say r3,r5-r8 + + bne ref_roundkey_shares_s_loop + savelfsr + clear03 24 +ref_roundkey_shares_s_exit: + pop {r4,r14} + CHK_CANARY r4,CTAG8 + bx r14 + +.balign 4 +.thumb_func +@ Rotates roundkey vperms by random amounts +@ Trashes r0-r9 +ref_roundkey_hvperms_s: + movs r7,#30 +ref_roundkey_hvperms_s_test: @ entry point for test code to do fewer than 30 key shares + GET_CANARY r0,CTAG9 + push {r0,r14} + bl gen_rand_lfsr_nonpres + ldr r1,=rkey_s +ref_roundkey_hvperms_s_loop: + cmp r7,#15 + bne 2f +@ Get a new random r0 after using 15 x 2 bits of the original one +@ Note that the junk bits (2-31) in the vperms are not adjusted independently, but that's no big loss, +@ and the gain is only calling gen_rand_lfsr twice instead of 30 times. + push {r1}; bl gen_rand_lfsr_nonpres; pop {r1} + 2: + ldmia r1,{r2-r5,r9} @ roundkey share A/B=r2-r5, vperm=r9 (including junk bits) + mov r8,r9,lsr#30 @ r8=old vperm (low) + add r6,r9,r0 @ r6=new vperm (high) | new junk + str r6,[r1,#16] + rsb r6,r8,r6,lsr#30 @ r6=(new vperm low)-(old vperm low) | junk bits + ands r6,r6,#3; str r2,[r1,r6,lsl#2]; adds r6,r6,#1 + ands r6,r6,#3; str r3,[r1,r6,lsl#2]; adds r6,r6,#1 + ands r6,r6,#3; str r4,[r1,r6,lsl#2]; adds r6,r6,#1 + ands r6,r6,#3; str r5,[r1,r6,lsl#2] + adds r1,r1,#20 + movs r0,r0,ror#2 + subs r7,r7,#1 + bne ref_roundkey_hvperms_s_loop + clear03 28 +ref_roundkey_hvperms_s_exit: @ label exit point to be to able to specify to analysis code + pop {r0,r14} + CHK_CANARY r0,CTAG9 + bx r14 + +.endif + +.if ST_VPERM +.balign 4 +.thumb_func +@ Rotate share registers r4-r7, r8-r11 (r4->r5-r6->r7->r4 etc.) by an addtional amount +@ given in the bottom two bits of R0 and update the rotation recorded at statevperm. +@ On entry R1 must point to statevperm. +@ Trashes r0-r3,r12 +@ Maintains r4=rorig(4+(-!r1)%4), r5=rorig(4+(1-!r1)%4), ... +@ r8=rorig(8+(-!r1)%4), r9=rorig(8+(1-!r1)%4), ... +@ Note: only low 2 bits of !r1 are used. The rest are random to add to the noise. +addstatevperm: + ldr r2,[r1] + adds r2,r2,r0 + str r2,[r1] + + ldr r1,=shareA + ands r0,r0,#3; str r4,[r1,r0,lsl#2]; adds r0,r0,#1 + ands r0,r0,#3; str r5,[r1,r0,lsl#2]; adds r0,r0,#1 + ands r0,r0,#3; str r6,[r1,r0,lsl#2]; adds r0,r0,#1 + ands r0,r0,#3; str r7,[r1,r0,lsl#2]; adds r0,r0,#1 + ldmia r1,{r4-r7} + + getchaffaddress r12 @ Overwrite temporary storage with random numbers + ldmia r12!,{r2,r3} + stmia r1!,{r2,r3} + ldmia r12!,{r2,r3} + stmia r1!,{r2,r3} + + ldr r1,=shareB + ands r0,r0,#3; str r8, [r1,r0,lsl#2]; adds r0,r0,#1 + ands r0,r0,#3; str r9, [r1,r0,lsl#2]; adds r0,r0,#1 + ands r0,r0,#3; str r10,[r1,r0,lsl#2]; adds r0,r0,#1 + ands r0,r0,#3; str r11,[r1,r0,lsl#2]; adds r0,r0,#1 + ldmia r1,{r8-r11} + + getchaffaddress r0,16 @ Overwrite temporary storage with random numbers + ldmia r0!,{r2,r3} + stmia r1!,{r2,r3} + ldmia r0!,{r2,r3} + stmia r1!,{r2,r3} + +addstatevperm_exit: @ label exit point to be to able to specify to analysis code + bx r14 +.endif + +@ Switch from non-shared to shared state +@ Trashes r0-r3,r12 +.balign 4 +ns_to_s: + GET_CANARY r12,CTAG11 + push {r12,r14} +.if ST_SHAREC + bl gen_rand_sha_nonpres @ Create state share C; all bytes the same + ands r0,r0,#255 + orrs r0,r0,r0,lsl#8 + orrs r12,r0,r0,lsl#16 + ldr r1,=shareC + str r12,[r1] +.else + movs r12,#0 +.endif + bl gen_rand_sha_nonpres + eors r4,r4,r0 + eor r8,r12,r0,ror#16 + bl gen_rand_sha_nonpres + eors r5,r5,r0 + eor r9,r12,r0,ror#16 + bl gen_rand_sha_nonpres + eors r6,r6,r0 + eor r10,r12,r0,ror#16 + bl gen_rand_sha_nonpres + eors r7,r7,r0 + eor r11,r12,r0,ror#16 +.if ST_VPERM + bl gen_rand_sha_nonpres + ldr r1,=statevperm + movs r2,#0 + str r2,[r1] + bl addstatevperm @ Initialise state vperm with SHA RNG, refresh with LFSR RNG +.endif + pop {r12,r14} + CHK_CANARY r12,CTAG11 + bx r14 + +@ Conjugate lut_a, lut_b with shareC +@ I.e., EOR the input and output with shareC. +@ We need to pick one input for each share A and B, and one output for ONE of the shares A and B +@ Arbitrarily choosing a0, b1 and d0 +.balign 4 +conjshareC: +.if ST_SHAREC + ldr r1,=shareC + ldr r0,[r1] @ Get shareC as a word (all bytes the same) + ldr r1,=lut_a @ Need to EOR share C into inputs of both lut_a and lut_b, and one of their outputs... + ldr r2,[r1,#0x100] + eors r2,r2,r0,lsr#24 + str r2,[r1,#0x100] + movs r0,r0,lsr#16 + ldr r1,=lut_b @ ... (continued) Here we're EORing share C into a0, b1 and d0. + ldr r2,[r1,#0x100] + eors r2,r2,r0,lsl#8 + str r2,[r1,#0x100] +.endif + bx r14 + +.balign 4 +.thumb_func +shift_rows_s: +@ First "rotate" the two most-significant bytes of the state by two registers +@ Trashes r0-r3 +@ Slightly faster (but not shorter?) with ubfx/bfi + eors r0,r4,r6 @ ta=state[0]^state[2]; ta&=0xffff0000; state[0]^=ta; state[2]^=ta; + lsrs r0,r0,#16 + lsls r0,r0,#16 + eors r4,r4,r0 + eors r6,r6,r0 + eors r0,r5,r7 @ ta=state[1]^state[3]; ta&=0xffff0000; state[1]^=ta; state[3]^=ta; + lsrs r0,r0,#16 + lsls r0,r0,#16 + eors r5,r5,r0 + eors r7,r7,r0 +@ next "rotate" the two odd-significance bytes of the state by one register + eors r1,r7,r4 @ tb=state[3]^state[0]; tb&=0xff00ff00; + ands r1,r1,#0xff00ff00 + eors r0,r4,r5 @ ta=state[0]^state[1]; ta&=0xff00ff00; state[0]^=ta; + ands r0,r0,#0xff00ff00 + eors r4,r4,r0 + eors r0,r5,r6 @ ta=state[1]^state[2]; ta&=0xff00ff00; state[1]^=ta; + ands r0,r0,#0xff00ff00 + eors r5,r5,r0 + eors r0,r6,r7 @ ta=state[2]^state[3]; ta&=0xff00ff00; state[2]^=ta; + ands r0,r0,#0xff00ff00 + eors r6,r6,r0 + eors r7,r7,r1 @ state[3]^=tb; +@ repeat for other share, conjugated by ror#16 + clear01 @ barrier + eors r0,r8,r10 @ ta=state[0]^state[2]; ta&=0x0000ffff; state[0]^=ta; state[2]^=ta; + lsls r0,r0,#16 + lsrs r0,r0,#16 + eors r8,r8,r0 + eors r10,r10,r0 + eors r0,r9,r11 @ ta=state[1]^state[3]; ta&=0x0000ffff; state[1]^=ta; state[3]^=ta; + lsls r0,r0,#16 + lsrs r0,r0,#16 + eors r9,r9,r0 + eors r11,r11,r0 + eors r1,r11,r8 @ tb=state[3]^state[0]; tb&=0xff00ff00; + ands r1,r1,#0xff00ff00 + eors r0,r8,r9 @ ta=state[0]^state[1]; ta&=0xff00ff00; state[0]^=ta; + ands r0,r0,#0xff00ff00 + eors r8,r8,r0 + eors r0,r9,r10 @ ta=state[1]^state[2]; ta&=0xff00ff00; state[1]^=ta; + ands r0,r0,#0xff00ff00 + eors r9,r9,r0 + eors r0,r10,r11 @ ta=state[2]^state[3]; ta&=0xff00ff00; state[2]^=ta; + ands r0,r0,#0xff00ff00 + eors r10,r10,r0 + + eors r11,r11,r1 @ state[3]^=tb; + + clear01 @ barrier + bx r14 + +@ multiply polynomial over GF(2⁸) by c(x) = 0x03x³ + 0x01x² + 0x01x + 0x02 modulo x⁴+1 +@ r0x00 is a register holding 0x00000000; r0x1b is a register holding 0x1b1b1b1b +.macro mixcol rx,rt,ru,r0x00,r0x1b + @ let rx=(a,b,c,d) + uadd8 \rt,\rx,\rx @ MSB of each byte into the GE flags + sel \ru,\r0x1b,\r0x00 @ get bytewise correction for bytewise field multiplication by 2 + eors \rt,\rt,\ru @ (2a,2b,2c,2d) + + eors \ru,\rt,\rx @ (3a,3b,3c,3d) + eors \rt,\rt,\rx,ror#24 @ (2a+b,2b+c,2c+d,2d+a) + eors \rt,\rt,\rx,ror#16 @ (2a+b+c,2b+c+d,2c+d+a,2d+a+b) + eors \rx,\rt,\ru,ror#8 @ (2a+b+c+3d,2b+c+d+3a,2c+d+a+3b,2d+a+b+3c) +.endm + +@ multiply polynomial over GF(2⁸) by d(x) = 0x0Bx³ + 0x0Dx² + 0x09x + 0x0E modulo x⁴+1; c(x)d(x)=1 modulo x⁴+1 +.macro invmixcol rx,rt,ru,rv,rw,r0x00,r0x1b +@ !!! can probably save some registers, e.g. allow trashing of r0x00, r0x1b +@ can possibly also simplify slightly with refactorisation + uadd8 \rt,\rx,\rx @ field multiplication by 2 as above + sel \rw,\r0x1b,\r0x00 + eors \rt,\rt,\rw @ 2x + uadd8 \ru,\rt,\rt + sel \rw,\r0x1b,\r0x00 + eors \ru,\ru,\rw @ 4x + uadd8 \rv,\ru,\ru + sel \rw,\r0x1b,\r0x00 + eors \rv,\rv,\rw @ 8x + + eors \rx,\rx,\rv @ 9x + eors \rw,\rx,\rt @ 11x + eors \rw,\rw,\rx,ror#16 @ 11x ^ 9x ROL #16 + eors \rx,\rx,\ru @ 13x + eors \rw,\rw,\rx,ror#8 @ 11x ^ 9x ROL #16 ^ 13x ROL #24 + eors \rt,\rt,\ru @ 6x + eors \rt,\rt,\rv @ 14x + eors \rx,\rt,\rw,ror#8 @ 14x ^ 9x ROL #8 ^ 13x ROL #16 ^ 11x ROL #24 +.endm + +.balign 4 +.thumb_func +@ Trashes r0-r3,r12 +mix_cols_s: + mov r2,#0x00000000 + mov r3,#0x1b1b1b1b + mixcol r4 ,r0,r1,r2,r3 @ apply mixcol to each state word + mixcol r5 ,r0,r1,r2,r3 + mixcol r6 ,r0,r1,r2,r3 + mixcol r7 ,r0,r1,r2,r3 + ldr r12,=chaff + ldmia r12!,{r0,r1} @ overwrite sensitive shareA-related quantities r0,r1 with random numbers + mixcol r8 ,r0,r1,r2,r3 + mixcol r9 ,r0,r1,r2,r3 + mixcol r10,r0,r1,r2,r3 + mixcol r11,r0,r1,r2,r3 + ldmia r12!,{r0,r1} @ overwrite sensitive shareB-related quantities r0,r1 with random numbers + bx r14 + +.balign 4 +.thumb_func +gen_lut_sbox: +@ gen_lut_sbox sets both lut_a and lut_b to the S-box table and +@ returns r0=lut_a+256, r1=lut_b+256 +@ first set lut_a to be a table of GF(2⁸) inverses, using lut_b as temporary storage + ldr r0,=lut_a + ldr r1,=lut_b +@ first set lut_a to be a table of antilogarithms, lut_b a table of logarithms + mov r2,#0 + strb r2,[r0] @ (*) + mov r3,#1 @ we maintain invariant that r2=log(r3) +1: + strb r2,[r0,r3] @ log table + strb r3,[r1,r2] @ antilog table + lsls r12,r3,#25 + it cs + eorcs r12,r12,#0x1b000000 @ multiply by x + eor r3,r3,r12,lsr#24 @ multiply by x+1 ("3"), which is a primitive element + add r2,r2,#1 + cmp r2,#255 + bls 1b + movs r2,#255 +1: + ldrb r3,[r0,r2] @ for each i≠0, find log,... + eor r3,r3,#255 @ ... negate... + ldrb r3,[r1,r3] @ ... and antilog to get inverse + strb r3,[r0,r2] + subs r2,r2,#1 + bne 1b @ note that inverse(0)=0 by (*) above +@ At this point r0=lut_a, r1=lut_b, lut_a[] contains inverses and lut_b[] contains other stuff + mov r12,#256 +1: + ldrb r2,[r0] + eors r3,r2,r2,lsl#1 @ convolve byte with 0x1f + eors r3,r3,r3,lsl#2 + eors r3,r3,r2,lsl#4 + eors r2,r3,r3,lsr#8 + eor r2,r2,#0x63 @ and add 0x63 + strb r2,[r0],#1 @ let lut_a[i]=sbox[i] + strb r2,[r1],#1 @ let lut_b[i]=sbox[i] + subs r12,r12,#1 + bne 1b + bx r14 + +@ Lookup each byte of a word, Rtarg, in a table and replace Rtarg with the result (used for SBOX lookups) +.macro subbytes Rtarg,Rtable,Rspare0,Rspare1,Rspare2,Rspare3 + ubfx \Rspare0,\Rtarg,#0, #8 + ubfx \Rspare1,\Rtarg,#8, #8 + ubfx \Rspare2,\Rtarg,#16, #8 + ubfx \Rspare3,\Rtarg,#24, #8 + + ldrb \Rspare0,[\Rtable,\Rspare0] + ldrb \Rspare1,[\Rtable,\Rspare1] + ldrb \Rspare2,[\Rtable,\Rspare2] + ldrb \Rspare3,[\Rtable,\Rspare3] + orr \Rspare0,\Rspare0,\Rspare1,lsl#8 + orr \Rspare2,\Rspare2,\Rspare3,lsl#8 + orr \Rtarg,\Rspare0,\Rspare2,lsl#16 +.endm + +@ map all bytes of the state through the split LUT, lut_a and lut_b +@ Trashes r0-r3,r12 +.balign 4 +.thumb_func +map_sbox_s: + GET_CANARY r12,CTAG12 + push {r12,r14} + + ldr r0,=shareA @ Write out state share A to memory + stmia r0,{r4-r7} + clear03 @ barrier + + ldr r0,=shareB @ Write out state share B to memory + stmia r0,{r8-r11} + clear03 4 @ barrier + + bl makeperm16 @ Rebuild random 16-way permutation. Maybe do this less frequently +@ Now combine state shares A and B and apply the split sbox to each byte, in the order given by the above random permutation + + ldr r8,=lut_a + ldr r9,=lut_b + ldr r0,[r8,#0x100] @ R0 = a0 | a1<<8 | c0<<16 | c1<<24 (lut_a_map) + eors r10,r0,r0,lsr#8 + uxtb r10,r10 @ R10 = a0^a1 + ldr r1,[r9,#0x100] @ R1 = b0 | b1<<8 | d0<<16 | d1<<24 (lut_b_map) + eors r1,r0,r1 + eors r2,r1,r1,lsr#8 + uxtb r11,r2 @ R11 = a0^a1^b0^b1 + movs r12,r1,lsr#16 @ R12 = c0^d0 | (c1^d1)<<8 + + ldr r4,=perm16 + ldr r5,=shareA + ldr r6,=shareB +@ Using r0=loop counter, r4=perm16, r5=shareA, r6=shareB, r8=lut_a, r9=lut_b, r10=a0^a1, r11=a0^a1^b0^b1, r12=(c0^d0) | (c1^d1)<<8 + movs r0,#15 +1: @ (Ordering instructions to minimise result delays) + ldrb r1,[r4,r0] @ r1 = perm[r0] + eors r7,r1,#2 @ r7 = perm[r0]^2 + ldrb r2,[r5,r1] @ r2 = shareA[perm[r0]] + ldrb r3,[r6,r7] @ r3 = shareB[perm[r0]^2] + eors r2,r2,r10 @ r2 = shareA[perm[r0]]^a0^a1 + eors r2,r2,r3 @ r2 = shareA[perm[r0]]^a0^a1^shareB[perm[r0]^2] + ldrb r3,[r8,r2] @ r3 = lut_a[shareA[perm[r0]]^a0^a1^shareB[perm[r0]^2]] + eors r3,r3,r12 @ r3 = lut_a[shareA[perm[r0]]^a0^a1^shareB[perm[r0]^2]]^c0^d0 | (junk<<8) + eors r2,r2,r11 @ r2 = shareA[perm[r0]]^b0^b1^shareB[perm[r0]^2] + strb r3,[r5,r1] @ shareA'[perm[r0]] = lut_a[shareA[perm[r0]]^a0^a1^shareB[perm[r0]^2]]^c0^d0 + ldrb r3,[r9,r2] @ r3 = lut_b[shareA[perm[r0]]^b0^b1^shareB[perm[r0]^2]] + subs r0,r0,#1 + eor r3,r3,r12,lsr#8 @ r3 = lut_b[shareA[perm[r0]]^b0^b1^shareB[perm[r0]^2]]^c1^d1 + strb r3,[r6,r7] @ shareB'[perm[r0]^2] = lut_b[shareA[perm[r0]]^b0^b1^shareB[perm[r0]^2]]^c1^d1 + bpl 1b + clear03 8 @ barrier + + ldmia r6,{r8-r11} @ Read state share B back from memory + clear03 12 @ barrier + ldmia r5,{r4-r7} @ Read state share A back from memory + clear03 16 @ barrier + +@ Refresh state shares because luts only give imperfect share-by-value + + loadlfsr + steplfsr; eors r4,r4,r0; mov r12,#0; eors r8,r8,r0,ror#16 @ Barriers between each pair of eors to prevent implicit r4^r8 etc + steplfsr; eors r5,r5,r0; mov r12,#0; eors r9,r9,r0,ror#16 + steplfsr; eors r6,r6,r0; mov r12,#0; eors r10,r10,r0,ror#16 + steplfsr; eors r7,r7,r0; mov r12,#0; eors r11,r11,r0,ror#16 + savelfsr + + pop {r12,r14} + CHK_CANARY r12,CTAG12 + bx r14 + +.balign 4 +.thumb_func +randomisechaff: +@ Randomise 48 bytes of chaff values (random load values) +@ Uses 12 bytes of permscratch +@ Trashes r0-3 + GET_CANARY r0,CTAG13 + push {r0,r14} + movs r0,#12 + ldr r1,=permscratch + bl makesmallperm @ Store the random words in a random order to make 2nd order attacks harder + movs r1,#11 +1: + push {r1} + bl gen_rand_sha_nonpres + pop {r1} + ldr r2,=permscratch + ldrb r2,[r2,r1] + getchaffaddress r3 + str r0,[r3,r2,lsl#2] + subs r1,r1,#1 + bpl 1b + pop {r0,r14} + CHK_CANARY r0,CTAG13 + bx r14 + +.balign 4 +refreshchaff: +@ Update 48 bytes of chaff values (random load values) using faster RNG than used for randomisechaff +@ Uses 12 bytes of permscratch +@ Trashes r0-3,12 + GET_CANARY r0,CTAG14 + push {r0,r14} + movs r0,#12 + ldr r1,=permscratch + bl makesmallperm @ Update the random words in a random order to make 2nd order attacks harder + movs r1,#11 +1: + push {r1} + bl gen_rand_lfsr_nonpres + pop {r1} + ldr r2,=permscratch + ldr r3,=chaff + ldrb r2,[r2,r1] + ldr r12,[r3,r2,lsl#2] + add r0,r0,r12 + str r0,[r3,r2,lsl#2] + subs r1,r1,#1 + bpl 1b + pop {r0,r14} + CHK_CANARY r0,CTAG14 + bx r14 + +.balign 4 +.thumb_func +@ Do sbox on the four bytes of the 4-way share r4-r7 +@ Trashes r0,r8-r12 +init_key_sbox: + GET_CANARY r12,CTAG15 + push {r1-r3,r12,r14} + bl gen_rand_sha_nonpres; mov r8,r0 + bl gen_rand_sha_nonpres; mov r9,r0 + bl gen_rand_sha_nonpres; mov r10,r0 + bl gen_rand_sha_nonpres; mov r11,r0 + ldr r0,=fourway @ Write out 4-way share to memory + stmia r0,{r8-r11} @ Save random values first to obscure saving of state + stmia r0,{r4-r7} + movs r4,#0 @ Clear r4-r7 so that they don't interact with makesmallperm + movs r5,#0 + movs r6,#0 + movs r7,#0 + + bl randomisechaff @ Randomise block of memory mainly used for obscuring loads + + movs r0,#4 + ldr r1,=permscratch + bl makesmallperm @ Build random 4-way permutation determining order of bytes to be SBOXed + ldr r1,=permscratch @ Write out random addresses in advance to save two registers + ldr r4,[r1] + ldr r0,=fourway + uxtab r5,r0,r4 + uxtab r6,r0,r4,ror#8 + uxtab r7,r0,r4,ror#16 + uxtab r8,r0,r4,ror#24 + stmia r1,{r5-r8} @ Store fourway+perm[0], fourway+perm[1], fourway+perm[2], fourway+perm[3] + + bl gen_rand_sha @ Save some randomness for the resharing operation later + movs r7,r0 + bl gen_rand_sha + movs r8,r0 + + ldr r2,=lut_a + ldr r3,=lut_b + ldr r0,[r2,#0x100] @ R0 = a0 | a1<<8 | c0<<16 | c1<<24 (lut_a_map) + eors r10,r0,r0,lsr#8 + uxtb r10,r10 @ R10 = a0^a1 + ldr r1,[r3,#0x100] @ R1 = b0 | b1<<8 | d0<<16 | d1<<24 (lut_b_map) + eors r1,r0,r1 + eors r4,r1,r1,lsr#8 + uxtb r11,r4 @ R11 = a0^a1^b0^b1 + eor r10,r10,r11,lsl#8 @ R10 = a0^a1 | (a0^a1^b0^b1)<<8 + movs r12,r1,ror#16 @ R12 = c0^d0 | (c1^d1)<<8 | junk<<16 | junk<<24 + + ldr r1,=permscratch + ldr r11,=chaff +@ Using r1=permutedfourwaypointer, r2=lut_a, r3=lut_b, r7,r8=randomness, r10=(a0^a1)|(a0^a1^b0^b1)<<8, r11=chaff, r12=(c0^d0)|(c1^d1)<<8|junk +1: + ands r5,r1,#12 + adds r5,r11,r5 @ Align chaff address to r1 + ldr r6,[r1],#4 @ r6 = fourway + perm[i] (i=0-3, loop iteration) + ldr r5,[r5] @ Random load to mask previous load + + ands r9,r6,#12 @ r9 = chaff address aligned to r6 mod 16 + add r9,r11,r9 + ldrb r4,[r6,#0] + ldr r14,[r9,#0] @ Random load to mask previous load + eor r4,r4,r10 + eor r4,r4,r14,lsl#8 @ Add in some junk in bits 8-31 + + ldrb r5,[r6,#4] + ldr r14,[r9,#4] @ Random load to mask previous load + eors r4,r4,r5 + eor r4,r4,r14,lsl#8 @ Add in some junk in bits 8-31 + + ldrb r5,[r6,#8] + ldr r14,[r9,#8] @ Random load to mask previous load + eors r4,r4,r5 + eor r4,r4,r14,lsl#8 @ Add in some junk in bits 8-31 + + ldrb r5,[r6,#12] + ldr r14,[r9,#12] @ Random load to mask previous load + eors r4,r4,r5 @ r4 = unsharedbyte[perm[i]]^a0^a1 | junk + eor r4,r4,r14,lsl#8 @ Add in some junk in bits 8-31 + + ands r14,r4,#255 + ldrb r5,[r2,r14] @ r5 = lut_a[unsharedbyte[perm[i]]^a0^a1] + and r14,r4,#15 + add r14,r14,#32 + ldrb r14,[r11,r14] @ Random load to mask previous load (r2 and r11 are both 0 mod 16) + eors r5,r5,r12 @ r5 = lut_a[unsharedbyte[perm[i]]^a0^a1]^c0^d0 | junk<<8 | junk<<16 | junk<<24 +@ split r5 into two shares and store at [r6,#0] and [r6,#4] + strb r7,[r6,#0] + eors r5,r5,r7 + strb r5,[r6,#4] + + mov r5,r10,lsr#8 @ r5=a0^a1^b0^b1 + ldr r14,[r11,#44] @ Need to eor into a random destination register + eors r14,r4,r5 @ r14 = unsharedbyte[perm[i]]^b0^b1 | junk<<8 + and r14,r14,#255 + + ldrb r5,[r3,r14] @ r5 = lut_b[unsharedbyte[perm[i]]^b0^b1] + and r14,r14,#15 + add r4,r11,#24 + ldrb r14,[r4,r14] @ Random load to mask previous load (r3==8 and r11==0 mod 16) + eor r5,r5,r12,ror#8 @ r5 = lut_b[unsharedbyte[perm[i]]^b0^b1]^c1^d1 | junk<<8 | junk<<16 | junk<<24 +@ split r5 into two shares and store at [r6,#8] and [r6,#12] + strb r8,[r6,#8] + eors r5,r5,r8 + strb r5,[r6,#12] + + movs r7,r7,ror#8 + movs r8,r8,ror#8 + + tst r1,#12 @ This does 4 loop iterations because permscratch is guaranteed to be 0 mod 16 + bne 1b + + ldr r0,=fourway + ldmia r0,{r4-r7} @ Load SBOXed values back into register r4-r7 + ldmia r11,{r8-r12,r14} @ Random load to mask previous load and to obfuscate registers + + pop {r1-r3,r12,r14} + CHK_CANARY r12,CTAG15 + bx r14 + +.balign 4 +.thumb_func +@ r1 = pointer to 4 x 4-way share (16 words); left unchanged +@ r3 = rkey_s+40*roundkeynumber; advanced by 40 +@ Trashes r8-r12 +@ If i = word number 0..3, +@ Aptr=memory word pointer to block of 20 bytes containing H&V-rotated share A roundkey (similarly B), then +@ vpermA=Aptr[4]>>30, vpermB=Bptr[4]>>30, and +@ roundkey shareA(i) = Aptr[i+vpermA mod 4] ror #((i+vpermA mod 4)^th byte of Aptr[4]) +@ roundkey shareB(i) = Bptr[i+vpermB mod 4] ror #((i+vpermB mod 4)^th byte of Bptr[4])+16 +storeroundkey: + GET_CANARY r8,CTAG16 + push {r2,r8,r14} + +@ eor two 4-way share components to make a component of a 2-way share +@ Note that we load from 4-way share at a random address then convert to 2-way share and +@ store at a fixed address, rather than the other way around, so that 2-way shares are obscured +@ by vperm (we don't know which 2-way share is being processed at a particular point in time). +@ And (if RK_ROR) we rotate first before EORing down to 2-way, so there is never an unrotated 2-way share + + bl gen_rand_sha @ Get r0 = vperm for shareA of the round key + str r0,[r3,#16] + mov r8,r0,lsr#30 + rsb r8,r8,#0 @ r8=-vperm +.if RK_ROR + movs r2,#0 + usub8 r2,r2,r0 @ r2=-hperms +.endif + mov r9,#4 +1: + and r8,r8,#3 + adds r0,r1,r8,lsl#4 + + ldmia r0,{r10,r11} +.if RK_ROR + mov r10,r10,ror r2 + mov r11,r11,ror r2 + movs r2,r2,ror#8 +.endif + eor r10,r10,r11 + str r10,[r3],#4 + add r8,r8,#1 + subs r9,r9,#1 + bne 1b + + adds r1,r1,#8 + adds r3,r3,#4 @ skip over vperm (already stored) + + bl gen_rand_sha @ Get r0 = vperm for shareB of the round key + str r0,[r3,#16] + mov r8,r0,lsr#30 + rsb r8,r8,#0 @ r8=-vperm +.if RK_ROR + movs r2,#0 + usub8 r2,r2,r0 @ r2=-hperms +.endif + mov r9,#4 + ldr r12,=RKshareC + ldr r12,[r12] +1: + and r8,r8,#3 + adds r0,r1,r8,lsl#4 + ldmia r0,{r10,r11} + eor r10,r10,r12 @ Mix in RKshareC into round key shareB +.if RK_ROR + mov r10,r10,ror r2 + mov r11,r11,ror r2 + movs r2,r2,ror#8 +.endif + mov r10,r10,ror#16 + mov r11,r11,ror#16 + eor r10,r10,r11 + str r10,[r3],#4 + add r8,r8,#1 + subs r9,r9,#1 + bne 1b + + subs r1,r1,#8 @ Restore r1 = (r1 on entry) + adds r3,r3,#4 @ Set r3 = (r3 on entry) + 40 + + pop {r2,r8,r14} + CHK_CANARY r8,CTAG16 + bx r14 + +.balign 4 +.thumb_func +init_key: +@ On entry, r0 points to 4-way shared raw key data (128 bytes) +@ The format is a0 b0 c0 d0 a1 b1 c1 d1 ... a7 b7 c7 d7 +@ That is, each word, K, of the original 256-bit key is expanded into four words whose exclusive OR is K. +@ +@ On exit, rkeys_s, a 40*15=600-byte region, is filled as follows. +@ Each of the 15 round keys is represented as two 5-word regions rka[0..4] and rkb[0..4], +@ each of which consists of 4 words of round key followed by a word encoding vperm and rotation (RK_ROR) information. +@ In addition a common share word, RKshareC, is set randomly. +@ For a given round, rk[i] = the i^th word of the actual round key is given by: +@ vpermA=rka[4]>>30 +@ vpermB=rkb[4]>>30 +@ rka_unrot[i] = rka[i+vpermA mod 4] ror #((i+vpermA mod 4)^th byte of rka[4]) +@ rkb_unrot[i] = rkb[i+vpermB mod 4] ror #((i+vpermB mod 4)^th byte of rkb[4])+16 +@ rk[i] = rka_unrot[i] ^ rkb_unrot[i] ^ RKshareC + + GET_CANARY r12,CTAG17 + push {r4-r11,r12,r14} + + mov r5,r0 @ r5=4-way key input + bl randomisechaff + ldr r4,=rkey4way + movs r6,#8 +1: + ldmia r5!,{r0-r3} + stmia r4!,{r0-r3} + subs r6,r6,#1 + bne 1b + +@ Now raw key is stored in rkey4way[], construct 2-way share in rkey_s[] for +@ the 128-bit roundkeys 0 and 1, then expand from 2 to 15 roundkeys. + bl gen_rand_sha_nonpres + ldr r12,=RKshareC + str r0,[r12] @ Make RKshareC random word + ldr r3,=rkey_s @ r3=rkey_s + ldr r1,=rkey4way @ r1=rkey4way + bl storeroundkey @ Store round key 0 and advance r3 by 40 + adds r1,r1,#64 + bl storeroundkey @ Store round key 1 and advance r3 by 40 + adds r1,r1,#48 + ldmia r1!,{r4-r7} @ r4-r7 = 4-way share of previous round key word + @ r1=rkey4way+128 on entry to main loop + movs r2,#0 @ r2=word counter (0-51), offset from word 8 + +@ Note that r1-r3 are not sensitive values, so it's safe to stack +@ them and conditionally branch on them. + +@ rkey4way = 8 x 4 consecutive 4-way share words as cyclic buffer of +@ Rounds 0,1 Rounds 2,3 Rounds 12,13 Round 14 +@ a0 b0 c0 d0 -> a8 b8 c8 d8 -> ... -> a48 b48 c48 d48 -> a56 b56 c56 d56 +@ a1 b1 c1 d1 -> a9 b9 c9 d9 a49 b49 c49 d49 a57 b57 c57 d57 +@ a2 b2 c2 d2 etc a50 b50 c50 d50 a58 b58 c58 d58 +@ a3 b3 c3 d3 a51 b51 c51 d51 a59 b59 c59 d59 +@ a4 b4 c4 d4 a52 b52 c52 d52 =============== +@ a5 b5 c5 d5 a53 b53 c53 d53 +@ a6 b6 c6 d6 a54 b54 c54 d54 +@ a7 b7 c7 d7 a55 b55 c55 d55 + +init_key_expandloop: +@ r1 = pointer past one of eight 4-way shares of a roundkey word in the above cyclic buffer (r1=rkey4way+16i for i=1,...,8) +@ r2 = round key word counter (0-51), offset from word 8 (counting expanded roundkey words) +@ r3 = pointer to rkey_s+40*roundnumber = rkey_s+40*(2+[r2/4]) +@ r4-r7 = 4-way share of previous roundkey word + + tst r2,#7 + bne 1f + subs r1,r1,#128 @ Every 8th word, reset cyclic buffer pointer and do ROTWORD + movs r4,r4,ror#8 + movs r5,r5,ror#8 + movs r6,r6,ror#8 + movs r7,r7,ror#8 +1: + + tst r2,#3 + bne 1f + bl init_key_sbox @ Every 4th word, do SUBBYTES (sbox) on r4-r7 +1: + + tst r2,#7 + bne 1f + movs r0,r2,lsr#3 + mov r8,#1 + movs r8,r8,lsl r0 + eors r4,r4,r8 @ Every 8th word, add in round constant +1: + + ldmia r1,{r8-r11} @ eor with key from two rounds ago and advance r1 by 16 + eors r4,r4,r8 + eors r5,r5,r9 + eors r6,r6,r10 + eors r7,r7,r11 + stmia r1!,{r4-r7} + + add r2,r2,#1 + tst r2,#3 + bne 1f + subs r1,r1,#64 + bl storeroundkey @ Store round key 1+r2/4 and advance r3 by 40 + adds r1,r1,#64 +1: + + cmp r2,#52 + bne init_key_expandloop + + pop {r4-r11,r12,r14} + CHK_CANARY r12,CTAG17 + bx r14 + +@ Add the round key shares pointed to by r12 into the state shares +@ Trashes r0-r3 +.balign 4 +addrkey_s: + + ldr r0,=chaff @ guaranteed 0 mod 16 +.if ST_VPERM + ldr r3,=statevperm + ldr r3,[r3] @ r3=vperm state rotation in bottom two bits + ldr r2,[r0,#12] @ barrier load +.else + movs r3,#0 +.endif + bfi r0,r12,#0,#4 @ match chaff pointer (r0) to roundkey ptr (r12) mod 16 + ldr r1,[r12,#16] @ r1=vperm key rotation in top two bits + ldr r2,[r0,#16] @ barrier load + + rsbs r2,r3,r1,lsr#30 @ r2=vpermkeyrot-vpermstaterot +@ Read shareA of roundkey, offset by vpermkeyrot-vpermstaterot, and eor it into shareA of state, offset by -vpermstaterot +@ r1=rkeyArotdata, r2=vpermkeyrot-vpermstaterot, r3=statevperm, r4-r11=state, r12=roundkeyAptr +.if RK_ROR + movs r0,r2,lsl#3 + movs r1,r1,ror r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; rors r0,r0,r1; eors r4,r4,r0; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; movs r1,r1,ror#8; rors r0,r0,r1; eors r5,r5,r0; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; movs r1,r1,ror#8; rors r0,r0,r1; eors r6,r6,r0; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; movs r1,r1,ror#8; rors r0,r0,r1; eors r7,r7,r0 +.else + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r4,r4,r0; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r5,r5,r0; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r6,r6,r0; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r7,r7,r0 +.endif + clear03_preserve_r3 + add r12,r12,#20 + @ r0=chaff+16, r3=statevperm, r4-r11=state, r12=roundkeyBptr + + bfi r0,r12,#0,#4 @ match chaff pointer (r0) to roundkey ptr (r12) mod 16 + ldr r1,[r12,#16] @ r1=vperm key rotation in top two bits + ldr r2,[r0,#16] @ barrier load + rsbs r2,r3,r1,lsr#30 @ r2=vpermkeyrot-vpermstaterot + ldr r3,=RKshareC @ r3=common round key shareC + bfi r0,r3,#0,#4 + ldr r3,[r3] + ldr r0,[r0] @ barrier load + +@ Read shareB of roundkey, offset by vpermkeyrot-vpermstaterot, and eor it into shareB of state, offset by -vpermstaterot +@ r1=rkeyBrotdata, r2=vpermkeyrot-vpermstaterot, r3=RKshareC, r4-r11=state, r12=roundkeyB ptr +.if RK_ROR + movs r0,r2,lsl#3 + movs r1,r1,ror r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r8,r8,r3,ror#16; rors r0,r0,r1; eors r8,r8,r0; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r9,r9,r3,ror#16; movs r1,r1,ror#8; rors r0,r0,r1; eors r9,r9,r0; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r10,r10,r3,ror#16; movs r1,r1,ror#8; rors r0,r0,r1; eors r10,r10,r0; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r11,r11,r3,ror#16; movs r1,r1,ror#8; rors r0,r0,r1; eors r11,r11,r0 +.else + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r8,r8,r0; eors r8,r8,r3,ror#16; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r9,r9,r0; eors r9,r9,r3,ror#16; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r10,r10,r0; eors r10,r10,r3,ror#16; adds r2,r2,#1 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r11,r11,r0; eors r11,r11,r3,ror#16 +.endif + clear03 + + bx r14 + +.balign 4 +.thumb_func +@ de/encrypt data in place +@ r0: ivec +@ r1: buf +@ r2: n, number of blocks, n>0 +.if CT_BPERM +@ In AES-CTR each block can be independently en/decrypted as the encryption only depends on the IV, +@ the key, and the block number. We can therefore process them in any order, and using a +@ random order helps to defeat attacks that work on the output of the AES, since an attacker +@ wouldn't know what plaintext or ciphertext corresponds to a particular instruction. +.endif + +ctr_crypt_s: +@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks + GET_CANARY r12,CTAG0 + push {r0,r4-r11,r12,r14} + + push {r0-r2} + SET_COUNT 93 + +.if CT_BPERM +@ Initialise 32 random numbers (which fit in half-words) + ldr r4,=bperm_rand + movs r5,#32 +1: + bl gen_rand_sha + umull r0,r3,r0,r2 @ Random number between 0 and n-1 (n=#blocks) + strh r3,[r4],#2 + subs r5,r5,#1 + bne 1b +.endif + + bl randomisechaff + pop {r0-r2} + movs r3,#0 + CHK_COUNT 93 + +ctr_crypt_mainloop: + SET_COUNT 80 +@ here r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter + +@ Do as much preparatory stuff as possible that doesn't involve the IV (to reduce interaction with it) + push {r0-r3} +@ It's OK for execution time to depend on the block counter r3 ("public"), but not the block number (secret) + + tst r3,#(REFCHAFF_PERIOD-1) + bne 1f + bl refreshchaff +1: + + ldr r3,[r13,#12] @ get block count off the stack + tst r3,#(REMAP_PERIOD-1) + bne 1f + bl remap @ shuffle the LUTs; this preserves R3 +1: + CHK_COUNT 80 + + tst r3,#(REFROUNDKEYSHARES_PERIOD-1) + bne 1f + bl ref_roundkey_shares_s @ refresh the round key shares +1: + + ldr r3,[r13,#12] @ get block count off the stack + tst r3,#(REFROUNDKEYHVPERMS_PERIOD-1) + bne 1f + bl ref_roundkey_hvperms_s @ refresh the round key vperms +1: + + CHK_COUNT 81 + pop {r0-r3} +@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter + +@ Now calculate r12 = block number-to-be-deciphered from r3 = block counter +.if CT_BPERM +@ Use a "swap-or-not" method to generate an "oblivious" permutation; see makeperm.py version 7 + push {r0,r1} + ldr r0,=murmur3_constants + ldmia r0,{r9-r12,r14} @ load five murmur3_32 hash constants + ldr r0,=bperm_rand + movs r1,#31 + movs r4,r3 @ r4=i +1: + ldrh r5,[r0],#2 @ r5=k + subs r5,r5,r4 @ r5=k-i + ands r6,r2,r5,asr#31 @ r6=n*(k-i<0) + adds r5,r5,r6 @ r5=j=(k-i)%n + adds r6,r4,r5 @ r6=i+j + subs r7,r4,r5 @ r7=i-j + and r8,r7,r7,asr#31 @ r8=min(i-j,0) + sub r7,r7,r8,lsl#1 @ r7=|i-j| + mla r6,r6,r2,r7 @ r6=n(i+j)+|i-j|, encodes the unordered pair {i,j} + eors r6,r6,r1,lsl#27 @ mix with swap-or-not round counter to get different hash functions +@ Now do murmur3_32 hash of r6 + mul r6,r6,r9 + movs r6,r6,ror#17 + mul r6,r6,r10 + movs r6,r6,ror#19 + adds r6,r6,r6,lsl#2 + add r6,r6,r11 + eors r6,r6,#4 + eors r6,r6,r6,lsr#16 + mul r6,r6,r12 + eors r6,r6,r6,lsr#13 + mul r6,r6,r14 + eors r6,r6,r6,lsr#16 @ not actually used here +@ Now set i to j, conditional on the top bit of r6 + subs r7,r5,r4 @ r7=j-i + ands r7,r7,r6,asr#31 @ r7=(j-i)*(top bit of r6) + adds r4,r4,r7 @ r4=j if top bit of r6, else i + subs r1,r1,#1 + bpl 1b + pop {r0,r1} + mov r12,r4 +.else + mov r12,r3 +.endif + CHK_COUNT 82 + +@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter, r12=block to be deciphered + push {r0-r3,r12} + +processIV: @ non-target label to assist power analysis + +@ It is not clear if the following addition of the block number in r12 to the IV can usefully +@ be done in terms of shares. Instead we do an addition and subtraction whose overall effect +@ is the same, and which provides a small degree of masking. The IV is not traditionally a secret, +@ though it will make it harder for the attacker if it is obscured. + bl gen_rand_sha + movs r8,r0,lsr#16 @ only use 16 low bits so we don't get any overflows in the following, and so that a carry from the first word is rare + add r9,r8,r12 @ "masked" block number +@ r8=random, r9=(block number)+r8, stack=IV,... + + ldr r0,[r13] @ peek at stack to restore r0=IV ptr + ldmia r0,{r4-r7} @ load IV + clear03 @ barrier to remove traces of IV from internal CPU load registers + push {r0-r3} @ We want to randomise the internal memory registers associated with the above LDM load, but this + pop {r0-r3} @ may come from non-scratch memory and have its own internal registers, so we clear it using a + @ stack save/load. Either R13 is in non-scratch memory, in which case this works, or it isn't, in + @ which case it doesn't matter, because the only subsequent use of non-scratch memory is the stack. + +@ Add in r9 in byte-big-endian, bit-little-endian (!) fashion, while trying to avoid rev operations +@ as far as possible as these tend to expose (via power fluctuations) byte-level hamming weights. +@ It's worth avoiding revs on r6, r5, r4, even at the cost of introducing a small timing dependency. + +@ First do 128-bit addition of r9 to byte-reversed IV + rev r7,r7; adds r7,r7,r9; bcc 1f + rev r6,r6; adcs r6,r6,#0; rev r6,r6; bcc 1f + rev r5,r5; adcs r5,r5,#0; rev r5,r5; bcc 1f + rev r4,r4; adcs r4,r4,#0; rev r4,r4 +1: +@ At this point, r7 is reversed and r4-r6 are not +@ Now do 128-bit subtraction of r8 from byte-reversed IV + subs r7,r7,r8; rev r7,r7; bcs 1f + rev r6,r6; sbcs r6,r6,#0; rev r6,r6; bcs 1f + rev r5,r5; sbcs r5,r5,#0; rev r5,r5; bcs 1f + rev r4,r4; sbcs r4,r4,#0; rev r4,r4 +1: + clear01 16 + CHK_COUNT 83 + +@ r4-r7 = IV for the current block + bl ns_to_s @ convert IV+x to shares, which includes choosing and incorporating a random shareC + CHK_COUNT 84 + bl conjshareC @ Add the effect of shareC to lut_a, lut_b + CHK_COUNT 85 +@ now perform the 15 encryption rounds on (key, state=IV+x) +@ here r4-r7, r8-r11: state + mov r2,#0 @ round counter +rounds_s_mainloop: + ldr r12,=rkey_s + add r12,r12,r2,lsl#5 @ pointer to key shares for this round + add r12,r12,r2,lsl#3 + push {r2} @ save round count + bl addrkey_s + bl map_sbox_s + bl shift_rows_s +.if ST_VPERM + ldmia r13,{r2} @ peek at stack to get round count + cmp r2,#NUMREFSTATEVPERM + bcs 1f + bl gen_rand_lfsr_nonpres + ldr r1,=statevperm + bl addstatevperm @ V shuffle of r4-r11 +1: +.endif + pop {r2} + adds r2,r2,#1 @ increment round counter + cmp r2,#14 + beq 2f @ break from loop? (last round has no mix_cols) + push {r2} + bl mix_cols_s + pop {r2} + b rounds_s_mainloop +2: + CHK_COUNT 86 + ldr r12,=rkey_s+14*40 @ final round key shares + bl addrkey_s + CHK_COUNT 87 + bl conjshareC @ Undo the effect of shareC from lut_a, lut_b + CHK_COUNT 88 +.if ST_VPERM +@ Undo the effects of vperm rotation recorded in statevperm + ldr r1,=statevperm + ldr r2,[r1] + rsbs r0,r2,#0 + bl addstatevperm +.endif + + pop {r0-r3,r12} + push {r0,r3} +@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter, r12=block to be deciphered + +@ Decrypt ciphertext using AES output in shares: r4-r11 +.if ST_SHAREC + ldr r0,=shareC + ldr r0,[r0] +.else + movs r0,#0 +.endif + CHK_COUNT 89 + add r1,r1,r12,lsl#4 @ Temporarily r1 points to block-to-be-deciphered + ldr r3,[r1] + eors r3,r3,r4 + eors r3,r3,r8,ror#16 @ Now r4 and r8 are free + eors r3,r3,r0 + str r3,[r1] + ldr r3,[r1,#4] + eors r3,r3,r5 + eors r3,r3,r9,ror#16 + eors r3,r3,r0 + str r3,[r1,#4] + ldr r3,[r1,#8] + eors r3,r3,r6 + eors r3,r3,r10,ror#16 + eors r3,r3,r0 + str r3,[r1,#8] + ldr r3,[r1,#12] + eors r3,r3,r7 + eors r3,r3,r11,ror#16 + eors r3,r3,r0 + str r3,[r1,#12] + sub r1,r1,r12,lsl#4 @ Restore r1 to point to start of buffer + CHK_COUNT 90 + + pop {r0,r3} @ Restore IV and block counter +@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter + + adds r3,r3,#1 + cmp r3,r2 + CHK_COUNT 91 + bne ctr_crypt_mainloop + pop {r0,r4-r11,r12,r14} + CHK_CANARY r12,CTAG0 + bx r14 diff --git a/enc_bootloader/config.h b/enc_bootloader/config.h new file mode 100644 index 00000000..dd0c9898 --- /dev/null +++ b/enc_bootloader/config.h @@ -0,0 +1,70 @@ +#pragma once + +// These options should be enabled because the security risk of not using them is too high +// or because the time cost is very low so you may as well have them. +// They can be set to 0 for analysis or testing purposes. + +#ifndef GEN_RAND_SHA +#define GEN_RAND_SHA 1 // use SHA256 hardware to generate some random numbers +#endif + // Some RNG calls are hard coded to LFSR RNG, others to SHA RNG + // Setting GEN_RAND_SHA to 0 has the effect of redirecting the latter to LFSR RNG +#ifndef ST_SHAREC +#define ST_SHAREC 1 // This creates a partial extra share at almost no extra cost +#endif +#ifndef ST_VPERM +#define ST_VPERM 1 // insert random vertical permutations in state during de/encryption? +#endif +#ifndef CT_BPERM +#define CT_BPERM 1 // process blocks in a random order in counter mode? +#endif +#ifndef RK_ROR +#define RK_ROR 1 // store round key shares with random rotations within each word +#endif + +// The following options should be enabled to increase resistance to glitching attacks. + +#ifndef RC_CANARY +#define RC_CANARY 1 // use rcp_canary feature +#endif +#ifndef RC_COUNT +#define RC_COUNT 1 // use rcp_count feature +#endif + +// Although enabling the following option likely has little theoretical benefit, in +// practice randomising the timing of operations can make side-channel attacks very +// much more effort to carry out. It can be disabled for analysis or testing purposes. + +#ifndef RC_JITTER +#define RC_JITTER 1 // use random-delay versions of RCP instructions +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// The following options can be adjusted, affecting the performance/security tradeoff + +// Period = X means that the operation in question occurs every X blocks, so higher = more performance and lower security. +// No point in making them more than 16 or so, since the time taken by the subroutines would be negligible. +// These must be a power of 2. Timings as of commit 24277d13 +// RK_ROR=0 RK_ROR=1 +// Baseline time per 16-byte block = { 14066 14336 } cycles +#ifndef REFCHAFF_PERIOD +#define REFCHAFF_PERIOD 1 // Extra cost per 16-byte block = { 462 462 }/REFCHAFF_PERIOD cycles +#endif +#ifndef REMAP_PERIOD +#define REMAP_PERIOD 4 // Extra cost per 16-byte block = { 4131 4131 }/REMAP_PERIOD cycles +#endif +#ifndef REFROUNDKEYSHARES_PERIOD +#define REFROUNDKEYSHARES_PERIOD 1 // Extra cost per 16-byte block = { 1107 1212 }/REFROUNDKEYSHARES_PERIOD cycles +#endif +#ifndef REFROUNDKEYHVPERMS_PERIOD +#define REFROUNDKEYHVPERMS_PERIOD 1 // Extra cost per 16-byte block = { 936 1422 }/REFROUnDKEYVPERM_PERIOD cycles +#endif + +// Setting NUMREFSTATEVPERM to X means that state vperm refreshing happens on the first X AES rounds only, +// so lower = more performance and lower security. +// The rationale for doing it this way is that later rounds should be protected by CT_BPERM. +// NUMREFSTATEVPERM can be from 0 to 14. +#ifndef NUMREFSTATEVPERM +#define NUMREFSTATEVPERM 7 // Extra cost per 16-byte block = 80*NUMREFSTATEVPERM cycles +#endif diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c new file mode 100644 index 00000000..a85aff18 --- /dev/null +++ b/enc_bootloader/enc_bootloader.c @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include "pico/stdlib.h" +#include "boot/picobin.h" +#include "hardware/uart.h" +#include "pico/bootrom.h" +#include "pico/rand.h" +#include "hardware/structs/otp.h" +#include "hardware/structs/qmi.h" +#include "hardware/structs/xip_ctrl.h" + +#include "pico/binary_info.h" + +#include "config.h" + +volatile uint32_t systick_data[18]; // count, R0-R15,RETPSR + +extern void remap(); +extern uint32_t gen_rand_sha(); +extern void init_key(uint8_t *key); +extern void gen_lut_sbox(); +extern int ctr_crypt_s(uint8_t*iv,uint8_t*buf,int nblk); + +extern uint8_t rkey_s[480]; +extern uint8_t lut_a[256]; +extern uint8_t lut_b[256]; +extern uint32_t lut_a_map[1]; +extern uint32_t lut_b_map[1]; +extern uint32_t rstate_sha[4],rstate_lfsr[2]; + +void resetrng() { + uint32_t f0,f1; + do f0=get_rand_32(); while(f0==0); // make sure we don't initialise the LFSR to zero + f1=get_rand_32(); + rstate_sha[0]=f0&0xffffff00; // bottom byte must be zero (or 4) for SHA, representing "out of data" + rstate_sha[1]=f1; + rstate_sha[2]=0x41414141; + rstate_sha[3]=0x41414141; + rstate_lfsr[0]=f0; // must be nonzero for non-degenerate LFSR + rstate_lfsr[1]=0x1d872b41; // constant that defines LFSR +#if GEN_RAND_SHA + reset_block(RESETS_RESET_SHA256_BITS); + unreset_block(RESETS_RESET_SHA256_BITS); +#endif +} + +static void init_lut_map() { + int i; + for(i=0;i<256;i++) lut_b[i]=gen_rand_sha()&0xff, lut_a[i]^=lut_b[i]; + lut_a_map[0]=0; + lut_b_map[0]=0; + remap(); +} + +static void init_aes() { + resetrng(); + gen_lut_sbox(); + init_lut_map(); +} + +static __attribute__((aligned(4))) uint8_t workarea[4 * 1024]; + +int main() { + stdio_init_all(); + + printf("Decrypting the image\n"); + printf("OTP Valid Keys %x\n", otp_hw->key_valid); + printf("Unlocking\n"); + for (int i=0; i<4; i++) { + uint32_t key_i = ((i*2+1) << 24) | ((i*2+1) << 16) | + (i*2 << 8) | i*2; + otp_hw->crt_key_w[i] = key_i; + } + + bi_decl(bi_program_feature_group(0x1111, 0x2222, "encryption_config")); + bi_decl(bi_ptr_int32(0x1111, 0x2222, data_start_addr, 0x20000000)); + bi_decl(bi_ptr_int32(0x1111, 0x2222, data_size, 0x78000)); + bi_decl(bi_ptr_int32(0x1111, 0x2222, iv0, 0)); + bi_decl(bi_ptr_int32(0x1111, 0x2222, iv1, 1)); + bi_decl(bi_ptr_int32(0x1111, 0x2222, iv2, 2)); + bi_decl(bi_ptr_int32(0x1111, 0x2222, iv3, 3)); + bi_decl(bi_ptr_int32(0x1111, 0x2222, otp_key_page, 30)); + + // Initialise IV from binary info words + uint8_t iv[16]; + memcpy(iv, (void*)&iv0, sizeof(iv0)); + memcpy(iv + 4, (void*)&iv1, sizeof(iv1)); + memcpy(iv + 8, (void*)&iv2, sizeof(iv2)); + memcpy(iv + 12, (void*)&iv3, sizeof(iv3)); + + printf("Pre decryption image begins with\n"); + for (int i=0; i < 4; i++) + printf("%08x\n", *(uint32_t*)(data_start_addr + i*4)); + + init_aes(); + // Read key directly from OTP - guarded reads will throw a bus fault if there are any errors + uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE; + + init_key((uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & (otp_key_page * 0x40))])); + otp_hw->sw_lock[otp_key_page] = 0xf; + ctr_crypt_s(iv, (void*)data_start_addr, data_size/16); + + printf("Post decryption image begins with\n"); + for (int i=0; i < 4; i++) + printf("%08x\n", *(uint32_t*)(data_start_addr + i*4)); + + printf("Chaining into %x, size %x\n", data_start_addr, data_size); + + stdio_deinit_all(); + + int rc = rom_chain_image( + workarea, + sizeof(workarea), + data_start_addr, + data_size + ); + + stdio_init_all(); + printf("Shouldn't return from ROM call %d\n", rc); + + reset_usb_boot(0, 0); +} diff --git a/enc_bootloader/enc_bootloader.elf b/enc_bootloader/enc_bootloader.elf new file mode 100755 index 0000000000000000000000000000000000000000..08dd1dfffce00a867951e06b6e4251d95b0f58e4 GIT binary patch literal 58340 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 literal 0 HcmV?d00001 diff --git a/main.cpp b/main.cpp index 68173555..b550ac18 100644 --- a/main.cpp +++ b/main.cpp @@ -33,6 +33,7 @@ #include "boot/uf2.h" #include "boot/picobin.h" +#include "enc_bootloader.h" #if HAS_LIBUSB #include "picoboot_connection_cxx.h" #include "rp2350.rom.h" @@ -481,6 +482,10 @@ struct _settings { std::vector rollback_rows; } seal; + struct { + bool embed = false; + } encrypt; + struct { uint32_t align = 0x1000; } link; @@ -789,6 +794,7 @@ struct encrypt_command : public cmd { return ( option("--quiet").set(settings.quiet) % "Don't print any output" + option("--verbose").set(settings.verbose) % "Print verbose output" + + option("--embed").set(settings.encrypt.embed) % "Embed bootloader in output file" + ( option("--hash").set(settings.seal.hash) % "Hash the encrypted file" + option("--sign").set(settings.seal.sign) % "Sign the encrypted file" @@ -4793,104 +4799,6 @@ bool load_command::execute(device_map &devices) { } #endif -#if HAS_MBEDTLS -bool encrypt_command::execute(device_map &devices) { - bool isElf = false; - bool isBin = false; - if (get_file_type() == filetype::elf) { - isElf = true; - } else if (get_file_type() == filetype::bin) { - isBin = true; - } else { - fail(ERROR_ARGS, "Can only sign ELFs or BINs"); - } - - if (get_file_type_idx(1) != get_file_type()) { - fail(ERROR_ARGS, "Can only sign to same file type"); - } - - if (get_file_type_idx(2) != filetype::bin) { - fail(ERROR_ARGS, "Can only read AES key share from BIN file"); - } - - if (settings.seal.sign && settings.filenames[3].empty()) { - fail(ERROR_ARGS, "missing key file for signing after encryption"); - } - - if (!settings.filenames[3].empty() && get_file_type_idx(3) != filetype::pem) { - fail(ERROR_ARGS, "Can only read pem keys"); - } - - - auto aes_file = get_file_idx(ios::in|ios::binary, 2); - aes_file->exceptions(std::iostream::failbit | std::iostream::badbit); - - aes_key_share_t aes_key_share; - aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); - - aes_key_t aes_key; - // Key is stored as a 4-way share of each word, ie X[0] = A[0] ^ B[0] ^ C[0] ^ D[0], stored as A[0], B[0], C[0], D[0] - for (int i=0; i < count_of(aes_key.words); i++) { - aes_key.words[i] = aes_key_share.words[i*4] - ^ aes_key_share.words[i*4 + 1] - ^ aes_key_share.words[i*4 + 2] - ^ aes_key_share.words[i*4 + 3]; - } - - private_t private_key = {}; - public_t public_key = {}; - - if (settings.seal.sign) read_keys(settings.filenames[3], &public_key, &private_key); - - if (isElf) { - elf_file source_file(settings.verbose); - elf_file *elf = &source_file; - elf->read_file(get_file(ios::in|ios::binary)); - // Remove any holes in the ELF file, as these cause issues when encrypting - elf->remove_sh_holes(); - - std::unique_ptr first_block = find_first_block(elf); - if (!first_block) { - fail(ERROR_FORMAT, "No first block found"); - } - elf->editable = false; - block new_block = place_new_block(elf, first_block); - elf->editable = true; - - encrypt(elf, &new_block, aes_key, public_key, private_key, settings.seal.hash, settings.seal.sign); - - auto out = get_file_idx(ios::out|ios::binary, 1); - elf->write(out); - out->close(); - } else if (isBin) { - auto binfile = get_file_memory_access(0); - auto rmap = binfile.get_rmap(); - auto ranges = rmap.ranges(); - assert(ranges.size() == 1); - auto bin_start = ranges[0].from; - auto bin_size = ranges[0].len(); - - vector bin = binfile.read_vector(bin_start, bin_size, false); - - std::unique_ptr first_block = find_first_block(bin, bin_start); - if (!first_block) { - fail(ERROR_FORMAT, "No first block found"); - } - auto bin_cp = bin; - block new_block = place_new_block(bin_cp, bin_start, first_block); - - auto enc_data = encrypt(bin, bin_start, bin_start, &new_block, aes_key, public_key, private_key, settings.seal.hash, settings.seal.sign); - - auto out = get_file_idx(ios::out|ios::binary, 1); - out->write((const char *)enc_data.data(), enc_data.size()); - out->close(); - } else { - fail(ERROR_ARGS, "Must be ELF or BIN"); - } - - return false; -} - #if HAS_MBEDTLS void sign_guts_elf(elf_file* elf, private_t private_key, public_t public_key) { std::unique_ptr first_block = find_first_block(elf); @@ -4941,9 +4849,12 @@ void sign_guts_elf(elf_file* elf, private_t private_key, public_t public_key) { } } } - auto segment = elf->segment_from_physical_address(vtor_loc); + auto segment = elf->segment_from_virtual_address(vtor_loc); + if (segment == nullptr) { + fail(ERROR_NOT_POSSIBLE, "The ELF file does not contain the vector table location %x", vtor_loc); + } auto content = elf->content(*segment); - auto offset = vtor_loc - segment->physical_address(); + auto offset = vtor_loc - segment->virtual_address(); uint32_t ep; memcpy(&ep, content.data() + offset + 4, sizeof(ep)); uint32_t sp; @@ -5020,7 +4931,179 @@ vector sign_guts_bin(iostream_memory_access in, private_t private_key, return sig_data; } -#endif + +bool encrypt_command::execute(device_map &devices) { + bool isElf = false; + bool isBin = false; + if (get_file_type() == filetype::elf) { + isElf = true; + } else if (get_file_type() == filetype::bin) { + isBin = true; + } else { + fail(ERROR_ARGS, "Can only sign ELFs or BINs"); + } + + if (get_file_type_idx(1) != get_file_type()) { + fail(ERROR_ARGS, "Can only sign to same file type"); + } + + if (get_file_type_idx(2) != filetype::bin) { + fail(ERROR_ARGS, "Can only read AES key share from BIN file"); + } + + if (settings.seal.sign && settings.filenames[3].empty()) { + fail(ERROR_ARGS, "missing key file for signing after encryption"); + } + + if (!settings.filenames[3].empty() && get_file_type_idx(3) != filetype::pem) { + fail(ERROR_ARGS, "Can only read pem keys"); + } + + + auto aes_file = get_file_idx(ios::in|ios::binary, 2); + aes_file->exceptions(std::iostream::failbit | std::iostream::badbit); + + aes_key_share_t aes_key_share; + aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); + + aes_key_t aes_key; + // Key is stored as a 4-way share of each word, ie X[0] = A[0] ^ B[0] ^ C[0] ^ D[0], stored as A[0], B[0], C[0], D[0] + for (int i=0; i < count_of(aes_key.words); i++) { + aes_key.words[i] = aes_key_share.words[i*4] + ^ aes_key_share.words[i*4 + 1] + ^ aes_key_share.words[i*4 + 2] + ^ aes_key_share.words[i*4 + 3]; + } + + private_t private_key = {}; + public_t public_key = {}; + + if (settings.seal.sign) read_keys(settings.filenames[3], &public_key, &private_key); + + if (isElf) { + elf_file source_file(settings.verbose); + elf_file *elf = &source_file; + elf->read_file(get_file(ios::in|ios::binary)); + // Remove any holes in the ELF file, as these cause issues when encrypting + elf->remove_sh_holes(); + + std::unique_ptr first_block = find_first_block(elf); + if (!first_block) { + fail(ERROR_FORMAT, "No first block found"); + } + elf->editable = false; + block new_block = place_new_block(elf, first_block); + elf->editable = true; + + if (settings.encrypt.embed) { + if (!isElf) { + fail(ERROR_ARGS, "Can only embed decrypting bootloader into elfs"); + } + + std::vector iv_data; + std::vector enc_data; + uint32_t data_start_address = SRAM_START; + encrypt_guts(elf, &new_block, aes_key, iv_data, enc_data); + + auto tmp = std::make_shared(); + auto file = get_enc_bootloader(); + *tmp << file->rdbuf(); + + auto program = get_iostream_memory_access(tmp, filetype::elf, true); + program.set_model(rp2350); + + settings.config.group = "encryption_config"; + // data_start_addr + settings.config.key = "data_start_addr"; + settings.config.value = hex_string(data_start_address); + config_guts(program); + // data_size + settings.config.key = "data_size"; + settings.config.value = hex_string(enc_data.size()); + config_guts(program); + // iv + for (int i=0; i < 4; i++) { + std::stringstream ss; + ss << "iv" << i; + settings.config.key = ss.str(); + settings.config.value = hex_string(*(uint32_t*)(iv_data.data() + i*sizeof(uint32_t))); + config_guts(program); + } + // otp_key_page + if (false) { + settings.config.key = "otp_key_page"; + settings.config.value = hex_string(30); + config_guts(program); + } + + elf_file source_file(settings.verbose); + elf_file *enc_elf = &source_file; + enc_elf->read_file(tmp); + + // Bootloader size + auto bootloader_txt = enc_elf->get_section(".text"); + uint32_t bootloader_size = 0x20082000 - bootloader_txt->virtual_address(); + printf("Bootloader size %08x start %08x\n", bootloader_size, bootloader_txt->virtual_address()); + + // Move bootloader down in physical space to start of SRAM (which will be start of flash once packaged) + enc_elf->move_all(data_start_address - bootloader_txt->virtual_address()); + + // Add encrypted blob + enc_elf->append_segment(data_start_address, data_start_address + bootloader_size, enc_data.size(), ".enc_data"); + auto data_section = enc_elf->get_section(".enc_data"); + assert(data_section); + assert(data_section->virtual_address() == data_start_address); + + if (data_section->size < enc_data.size()) { + fail(ERROR_UNKNOWN, "Block is too big for elf section\n"); + } + + DEBUG_LOG("Adding enc_data len %d\n", (int)enc_data.size()); + for (auto x : enc_data) DEBUG_LOG("%02x", x); + DEBUG_LOG("\n"); + + enc_elf->content(*data_section, enc_data); + + // Sign the final thing + sign_guts_elf(enc_elf, private_key, public_key); + + auto out = get_file_idx(ios::out|ios::binary, 1); + enc_elf->write(out); + out->close(); + } else { + encrypt(elf, &new_block, aes_key, public_key, private_key, settings.seal.hash, settings.seal.sign); + auto out = get_file_idx(ios::out|ios::binary, 1); + elf->write(out); + out->close(); + } + } else if (isBin) { + auto binfile = get_file_memory_access(0); + auto rmap = binfile.get_rmap(); + auto ranges = rmap.ranges(); + assert(ranges.size() == 1); + auto bin_start = ranges[0].from; + auto bin_size = ranges[0].len(); + + vector bin = binfile.read_vector(bin_start, bin_size, false); + + std::unique_ptr first_block = find_first_block(bin, bin_start); + if (!first_block) { + fail(ERROR_FORMAT, "No first block found"); + } + auto bin_cp = bin; + block new_block = place_new_block(bin_cp, bin_start, first_block); + + auto enc_data = encrypt(bin, bin_start, bin_start, &new_block, aes_key, public_key, private_key, settings.seal.hash, settings.seal.sign); + + auto out = get_file_idx(ios::out|ios::binary, 1); + out->write((const char *)enc_data.data(), enc_data.size()); + out->close(); + } else { + fail(ERROR_ARGS, "Must be ELF or BIN"); + } + + return false; +} bool seal_command::execute(device_map &devices) { bool isElf = false; From 76baca1d710e217b7e52446b6a20a32f7d4ef41a Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 20 Jan 2025 14:08:51 +0000 Subject: [PATCH 06/80] Apply encrypted-example 6de8084b6eda --- enc_bootloader/aes.S | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index d51605a4..ad6c448d 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -1427,15 +1427,15 @@ addrkey_s: .if RK_ROR movs r0,r2,lsl#3 movs r1,r1,ror r0 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; rors r0,r0,r1; eors r4,r4,r0; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; movs r1,r1,ror#8; rors r0,r0,r1; eors r5,r5,r0; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; movs r1,r1,ror#8; rors r0,r0,r1; eors r6,r6,r0; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; movs r1,r1,ror#8; rors r0,r0,r1; eors r7,r7,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; adds r2,r2,#1; rors r0,r0,r1; eors r4,r4,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; adds r2,r2,#1; movs r1,r1,ror#8; rors r0,r0,r1; eors r5,r5,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; adds r2,r2,#1; movs r1,r1,ror#8; rors r0,r0,r1; eors r6,r6,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; movs r1,r1,ror#8; rors r0,r0,r1; eors r7,r7,r0 .else - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r4,r4,r0; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r5,r5,r0; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r6,r6,r0; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r7,r7,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; adds r2,r2,#1; eors r4,r4,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; adds r2,r2,#1; eors r5,r5,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; adds r2,r2,#1; eors r6,r6,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r7,r7,r0 .endif clear03_preserve_r3 add r12,r12,#20 @@ -1455,15 +1455,15 @@ addrkey_s: .if RK_ROR movs r0,r2,lsl#3 movs r1,r1,ror r0 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r8,r8,r3,ror#16; rors r0,r0,r1; eors r8,r8,r0; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r9,r9,r3,ror#16; movs r1,r1,ror#8; rors r0,r0,r1; eors r9,r9,r0; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r10,r10,r3,ror#16; movs r1,r1,ror#8; rors r0,r0,r1; eors r10,r10,r0; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r11,r11,r3,ror#16; movs r1,r1,ror#8; rors r0,r0,r1; eors r11,r11,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eor r8,r8,r3,ror#16; adds r2,r2,#1; rors r0,r0,r1; eor r8,r8,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eor r9,r9,r3,ror#16; adds r2,r2,#1; movs r1,r1,ror#8; rors r0,r0,r1; eor r9,r9,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eor r10,r10,r3,ror#16; adds r2,r2,#1; movs r1,r1,ror#8; rors r0,r0,r1; eor r10,r10,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eor r11,r11,r3,ror#16; movs r1,r1,ror#8; rors r0,r0,r1; eor r11,r11,r0 .else - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r8,r8,r0; eors r8,r8,r3,ror#16; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r9,r9,r0; eors r9,r9,r3,ror#16; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r10,r10,r0; eors r10,r10,r3,ror#16; adds r2,r2,#1 - ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eors r11,r11,r0; eors r11,r11,r3,ror#16 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eor r8,r8,r3,ror#16; adds r2,r2,#1; eors r8,r8,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eor r9,r9,r3,ror#16; adds r2,r2,#1; eors r9,r9,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eor r10,r10,r3,ror#16; adds r2,r2,#1; eors r10,r10,r0 + ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eor r11,r11,r3,ror#16; eors r11,r11,r0 .endif clear03 From cc6fbab50071645c0066703b410cd8abed37a28f Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 20 Jan 2025 14:13:19 +0000 Subject: [PATCH 07/80] Fixup no libusb build --- CMakeLists.txt | 67 ++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b9e21e1..b57945bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,11 +57,39 @@ endif() list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) +if (NOT DEFINED USE_PRECOMPILED) + set(USE_PRECOMPILED true) +endif() + +# compile enc_bootloader.elf +if (NOT DEFINED USE_PRECOMPILED) +set(USE_PRECOMPILED true) +endif() +ExternalProject_Add(enc_bootloader + PREFIX enc_bootloader + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/enc_bootloader + BINARY_DIR ${CMAKE_BINARY_DIR}/enc_bootloader + CMAKE_ARGS + "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}" + "-DPICO_SDK_PATH:FILEPATH=${PICO_SDK_PATH}" + "-DUSE_PRECOMPILED:BOOL=${USE_PRECOMPILED}" + "-DPICO_DEBUG_INFO_IN_RELEASE=OFF" + BUILD_ALWAYS 1 # todo remove this + INSTALL_COMMAND "" + ) + +set(ENC_BOOTLOADER_ELF ${CMAKE_BINARY_DIR}/enc_bootloader/enc_bootloader.elf) +add_executable(enc_bootloader_elf IMPORTED) +add_dependencies(enc_bootloader_elf enc_bootloader) +set_property(TARGET enc_bootloader_elf PROPERTY IMPORTED_LOCATION ${ENC_BOOTLOADER_ELF}) +# copy enc_bootloader.elf into build directory +add_custom_command(TARGET enc_bootloader +COMMAND ${CMAKE_COMMAND} -E copy ${ENC_BOOTLOADER_ELF} ${CMAKE_BINARY_DIR}/enc_bootloader.elf +DEPENDS enc_bootloader +) + if (NOT PICOTOOL_NO_LIBUSB) # compile xip_ram_perms.elf - if (NOT DEFINED USE_PRECOMPILED) - set(USE_PRECOMPILED true) - endif() ExternalProject_Add(xip_ram_perms PREFIX xip_ram_perms SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/xip_ram_perms @@ -85,33 +113,6 @@ if (NOT PICOTOOL_NO_LIBUSB) DEPENDS xip_ram_perms ) - # compile enc_bootloader.elf - if (NOT DEFINED USE_PRECOMPILED) - set(USE_PRECOMPILED true) - endif() - ExternalProject_Add(enc_bootloader - PREFIX enc_bootloader - SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/enc_bootloader - BINARY_DIR ${CMAKE_BINARY_DIR}/enc_bootloader - CMAKE_ARGS - "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}" - "-DPICO_SDK_PATH:FILEPATH=${PICO_SDK_PATH}" - "-DUSE_PRECOMPILED:BOOL=${USE_PRECOMPILED}" - "-DPICO_DEBUG_INFO_IN_RELEASE=OFF" - BUILD_ALWAYS 1 # todo remove this - INSTALL_COMMAND "" - ) - - set(ENC_BOOTLOADER_ELF ${CMAKE_BINARY_DIR}/enc_bootloader/enc_bootloader.elf) - add_executable(enc_bootloader_elf IMPORTED) - add_dependencies(enc_bootloader_elf enc_bootloader) - set_property(TARGET enc_bootloader_elf PROPERTY IMPORTED_LOCATION ${ENC_BOOTLOADER_ELF}) - # copy enc_bootloader.elf into build directory - add_custom_command(TARGET enc_bootloader - COMMAND ${CMAKE_COMMAND} -E copy ${ENC_BOOTLOADER_ELF} ${CMAKE_BINARY_DIR}/enc_bootloader.elf - DEPENDS enc_bootloader - ) - # compile flash_id ExternalProject_Add(flash_id PREFIX picoboot_flash_id @@ -196,10 +197,12 @@ if (NOT PICOTOOL_NO_LIBUSB) endif() endif() +add_custom_target(binary_data_no_libusb DEPENDS + ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h) + add_custom_target(binary_data DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h ${CMAKE_CURRENT_BINARY_DIR}/xip_ram_perms_elf.h - ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h ${CMAKE_CURRENT_BINARY_DIR}/flash_id_bin.h) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h COMMAND ${CMAKE_COMMAND} @@ -272,7 +275,7 @@ add_executable(picotool enc_bootloader.cpp ${OTP_EXE} main.cpp) -add_dependencies(picotool enc_bootloader_elf) +add_dependencies(picotool enc_bootloader_elf binary_data_no_libusb) if (NOT PICOTOOL_NO_LIBUSB) target_sources(picotool PRIVATE xip_ram_perms.cpp) add_dependencies(picotool generate_otp_header xip_ram_perms_elf binary_data) From 0d816001211b62e28ce7728072c474d62b79c154 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 20 Jan 2025 15:38:04 +0000 Subject: [PATCH 08/80] Remove debug prints, and add USE_USB_DPRAM option --- enc_bootloader/CMakeLists.txt | 11 ++++++++++- enc_bootloader/enc_bootloader.c | 23 +++++++++++++++++++---- enc_bootloader/enc_bootloader.elf | Bin 58340 -> 37692 bytes main.cpp | 1 - 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 1ef39fd4..5206e201 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -38,10 +38,19 @@ if (NOT USE_PRECOMPILED) pico_rand ) + # use stack guards, as AES variables are written near the stack + target_compile_definitions(enc_bootloader PRIVATE PICO_USE_STACK_GUARDS=1) + pico_set_binary_type(enc_bootloader no_flash) + set(USE_USB_DPRAM FALSE) # create linker script to run from 0x20070000 file(READ ${PICO_LINKER_SCRIPT_PATH}/memmap_no_flash.ld LINKER_SCRIPT) - string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x20078000, LENGTH = 32k" LINKER_SCRIPT "${LINKER_SCRIPT}") + if (USE_USB_DPRAM) + string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x2007D000, LENGTH = 12k" LINKER_SCRIPT "${LINKER_SCRIPT}") + target_compile_definitions(enc_bootloader PRIVATE USE_USB_DPRAM=1) + else() + string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x2007C000, LENGTH = 16k" LINKER_SCRIPT "${LINKER_SCRIPT}") + endif() file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/memmap_enc_bootloader.ld "${LINKER_SCRIPT}") pico_set_linker_script(enc_bootloader ${CMAKE_CURRENT_BINARY_DIR}/memmap_enc_bootloader.ld) else() diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index a85aff18..f4b7a94d 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -3,16 +3,19 @@ * * SPDX-License-Identifier: BSD-3-Clause */ + +#ifdef DEBUG_PRINT #include +#endif #include #include "pico/stdlib.h" #include "boot/picobin.h" -#include "hardware/uart.h" #include "pico/bootrom.h" #include "pico/rand.h" #include "hardware/structs/otp.h" -#include "hardware/structs/qmi.h" -#include "hardware/structs/xip_ctrl.h" +#if USE_USB_DPRAM +#include "hardware/structs/usb_dpram.h" +#endif #include "pico/binary_info.h" @@ -63,14 +66,20 @@ static void init_aes() { init_lut_map(); } +#if USE_USB_DPRAM +uint8_t* workarea = (uint8_t*)USBCTRL_DPRAM_BASE; +#else static __attribute__((aligned(4))) uint8_t workarea[4 * 1024]; +#endif int main() { +#ifdef DEBUG_PRINT stdio_init_all(); printf("Decrypting the image\n"); printf("OTP Valid Keys %x\n", otp_hw->key_valid); printf("Unlocking\n"); +#endif for (int i=0; i<4; i++) { uint32_t key_i = ((i*2+1) << 24) | ((i*2+1) << 16) | (i*2 << 8) | i*2; @@ -93,9 +102,11 @@ int main() { memcpy(iv + 8, (void*)&iv2, sizeof(iv2)); memcpy(iv + 12, (void*)&iv3, sizeof(iv3)); +#ifdef DEBUG_PRINT printf("Pre decryption image begins with\n"); for (int i=0; i < 4; i++) printf("%08x\n", *(uint32_t*)(data_start_addr + i*4)); +#endif init_aes(); // Read key directly from OTP - guarded reads will throw a bus fault if there are any errors @@ -105,6 +116,7 @@ int main() { otp_hw->sw_lock[otp_key_page] = 0xf; ctr_crypt_s(iv, (void*)data_start_addr, data_size/16); +#ifdef DEBUG_PRINT printf("Post decryption image begins with\n"); for (int i=0; i < 4; i++) printf("%08x\n", *(uint32_t*)(data_start_addr + i*4)); @@ -112,16 +124,19 @@ int main() { printf("Chaining into %x, size %x\n", data_start_addr, data_size); stdio_deinit_all(); +#endif int rc = rom_chain_image( workarea, - sizeof(workarea), + 4 * 1024, data_start_addr, data_size ); +#ifdef DEBUG_PRINT stdio_init_all(); printf("Shouldn't return from ROM call %d\n", rc); +#endif reset_usb_boot(0, 0); } diff --git a/enc_bootloader/enc_bootloader.elf b/enc_bootloader/enc_bootloader.elf index 08dd1dfffce00a867951e06b6e4251d95b0f58e4..0eb804a532a06a44d5478047adc04c9cc294d8de 100755 GIT binary patch delta 12268 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 I^A~;pA1)b7@&Et; literal 58340 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 diff --git a/main.cpp b/main.cpp index b550ac18..4471bbb9 100644 --- a/main.cpp +++ b/main.cpp @@ -5043,7 +5043,6 @@ bool encrypt_command::execute(device_map &devices) { // Bootloader size auto bootloader_txt = enc_elf->get_section(".text"); uint32_t bootloader_size = 0x20082000 - bootloader_txt->virtual_address(); - printf("Bootloader size %08x start %08x\n", bootloader_size, bootloader_txt->virtual_address()); // Move bootloader down in physical space to start of SRAM (which will be start of flash once packaged) enc_elf->move_all(data_start_address - bootloader_txt->virtual_address()); From d0dd100d0f76cb8da45dbc7c5bc8c50e22c7d201 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 20 Jan 2025 17:37:05 +0000 Subject: [PATCH 09/80] Reduce binary size Add pico_minimize_runtime and some other runtime skips Re-use scratch_y for the workarea, without overwriting the stack --- enc_bootloader/CMakeLists.txt | 16 +++++++++++++--- enc_bootloader/enc_bootloader.c | 15 ++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 5206e201..c681401b 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -39,20 +39,30 @@ if (NOT USE_PRECOMPILED) ) # use stack guards, as AES variables are written near the stack - target_compile_definitions(enc_bootloader PRIVATE PICO_USE_STACK_GUARDS=1) + target_compile_definitions(enc_bootloader PRIVATE + PICO_USE_STACK_GUARDS=1 + PICO_STACK_SIZE=0x200 + # No heap is used + PICO_HEAP_SIZE=0 + # These inits are not required + PICO_RUNTIME_SKIP_INIT_PER_CORE_IRQ_PRIORITIES=1 + PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1) + + pico_minimize_runtime(enc_bootloader) pico_set_binary_type(enc_bootloader no_flash) set(USE_USB_DPRAM FALSE) # create linker script to run from 0x20070000 file(READ ${PICO_LINKER_SCRIPT_PATH}/memmap_no_flash.ld LINKER_SCRIPT) if (USE_USB_DPRAM) - string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x2007D000, LENGTH = 12k" LINKER_SCRIPT "${LINKER_SCRIPT}") + string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x2007F000, LENGTH = 4k" LINKER_SCRIPT "${LINKER_SCRIPT}") target_compile_definitions(enc_bootloader PRIVATE USE_USB_DPRAM=1) else() - string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x2007C000, LENGTH = 16k" LINKER_SCRIPT "${LINKER_SCRIPT}") + string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x2007F000, LENGTH = 4k" LINKER_SCRIPT "${LINKER_SCRIPT}") endif() file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/memmap_enc_bootloader.ld "${LINKER_SCRIPT}") pico_set_linker_script(enc_bootloader ${CMAKE_CURRENT_BINARY_DIR}/memmap_enc_bootloader.ld) + pico_add_dis_output(enc_bootloader) else() project(enc_bootloader C CXX ASM) message("Using precompiled enc_bootloader.elf") diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index f4b7a94d..67be909b 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -21,8 +21,6 @@ #include "config.h" -volatile uint32_t systick_data[18]; // count, R0-R15,RETPSR - extern void remap(); extern uint32_t gen_rand_sha(); extern void init_key(uint8_t *key); @@ -36,7 +34,7 @@ extern uint32_t lut_a_map[1]; extern uint32_t lut_b_map[1]; extern uint32_t rstate_sha[4],rstate_lfsr[2]; -void resetrng() { +void __scratch_x("aes") resetrng() { uint32_t f0,f1; do f0=get_rand_32(); while(f0==0); // make sure we don't initialise the LFSR to zero f1=get_rand_32(); @@ -52,7 +50,7 @@ void resetrng() { #endif } -static void init_lut_map() { +static void __scratch_x("aes") init_lut_map() { int i; for(i=0;i<256;i++) lut_b[i]=gen_rand_sha()&0xff, lut_a[i]^=lut_b[i]; lut_a_map[0]=0; @@ -60,7 +58,7 @@ static void init_lut_map() { remap(); } -static void init_aes() { +static void __scratch_x("aes") init_aes() { resetrng(); gen_lut_sbox(); init_lut_map(); @@ -69,7 +67,8 @@ static void init_aes() { #if USE_USB_DPRAM uint8_t* workarea = (uint8_t*)USBCTRL_DPRAM_BASE; #else -static __attribute__((aligned(4))) uint8_t workarea[4 * 1024]; +// static __attribute__((aligned(4))) uint8_t workarea[4 * 1024]; +uint8_t* workarea = (uint8_t*)SRAM_SCRATCH_Y_BASE; #endif int main() { @@ -128,7 +127,7 @@ int main() { int rc = rom_chain_image( workarea, - 4 * 1024, + 4 * 1024 - PICO_STACK_SIZE, // Don't use stack in workarea data_start_addr, data_size ); @@ -137,6 +136,4 @@ int main() { stdio_init_all(); printf("Shouldn't return from ROM call %d\n", rc); #endif - - reset_usb_boot(0, 0); } From 5596923f3bfa713a74c52e5d58c39447ab8a756a Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 21 Jan 2025 12:18:44 +0000 Subject: [PATCH 10/80] Remove pico_rand and use boot random instead Rejig everything to fit into scratch, so full 512k of SRAM is available for the user --- enc_bootloader/CMakeLists.txt | 15 +- enc_bootloader/enc_bootloader.c | 18 +- enc_bootloader/memmap_enc_bootloader.ld | 259 ++++++++++++++++++++++++ main.cpp | 2 +- 4 files changed, 272 insertions(+), 22 deletions(-) create mode 100644 enc_bootloader/memmap_enc_bootloader.ld diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index c681401b..baa1792b 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -35,13 +35,13 @@ if (NOT USE_PRECOMPILED) target_link_libraries(enc_bootloader pico_stdlib - pico_rand ) # use stack guards, as AES variables are written near the stack target_compile_definitions(enc_bootloader PRIVATE PICO_USE_STACK_GUARDS=1 PICO_STACK_SIZE=0x200 + PICO_NO_PROGRAM_INFO=1 # No heap is used PICO_HEAP_SIZE=0 # These inits are not required @@ -51,17 +51,8 @@ if (NOT USE_PRECOMPILED) pico_minimize_runtime(enc_bootloader) pico_set_binary_type(enc_bootloader no_flash) - set(USE_USB_DPRAM FALSE) - # create linker script to run from 0x20070000 - file(READ ${PICO_LINKER_SCRIPT_PATH}/memmap_no_flash.ld LINKER_SCRIPT) - if (USE_USB_DPRAM) - string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x2007F000, LENGTH = 4k" LINKER_SCRIPT "${LINKER_SCRIPT}") - target_compile_definitions(enc_bootloader PRIVATE USE_USB_DPRAM=1) - else() - string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x2007F000, LENGTH = 4k" LINKER_SCRIPT "${LINKER_SCRIPT}") - endif() - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/memmap_enc_bootloader.ld "${LINKER_SCRIPT}") - pico_set_linker_script(enc_bootloader ${CMAKE_CURRENT_BINARY_DIR}/memmap_enc_bootloader.ld) + set(USE_USB_DPRAM TRUE) + pico_set_linker_script(enc_bootloader ${CMAKE_CURRENT_LIST_DIR}/memmap_enc_bootloader.ld) pico_add_dis_output(enc_bootloader) else() project(enc_bootloader C CXX ASM) diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 67be909b..445b5179 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -11,7 +11,6 @@ #include "pico/stdlib.h" #include "boot/picobin.h" #include "pico/bootrom.h" -#include "pico/rand.h" #include "hardware/structs/otp.h" #if USE_USB_DPRAM #include "hardware/structs/usb_dpram.h" @@ -34,10 +33,12 @@ extern uint32_t lut_a_map[1]; extern uint32_t lut_b_map[1]; extern uint32_t rstate_sha[4],rstate_lfsr[2]; -void __scratch_x("aes") resetrng() { +void resetrng() { uint32_t f0,f1; - do f0=get_rand_32(); while(f0==0); // make sure we don't initialise the LFSR to zero - f1=get_rand_32(); + uint32_t boot_random[4]; + rom_get_boot_random(boot_random); + do f0=boot_random[0]; while(f0==0); // make sure we don't initialise the LFSR to zero + f1=boot_random[1]; rstate_sha[0]=f0&0xffffff00; // bottom byte must be zero (or 4) for SHA, representing "out of data" rstate_sha[1]=f1; rstate_sha[2]=0x41414141; @@ -50,7 +51,7 @@ void __scratch_x("aes") resetrng() { #endif } -static void __scratch_x("aes") init_lut_map() { +static void init_lut_map() { int i; for(i=0;i<256;i++) lut_b[i]=gen_rand_sha()&0xff, lut_a[i]^=lut_b[i]; lut_a_map[0]=0; @@ -58,7 +59,7 @@ static void __scratch_x("aes") init_lut_map() { remap(); } -static void __scratch_x("aes") init_aes() { +static void init_aes() { resetrng(); gen_lut_sbox(); init_lut_map(); @@ -67,8 +68,7 @@ static void __scratch_x("aes") init_aes() { #if USE_USB_DPRAM uint8_t* workarea = (uint8_t*)USBCTRL_DPRAM_BASE; #else -// static __attribute__((aligned(4))) uint8_t workarea[4 * 1024]; -uint8_t* workarea = (uint8_t*)SRAM_SCRATCH_Y_BASE; +uint8_t* workarea = (uint8_t*)0x20080200; // AES Code & workspace from 0x20080180 -> 0x20081600 #endif int main() { @@ -127,7 +127,7 @@ int main() { int rc = rom_chain_image( workarea, - 4 * 1024 - PICO_STACK_SIZE, // Don't use stack in workarea + 4 * 1024, data_start_addr, data_size ); diff --git a/enc_bootloader/memmap_enc_bootloader.ld b/enc_bootloader/memmap_enc_bootloader.ld new file mode 100644 index 00000000..7becc192 --- /dev/null +++ b/enc_bootloader/memmap_enc_bootloader.ld @@ -0,0 +1,259 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + RAM_START(rwx) : ORIGIN = 0x20080000, LENGTH = 0x180 + SCRATCH_X(rwx) : ORIGIN = 0x20080180, LENGTH = 0xE80 + SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 0x800 + RAM(rwx) : ORIGIN = 0x20081800, LENGTH = 0x800 +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* Note unlike RP2040, we start the image with a vector table even for + NO_FLASH builds. On Arm, the bootrom expects a VT at the start of the + image by default; on RISC-V, the default is to enter the image at its + lowest address, so an IMAGEDEF item is required to specify the + nondefault entry point. */ + + .start_text : { + __logical_binary_start = .; + /* Vectors require 512-byte alignment on v8-M when >48 IRQs are used, + so we would waste RAM if the vector table were not at the + start. */ + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + } > RAM_START + + .text : { + __reset_start = .; + KEEP (*(.reset)) + __reset_end = .; + *(.time_critical*) + *(.text*) + . = ALIGN(4); + *(.init) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + } > RAM + + .rodata : { + . = ALIGN(4); + *(.rodata*) + *(.srodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > RAM + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > RAM + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > RAM + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > RAM + __binary_info_end = .; + . = ALIGN(4); + + .data : { + __data_start__ = .; + *(vtable) + *(.data*) + *(.sdata*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM + PROVIDE(__data_end__ = .); + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + PROVIDE(__global_pointer$ = . + 2K); + *(.sbss*) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + } > RAM + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary") + ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") + + /* todo assert on extra code */ +} diff --git a/main.cpp b/main.cpp index 4471bbb9..e175e2dc 100644 --- a/main.cpp +++ b/main.cpp @@ -5041,7 +5041,7 @@ bool encrypt_command::execute(device_map &devices) { enc_elf->read_file(tmp); // Bootloader size - auto bootloader_txt = enc_elf->get_section(".text"); + auto bootloader_txt = enc_elf->get_section(".start_text"); uint32_t bootloader_size = 0x20082000 - bootloader_txt->virtual_address(); // Move bootloader down in physical space to start of SRAM (which will be start of flash once packaged) From 0dc6c6243ba344ccd391319b0895d319e6fdeae3 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 21 Jan 2025 12:21:36 +0000 Subject: [PATCH 11/80] Update precompiled enc_bootloader.elf --- enc_bootloader/enc_bootloader.elf | Bin 37692 -> 22592 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/enc_bootloader/enc_bootloader.elf b/enc_bootloader/enc_bootloader.elf index 0eb804a532a06a44d5478047adc04c9cc294d8de..b5c1dbbae51aede00e02093199360c83b10c5985 100755 GIT binary patch delta 7927 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 x_DtpM4wpL{|3v7{;O`K;%pmY1;dWpR$Kah~Tp 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

(Xq=6Dq8yr&&5(SBO*~DUPFJsVxT|2dkiKLH zY(hzGwyr2RR2@lr(5|)1|6uYlrWB!HmMVz6+?i{>w- z?j`7h5HhAbLZc$j?oz-E2s4DPatM=?2ffgJSx8b%bH+)+?;6(7m=*42Zp_H!LJ7mR z*bI{Xh*BDdQ{&+ynFyn&%%4jdBF+RwejD0pp&J9UtP?jSlbt6=#Pp`S_jD`Vc=n*v zu^8s^LEAEEz(NCJPKSY-UP6|tk}FCR%4cGv!wz&Zk3uNu^03n7PDn~=eY=&1MlQTU zjwt9fVu!nFRCPMMRV&1b)q-gb4YHORpAYj;sR-jmPxUg4Ko!EAKx_yMMUfrER$aFQ zc?i?n2QiG}yn0)WSJEvx^|ETGl!25T0_MV4M(PL^N9dB!GgPk{+AmbbgGox)%!zVD z&0!R2+<`KS+9O60MrP5N%9=s*KUu8>%Y>B0Ne$VKy4nTe=y`KTzo}(&DGyoesEpmo zbv1>Q8>W9Y+cIZ43~g7#V|x5kP0=-9sukNqZh#z`VfNg5o|;-regs1!=qBtx3hQ&y zh$%k#H^g|9m%j8t zS>>3>LoBnDSxyd!J!jUX`f@dd?*5bPUG`BnCGyxs5|Zsd?!tgTOABZ@NwMe5$t-$m z9z-)%)I3-%*HWBf)UK$dW#Ui9?J79+Aq;diUUVyY1u6`)^v6yl)fYw#Wv?iARF^xe zM5?%G6|4sgwGDPgZX^7G7g4h((!y1!6P{<)u2_Qo7TkSa@$Qm+LYK<)){v26nTjbL z%M^nI^Tjf;s*+Y-4`IxaNYFFHp&xAhc#yofTCy$g_O75z$ud>cEm;CkUbAEgx&h0~ zpFK0v;lqHu06Qp1p~AVKj+S?oHL~BaV1d`UOh(XE+3SGPFYJzUGpfSZYBD1#DSIou zjwMcPasAuZAaG&}jL|-*u%C*3v7r2+9)jrk2wf>~#7FNh(R=y9;dov^0n$bLcyx_~ zllkcR$1DVx1d8}0^NpAI=(Q_)4(kWs!#(g#k@*mjo)3I8UBM*LD5L<6%1Y_cdp6gR z9_TL%!rX(LShLR^hvzI}xDB*C<8UAn|pz!992m zB+=+bX^`6t2ibXW6Rvx~M{Oiua$YYZK=u>uDexVTnH02og`dbJ5c~@ANSB<(d%&`v zNJqgpRN|qVO#4vbr|=8lGm=NTWWH0tvY$xNc)%#jjId1mm%>ls1HgBMJklleT>>V* zEWbVw-mhe$L?ig4 Date: Tue, 21 Jan 2025 12:25:36 +0000 Subject: [PATCH 12/80] Remove unused use_usb_dpram --- enc_bootloader/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index baa1792b..2589e7c4 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -51,7 +51,6 @@ if (NOT USE_PRECOMPILED) pico_minimize_runtime(enc_bootloader) pico_set_binary_type(enc_bootloader no_flash) - set(USE_USB_DPRAM TRUE) pico_set_linker_script(enc_bootloader ${CMAKE_CURRENT_LIST_DIR}/memmap_enc_bootloader.ld) pico_add_dis_output(enc_bootloader) else() From a605be7502c6e8e0302e7faef36956a4f60234b3 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 21 Jan 2025 17:21:43 +0000 Subject: [PATCH 13/80] Re-jig to create more space for the AES code Move vtor into main code body, leaving only binary info and embedded block at start Reduce initial stack size, then increase after AES code to overwrite the workspace Reduce vector table IRQs (requires pico-sdk pr 2200) --- enc_bootloader/CMakeLists.txt | 9 ++++- enc_bootloader/enc_bootloader.c | 51 ++++++------------------- enc_bootloader/memmap_enc_bootloader.ld | 16 ++++---- 3 files changed, 26 insertions(+), 50 deletions(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 2589e7c4..e44aff86 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -40,13 +40,18 @@ if (NOT USE_PRECOMPILED) # use stack guards, as AES variables are written near the stack target_compile_definitions(enc_bootloader PRIVATE PICO_USE_STACK_GUARDS=1 - PICO_STACK_SIZE=0x200 + PICO_STACK_SIZE=0x100 PICO_NO_PROGRAM_INFO=1 # No heap is used PICO_HEAP_SIZE=0 # These inits are not required PICO_RUNTIME_SKIP_INIT_PER_CORE_IRQ_PRIORITIES=1 - PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1) + PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1 + # Don't need any vtor irqs + PICO_VECTOR_TABLE_NUM_IRQS=0) + + # print memory usage + target_link_options(enc_bootloader PUBLIC -Wl,--print-memory-usage) pico_minimize_runtime(enc_bootloader) diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 445b5179..b90aa7e5 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -4,17 +4,11 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#ifdef DEBUG_PRINT -#include -#endif #include #include "pico/stdlib.h" #include "boot/picobin.h" #include "pico/bootrom.h" #include "hardware/structs/otp.h" -#if USE_USB_DPRAM -#include "hardware/structs/usb_dpram.h" -#endif #include "pico/binary_info.h" @@ -65,20 +59,8 @@ static void init_aes() { init_lut_map(); } -#if USE_USB_DPRAM -uint8_t* workarea = (uint8_t*)USBCTRL_DPRAM_BASE; -#else -uint8_t* workarea = (uint8_t*)0x20080200; // AES Code & workspace from 0x20080180 -> 0x20081600 -#endif - int main() { -#ifdef DEBUG_PRINT - stdio_init_all(); - - printf("Decrypting the image\n"); - printf("OTP Valid Keys %x\n", otp_hw->key_valid); - printf("Unlocking\n"); -#endif + // Unlock OTP page with hardcoded key for (int i=0; i<4; i++) { uint32_t key_i = ((i*2+1) << 24) | ((i*2+1) << 16) | (i*2 << 8) | i*2; @@ -101,12 +83,6 @@ int main() { memcpy(iv + 8, (void*)&iv2, sizeof(iv2)); memcpy(iv + 12, (void*)&iv3, sizeof(iv3)); -#ifdef DEBUG_PRINT - printf("Pre decryption image begins with\n"); - for (int i=0; i < 4; i++) - printf("%08x\n", *(uint32_t*)(data_start_addr + i*4)); -#endif - init_aes(); // Read key directly from OTP - guarded reads will throw a bus fault if there are any errors uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE; @@ -115,25 +91,20 @@ int main() { otp_hw->sw_lock[otp_key_page] = 0xf; ctr_crypt_s(iv, (void*)data_start_addr, data_size/16); -#ifdef DEBUG_PRINT - printf("Post decryption image begins with\n"); - for (int i=0; i < 4; i++) - printf("%08x\n", *(uint32_t*)(data_start_addr + i*4)); - - printf("Chaining into %x, size %x\n", data_start_addr, data_size); + // Increase stack limit by 0x100 + pico_default_asm_volatile( + "mrs r0, msplim\n" + "subs r0, 0x100\n" + "msr msplim, r0" + :::"r0"); - stdio_deinit_all(); -#endif - - int rc = rom_chain_image( - workarea, + // Chain into decrypted image + rom_chain_image( + (uint8_t*)0x20080200, // AES Code & workspace from 0x20080030 -> 0x20081500 4 * 1024, data_start_addr, data_size ); -#ifdef DEBUG_PRINT - stdio_init_all(); - printf("Shouldn't return from ROM call %d\n", rc); -#endif + __breakpoint(); } diff --git a/enc_bootloader/memmap_enc_bootloader.ld b/enc_bootloader/memmap_enc_bootloader.ld index 7becc192..aa9602a6 100644 --- a/enc_bootloader/memmap_enc_bootloader.ld +++ b/enc_bootloader/memmap_enc_bootloader.ld @@ -23,10 +23,10 @@ MEMORY { - RAM_START(rwx) : ORIGIN = 0x20080000, LENGTH = 0x180 - SCRATCH_X(rwx) : ORIGIN = 0x20080180, LENGTH = 0xE80 - SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 0x800 - RAM(rwx) : ORIGIN = 0x20081800, LENGTH = 0x800 + RAM_START(rwx) : ORIGIN = 0x20080000, LENGTH = 0x30 + SCRATCH_X(rwx) : ORIGIN = 0x20080030, LENGTH = 0xFD0 + SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 0x700 + RAM(rwx) : ORIGIN = 0x20081700, LENGTH = 0x900 } ENTRY(_entry_point) @@ -41,10 +41,6 @@ SECTIONS .start_text : { __logical_binary_start = .; - /* Vectors require 512-byte alignment on v8-M when >48 IRQs are used, - so we would waste RAM if the vector table were not at the - start. */ - KEEP (*(.vectors)) KEEP (*(.binary_info_header)) __binary_info_header_end = .; KEEP (*(.embedded_block)) @@ -52,6 +48,10 @@ SECTIONS } > RAM_START .text : { + /* Vectors require 512-byte alignment on v8-M when >48 IRQs are used, + so we would waste RAM if the vector table were not at the + start. */ + KEEP (*(.vectors)) __reset_start = .; KEEP (*(.reset)) __reset_end = .; From cff9a9e2b531b33d646d8fd770c95681d1a19b54 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 22 Jan 2025 13:11:05 +0000 Subject: [PATCH 14/80] Integrate new aes.S Uses a modified version of get_rand_32 temporarily, until the aes.S code is modified to use rom_get_boot_random From commit d4f4b2a7d9f00 --- enc_bootloader/aes.S | 283 +++++++++++++++++++------------- enc_bootloader/enc_bootloader.c | 53 +++--- 2 files changed, 187 insertions(+), 149 deletions(-) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index ad6c448d..8b58f067 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -1,3 +1,13 @@ +/* MEMORY LAYOUT ASSUMPTIONS + +The "chaff" area must be located at the start of Y scratch RAM, 0x20081000: see +the macro getchaffaddress. + +The stack must be located at the end of Y scratch RAM: see the calls to memwipe +at the end of ctr_crypt_s where memory between __scratch_y_end__ and the stack +pointer is overwritten. +*/ + .syntax unified .cpu cortex-m33 .thumb @@ -7,24 +17,18 @@ #include "hardware/regs/addressmap.h" #include "hardware/regs/sha256.h" #include "hardware/rcp.h" +#include "hardware/regs/resets.h" -.global gen_lut_sbox +.global init_aes +.global init_key_4way .global ctr_crypt_s -.global remap -.global gen_rand_sha -.global init_key - -.global rkey_s -.global lut_a,lut_a_map -.global lut_b,lut_b_map -.global rstate_sha,rstate_lfsr @ RCP macros #define CTAG0 0x2a #define CTAG1 0x2b #define CTAG2 0x2c -#define CTAG3 0x2d @ not used +#define CTAG3 0x2d #define CTAG4 0x2e #define CTAG5 0x30 #define CTAG6 0x31 @@ -39,7 +43,7 @@ #define CTAG15 0x3a #define CTAG16 0x3b #define CTAG17 0x3c -#define CTAG18 0x3d @ not used +#define CTAG18 0x3d .macro SET_COUNT n .if RC_COUNT @@ -112,7 +116,9 @@ @ Put workspace in the second scratch area @ The "a"=allocatable attribute (and possibly the %progbits attribute) are necessary to store the murmur3 constants, @ otherwise they may end up silently replaced with 0 or 0xffffffff -.section .scratch_y.aes,"a",%progbits +.section .scratch_y.aes,"aw",%progbits + +workspace_start: @ chaff has to be at the start of scratch_y = 0x20081000 because this is assumed by the following macro, getchaffaddress @ (It seems ADR does not work, nor is it possible to assert that chaff==0x20081000) @@ -126,6 +132,39 @@ chaff: .space 48 +.balign 16 +rkey_s: @ round key shares: 600 bytes = 15 rounds * 2 shares * (4+1) words + @ see comment at init_key_4way for description of layout and meaning of rkey_s +.space 600 +rkey4way: @ scratch area for init_key_4way; could overlap this with other scratch space if need to save space +.space 128 +.if CT_BPERM +bperm_rand: @ 32 half words that define the oblivious permutation of blocks +.space 64 +.endif + +.balign 16 +permscratch: @ Must be 0 mod 16; 16 bytes of scratch space to store permutation(s) +perm16: +.space 16 +@ Scratch space of 32 bytes used both by init_key_sbox and map_sbox_s +.balign 16 +fourway: @ Must be 0 mod 16 +shareA: @ 0 mod 16 +.space 20 @ Only need 16 bytes, but choosing shareB!=shareA mod 16 +shareB: @ 4 mod 16 +.space 20 +shareC: @ 8 mod 16 +.space 4 +statevperm: @ 12 mod 16 +.space 4 @ vperm state rotation: only last two bits are operational; other bits random +RKshareC: @ Round key common share C; see comment at init_key_4way for explanation +.space 4 +RKshareCchange: @ Temporary used by ref_roundkey_share_s +.space 4 + +workspace_end: + @ Regardless of configuration, the code uses a single 256-entry LUT, @ which is a simple S-box table. @ The LUT is represented as two shares, lut_a and lut_b, @@ -143,7 +182,22 @@ chaff: @ lut_b[x ^ b₀ ^ b₁] ^ c₁ ^ d₁ .balign 16 lut_a: @ LUT share A (must be 0 mod 16 so that init_key_sbox knows how to mask the lookup) -.space 256 +.byte 0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76 +.byte 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0 +.byte 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15 +.byte 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75 +.byte 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84 +.byte 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf +.byte 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8 +.byte 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2 +.byte 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73 +.byte 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb +.byte 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79 +.byte 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08 +.byte 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a +.byte 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e +.byte 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf +.byte 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16 lut_a_map: @ the current scrambling of lut_a; not particularly secret since it can be deduced from the contents of lut_a and lut_b .space 4 .space 4 @ align to 8 mod 16 @@ -152,38 +206,12 @@ lut_b: @ LUT share B (must be 8 mod 16 so that init_key_sb lut_b_map: .space 4 .space 4 @ align to multiple of 8 -rkey_s: @ round key shares: 600 bytes = 15 rounds * 2 shares * (4+1) words - @ every fourth word has a word that is used as a vperm count, and also as a spacer to misalign the shares mod 16 -.space 600 -rkey4way: @ scratch area for init_key; could overlap this with other scratch space if need to save space -.space 128 -.if CT_BPERM -bperm_rand: @ 32 half words that define the oblivious permutation of blocks -.space 64 -.endif + .balign 16 rstate_sha: @ 128-bit SHA random state, to be initialised to TRNG bytes; zeroth byte must be initialised to zero .space 16 rstate_lfsr: @ 32-bit LFSR random state and constant used to step it (initialised by C program) .space 8 -.balign 16 -permscratch: @ Must be 0 mod 16; 16 bytes of scratch space to store permutation(s) -perm16: -.space 16 -@ Scratch space of 32 bytes used both by init_key_sbox and map_sbox_s -.balign 16 -fourway: @ Must be 0 mod 16 -shareA: @ 0 mod 16 -.space 20 @ Only need 16 bytes, but choosing shareB!=shareA mod 16 -shareB: @ 4 mod 16 -.space 20 -shareC: @ 8 mod 16 -.space 4 -statevperm: @ 12 mod 16 -.space 4 @ vperm state rotation: only last two bits are operational; other bits random -RKshareC: -.space 4 -.balign 16 .if CT_BPERM .balign 16 @@ -311,6 +339,43 @@ gen_rand_lfsr_nonpres: .ltorg +.balign 4 +.thumb_func +init_aes: + push {r14} + GET_CANARY r14,CTAG3 + push {r0-r12,r14} +@ first initialise the random number generator +1: + bl get_rand_32 + movs r4,r0 + beq 1b @ get a non-zero random number + bl get_rand_32 + ldr r1,=rstate_sha + str r0,[r1,#4] @ rstate_sha[1]=f1; + bic r0,r4,#0xff + str r0,[r1] @ rstate_sha[0]=f0&0xffffff00; bottom byte is set to zero, representing "out of data" + mov r2,#0x41414141 + str r2,[r1,#8] @ rstate_sha[2]=0x41414141; + str r2,[r1,#12] @ rstate_sha[3]=0x41414141; + str r4,[r1,#rstate_lfsr-rstate_sha] @ must be nonzero for non-degenerate LFSR + ldr r2,=0x1d872b41; + str r2,[r1,#rstate_lfsr-rstate_sha+4] @ constant that defines a maximal-length LFSR +.if GEN_RAND_SHA + ldr r1,=RESETS_BASE+RESETS_RESET_OFFSET + ldr r2,[r1] + orr r3,r2,#RESETS_RESET_SHA256_BITS + str r3,[r1] @ reset the SHA hardware + str r2,[r1] @ release the reset +.endif + +init_aes_norand_entry: @ Entry point for test code that doesn't want to reset the RNGs + + bl remap @ scramble the LUTs + pop {r0-r12,r14} + CHK_CANARY r14,CTAG3 + pop {r15} + .balign 4 .thumb_func makesmallperm: @@ -511,7 +576,7 @@ ref_roundkey_shares_s_test: @ entry point for test code to do fewer than 15 rou ldr r4,=rkey_s loadlfsr steplfsr @ r0=change in RKshareC - adr r2,RKshareCchange + ldr r2,=RKshareCchange str r0,[r2] ldr r3,=RKshareC ldr r5,[r3] @@ -535,7 +600,8 @@ ref_roundkey_shares_s_loop: steplfsr; eors r7,r7,r0; ands r9,r9,#3; ldr r3,[r4,r9,lsl#2]; ror r2,r0,r12; eors r3,r3,r2,ror#16; mov r12,r12,ror#8; str r3,[r4,r9,lsl#2]; adds r9,r9,#1 steplfsr; eors r8,r8,r0; ands r9,r9,#3; ldr r3,[r4,r9,lsl#2]; ror r2,r0,r12; eors r3,r3,r2,ror#16; str r3,[r4,r9,lsl#2] - ldr r3,RKshareCchange + ldr r3,=RKshareCchange + ldr r3,[r3] movs r2,#0 usub8 r10,r2,r10 ror r2,r3,r10; mov r10,r10,ror#8; eors r5,r5,r2 @@ -554,9 +620,6 @@ ref_roundkey_shares_s_loop: clear03 24 ref_roundkey_shares_s_exit: bx r14 - .balign 4 -RKshareCchange: - .space 4 .balign 4 .thumb_func @@ -863,8 +926,6 @@ shift_rows_s: @ multiply polynomial over GF(2⁸) by d(x) = 0x0Bx³ + 0x0Dx² + 0x09x + 0x0E modulo x⁴+1; c(x)d(x)=1 modulo x⁴+1 .macro invmixcol rx,rt,ru,rv,rw,r0x00,r0x1b -@ !!! can probably save some registers, e.g. allow trashing of r0x00, r0x1b -@ can possibly also simplify slightly with refactorisation uadd8 \rt,\rx,\rx @ field multiplication by 2 as above sel \rw,\r0x1b,\r0x00 eors \rt,\rt,\rw @ 2x @@ -904,51 +965,6 @@ mix_cols_s: ldmia r12!,{r0,r1} @ overwrite sensitive shareB-related quantities r0,r1 with random numbers bx r14 -.balign 4 -.thumb_func -gen_lut_sbox: -@ gen_lut_sbox sets both lut_a and lut_b to the S-box table and -@ returns r0=lut_a+256, r1=lut_b+256 -@ first set lut_a to be a table of GF(2⁸) inverses, using lut_b as temporary storage - ldr r0,=lut_a - ldr r1,=lut_b -@ first set lut_a to be a table of antilogarithms, lut_b a table of logarithms - mov r2,#0 - strb r2,[r0] @ (*) - mov r3,#1 @ we maintain invariant that r2=log(r3) -1: - strb r2,[r0,r3] @ log table - strb r3,[r1,r2] @ antilog table - lsls r12,r3,#25 - it cs - eorcs r12,r12,#0x1b000000 @ multiply by x - eor r3,r3,r12,lsr#24 @ multiply by x+1 ("3"), which is a primitive element - add r2,r2,#1 - cmp r2,#255 - bls 1b - movs r2,#255 -1: - ldrb r3,[r0,r2] @ for each i≠0, find log,... - eor r3,r3,#255 @ ... negate... - ldrb r3,[r1,r3] @ ... and antilog to get inverse - strb r3,[r0,r2] - subs r2,r2,#1 - bne 1b @ note that inverse(0)=0 by (*) above -@ At this point r0=lut_a, r1=lut_b, lut_a[] contains inverses and lut_b[] contains other stuff - mov r12,#256 -1: - ldrb r2,[r0] - eors r3,r2,r2,lsl#1 @ convolve byte with 0x1f - eors r3,r3,r3,lsl#2 - eors r3,r3,r2,lsl#4 - eors r2,r3,r3,lsr#8 - eor r2,r2,#0x63 @ and add 0x63 - strb r2,[r0],#1 @ let lut_a[i]=sbox[i] - strb r2,[r1],#1 @ let lut_b[i]=sbox[i] - subs r12,r12,#1 - bne 1b - bx r14 - @ Lookup each byte of a word, Rtarg, in a table and replace Rtarg with the result (used for SBOX lookups) .macro subbytes Rtarg,Rtable,Rspare0,Rspare1,Rspare2,Rspare3 ubfx \Rspare0,\Rtarg,#0, #8 @@ -1296,7 +1312,7 @@ storeroundkey: .balign 4 .thumb_func -init_key: +init_key_4way: @ On entry, r0 points to 4-way shared raw key data (128 bytes) @ The format is a0 b0 c0 d0 a1 b1 c1 d1 ... a7 b7 c7 d7 @ That is, each word, K, of the original 256-bit key is expanded into four words whose exclusive OR is K. @@ -1313,16 +1329,20 @@ init_key: @ rk[i] = rka_unrot[i] ^ rkb_unrot[i] ^ RKshareC GET_CANARY r12,CTAG17 - push {r4-r11,r12,r14} + push {r0-r12,r14} +@ Transfer 4-way key into local workspace, rerandomising the shares mov r5,r0 @ r5=4-way key input bl randomisechaff - ldr r4,=rkey4way - movs r6,#8 + ldr r6,=rkey4way + movs r7,#8 1: - ldmia r5!,{r0-r3} - stmia r4!,{r0-r3} - subs r6,r6,#1 + ldmia r5!,{r1-r4} + bl gen_rand_sha; eors r1,r1,r0; eors r4,r4,r0 + bl gen_rand_sha; eors r2,r2,r0; eors r4,r4,r0 + bl gen_rand_sha; eors r3,r3,r0; eors r4,r4,r0 + stmia r6!,{r1-r4} + subs r7,r7,#1 bne 1b @ Now raw key is stored in rkey4way[], construct 2-way share in rkey_s[] for @@ -1400,7 +1420,7 @@ init_key_expandloop: cmp r2,#52 bne init_key_expandloop - pop {r4-r11,r12,r14} + pop {r0-r12,r14} CHK_CANARY r12,CTAG17 bx r14 @@ -1421,7 +1441,7 @@ addrkey_s: ldr r1,[r12,#16] @ r1=vperm key rotation in top two bits ldr r2,[r0,#16] @ barrier load - rsbs r2,r3,r1,lsr#30 @ r2=vpermkeyrot-vpermstaterot + rsb r2,r3,r1,lsr#30 @ r2=vpermkeyrot-vpermstaterot @ Read shareA of roundkey, offset by vpermkeyrot-vpermstaterot, and eor it into shareA of state, offset by -vpermstaterot @ r1=rkeyArotdata, r2=vpermkeyrot-vpermstaterot, r3=statevperm, r4-r11=state, r12=roundkeyAptr .if RK_ROR @@ -1444,7 +1464,7 @@ addrkey_s: bfi r0,r12,#0,#4 @ match chaff pointer (r0) to roundkey ptr (r12) mod 16 ldr r1,[r12,#16] @ r1=vperm key rotation in top two bits ldr r2,[r0,#16] @ barrier load - rsbs r2,r3,r1,lsr#30 @ r2=vpermkeyrot-vpermstaterot + rsb r2,r3,r1,lsr#30 @ r2=vpermkeyrot-vpermstaterot ldr r3,=RKshareC @ r3=common round key shareC bfi r0,r3,#0,#4 ldr r3,[r3] @@ -1466,7 +1486,6 @@ addrkey_s: ands r2,r2,#3; ldr r0,[r12,r2,lsl#2]; eor r11,r11,r3,ror#16; eors r11,r11,r0 .endif clear03 - bx r14 .balign 4 @@ -1485,7 +1504,7 @@ addrkey_s: ctr_crypt_s: @ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks GET_CANARY r12,CTAG0 - push {r0,r4-r11,r12,r14} + push {r0-r12,r14} @ save all registers so that when we restore we overwrite any secrets push {r0-r2} SET_COUNT 93 @@ -1647,7 +1666,7 @@ rounds_s_mainloop: bl map_sbox_s bl shift_rows_s .if ST_VPERM - ldmia r13,{r2} @ peek at stack to get round count + ldr r2,[r13] @ peek at stack to get round count cmp r2,#NUMREFSTATEVPERM bcs 1f bl gen_rand_lfsr_nonpres @@ -1682,6 +1701,7 @@ rounds_s_mainloop: push {r0,r3} @ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter, r12=block to be deciphered +decryption_start: @ Decrypt ciphertext using AES output in shares: r4-r11 .if ST_SHAREC ldr r0,=shareC @@ -1689,38 +1709,71 @@ rounds_s_mainloop: .else movs r0,#0 .endif + ldr r14,=chaff +@ r0=shareC, r1=cipher/plaintext buffer, r2=number of blocks, r3=free, r4-r11=stateA/B, r12=block to be deciphered, r14=chaff CHK_COUNT 89 add r1,r1,r12,lsl#4 @ Temporarily r1 points to block-to-be-deciphered - ldr r3,[r1] - eors r3,r3,r4 - eors r3,r3,r8,ror#16 @ Now r4 and r8 are free - eors r3,r3,r0 - str r3,[r1] - ldr r3,[r1,#4] + ldr r3,[r1] @ r3=ciphertext word + eors r3,r3,r4 @ r3=r3^shareA + ldr r4,[r14] @ barrier load + eor r3,r3,r8,ror#16 @ r3=r3^shareB + eors r3,r3,r0 @ r3=r3^shareC + str r3,[r1] @ plaintext word=r3 + ldr r3,[r1,#4] @ and similarly for words 1,2,3 of block... + ldr r4,[r14,#4] eors r3,r3,r5 - eors r3,r3,r9,ror#16 + eor r3,r3,r9,ror#16 eors r3,r3,r0 str r3,[r1,#4] ldr r3,[r1,#8] + ldr r4,[r14,#8] eors r3,r3,r6 - eors r3,r3,r10,ror#16 + eor r3,r3,r10,ror#16 eors r3,r3,r0 str r3,[r1,#8] ldr r3,[r1,#12] + ldr r4,[r14,#12] eors r3,r3,r7 - eors r3,r3,r11,ror#16 + eor r3,r3,r11,ror#16 eors r3,r3,r0 str r3,[r1,#12] + sub r1,r1,r12,lsl#4 @ Restore r1 to point to start of buffer CHK_COUNT 90 pop {r0,r3} @ Restore IV and block counter @ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter +decryption_end: adds r3,r3,#1 cmp r3,r2 CHK_COUNT 91 bne ctr_crypt_mainloop - pop {r0,r4-r11,r12,r14} + ldr r0,=workspace_start + movs r1,#(workspace_end-workspace_start)/4 + bl memwipe @ overwrite the workspace + ldr r0,=__scratch_y_end__ + sub r1,r13,r0 + lsrs r1,#2 + subs r1,r1,#4 @ allow for memwipe's own stack use + bl memwipe @ overwrite everything below the stack pointer + pop {r0-r12,r14} CHK_CANARY r12,CTAG0 bx r14 + +.balign 4 +.thumb_func +@ fill r1 words of memory starting at r0 with random data +memwipe: + GET_CANARY r12,CTAG18 + push {r4,r5,r12,r14} + mov r4,r0 + mov r5,r1 +1: + bl gen_rand_sha_nonpres + stmia r4!,{r0} + subs r5,r5,#1 + bne 1b + pop {r4,r5,r12,r14} + CHK_CANARY r12,CTAG18 + bx r14 diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index b90aa7e5..443f377c 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -7,6 +7,7 @@ #include #include "pico/stdlib.h" #include "boot/picobin.h" +#include "boot/picoboot.h" #include "pico/bootrom.h" #include "hardware/structs/otp.h" @@ -14,11 +15,9 @@ #include "config.h" -extern void remap(); -extern uint32_t gen_rand_sha(); -extern void init_key(uint8_t *key); -extern void gen_lut_sbox(); -extern int ctr_crypt_s(uint8_t*iv,uint8_t*buf,int nblk); +extern void init_aes(); +extern void init_key_4way(uint8_t*key); +extern int ctr_crypt_s(uint8_t*iv,uint8_t(*buf)[16],int nblk); extern uint8_t rkey_s[480]; extern uint8_t lut_a[256]; @@ -27,36 +26,22 @@ extern uint32_t lut_a_map[1]; extern uint32_t lut_b_map[1]; extern uint32_t rstate_sha[4],rstate_lfsr[2]; -void resetrng() { - uint32_t f0,f1; - uint32_t boot_random[4]; - rom_get_boot_random(boot_random); - do f0=boot_random[0]; while(f0==0); // make sure we don't initialise the LFSR to zero - f1=boot_random[1]; - rstate_sha[0]=f0&0xffffff00; // bottom byte must be zero (or 4) for SHA, representing "out of data" - rstate_sha[1]=f1; - rstate_sha[2]=0x41414141; - rstate_sha[3]=0x41414141; - rstate_lfsr[0]=f0; // must be nonzero for non-degenerate LFSR - rstate_lfsr[1]=0x1d872b41; // constant that defines LFSR -#if GEN_RAND_SHA - reset_block(RESETS_RESET_SHA256_BITS); - unreset_block(RESETS_RESET_SHA256_BITS); -#endif -} -static void init_lut_map() { - int i; - for(i=0;i<256;i++) lut_b[i]=gen_rand_sha()&0xff, lut_a[i]^=lut_b[i]; - lut_a_map[0]=0; - lut_b_map[0]=0; - remap(); -} +// Temporary - should be replaced with ASM code in aes.S +uint32_t get_rand_32() { + static uint8_t i = 0; -static void init_aes() { - resetrng(); - gen_lut_sbox(); - init_lut_map(); + if (i > 3) { + // can only provide 4 random numbers before reboot is needed + rom_reboot(REBOOT2_FLAG_REBOOT_TYPE_NORMAL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS, 10, 0, 0); + __builtin_unreachable(); + } + + uint32_t boot_random[4]; + rom_get_boot_random(boot_random); + uint32_t ret = boot_random[i]; + i++; + return ret; } int main() { @@ -87,7 +72,7 @@ int main() { // Read key directly from OTP - guarded reads will throw a bus fault if there are any errors uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE; - init_key((uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & (otp_key_page * 0x40))])); + init_key_4way((uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & (otp_key_page * 0x40))])); otp_hw->sw_lock[otp_key_page] = 0xf; ctr_crypt_s(iv, (void*)data_start_addr, data_size/16); From d04d513a6d72db6b427b9ff7162e2e0bdcb2571f Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 22 Jan 2025 14:33:12 +0000 Subject: [PATCH 15/80] Use minimal vector table Add temporary workaround to clone SDK branch with support for PICO_MINIMAL_VECTOR_TABLE define --- enc_bootloader/CMakeLists.txt | 12 +++++-- enc_bootloader/memmap_enc_bootloader.ld | 12 +++---- enc_bootloader/tmp_pico_sdk_import.cmake | 45 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 enc_bootloader/tmp_pico_sdk_import.cmake diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index e44aff86..42c1c277 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -14,7 +14,15 @@ if (NOT USE_PRECOMPILED) unset(ENV{LDFLAGS}) # Pull in SDK (must be before project) - include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) + + # Temporary import handling, to ensure SDK branch with PICO_MINIMAL_VECTOR_TABLE is present + file(READ ${PICO_SDK_PATH}/src/rp2_common/pico_crt0/crt0.S CRT0_S) + if (CRT0_S MATCHES "PICO_MINIMAL_VECTOR_TABLE") + include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) + else() + # This clones the will-v-pi/pico-sdk repo on the min-vtor branch + include(tmp_pico_sdk_import.cmake) + endif() project(enc_bootloader C CXX ASM) set(CMAKE_C_STANDARD 11) @@ -48,7 +56,7 @@ if (NOT USE_PRECOMPILED) PICO_RUNTIME_SKIP_INIT_PER_CORE_IRQ_PRIORITIES=1 PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1 # Don't need any vtor irqs - PICO_VECTOR_TABLE_NUM_IRQS=0) + PICO_MINIMAL_VECTOR_TABLE=1) # print memory usage target_link_options(enc_bootloader PUBLIC -Wl,--print-memory-usage) diff --git a/enc_bootloader/memmap_enc_bootloader.ld b/enc_bootloader/memmap_enc_bootloader.ld index aa9602a6..e013bf96 100644 --- a/enc_bootloader/memmap_enc_bootloader.ld +++ b/enc_bootloader/memmap_enc_bootloader.ld @@ -23,8 +23,8 @@ MEMORY { - RAM_START(rwx) : ORIGIN = 0x20080000, LENGTH = 0x30 - SCRATCH_X(rwx) : ORIGIN = 0x20080030, LENGTH = 0xFD0 + RAM_START(rwx) : ORIGIN = 0x20080000, LENGTH = 0x44 + SCRATCH_X(rwx) : ORIGIN = 0x20080044, LENGTH = 0xFBC SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 0x700 RAM(rwx) : ORIGIN = 0x20081700, LENGTH = 0x900 } @@ -41,6 +41,10 @@ SECTIONS .start_text : { __logical_binary_start = .; + /* Vectors require 512-byte alignment on v8-M when >48 IRQs are used, + so we would waste RAM if the vector table were not at the + start. */ + KEEP (*(.vectors)) KEEP (*(.binary_info_header)) __binary_info_header_end = .; KEEP (*(.embedded_block)) @@ -48,10 +52,6 @@ SECTIONS } > RAM_START .text : { - /* Vectors require 512-byte alignment on v8-M when >48 IRQs are used, - so we would waste RAM if the vector table were not at the - start. */ - KEEP (*(.vectors)) __reset_start = .; KEEP (*(.reset)) __reset_end = .; diff --git a/enc_bootloader/tmp_pico_sdk_import.cmake b/enc_bootloader/tmp_pico_sdk_import.cmake new file mode 100644 index 00000000..ef500813 --- /dev/null +++ b/enc_bootloader/tmp_pico_sdk_import.cmake @@ -0,0 +1,45 @@ +# This is a modified copy of /external/pico_sdk_import.cmake to use a specific git repo and branch + +set(PICO_SDK_FETCH_FROM_GIT_URL "https://github.com/will-v-pi/pico-sdk") +set(PICO_SDK_FETCH_FROM_GIT_TAG "min-vtor") + +include(FetchContent) +FetchContent_Declare( + pico_sdk + GIT_REPOSITORY ${PICO_SDK_FETCH_FROM_GIT_URL} + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} +) + +if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY ${PICO_SDK_FETCH_FROM_GIT_URL} + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY ${PICO_SDK_FETCH_FROM_GIT_URL} + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) +endif () + + +include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) From d2c8419d6af90ec55a01b68191bd22a7939f965b Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 22 Jan 2025 17:44:26 +0000 Subject: [PATCH 16/80] Pass TBYB and version from encrypted binary metadata to the decryption stage metadata This means that the bootrom will treat the binary with embedded decryption stage the same as the non-encrypted binary, for rollback/versions/tbyb/etc --- main.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/main.cpp b/main.cpp index e175e2dc..8df8f8be 100644 --- a/main.cpp +++ b/main.cpp @@ -476,6 +476,7 @@ struct _settings { bool hash = false; bool sign = false; bool clear_sram = false; + bool set_tbyb = false; uint16_t major_version = 0; uint16_t minor_version = 0; uint16_t rollback_version = 0; @@ -4808,6 +4809,12 @@ void sign_guts_elf(elf_file* elf, private_t private_key, public_t public_key) { block new_block = place_new_block(elf, first_block); + if (settings.seal.set_tbyb) { + // Set the TBYB bit on the image_type_item + std::shared_ptr image_type = new_block.get_item(); + image_type->flags |= PICOBIN_IMAGE_TYPE_EXE_TBYB_BITS; + } + if (settings.seal.major_version || settings.seal.minor_version || settings.seal.rollback_version) { std::shared_ptr version = new_block.get_item(); if (version != nullptr) { @@ -4938,6 +4945,9 @@ bool encrypt_command::execute(device_map &devices) { if (get_file_type() == filetype::elf) { isElf = true; } else if (get_file_type() == filetype::bin) { + if (settings.encrypt.embed) { + fail(ERROR_ARGS, "Can only embed decrypting bootloader into ELFs"); + } isBin = true; } else { fail(ERROR_ARGS, "Can only sign ELFs or BINs"); @@ -4996,10 +5006,6 @@ bool encrypt_command::execute(device_map &devices) { elf->editable = true; if (settings.encrypt.embed) { - if (!isElf) { - fail(ERROR_ARGS, "Can only embed decrypting bootloader into elfs"); - } - std::vector iv_data; std::vector enc_data; uint32_t data_start_address = SRAM_START; @@ -5063,6 +5069,23 @@ bool encrypt_command::execute(device_map &devices) { enc_elf->content(*data_section, enc_data); + // Get the version from the encrypted binary + std::shared_ptr version = new_block.get_item(); + if (version != nullptr) { + settings.seal.major_version = version->major; + settings.seal.minor_version = version->minor; + settings.seal.rollback_version = version->rollback; + for (auto row : version->otp_rows) { + settings.seal.rollback_rows.push_back(row); + } + } + + // Get the TBYB from the encrypted binary + std::shared_ptr image_type = new_block.get_item(); + if (image_type->tbyb()) { + settings.seal.set_tbyb = true; + } + // Sign the final thing sign_guts_elf(enc_elf, private_key, public_key); From af31a000437d5423ec1ea8afe215d96785cd0e6d Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 23 Jan 2025 11:55:27 +0000 Subject: [PATCH 17/80] Add --otp-key-page argument Allows specifying the OTP page the AES key is stored on --- enc_bootloader/enc_bootloader.c | 4 +++- main.cpp | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 443f377c..128138d6 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -45,7 +45,9 @@ uint32_t get_rand_32() { } int main() { - // Unlock OTP page with hardcoded key + // This works around E21 by locking down the page with a key, + // as there is no way to enter a key over the picoboot interface. + // Unlocks the OTP page with hardcoded key [0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7] for (int i=0; i<4; i++) { uint32_t key_i = ((i*2+1) << 24) | ((i*2+1) << 16) | (i*2 << 8) | i*2; diff --git a/main.cpp b/main.cpp index 8df8f8be..aca9b010 100644 --- a/main.cpp +++ b/main.cpp @@ -485,6 +485,8 @@ struct _settings { struct { bool embed = false; + bool otp_key_page_set = false; + uint16_t otp_key_page = 30; } encrypt; struct { @@ -796,6 +798,10 @@ struct encrypt_command : public cmd { option("--quiet").set(settings.quiet) % "Don't print any output" + option("--verbose").set(settings.verbose) % "Print verbose output" + option("--embed").set(settings.encrypt.embed) % "Embed bootloader in output file" + + ( + option("--otp-key-page").set(settings.encrypt.otp_key_page_set) % "Specify the OTP page storing the AES key" & + integer("page").set(settings.encrypt.otp_key_page) % "OTP page (default 30)" + ).force_expand_help(true) + ( option("--hash").set(settings.seal.hash) % "Hash the encrypted file" + option("--sign").set(settings.seal.sign) % "Sign the encrypted file" @@ -5036,9 +5042,9 @@ bool encrypt_command::execute(device_map &devices) { config_guts(program); } // otp_key_page - if (false) { + if (settings.encrypt.otp_key_page_set) { settings.config.key = "otp_key_page"; - settings.config.value = hex_string(30); + settings.config.value = hex_string(settings.encrypt.otp_key_page); config_guts(program); } From 6e628759e12eda529ee336f082d5cc9d6903c20e Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 23 Jan 2025 13:02:07 +0000 Subject: [PATCH 18/80] Update aes.S From commit 3d93bdd06c55 --- enc_bootloader/aes.S | 67 ++++++++++++++++++++++++++++------------- enc_bootloader/config.h | 26 ++++++++++------ 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index 8b58f067..9eee3761 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -97,6 +97,16 @@ pointer is overwritten. .endif .endm +.macro BRANCH_JITTER rx,tag +.if BR_JITTER + ldr \rx,=branch_jitter + ldr \rx,[\rx] + tst \rx,1<<(\tag) + bne \@f +\@: +.endif +.endm + .macro clear03 offset=0 getchaffaddress r0,\offset ldmia r0,{r0-r3} @@ -210,8 +220,9 @@ lut_b_map: .balign 16 rstate_sha: @ 128-bit SHA random state, to be initialised to TRNG bytes; zeroth byte must be initialised to zero .space 16 -rstate_lfsr: @ 32-bit LFSR random state and constant used to step it (initialised by C program) -.space 8 +rstate_lfsr: @ 32-bit LFSR random state and constant used to step it +.space 4 +.word 0x1d872b41 @ constant that defines a maximal-length LFSR .if CT_BPERM .balign 16 @@ -223,6 +234,13 @@ murmur3_constants: @ Five constants used in murmur3_32 hash .word 0xc2b2ae35 .endif +.if BR_JITTER +.balign 4 +branch_jitter: +.space 4 +.endif + + @ Put main code in first scratch area .section .scratch_x.aes,"ax",%progbits @@ -359,8 +377,6 @@ init_aes: str r2,[r1,#8] @ rstate_sha[2]=0x41414141; str r2,[r1,#12] @ rstate_sha[3]=0x41414141; str r4,[r1,#rstate_lfsr-rstate_sha] @ must be nonzero for non-degenerate LFSR - ldr r2,=0x1d872b41; - str r2,[r1,#rstate_lfsr-rstate_sha+4] @ constant that defines a maximal-length LFSR .if GEN_RAND_SHA ldr r1,=RESETS_BASE+RESETS_RESET_OFFSET ldr r2,[r1] @@ -1011,6 +1027,8 @@ map_sbox_s: uxtb r11,r2 @ R11 = a0^a1^b0^b1 movs r12,r1,lsr#16 @ R12 = c0^d0 | (c1^d1)<<8 + BRANCH_JITTER r0,1 + ldr r4,=perm16 ldr r5,=shareA ldr r6,=shareB @@ -1469,6 +1487,7 @@ addrkey_s: bfi r0,r3,#0,#4 ldr r3,[r3] ldr r0,[r0] @ barrier load + BRANCH_JITTER r0,0 @ Read shareB of roundkey, offset by vpermkeyrot-vpermstaterot, and eor it into shareB of state, offset by -vpermstaterot @ r1=rkeyBrotdata, r2=vpermkeyrot-vpermstaterot, r3=RKshareC, r4-r11=state, r12=roundkeyB ptr @@ -1558,6 +1577,7 @@ ctr_crypt_mainloop: 1: CHK_COUNT 81 + pop {r0-r3} @ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter @@ -1607,7 +1627,7 @@ ctr_crypt_mainloop: .endif CHK_COUNT 82 -@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter, r12=block to be deciphered +@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter (monotonic), r12=block number (block to be deciphered) push {r0-r3,r12} processIV: @ non-target label to assist power analysis @@ -1624,28 +1644,28 @@ processIV: @ non-target label to assist power analysis ldr r0,[r13] @ peek at stack to restore r0=IV ptr ldmia r0,{r4-r7} @ load IV clear03 @ barrier to remove traces of IV from internal CPU load registers - push {r0-r3} @ We want to randomise the internal memory registers associated with the above LDM load, but this - pop {r0-r3} @ may come from non-scratch memory and have its own internal registers, so we clear it using a - @ stack save/load. Either R13 is in non-scratch memory, in which case this works, or it isn't, in - @ which case it doesn't matter, because the only subsequent use of non-scratch memory is the stack. @ Add in r9 in byte-big-endian, bit-little-endian (!) fashion, while trying to avoid rev operations @ as far as possible as these tend to expose (via power fluctuations) byte-level hamming weights. -@ It's worth avoiding revs on r6, r5, r4, even at the cost of introducing a small timing dependency. - @ First do 128-bit addition of r9 to byte-reversed IV - rev r7,r7; adds r7,r7,r9; bcc 1f - rev r6,r6; adcs r6,r6,#0; rev r6,r6; bcc 1f - rev r5,r5; adcs r5,r5,#0; rev r5,r5; bcc 1f - rev r4,r4; adcs r4,r4,#0; rev r4,r4 + rev r7,r7 + cmn r7,#MAX_NUM_BLOCKS @ Compare against maximum number of blocks + bcs 1f + add r7,r7,r9 @ This can temporarily overflow but it doesn't matter as we know that r7+r12 does not overflow + sub r7,r7,r8 + b 2f 1: -@ At this point, r7 is reversed and r4-r6 are not + adds r7,r7,r9 + rev r6,r6; adcs r6,r6,#0 + rev r5,r5; adcs r5,r5,#0 + rev r4,r4; adcs r4,r4,#0 @ Now do 128-bit subtraction of r8 from byte-reversed IV - subs r7,r7,r8; rev r7,r7; bcs 1f - rev r6,r6; sbcs r6,r6,#0; rev r6,r6; bcs 1f - rev r5,r5; sbcs r5,r5,#0; rev r5,r5; bcs 1f - rev r4,r4; sbcs r4,r4,#0; rev r4,r4 -1: + subs r7,r7,r8 + sbcs r6,r6,#0; rev r6,r6 + sbcs r5,r5,#0; rev r5,r5 + sbcs r4,r4,#0; rev r4,r4 +2: + rev r7,r7 clear01 16 CHK_COUNT 83 @@ -1662,6 +1682,11 @@ rounds_s_mainloop: add r12,r12,r2,lsl#5 @ pointer to key shares for this round add r12,r12,r2,lsl#3 push {r2} @ save round count +.if BR_JITTER + bl gen_rand_sha_nonpres + ldr r1,=branch_jitter + str r0,[r1] +.endif bl addrkey_s bl map_sbox_s bl shift_rows_s diff --git a/enc_bootloader/config.h b/enc_bootloader/config.h index dd0c9898..7d1b210d 100644 --- a/enc_bootloader/config.h +++ b/enc_bootloader/config.h @@ -36,7 +36,11 @@ // much more effort to carry out. It can be disabled for analysis or testing purposes. #ifndef RC_JITTER -#define RC_JITTER 1 // use random-delay versions of RCP instructions +#define RC_JITTER 0 // use random-delay versions of RCP instructions +#endif + +#ifndef BR_JITTER +#define BR_JITTER 1 // Insert random delays as branches #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -45,20 +49,20 @@ // Period = X means that the operation in question occurs every X blocks, so higher = more performance and lower security. // No point in making them more than 16 or so, since the time taken by the subroutines would be negligible. -// These must be a power of 2. Timings as of commit 24277d13 -// RK_ROR=0 RK_ROR=1 -// Baseline time per 16-byte block = { 14066 14336 } cycles +// These must be a power of 2. Timings as of commit 82d31652 +// +// Baseline time per 16-byte block = 14109 (with no jitter) cycles #ifndef REFCHAFF_PERIOD -#define REFCHAFF_PERIOD 1 // Extra cost per 16-byte block = { 462 462 }/REFCHAFF_PERIOD cycles +#define REFCHAFF_PERIOD 1 // Extra cost per 16-byte block = 474/REFCHAFF_PERIOD cycles #endif #ifndef REMAP_PERIOD -#define REMAP_PERIOD 4 // Extra cost per 16-byte block = { 4131 4131 }/REMAP_PERIOD cycles +#define REMAP_PERIOD 4 // Extra cost per 16-byte block = 4148/REMAP_PERIOD cycles #endif #ifndef REFROUNDKEYSHARES_PERIOD -#define REFROUNDKEYSHARES_PERIOD 1 // Extra cost per 16-byte block = { 1107 1212 }/REFROUNDKEYSHARES_PERIOD cycles +#define REFROUNDKEYSHARES_PERIOD 1 // Extra cost per 16-byte block = 1304/REFROUNDKEYSHARES_PERIOD cycles #endif #ifndef REFROUNDKEYHVPERMS_PERIOD -#define REFROUNDKEYHVPERMS_PERIOD 1 // Extra cost per 16-byte block = { 936 1422 }/REFROUnDKEYVPERM_PERIOD cycles +#define REFROUNDKEYHVPERMS_PERIOD 1 // Extra cost per 16-byte block = 1486/REFROUNDKEYVPERM_PERIOD cycles #endif // Setting NUMREFSTATEVPERM to X means that state vperm refreshing happens on the first X AES rounds only, @@ -66,5 +70,9 @@ // The rationale for doing it this way is that later rounds should be protected by CT_BPERM. // NUMREFSTATEVPERM can be from 0 to 14. #ifndef NUMREFSTATEVPERM -#define NUMREFSTATEVPERM 7 // Extra cost per 16-byte block = 80*NUMREFSTATEVPERM cycles +#define NUMREFSTATEVPERM 7 // Extra cost per 16-byte block = 61*NUMREFSTATEVPERM cycles #endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define MAX_NUM_BLOCKS 32768 From 4ef64509825bac567773f9ee049b46c02f7643f8 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 29 Jan 2025 12:00:34 +0000 Subject: [PATCH 19/80] Shrink enc_bootloader.c code further Now fits inside 2K, so moved to 0x20081800, giving more room for AES workspace --- enc_bootloader/CMakeLists.txt | 9 ++++++++- enc_bootloader/memmap_enc_bootloader.ld | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 42c1c277..f5c9e21f 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -5,6 +5,9 @@ if (NOT USE_PRECOMPILED) set(PICO_NO_PICOTOOL 1) + # Ensure we're using a Release build - MinSizeRel doesn't work for some reason + set(CMAKE_BUILD_TYPE Release) + # If the user set these environment variables to influence the picotool # build, unset them here so that they do not influence the pico-sdk # build. This is especially required for flags that are not supported @@ -45,14 +48,18 @@ if (NOT USE_PRECOMPILED) pico_stdlib ) - # use stack guards, as AES variables are written near the stack target_compile_definitions(enc_bootloader PRIVATE + # use stack guards, as AES variables are written near the stack PICO_USE_STACK_GUARDS=1 PICO_STACK_SIZE=0x100 + # The following are to reduce the size of the binary PICO_NO_PROGRAM_INFO=1 + # No spinlocks used + PICO_USE_SW_SPIN_LOCKS=0 # No heap is used PICO_HEAP_SIZE=0 # These inits are not required + PICO_RUNTIME_SKIP_INIT_SPIN_LOCKS_RESET=1 PICO_RUNTIME_SKIP_INIT_PER_CORE_IRQ_PRIORITIES=1 PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1 # Don't need any vtor irqs diff --git a/enc_bootloader/memmap_enc_bootloader.ld b/enc_bootloader/memmap_enc_bootloader.ld index e013bf96..1db09f03 100644 --- a/enc_bootloader/memmap_enc_bootloader.ld +++ b/enc_bootloader/memmap_enc_bootloader.ld @@ -25,8 +25,8 @@ MEMORY { RAM_START(rwx) : ORIGIN = 0x20080000, LENGTH = 0x44 SCRATCH_X(rwx) : ORIGIN = 0x20080044, LENGTH = 0xFBC - SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 0x700 - RAM(rwx) : ORIGIN = 0x20081700, LENGTH = 0x900 + SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 0x800 + RAM(rwx) : ORIGIN = 0x20081800, LENGTH = 0x800 } ENTRY(_entry_point) From e17112d21c08db72e033d02d5b2b28f2cb072907 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 29 Jan 2025 15:09:12 +0000 Subject: [PATCH 20/80] Add otp file to enc --- main.cpp | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/main.cpp b/main.cpp index aca9b010..1d0b2612 100644 --- a/main.cpp +++ b/main.cpp @@ -402,8 +402,8 @@ struct multi_cmd : public cmd { }; struct _settings { - std::array filenames; - std::array file_types; + std::array filenames; + std::array file_types; uint32_t binary_start = FLASH_START; int bus=-1; int address=-1; @@ -813,7 +813,8 @@ struct encrypt_command : public cmd { ).force_expand_help(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + named_typed_file_selection_x("aes_key", 2, "bin") % "AES Key Share" + - optional_typed_file_selection_x("signing_key", 3, "pem") % "Signing Key file" + optional_typed_file_selection_x("signing_key", 3, "pem") % "Signing Key file" + + optional_typed_file_selection_x("otp", 4, "json") % "File to save OTP to (will edit existing file if it exists)" ); } @@ -5130,6 +5131,38 @@ bool encrypt_command::execute(device_map &devices) { fail(ERROR_ARGS, "Must be ELF or BIN"); } + if (!settings.filenames[4].empty()) { + if (get_file_type_idx(4) != filetype::json) { + fail(ERROR_ARGS, "Can only output OTP json"); + } + auto check_json_file = std::ifstream(settings.filenames[4]); + json otp_json; + if (check_json_file.good()) { + otp_json = json::parse(check_json_file); + DEBUG_LOG("Appending to existing otp json\n"); + check_json_file.close(); + } + auto json_out = get_file_idx(ios::out, 4); + + // Add otp AES key page + for (int i = 0; i < 128; ++i) { + std::stringstream ss; + ss << settings.encrypt.otp_key_page << ":0"; + otp_json[ss.str()]["ecc"] = true; + otp_json[ss.str()]["value"][i] = aes_key_share.bytes[i]; + } + + // Add page locks to prevent BL and NS access, and only allow S reads + { + std::stringstream ss; + ss << "PAGE" << settings.encrypt.otp_key_page << "_LOCK1"; + otp_json[ss.str()] = "0x3d3d3d"; + } + + *json_out << std::setw(4) << otp_json << std::endl; + json_out->close(); + } + return false; } From 82d6116729d1b40466070a1636e1bb898b6a0c97 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 29 Jan 2025 18:01:08 +0000 Subject: [PATCH 21/80] Increase stack size by 0x80 to provide some headroom --- enc_bootloader/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index f5c9e21f..84e169a4 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -51,7 +51,7 @@ if (NOT USE_PRECOMPILED) target_compile_definitions(enc_bootloader PRIVATE # use stack guards, as AES variables are written near the stack PICO_USE_STACK_GUARDS=1 - PICO_STACK_SIZE=0x100 + PICO_STACK_SIZE=0x180 # The following are to reduce the size of the binary PICO_NO_PROGRAM_INFO=1 # No spinlocks used From 9cf530f089c8273ddf1848f0024befa8225a076c Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 4 Feb 2025 12:54:38 +0000 Subject: [PATCH 22/80] Integrate latest aes.S code From commit 052dab85a9ac38 --- enc_bootloader/CMakeLists.txt | 3 +- enc_bootloader/aes.S | 441 ++++++++++++++---------- enc_bootloader/config.h | 24 +- enc_bootloader/enc_bootloader.c | 166 +++++++-- enc_bootloader/memmap_enc_bootloader.ld | 1 + 5 files changed, 418 insertions(+), 217 deletions(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 84e169a4..c3425ce3 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -61,7 +61,8 @@ if (NOT USE_PRECOMPILED) # These inits are not required PICO_RUNTIME_SKIP_INIT_SPIN_LOCKS_RESET=1 PICO_RUNTIME_SKIP_INIT_PER_CORE_IRQ_PRIORITIES=1 - PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1 + PICO_BOOTROM_LOCKING_ENABLED=0 + # PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1 # Don't need any vtor irqs PICO_MINIMAL_VECTOR_TABLE=1) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index 9eee3761..35a9f854 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -15,13 +15,15 @@ pointer is overwritten. #include "config.h" #include "hardware/platform_defs.h" #include "hardware/regs/addressmap.h" +#include "hardware/regs/clocks.h" #include "hardware/regs/sha256.h" -#include "hardware/rcp.h" #include "hardware/regs/resets.h" +#include "hardware/rcp.h" -.global init_aes -.global init_key_4way -.global ctr_crypt_s +.global decrypt +.global rstate_sha +.global do_not_wipe +.global chaff @ RCP macros @@ -43,11 +45,12 @@ pointer is overwritten. #define CTAG15 0x3a #define CTAG16 0x3b #define CTAG17 0x3c -#define CTAG18 0x3d +#define CTAG18 0x3d @ not used -.macro SET_COUNT n +@ The lower the jitterpriorty the more the jitter +.macro SET_COUNT n,jitterpriority .if RC_COUNT -.if RC_JITTER +.if RC_JITTER > \jitterpriority rcp_count_set \n .else rcp_count_set_nodelay \n @@ -55,9 +58,9 @@ pointer is overwritten. .endif .endm -.macro CHK_COUNT n +.macro CHK_COUNT n,jitterpriority .if RC_COUNT -.if RC_JITTER +.if RC_JITTER > \jitterpriority rcp_count_check \n .else rcp_count_check_nodelay \n @@ -65,9 +68,9 @@ pointer is overwritten. .endif .endm -.macro GET_CANARY rx,tag +.macro GET_CANARY rx,tag,jitterpriority .if RC_CANARY -.if RC_JITTER +.if RC_JITTER > \jitterpriority rcp_canary_get \rx,\tag .else rcp_canary_get_nodelay \rx,\tag @@ -75,9 +78,9 @@ pointer is overwritten. .endif .endm -.macro CHK_CANARY rx,tag +.macro CHK_CANARY rx,tag,jitterpriority .if RC_CANARY -.if RC_JITTER +.if RC_JITTER > \jitterpriority rcp_canary_check \rx,\tag .else rcp_canary_check_nodelay \rx,\tag @@ -85,28 +88,6 @@ pointer is overwritten. .endif .endm -.macro GET_CANARY_NJ rx,tag @ with no jitter even if you ask for it (for situations where it would otherwise slow things down a lot) -.if RC_CANARY - rcp_canary_get_nodelay \rx,\tag -.endif -.endm - -.macro CHK_CANARY_NJ rx,tag @ with no jitter even if you ask for it -.if RC_CANARY - rcp_canary_check_nodelay \rx,\tag -.endif -.endm - -.macro BRANCH_JITTER rx,tag -.if BR_JITTER - ldr \rx,=branch_jitter - ldr \rx,[\rx] - tst \rx,1<<(\tag) - bne \@f -\@: -.endif -.endm - .macro clear03 offset=0 getchaffaddress r0,\offset ldmia r0,{r0-r3} @@ -173,8 +154,6 @@ RKshareC: @ Round key common share C; see comment at init_key RKshareCchange: @ Temporary used by ref_roundkey_share_s .space 4 -workspace_end: - @ Regardless of configuration, the code uses a single 256-entry LUT, @ which is a simple S-box table. @ The LUT is represented as two shares, lut_a and lut_b, @@ -217,12 +196,20 @@ lut_b_map: .space 4 .space 4 @ align to multiple of 8 +do_not_wipe: +.byte 0x00 @ Flag: if this is zero then memory will be wiped after decryption + @ The purpose of this flag is that some test modes require the memory not to be wiped + .balign 16 +rstate_all_start: @ Mark start of RNG data to allow selective memory wipe rstate_sha: @ 128-bit SHA random state, to be initialised to TRNG bytes; zeroth byte must be initialised to zero .space 16 +jstate: @ 32-bit jitter state +.space 4 rstate_lfsr: @ 32-bit LFSR random state and constant used to step it .space 4 .word 0x1d872b41 @ constant that defines a maximal-length LFSR +rstate_all_end: @ Mark end of RNG data to allow selective memory wipe .if CT_BPERM .balign 16 @@ -234,13 +221,6 @@ murmur3_constants: @ Five constants used in murmur3_32 hash .word 0xc2b2ae35 .endif -.if BR_JITTER -.balign 4 -branch_jitter: -.space 4 -.endif - - @ Put main code in first scratch area .section .scratch_x.aes,"ax",%progbits @@ -255,11 +235,26 @@ branch_jitter: .balign 4 gen_rand_sha: push {r14} - GET_CANARY_NJ r14,CTAG1 + GET_CANARY r14,CTAG1,2 push {r1-r3,r14} +.if SH_JITTER + ldr r2,=rstate_sha + ldr r0,[r2,#jstate-rstate_sha] + movs r1,#1 + movs r3,r0,lsl#2 + ands r3,r3,#31 + movs r3,r1,lsl r3 @ 1<<(4*(r0&7)) + udiv r3,r3,r1 @ Takes constant + (r0&7) cycles + lsrs r0,r0,#1 + bne 1f + bl gen_rand_sha_nonpres + ldr r2,=rstate_sha +1: + str r0,[r2,#jstate-rstate_sha] +.endif bl gen_rand_sha_nonpres pop {r1-r3,r14} - CHK_CANARY_NJ r14,CTAG1 + CHK_CANARY r14,CTAG1,0 pop {r15} @ Return single random word in r0 @@ -275,6 +270,23 @@ gen_rand_sha_nonpres: strb r3,[r2] @ save updated SUM register offset in bottom byte of rstate_sha[] bx r14 1: +.if CK_JITTER + ldr r3,=CLOCKS_BASE+CLOCKS_CLK_REF_CTRL_OFFSET + ldr r1,[r3,#0] + push {r1} + bic r1,r1,#3 + str r1,[r3,#0] @ switch ref_clk to ROSC + ldr r1,[r3,#CLOCKS_CLK_SYS_CTRL_OFFSET-CLOCKS_CLK_REF_CTRL_OFFSET] + push {r1} + bic r1,r1,#3 + str r1,[r3,#CLOCKS_CLK_SYS_CTRL_OFFSET-CLOCKS_CLK_REF_CTRL_OFFSET] @ switch sys_clk to ref_clk + @ run for a couple of cycles off ROSC (ca. 11MHz rather than 150MHz) to jitter the timing + @ could also insert a random delay here + pop {r1} + str r1,[r3,#CLOCKS_CLK_SYS_CTRL_OFFSET-CLOCKS_CLK_REF_CTRL_OFFSET] @ switch sys_clk back to clksrc_clk_sys_aux + pop {r1} + str r1,[r3,#0] +.endif movs r3,#SHA256_SUM6_OFFSET+1 strb r3,[r2] @ reset word counter: the +1 is compensated for later movw r1,#(1< 3) { - // can only provide 4 random numbers before reboot is needed - rom_reboot(REBOOT2_FLAG_REBOOT_TYPE_NORMAL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS, 10, 0, 0); - __builtin_unreachable(); - } - - uint32_t boot_random[4]; - rom_get_boot_random(boot_random); - uint32_t ret = boot_random[i]; - i++; - return ret; +extern uint32_t rstate_sha[4]; + +extern void decrypt(uint8_t* key4way, uint8_t* iv, uint8_t(*buf)[16], int nblk); + + +/* +The following is copied from the A2 boot ROM code at +src/main/arm/varm_boot_path.c with adjustments. +*/ + +void init_rstate() { + int i; + + hw_set_bits(&resets_hw->reset, RESETS_RESET_TRNG_BITS | RESETS_RESET_SHA256_BITS); + hw_clear_bits(&resets_hw->reset, RESETS_RESET_TRNG_BITS | RESETS_RESET_SHA256_BITS); + // As these are clk_sys-clocked, 1 APB read is sufficient delay (no polling loop) + (void)*hw_clear_alias(&resets_hw->reset); + // second read is because I'm feeling generous + (void)*hw_clear_alias(&resets_hw->reset); + + // RNG is derived by streaming a large number of TRNG ROSC samples + // into the SHA-256. TRNG_SAMPLE_BLOCKS is the number of SHA-256 + // blocks to hash, each containing 384 samples from the TRNG ROSC: + const unsigned int TRNG_SAMPLE_BLOCKS = 25; + + // Sample one ROSC bit into EHR every cycle, subject to CPU keeping up. + // More temporal resolution to measure ROSC phase noise is better, if we + // use a high quality hash function instead of naive VN decorrelation. + // (Also more metastability events, which are a secondary noise source) + + // Each half-block (192 samples) takes approx 235 cycles, so 470 cycles/block. + uintptr_t trng = (uintptr_t)trng_hw; + sha256_hw_t *sha256 = sha256_hw; + uint32_t _counter; + // we use 0xff instead of -1 below to set all bits, so make sure there are no bits above bit 7 + static_assert(0xffffff00u == ( + (TRNG_TRNG_DEBUG_CONTROL_RESERVED_BITS | ~TRNG_TRNG_DEBUG_CONTROL_BITS) & + (TRNG_RND_SOURCE_ENABLE_RESERVED_BITS | ~TRNG_RND_SOURCE_ENABLE_BITS) & + (TRNG_RNG_ICR_RESERVED_BITS | ~TRNG_RNG_ICR_BITS) & ~0xffu), ""); + pico_default_asm_volatile ( + "movs r1, #1\n" + "str r1, [%[trng], %[offset_trng_sw_reset]]\n" + // Fixed delay is required after TRNG soft reset -- this plus + // following sha256 write is sufficient: + "ldr %[counter], [%[trng], %[offset_trng_sw_reset]]\n" + // (reads as 0 -- initialises counter to 0.) + // Initialise SHA internal state by writing START bit + "movw r1, %[sha256_init]\n" + "str r1, [%[sha256]]\n" + // This is out of the loop because writing to this register seems to + // restart the sampling, slowing things down. We don't care if this write + // is skipped as that would just make sampling take longer. + "str %[counter], [%[trng], %[offset_sample_cnt1]]\n" + "adds %[counter], %[iterations] + 1\n" + "2:\n" + // TRNG setup is inside loop in case it is skipped. Disable checks and + // bypass decorrelators, to stream raw TRNG ROSC samples: + "movs r1, #0xff\n" + "str r1, [%[trng], %[offset_debug_control]]\n" + // Start ROSC if it is not already started + "str r1, [%[trng], %[offset_rnd_source_enable]]\n" + // Clear all interrupts (including EHR_VLD) + "str r1, [%[trng], %[offset_rng_icr]]\n" + // (hoist above polling loop to reduce poll->read delay) + "movs r0, %[trng]\n" + "adds r0, %[offset_ehr_data0]\n" + // Wait for 192 ROSC samples to fill EHR, this should take constant time: + "movs r2, %[offset_trng_busy]\n" + "1:\n" + "ldr r1, [%[trng], r2]\n" + "cmp r1, #0\n" + "bne 1b\n" + // Check counter and bail out if done -- we always end with a full + // EHR, which is sufficient time for SHA to complete too. + "subs %[counter], #1\n" + "beq 3f\n" + // r1 should now be 0, and we "check" by using it as the base for the loop count. + "rnd_to_sha_start:\n" + // Copy 6 EHR words to SHA-256, plus garbage (RND_SOURCE_ENABLE and + // SAMPLE_CNT1) which pads us out to half of a SHA-256 block. This + // means we can avoid checking SHA-256 ready whilst reading EHR, so + // we restart sampling sooner. (SHA-256 becomes non-ready for 57 + // cycles after each 16 words written.). + "adds r1, #8\n" + "1:\n" + "ldmia r0!, {r2, r3}\n" + "str r2, [%[sha256], #4]\n" + "str r3, [%[sha256], #4]\n" + "subs r1, #2\n" + "bne 1b\n" + "rnd_to_sha_end:\n" + // TRNG is now sampling again, having started after we read the last + // EHR word. Grab some in-progress SHA bits and use them to modulate + // the chain length, to reduce chance of injection locking: + "ldr r2, [%[sha256], #8]\n" + "str r2, [%[trng], %[offset_trng_config]]\n" + // Repeat for all blocks + "b.n 2b\n" + "3:\n" + // Done -- turn off rand source as it's a waste of power, and wipe SHA + // bits left in TRNG config. r1 is known to be 0 (even on RISC-V), so + // use that. + "str r1, [%[trng], %[offset_trng_config]]\n" + "str r1, [%[trng], %[offset_rnd_source_enable]]\n" + + // Function of the actual TRNG pointer we used, and the number of loop iterations + : + [counter] "=&l" (_counter), + // Not actually written, but we tell the compiler to assume such: + [trng] "+l" (trng), + [sha256] "+l" (sha256) + : + [iterations] "i" (TRNG_SAMPLE_BLOCKS * 2), + [sha256_init] "i" (SHA256_CSR_RESET | SHA256_CSR_START_BITS), + [offset_trng_sw_reset] "i" (TRNG_TRNG_SW_RESET_OFFSET - TRNG_RNG_IMR_OFFSET), + [offset_sample_cnt1] "i" (TRNG_SAMPLE_CNT1_OFFSET - TRNG_RNG_IMR_OFFSET), + [offset_debug_control] "i" (TRNG_TRNG_DEBUG_CONTROL_OFFSET - TRNG_RNG_IMR_OFFSET), + [offset_rnd_source_enable] "i" (TRNG_RND_SOURCE_ENABLE_OFFSET - TRNG_RNG_IMR_OFFSET), + [offset_rng_icr] "i" (TRNG_RNG_ICR_OFFSET - TRNG_RNG_IMR_OFFSET), + [offset_trng_busy] "i" (TRNG_TRNG_BUSY_OFFSET - TRNG_RNG_IMR_OFFSET), + [offset_ehr_data0] "i" (TRNG_EHR_DATA0_OFFSET - TRNG_RNG_IMR_OFFSET), + [offset_trng_config] "i" (TRNG_TRNG_CONFIG_OFFSET - TRNG_RNG_IMR_OFFSET) + : "r0", "r1", "r2", "r3" + ); + // No need to wait for SHA, as we polled the EHR one extra time, which + // takes longer than the SHA. + for(i=0;i<4;i++) rstate_sha[i]=sha256->sum[i]; } + int main() { // This works around E21 by locking down the page with a key, // as there is no way to enter a key over the picoboot interface. @@ -70,13 +174,13 @@ int main() { memcpy(iv + 8, (void*)&iv2, sizeof(iv2)); memcpy(iv + 12, (void*)&iv3, sizeof(iv3)); - init_aes(); + // Initialise random state + init_rstate(); + // Read key directly from OTP - guarded reads will throw a bus fault if there are any errors uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE; - - init_key_4way((uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & (otp_key_page * 0x40))])); + decrypt((uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & (otp_key_page * 0x40))]), iv, (void*)data_start_addr, data_size/16); otp_hw->sw_lock[otp_key_page] = 0xf; - ctr_crypt_s(iv, (void*)data_start_addr, data_size/16); // Increase stack limit by 0x100 pico_default_asm_volatile( diff --git a/enc_bootloader/memmap_enc_bootloader.ld b/enc_bootloader/memmap_enc_bootloader.ld index 1db09f03..0adb75a2 100644 --- a/enc_bootloader/memmap_enc_bootloader.ld +++ b/enc_bootloader/memmap_enc_bootloader.ld @@ -256,4 +256,5 @@ SECTIONS ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") /* todo assert on extra code */ + /*ASSERT(chaff==0x20081000, "Chaff array must be located at 0x20081000")*/ } From 1640e861cc661624ad2bc32f55aa6e5f7942f9d5 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 7 Feb 2025 15:54:09 +0000 Subject: [PATCH 23/80] Use ROSC randomisation instead of CK_JITTER --- enc_bootloader/CMakeLists.txt | 4 +- enc_bootloader/config.h | 2 +- enc_bootloader/enc_bootloader.c | 138 ++++++++++++++++++++++++++++++-- main.cpp | 1 - 4 files changed, 133 insertions(+), 12 deletions(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index c3425ce3..20f401b3 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -5,8 +5,8 @@ if (NOT USE_PRECOMPILED) set(PICO_NO_PICOTOOL 1) - # Ensure we're using a Release build - MinSizeRel doesn't work for some reason - set(CMAKE_BUILD_TYPE Release) + # Ensure we're using a MinSizeRel build + set(CMAKE_BUILD_TYPE MinSizeRel) # If the user set these environment variables to influence the picotool # build, unset them here so that they do not influence the pico-sdk diff --git a/enc_bootloader/config.h b/enc_bootloader/config.h index 9b45c22d..ee7a3b8a 100644 --- a/enc_bootloader/config.h +++ b/enc_bootloader/config.h @@ -47,7 +47,7 @@ #endif #ifndef CK_JITTER -#define CK_JITTER 1 // occasionally switch CPU clock to ROSC for extra timing variability +#define CK_JITTER 0 // occasionally switch CPU clock to ROSC for extra timing variability #endif diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 1be67e5c..618c3498 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -16,6 +16,10 @@ #include "pico/binary_info.h" +#include "hardware/clocks.h" +#include "hardware/xosc.h" +#include "hardware/structs/rosc.h" + #include "config.h" extern uint32_t rstate_sha[4]; @@ -145,6 +149,125 @@ void init_rstate() { // No need to wait for SHA, as we polled the EHR one extra time, which // takes longer than the SHA. for(i=0;i<4;i++) rstate_sha[i]=sha256->sum[i]; + + rosc_hw->random = rstate_sha[0]; +} + +// These just have to be higher than the actual frequency, to prevent overclocking unused peripherals +#define ROSC_HZ 300*MHZ +#define OTHER_CLK_DIV 30 + +void runtime_init_clocks(void) { + // Disable resus that may be enabled from previous software + clocks_hw->resus.ctrl = 0; + + bi_decl(bi_ptr_int32(0, 0, rosc_div, 2)); // default divider 2 + bi_decl(bi_ptr_int32(0, 0, rosc_drive, 0x0000)); // default drives of 0 + bi_decl(bi_ptr_int32(0, 0, xosc_mhz, 12)); // xosc freq in MHz + bi_decl(bi_ptr_int32(0, 0, clk_mhz, 150)); // xosc freq in MHz + + // Bump up ROSC speed to ~90MHz + rosc_hw->freqa = 0; // reset the drive strengths + rosc_hw->div = rosc_div | ROSC_DIV_VALUE_PASS; // set divider + // Increment the freqency range one step at a time - this is safe provided the current config is not TOOHIGH + // because ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM | ROSC_CTRL_FREQ_RANGE_VALUE_HIGH == ROSC_CTRL_FREQ_RANGE_VALUE_HIGH + static_assert(ROSC_CTRL_FREQ_RANGE_VALUE_LOW | ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM == ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM); + static_assert(ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM | ROSC_CTRL_FREQ_RANGE_VALUE_HIGH == ROSC_CTRL_FREQ_RANGE_VALUE_HIGH); + hw_set_bits(&rosc_hw->ctrl, ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM); + hw_set_bits(&rosc_hw->ctrl, ROSC_CTRL_FREQ_RANGE_VALUE_HIGH); + + // Enable rosc randomisation + rosc_hw->freqa = (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | + rosc_drive | ROSC_FREQA_DS1_RANDOM_BITS | ROSC_FREQA_DS0_RANDOM_BITS; // enable randomisation + + // Not used with FREQ_RANGE_VALUE_HIGH, but should still be set + rosc_hw->freqb = (ROSC_FREQB_PASSWD_VALUE_PASS << ROSC_FREQB_PASSWD_LSB) | + rosc_drive; + + // Calibrate ROSC frequency if XOSC present - otherwise just configure + uint32_t rosc_freq_mhz = 0; + if (xosc_mhz) { + xosc_init(); + // CLK_REF = XOSC + clock_configure_int_divider(clk_ref, + CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, + 0, + xosc_mhz * MHZ, + 1); + // CLK_SYS = CLK_REF + clock_configure_int_divider(clk_sys, + CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, // leave the aux source on ROSC + xosc_mhz * MHZ, + 1); + + // Max out rosc + rosc_hw->div = 1 | ROSC_DIV_VALUE_PASS; + rosc_hw->freqa = (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | + ROSC_FREQA_DS3_BITS | ROSC_FREQA_DS2_BITS | + ROSC_FREQA_DS1_RANDOM_BITS | ROSC_FREQA_DS0_RANDOM_BITS; // enable randomisation + rosc_hw->freqb = (ROSC_FREQB_PASSWD_VALUE_PASS << ROSC_FREQB_PASSWD_LSB) | + ROSC_FREQB_DS7_LSB | ROSC_FREQB_DS6_LSB | ROSC_FREQB_DS5_LSB | ROSC_FREQB_DS4_LSB; + + // Wait for ROSC to be stable + while(!(rosc_hw->status & ROSC_STATUS_STABLE_BITS)) { + tight_loop_contents(); + } + + rosc_freq_mhz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC_PH) / KHZ; + } + if (rosc_freq_mhz > clk_mhz) { + // CLK SYS = ROSC divided down to clk_mhz, according to XOSC calibration + clock_configure_mhz(clk_sys, + CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, + rosc_freq_mhz, + clk_mhz); + } else { // Either ROSC is running too slowly, or there was no XOSC to calibrate it against + // CLK SYS = ROSC directly, as it's running slowly enough + clock_configure_int_divider(clk_sys, + CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, + ROSC_HZ, // this doesn't have to be accurate + 1); + } + + // Configure other clocks - none of these need to be accurate + + // CLK_REF = ROSC / OTHER_CLK_DIV - this and other clocks aren't really used, so just need to be set to a low enough frequency + clock_configure_int_divider(clk_ref, + CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH, + 0, + ROSC_HZ, + OTHER_CLK_DIV); + + // CLK USB (not used) + clock_configure_int_divider(clk_usb, + 0, // No GLMUX + CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH, + ROSC_HZ, + OTHER_CLK_DIV); + + // CLK ADC (not used) + clock_configure_int_divider(clk_adc, + 0, // No GLMUX + CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH, + ROSC_HZ, + OTHER_CLK_DIV); + + // CLK PERI Used as reference clock for UART and SPI serial. (not used) + clock_configure_int_divider(clk_peri, + 0, + CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, + ROSC_HZ, + OTHER_CLK_DIV); + + // CLK_HSTX Transmit bit clock for the HSTX peripheral. (not used) + clock_configure_int_divider(clk_hstx, + 0, + CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLK_SYS, + ROSC_HZ, + OTHER_CLK_DIV); } @@ -158,14 +281,13 @@ int main() { otp_hw->crt_key_w[i] = key_i; } - bi_decl(bi_program_feature_group(0x1111, 0x2222, "encryption_config")); - bi_decl(bi_ptr_int32(0x1111, 0x2222, data_start_addr, 0x20000000)); - bi_decl(bi_ptr_int32(0x1111, 0x2222, data_size, 0x78000)); - bi_decl(bi_ptr_int32(0x1111, 0x2222, iv0, 0)); - bi_decl(bi_ptr_int32(0x1111, 0x2222, iv1, 1)); - bi_decl(bi_ptr_int32(0x1111, 0x2222, iv2, 2)); - bi_decl(bi_ptr_int32(0x1111, 0x2222, iv3, 3)); - bi_decl(bi_ptr_int32(0x1111, 0x2222, otp_key_page, 30)); + bi_decl(bi_ptr_int32(0, 0, data_start_addr, 0x20000000)); + bi_decl(bi_ptr_int32(0, 0, data_size, 0x78000)); + bi_decl(bi_ptr_int32(0, 0, iv0, 0)); + bi_decl(bi_ptr_int32(0, 0, iv1, 1)); + bi_decl(bi_ptr_int32(0, 0, iv2, 2)); + bi_decl(bi_ptr_int32(0, 0, iv3, 3)); + bi_decl(bi_ptr_int32(0, 0, otp_key_page, 30)); // Initialise IV from binary info words uint8_t iv[16]; diff --git a/main.cpp b/main.cpp index 1d0b2612..c21b538c 100644 --- a/main.cpp +++ b/main.cpp @@ -5025,7 +5025,6 @@ bool encrypt_command::execute(device_map &devices) { auto program = get_iostream_memory_access(tmp, filetype::elf, true); program.set_model(rp2350); - settings.config.group = "encryption_config"; // data_start_addr settings.config.key = "data_start_addr"; settings.config.value = hex_string(data_start_address); From b1b0015edbea890c5222b25206c64aebaed8b781 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 7 Feb 2025 16:04:53 +0000 Subject: [PATCH 24/80] Fix SDK branch for enc_bootloader --- enc_bootloader/CMakeLists.txt | 5 +++-- enc_bootloader/tmp_pico_sdk_import.cmake | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 20f401b3..c09c50e5 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -20,10 +20,11 @@ if (NOT USE_PRECOMPILED) # Temporary import handling, to ensure SDK branch with PICO_MINIMAL_VECTOR_TABLE is present file(READ ${PICO_SDK_PATH}/src/rp2_common/pico_crt0/crt0.S CRT0_S) - if (CRT0_S MATCHES "PICO_MINIMAL_VECTOR_TABLE") + file(READ ${PICO_SDK_PATH}/src/rp2_common/hardware_clocks/clocks.c CLOCKS_C) + if ((CRT0_S MATCHES "PICO_MINIMAL_VECTOR_TABLE") AND (CLOCKS_C MATCHES "clock_configure_mhz")) include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) else() - # This clones the will-v-pi/pico-sdk repo on the min-vtor branch + # This clones the will-v-pi/pico-sdk repo on the self-decrypting branch include(tmp_pico_sdk_import.cmake) endif() diff --git a/enc_bootloader/tmp_pico_sdk_import.cmake b/enc_bootloader/tmp_pico_sdk_import.cmake index ef500813..c7621184 100644 --- a/enc_bootloader/tmp_pico_sdk_import.cmake +++ b/enc_bootloader/tmp_pico_sdk_import.cmake @@ -1,7 +1,7 @@ # This is a modified copy of /external/pico_sdk_import.cmake to use a specific git repo and branch set(PICO_SDK_FETCH_FROM_GIT_URL "https://github.com/will-v-pi/pico-sdk") -set(PICO_SDK_FETCH_FROM_GIT_TAG "min-vtor") +set(PICO_SDK_FETCH_FROM_GIT_TAG "self-decrypting") include(FetchContent) FetchContent_Declare( From 3251f2266ae7d2329c5300ff528c6b84709652bc Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 7 Feb 2025 17:25:46 +0000 Subject: [PATCH 25/80] Add comments and slight size reduction --- enc_bootloader/enc_bootloader.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 618c3498..7872493a 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -180,14 +180,15 @@ void runtime_init_clocks(void) { rosc_hw->freqa = (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | rosc_drive | ROSC_FREQA_DS1_RANDOM_BITS | ROSC_FREQA_DS0_RANDOM_BITS; // enable randomisation - // Not used with FREQ_RANGE_VALUE_HIGH, but should still be set + // Not used with FREQ_RANGE_VALUE_HIGH, but should still be set to the maximum drive rosc_hw->freqb = (ROSC_FREQB_PASSWD_VALUE_PASS << ROSC_FREQB_PASSWD_LSB) | - rosc_drive; + ROSC_FREQB_DS7_LSB | ROSC_FREQB_DS6_LSB | ROSC_FREQB_DS5_LSB | ROSC_FREQB_DS4_LSB; // Calibrate ROSC frequency if XOSC present - otherwise just configure uint32_t rosc_freq_mhz = 0; if (xosc_mhz) { xosc_init(); + // Switch away from ROSC to avoid overclocking // CLK_REF = XOSC clock_configure_int_divider(clk_ref, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, @@ -197,17 +198,15 @@ void runtime_init_clocks(void) { // CLK_SYS = CLK_REF clock_configure_int_divider(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF, - CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, // leave the aux source on ROSC + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, // leave the aux source on ROSC to prevent glitches when we switch back to it xosc_mhz * MHZ, 1); - // Max out rosc + // Max out rosc now we've switched away from it rosc_hw->div = 1 | ROSC_DIV_VALUE_PASS; rosc_hw->freqa = (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | - ROSC_FREQA_DS3_BITS | ROSC_FREQA_DS2_BITS | + ROSC_FREQA_DS3_BITS | ROSC_FREQA_DS2_BITS | // maximum drive ROSC_FREQA_DS1_RANDOM_BITS | ROSC_FREQA_DS0_RANDOM_BITS; // enable randomisation - rosc_hw->freqb = (ROSC_FREQB_PASSWD_VALUE_PASS << ROSC_FREQB_PASSWD_LSB) | - ROSC_FREQB_DS7_LSB | ROSC_FREQB_DS6_LSB | ROSC_FREQB_DS5_LSB | ROSC_FREQB_DS4_LSB; // Wait for ROSC to be stable while(!(rosc_hw->status & ROSC_STATUS_STABLE_BITS)) { From 4819b557b18cec08f1806d419be6ee12a9b590ab Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 10 Feb 2025 14:05:14 +0000 Subject: [PATCH 26/80] Uncomment chaff assertion --- enc_bootloader/memmap_enc_bootloader.ld | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enc_bootloader/memmap_enc_bootloader.ld b/enc_bootloader/memmap_enc_bootloader.ld index 0adb75a2..439a95d9 100644 --- a/enc_bootloader/memmap_enc_bootloader.ld +++ b/enc_bootloader/memmap_enc_bootloader.ld @@ -256,5 +256,5 @@ SECTIONS ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") /* todo assert on extra code */ - /*ASSERT(chaff==0x20081000, "Chaff array must be located at 0x20081000")*/ + ASSERT(chaff==0x20081000, "Chaff array must be located at 0x20081000") } From 922c3e356239220c50707c428f550b202c91824c Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 10 Feb 2025 15:52:38 +0000 Subject: [PATCH 27/80] Shrink IV storage size Use bi_ptr_string instead of 4 bi_ptr_int32s --- enc_bootloader/enc_bootloader.c | 14 ++------------ main.cpp | 7 +++---- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 7872493a..8ea0ea95 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -282,25 +282,15 @@ int main() { bi_decl(bi_ptr_int32(0, 0, data_start_addr, 0x20000000)); bi_decl(bi_ptr_int32(0, 0, data_size, 0x78000)); - bi_decl(bi_ptr_int32(0, 0, iv0, 0)); - bi_decl(bi_ptr_int32(0, 0, iv1, 1)); - bi_decl(bi_ptr_int32(0, 0, iv2, 2)); - bi_decl(bi_ptr_int32(0, 0, iv3, 3)); + bi_decl(bi_ptr_string(0, 0, iv, "0123456789abcdef", 17)) bi_decl(bi_ptr_int32(0, 0, otp_key_page, 30)); - // Initialise IV from binary info words - uint8_t iv[16]; - memcpy(iv, (void*)&iv0, sizeof(iv0)); - memcpy(iv + 4, (void*)&iv1, sizeof(iv1)); - memcpy(iv + 8, (void*)&iv2, sizeof(iv2)); - memcpy(iv + 12, (void*)&iv3, sizeof(iv3)); - // Initialise random state init_rstate(); // Read key directly from OTP - guarded reads will throw a bus fault if there are any errors uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE; - decrypt((uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & (otp_key_page * 0x40))]), iv, (void*)data_start_addr, data_size/16); + decrypt((uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & (otp_key_page * 0x40))]), (uint8_t*)iv, (void*)data_start_addr, data_size/16); otp_hw->sw_lock[otp_key_page] = 0xf; // Increase stack limit by 0x100 diff --git a/main.cpp b/main.cpp index c21b538c..f7e5ba69 100644 --- a/main.cpp +++ b/main.cpp @@ -5035,10 +5035,9 @@ bool encrypt_command::execute(device_map &devices) { config_guts(program); // iv for (int i=0; i < 4; i++) { - std::stringstream ss; - ss << "iv" << i; - settings.config.key = ss.str(); - settings.config.value = hex_string(*(uint32_t*)(iv_data.data() + i*sizeof(uint32_t))); + string s((char*)iv_data.data(), iv_data.size()); + settings.config.key = "iv"; + settings.config.value = s; config_guts(program); } // otp_key_page From 4eab13edb506c384489fbb5ba51dcb87ca0d7123 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 10 Feb 2025 15:54:42 +0000 Subject: [PATCH 28/80] Update enc_bootloader.elf with latest --- enc_bootloader/enc_bootloader.elf | Bin 22592 -> 20960 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/enc_bootloader/enc_bootloader.elf b/enc_bootloader/enc_bootloader.elf index b5c1dbbae51aede00e02093199360c83b10c5985..5cf2785466cfe7c51e1fd6294cb84ac131124aaf 100755 GIT binary patch literal 20960 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 literal 22592 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 Date: Mon, 10 Feb 2025 16:18:36 +0000 Subject: [PATCH 29/80] Rejig clock init to prevent overclocking Now goes through configurations until it finds one under 150MHz --- enc_bootloader/CMakeLists.txt | 2 + enc_bootloader/enc_bootloader.c | 63 +++++++++++++++--------------- enc_bootloader/enc_bootloader.elf | Bin 20960 -> 20600 bytes 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index c09c50e5..7576d572 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -50,6 +50,8 @@ if (NOT USE_PRECOMPILED) ) target_compile_definitions(enc_bootloader PRIVATE + # increase startup delay for stability + PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64 # use stack guards, as AES variables are written near the stack PICO_USE_STACK_GUARDS=1 PICO_STACK_SIZE=0x180 diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 8ea0ea95..4a797452 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -163,8 +163,8 @@ void runtime_init_clocks(void) { bi_decl(bi_ptr_int32(0, 0, rosc_div, 2)); // default divider 2 bi_decl(bi_ptr_int32(0, 0, rosc_drive, 0x0000)); // default drives of 0 - bi_decl(bi_ptr_int32(0, 0, xosc_mhz, 12)); // xosc freq in MHz - bi_decl(bi_ptr_int32(0, 0, clk_mhz, 150)); // xosc freq in MHz + bi_decl(bi_ptr_int32(0, 0, xosc_hz, 12000000)); // xosc freq in Hz + bi_decl(bi_ptr_int32(0, 0, clk_khz, 150 * KHZ)); // maximum clk_sys freq in KHz // Bump up ROSC speed to ~90MHz rosc_hw->freqa = 0; // reset the drive strengths @@ -185,51 +185,50 @@ void runtime_init_clocks(void) { ROSC_FREQB_DS7_LSB | ROSC_FREQB_DS6_LSB | ROSC_FREQB_DS5_LSB | ROSC_FREQB_DS4_LSB; // Calibrate ROSC frequency if XOSC present - otherwise just configure - uint32_t rosc_freq_mhz = 0; - if (xosc_mhz) { + if (xosc_hz) { xosc_init(); // Switch away from ROSC to avoid overclocking // CLK_REF = XOSC clock_configure_int_divider(clk_ref, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, 0, - xosc_mhz * MHZ, + xosc_hz, 1); // CLK_SYS = CLK_REF clock_configure_int_divider(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF, CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, // leave the aux source on ROSC to prevent glitches when we switch back to it - xosc_mhz * MHZ, + xosc_hz, 1); - // Max out rosc now we've switched away from it - rosc_hw->div = 1 | ROSC_DIV_VALUE_PASS; - rosc_hw->freqa = (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | - ROSC_FREQA_DS3_BITS | ROSC_FREQA_DS2_BITS | // maximum drive - ROSC_FREQA_DS1_RANDOM_BITS | ROSC_FREQA_DS0_RANDOM_BITS; // enable randomisation - - // Wait for ROSC to be stable - while(!(rosc_hw->status & ROSC_STATUS_STABLE_BITS)) { - tight_loop_contents(); + // Go through configurations until you get below 150MHz + uint8_t div = 1; + uint32_t drives = 0x7777; + while (div < 4) { + rosc_hw->div = div | ROSC_DIV_VALUE_PASS; + rosc_hw->freqa = (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | + drives | + ROSC_FREQA_DS1_RANDOM_BITS | ROSC_FREQA_DS0_RANDOM_BITS; // enable randomisation + + // Wait for ROSC to be stable + while(!(rosc_hw->status & ROSC_STATUS_STABLE_BITS)) { + tight_loop_contents(); + } + + if (frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC_PH) < clk_khz) { + break; + } + + if (!drives) div++; + drives = drives ? 0x0000 : 0x7777; } - - rosc_freq_mhz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC_PH) / KHZ; - } - if (rosc_freq_mhz > clk_mhz) { - // CLK SYS = ROSC divided down to clk_mhz, according to XOSC calibration - clock_configure_mhz(clk_sys, - CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, - CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, - rosc_freq_mhz, - clk_mhz); - } else { // Either ROSC is running too slowly, or there was no XOSC to calibrate it against - // CLK SYS = ROSC directly, as it's running slowly enough - clock_configure_int_divider(clk_sys, - CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, - CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, - ROSC_HZ, // this doesn't have to be accurate - 1); } + // CLK SYS = ROSC directly, as it's running slowly enough + clock_configure_int_divider(clk_sys, + CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, + ROSC_HZ, // this doesn't have to be accurate + 1); // Configure other clocks - none of these need to be accurate diff --git a/enc_bootloader/enc_bootloader.elf b/enc_bootloader/enc_bootloader.elf index 5cf2785466cfe7c51e1fd6294cb84ac131124aaf..aafe5a6876e8bcd389f8c7a39dad7f7faf11ca38 100755 GIT binary patch delta 4798 zcmZvg4Qy0Z7RS$<&I~i{K&KV4Fi>Bo($;>=&J3ltv`xnr5NM&TF79Si><6%!0xBs6 zH!R@|LN@KDWWiTmHmRX%i7XolS_o=N(8$J*M8WLFkOg<$h=6EAD+P{T6!mHUFvx%=Hjr`>NHek$Enob6E?hUWPzvr-%Q z5v4y{?nm5jKe4;5vi_;IK>R-ww6ku! zux?{}d3|}~oVM7_*ShAkJyaiv*Io6bY;0Rt|F^cv_}U@m0UGVRMSanwm2pRoQ>D>a zpa@gY1xmm5$oJ1~EoxkB*jj3|{kD^ee)<>drrxTI@2}Oo|0sJ~DZTinv)Fl-)vsKp zyi1pr97mA$xzu^}H2U2{_5&1jsu@dX*Ha&jUYvj|$D__N=nI3qYNkP-Pf*Ye&2)p# zfTn@c4Ek`warESs(VcXPNQqHmMr>WSXTC}UGh!_WOB;%hUVQP_xYMiu&FIGS#PR$U z`Sk^>^d}0lYv)X=4dHlhJfS4sq@wJVjaOsz)~WObG?s`J#skX(8FZuHf9Hq7+vz#c z_&xP?6G8vA>#2#OHxio@*HgPGSHDmgo5?fMgz`(2IQQl7sQUJzfqQv=!p9u_^PNMt z$FDnH2oF#Ga8O@U+~~jr9x1MMRCVaz7f0ROp@*8nl26^-1dn%0YPI+_TDxgu>qgwu z$h^3R7EHEa?sXH`XUVzu9&iR&=&*NNr8WqWx=Uh?Qm@1uxj%z7VSvMT02Oo|KOdl+ zKzR)%06`kcZ9IRAlFeQurtz;jy4*n&IKj_^CkjWR3rZK3Jyc%N-m!Mw`rmSW&s>js z9Oc2;9(4rwYLrft>9F2UDDV;xd;rYLLNJd=Ocs%o z4nxjtI_xyUD<+TNFzbgQ#|@X0-hw{!ls@{WQc2+hC@@bcKwd7%KZl%ImRCvguOVla zNAgq&H(b9gscmy^aOc?~!NV#E$Agxm!lM7;xl zY;Vw(Yg7jXUe5&(DC8wJaL86LcvxaK;KZ)N4VP(<+Z%}XkNu28K1qMgU2@IzSd|pG zgHnmvp(=@4-aFNyi#qQzXxfV<>98?476V+S@#a&{n*#Cs)& zw#gk>_Pdkx2Q0?=Gdnnzr0}7{?7&%x*?}t(v-~@WS?QlOaxTPfx#*HuXtCQbCxG5@Q4bNI4m(c*d+0ZDTAiHQ~I8Y z`&a(~r+3&d;^3n*(CW^#`5f4DkIi3!n}PO56^ zWYPUO&!KiP39D)JC-8QO2lbN58tom_u2D=a2l9f`;0(N8F(+7TY4kDp?Npo3f%o`q zz6gHUWAk4SX}j?2r?g--B;i z%nm;f{!x-20lS^H{>R|1Db_bs1=}y7>Se2nO^k!{Ty~RbSb7B#r-QpCo&$c}Vs4)g zUhTGH64ghlY9iIB{hbvlcKk8$NhxyeV3!o>r}Z_}HSN1l+n;9pvIqQ*6@Rue0Nya& zmLCRRl6V-rVumgM1UxA51^s{3HQHCGwal?bJ_&fXg@0K z4#y@3z+zwe@s>UgK8bg7lV#u|@Z5CKK^mP0lPcItok&wTJ7$N1=gCZ7QFV!*DU3`BGl z?B~EfpZl2_8tj5XVwNyCHn|U+VNFR85jcPbpIHrfM2ql7;M|`LFuw$SdqiWC-vCcf zO+hLhxl8V&VenrO(M;%R?D08N*dcP!*I?U#m&U;3)&c4#AEm${J7P|{4=g)S%xn#S zr?3p%D@C9c%q?a|eB3ekY5NeFVm0Q`syV9Kk7Scj%*kLv9biO;^GE zQ9led9^f@Fzw?E?8D2~Q7Oflo8w%y9D3Cg626LJt8axZOJMhp>upOaP>H!~>BJ={- zUNb(t0*Lq%jqUN24M1+J5za~c*mlRbf#ksZ%&JG_T8@YVCr**!vFY%SFv#D2DspTd z;aTY04thy}em?X?hvy;ZtVIkc6>De^i?+h-z(KQ6@uXOlF!&2Fr$M5@B^cn3OWwCU zg2#vU)J<1r$G3$d71Z_gmbIIPsSo!h}=L YzBbhob?671bF)5ez#6dySjOh{f9WV-P5=M^ delta 5117 zcmZvg4{Q_n6~~`1wy{G(oCG@Zmy5%nK!ULoK!m_BaX<%W6qx0nqlcFw8^oq0!P)v)EHl) zc_%6BN?e)VNj5oz_Gy7lelFK0f1;cyIS`rRee`C_LjS_r4~Kr%u+ZNW$O|o~^9{`K z1=m*ja|7N-Z?)tG=KFI)^ZnVrV4%`}GEfns(p5e6F_VpAe;S@!UlE$IR4OvdFZ-?x zQIuj$!xbUP`Lb{NQi~jyyfD1{k*PSiw#*+4d>*FKr~fd^6k9!9KM>iyz4J+b|Fq>< zp<~sqYpvd;IJeQ!qiJZ~ z>I`bzdGzR0WRXtZ${afHTJG{I4KoS@*&{g!p=Bti7>SUoAJn+3A9&r)I%lmrR2+($ zqUWeoUQz$0m;P`mqk{UP-Wj2SWqFx2c--;LA0 zQ=^@O(QVQG)HbDahBv^gH-I*Tmd5@`IQt#vQ|?ElXNP3BG#LHCzL+;9CDxr%9H<|N zZto_ZiUwKd{r=)WcEE9wE}5e#-uf7^n*=w>QZ(}TpO=Ufb7{w7-WS>DFAh1p$E(wB zclYele?S{_wS_RQ`o9sI^uqBo%O0Tr{0|`KAfXOXwDum$s_VR+H$!>sf;qMca(_0$MFvH(DoJ2UB-pHSEWs9) zIaz#>Le?Z!oghPROVZm_=KGs3QYe*A$}Gp-*Jpz{XFBJ@e3dXBPc}Z!7{3tq9GY(LGEszhWpzOn40z>rt^@N*^D47L z&w_bD94e>KkHEZ0I)}l$aN{+7E0`Bk*GIs-fI9CtMTmG+b-@uBbPC)7?iQFM>H*_~ z!%V+{o;mKYSxs*&p|_}fJ;7$W0DIi!-z(_vK+i1d zZwvZC=$UnWgd@5q7?`Cv>jtcUAn3E9XV&#juzr}SZ-n=NTUc}Mzg}S0hYXI;^ic-b zV;E35^gjWgLPrUIJYYUo*u-g4Lrq+$q>MjiSFS=-HtgI!6!zd(^87vw*D0G$8P$qyhh|9Go|4$!mB$xE=b+r_pDyYRfXX7i`Nm_&&Hn;B?%@4T`BU zFERp6K+st6VrmsQ1KcKXA9@}TI348@d?k?(Hsa)8v?3go{%u0 zCE#v>SAu&4UIV@^a2R}3;9bhY%1K50A*~^O)Uosic#ptuD>LUWihKa+8dKa#LV58w za3*T3_)?9fYv2p12KRywrWyPIyx3;&6Yv&cJUf#2j=I<@T4~0`i^eO}hf>rKsH~Rzszv;v)t;p9NaD^U z?p89tn-{5ss}HqppM``i2J2;u1CPUoZ?T!yf%Q-wWTP!${$4DDCs`!XZg7t_lLMi= z0{#!)Q2b72eLMIpnCmidggPKFChQwI4c6~V2VUbB!E4c>K^x%jU=NA_eq8c^pM&!= z;<<7lfI%?HI@{?D)Wuwlvx839o4`f@5gTQ2;jBG59Vp<7!D0lzKq1W~ysQo2Vf%^t z1QIP`)-uQruLKvtVLc<8z$VR}9o`8}1QTg88Mb z^SfaEiO&;!0Opsru73>1W|VnY9&Zp&S!2R9O0$arSZN}df8e-*j}A4(1rTsmpbxkJ z{1H;fdx@7|1M7teB493qbo)2KMuu$kTd}beax~=W-0ei#S`fh!Qm3<`P`>QAG>^?b3m_kt*K5o<>JHM<*B5W z=Iz18^)2C-nq1|c_!qyj&sXiNaJi|mab38veBNxAN57ooD%UUPDtCRAGv~T0p(xICJvis-1Bt5~6|sPS4iZVNZNf?Wq{er?X=>&ER(t&JOYYz?-Co3|>nm$haG zH#Y}gYz#JU-4Ncmqos*|g6Mi{*??qM&BI&6t;&hzIbB_~9+N~?<<5#itNKWHDXDc% z^+&ZH#Zy;o;b&P)d9JQF{=3F3WpABRzdTdt Date: Wed, 12 Feb 2025 16:02:21 +0000 Subject: [PATCH 30/80] Update with latest aes.S From commit 59c6d71d7 --- enc_bootloader/aes.S | 148 +++++++++++++++++++++++--------- enc_bootloader/config.h | 10 ++- enc_bootloader/enc_bootloader.c | 145 +++---------------------------- 3 files changed, 125 insertions(+), 178 deletions(-) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index 35a9f854..f24f9718 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -18,13 +18,15 @@ pointer is overwritten. #include "hardware/regs/clocks.h" #include "hardware/regs/sha256.h" #include "hardware/regs/resets.h" +#include "hardware/regs/trng.h" #include "hardware/rcp.h" .global decrypt -.global rstate_sha .global do_not_wipe .global chaff +.extern lock_key + @ RCP macros #define CTAG0 0x2a @@ -47,6 +49,9 @@ pointer is overwritten. #define CTAG17 0x3c #define CTAG18 0x3d @ not used +@ number of blocks from the TRNG processed to initialise rstate_sha +#define TRNG_BLOCKS 25 + @ The lower the jitterpriorty the more the jitter .macro SET_COUNT n,jitterpriority .if RC_COUNT @@ -196,10 +201,6 @@ lut_b_map: .space 4 .space 4 @ align to multiple of 8 -do_not_wipe: -.byte 0x00 @ Flag: if this is zero then memory will be wiped after decryption - @ The purpose of this flag is that some test modes require the memory not to be wiped - .balign 16 rstate_all_start: @ Mark start of RNG data to allow selective memory wipe rstate_sha: @ 128-bit SHA random state, to be initialised to TRNG bytes; zeroth byte must be initialised to zero @@ -221,7 +222,87 @@ murmur3_constants: @ Five constants used in murmur3_32 hash .word 0xc2b2ae35 .endif -@ Put main code in first scratch area +scratch_y_end: + +@ Initialisation code in main .text section +.section .text,"ax",%progbits + +@ The following is copied from the A2 boot ROM code at src/main/arm/varm_boot_path.c with adjustments. +@ We feed a stream of bits from the TRNG into the SHA hardware accelerator to generate some +@ random numbers. +@ Trashes r0-r6 +.balign 4 +init_rstate: + CHK_COUNT 24,6 + ldr r4,=TRNG_BASE+TRNG_RNG_IMR_OFFSET + ldr r5,=SHA256_BASE + movs r1,#1 + str r1,[r4,#TRNG_TRNG_SW_RESET_OFFSET -TRNG_RNG_IMR_OFFSET] + ldr r6,[r4,#TRNG_TRNG_SW_RESET_OFFSET -TRNG_RNG_IMR_OFFSET] @ reads as 0 + movw r1,#SHA256_CSR_RESET|SHA256_CSR_START_BITS @ initialise SHA internal state by writing START bit + str r1,[r5,#SHA256_CSR_OFFSET] + str r6,[r4,#TRNG_SAMPLE_CNT1_OFFSET -TRNG_RNG_IMR_OFFSET] + movs r6,TRNG_BLOCKS*2+1 @ odd so that we break out of the loop half-way through loading the SHA hardware, giving + @ time for previous SHA computation to complete +2: + movs r1,#0xff @ TRNG setup is inside loop in case it is skipped. + str r1,[r4,#TRNG_TRNG_DEBUG_CONTROL_OFFSET-TRNG_RNG_IMR_OFFSET] @ disable checks and bypass decorrelators,to stream raw TRNG ROSC samples + str r1,[r4,#TRNG_RND_SOURCE_ENABLE_OFFSET -TRNG_RNG_IMR_OFFSET] @ start ROSC if it is not already started + str r1,[r4,#TRNG_RNG_ICR_OFFSET -TRNG_RNG_IMR_OFFSET] @ clear all interrupts (including EHR_VLD) + movs r0,r4 + adds r0,#TRNG_EHR_DATA0_OFFSET -TRNG_RNG_IMR_OFFSET + movs r2,#TRNG_TRNG_BUSY_OFFSET -TRNG_RNG_IMR_OFFSET +1: + ldr r1,[r4,r2] @ wait for 192 ROSC samples to fill EHR,should take constant time + cmp r1,#0 + bne 1b + subs r6,#1 @ done? + beq 3f + movs r1,#8 +1: + ldmia r0!,{r2} @ copy 6 EHR words to SHA-256, plus garbage (RND_SOURCE_ENABLE and SAMPLE_CNT1) + str r2,[r5,#SHA256_WDATA_OFFSET] @ for a total of half a SHA-256 block + subs r1,#1 + bne 1b + ldr r2,[r5,#SHA256_SUM0_OFFSET] @ TRNG is now sampling again; use some SHA bits to modulate the chain length + str r2,[r4,#TRNG_TRNG_CONFIG_OFFSET -TRNG_RNG_IMR_OFFSET] + b.n 2b + +3: + CHK_COUNT 25,6 + str r1,[r4,#TRNG_TRNG_CONFIG_OFFSET -TRNG_RNG_IMR_OFFSET] @ turn off rand source and wipe SHA bits left in TRNG config; r1=0 + str r1,[r4,#TRNG_RND_SOURCE_ENABLE_OFFSET -TRNG_RNG_IMR_OFFSET] + adds r5,r5,#SHA256_SUM0_OFFSET + ldmia r5!,{r0-r3} + ldr r5,=rstate_sha + stmia r5,{r0-r3} + CHK_COUNT 26,6 + +@ r5=rstate_sha + movs r0,#0 + strb r0,[r5] @ make sure rstate_sha[0] has byte 0 set to 0, representing "out of data" +@ try to find a non-zero initialiser to create a non-degenerate LFSR + ldr r1,[r5,#4] + cbnz r1,1f @ is word 1 non-zero? then use it + ldr r1,[r5,#8] + cbnz r1,1f @ otherwise, is word 2 non-zero? use it + ldr r1,[r5,#12] + cbnz r1,1f @ otherwise, is word 3 non-zero? use it + mov r1,r5 @ give up and use the address of rstate_sha (which is non-zero); this can't really happen (2^{-96} probability) +1: + str r1,[r5,#rstate_lfsr-rstate_sha] + CHK_COUNT 27,6 +.if GEN_RAND_SHA +.if SH_JITTER + movs r2,#0 + str r2,[r5,#jstate-rstate_sha] +.endif +.endif + + CHK_COUNT 28,6 + bx r14 + +@ Put AES core code in first scratch area .section .scratch_x.aes,"ax",%progbits .if GEN_RAND_SHA @@ -374,27 +455,10 @@ gen_rand_lfsr_nonpres: decrypt: push {r14} GET_CANARY r14,CTAG3,6 + SET_COUNT 23,6 push {r0-r12,r14} - ldr r5,=rstate_sha - movs r0,#0 - strb r0,[r5] @ make sure rstate_sha[0] has byte 0 set to 0, representing "out of data" -@ try to find a non-zero initialiser to create a non-degenerate LFSR - ldr r1,[r5,#4] - cbnz r1,1f @ is word 1 non-zero? then use it - ldr r1,[r5,#8] - cbnz r1,1f @ otherwise, is word 2 non-zero? use it - ldr r1,[r5,#12] - cbnz r1,1f @ otherwise, is word 3 non-zero? use it - mov r1,r5 @ give up and use the address of rstate_sha (which is non-zero) -1: - str r1,[r5,#rstate_lfsr-rstate_sha] -.if GEN_RAND_SHA -.if SH_JITTER - movs r2,#0 - str r2,[r5,#jstate-rstate_sha] -.endif - bl reset_sha -.endif + bl reset_sha_trng + bl init_rstate @ randomly re-share the LUT contents ldr r4,=lut_a mov r5,#64 @ 64 words = 256 bytes @@ -408,26 +472,31 @@ decrypt: stmia r4!,{r6} subs r5,r5,#1 bne 1b + CHK_COUNT 29,6 bl remap @ scramble the LUTs pop {r0} @ pointer to 4way key data + CHK_COUNT 30,6 bl init_key_4way + CHK_COUNT 31,6 + bl lock_key pop {r0-r2} bl ctr_crypt_s bl randomisechaff clear03 -@ !!! clear all registers here -@ !!! chain to code here pop {r4-r12,r14} CHK_CANARY r14,CTAG3,6 pop {r15} .balign 4 .thumb_func -reset_sha: +reset_sha_trng: ldr r1,=RESETS_BASE+RESETS_RESET_OFFSET ldr r2,[r1] - orr r3,r2,#RESETS_RESET_SHA256_BITS - str r3,[r1] @ reset the SHA hardware + ldr r3,=#RESETS_RESET_SHA256_BITS|RESETS_RESET_TRNG_BITS + orrs r2,r2,r3 + str r2,[r1] @ reset the SHA hardware and the TRNG hardware + CHK_COUNT 23,6 + bics r2,r2,r3 str r2,[r1] @ release the reset bx r14 @@ -1849,12 +1918,8 @@ decryption_end: CHK_COUNT 91,6 bne ctr_crypt_mainloop +#if WIPE_MEMORY @ Wipe memory from workspace_start up to the stack pointer - ldr r0,=do_not_wipe @ Some test modes require memory not to be wiped - ldrb r0,[r0] - movs r0,r0 - bne nowipe - @ First fill everything (except the RNG state itself) with random numbers to avoid any possibly useful power signals ldr r4,=workspace_start ldr r5,=rstate_all_start @@ -1864,23 +1929,26 @@ decryption_end: cmp r4,r5 bcc 1b ldr r4,=rstate_all_end + mov r5,r13 @ gcc arm assembler says cmp r4,r13 is deprecated, so use another register 1: bl gen_rand_sha_nonpres stmia r4!,{r0} - cmp r4,r13 + cmp r4,r5 bcc 1b @ Then fill everything with zeros so as not to leave behind clues about the RNG state ldr r4,=workspace_start movs r0,#0 + mov r5,r13 1: stmia r4!,{r0} - cmp r4,r13 + cmp r4,r5 bcc 1b -nowipe: +#endif .if GEN_RAND_SHA - bl reset_sha @ clear out the SHA hardware + SET_COUNT 23,6 + bl reset_sha_trng @ clear out the SHA hardware .endif pop {r0-r12,r14} CHK_CANARY r12,CTAG0,6 diff --git a/enc_bootloader/config.h b/enc_bootloader/config.h index ee7a3b8a..b4e28544 100644 --- a/enc_bootloader/config.h +++ b/enc_bootloader/config.h @@ -1,6 +1,6 @@ #pragma once -// These options should be enabled because the security risk of not using them is too high +// These options (up to long /////////////// line) should be enabled because the security risk of not using them is too high // or because the time cost is very low so you may as well have them. // They can be set to 0 for analysis or testing purposes. @@ -22,6 +22,10 @@ #define RK_ROR 1 // store round key shares with random rotations within each word #endif +#ifndef WIPE_MEMORY +#define WIPE_MEMORY 1 // Wipe memory after decryption +#endif + // The following options should be enabled to increase resistance to glitching attacks. #ifndef RC_CANARY @@ -37,7 +41,7 @@ // It is advisable to use a least one form of jitter. // RC_JITTER is quite slow, and is probably the most predictable of the three, so it is disabled by default. -// (However, it may be that the large delays it produces is advantageous in defeating some sorts of side-channel attacks.) +// (Leaving it as an option because it's just possible that the large delays it produces are advantageous in defeating certain side-channel attacks.) #ifndef RC_JITTER #define RC_JITTER 0 // 0-7. Higher = more jitter. Governs use of random-delay versions of RCP instructions. #endif @@ -47,7 +51,7 @@ #endif #ifndef CK_JITTER -#define CK_JITTER 0 // occasionally switch CPU clock to ROSC for extra timing variability +#define CK_JITTER 1 // occasionally switch CPU clock to ROSC for extra timing variability #endif diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 4a797452..12d5d878 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -22,137 +22,8 @@ #include "config.h" -extern uint32_t rstate_sha[4]; - extern void decrypt(uint8_t* key4way, uint8_t* iv, uint8_t(*buf)[16], int nblk); - -/* -The following is copied from the A2 boot ROM code at -src/main/arm/varm_boot_path.c with adjustments. -*/ - -void init_rstate() { - int i; - - hw_set_bits(&resets_hw->reset, RESETS_RESET_TRNG_BITS | RESETS_RESET_SHA256_BITS); - hw_clear_bits(&resets_hw->reset, RESETS_RESET_TRNG_BITS | RESETS_RESET_SHA256_BITS); - // As these are clk_sys-clocked, 1 APB read is sufficient delay (no polling loop) - (void)*hw_clear_alias(&resets_hw->reset); - // second read is because I'm feeling generous - (void)*hw_clear_alias(&resets_hw->reset); - - // RNG is derived by streaming a large number of TRNG ROSC samples - // into the SHA-256. TRNG_SAMPLE_BLOCKS is the number of SHA-256 - // blocks to hash, each containing 384 samples from the TRNG ROSC: - const unsigned int TRNG_SAMPLE_BLOCKS = 25; - - // Sample one ROSC bit into EHR every cycle, subject to CPU keeping up. - // More temporal resolution to measure ROSC phase noise is better, if we - // use a high quality hash function instead of naive VN decorrelation. - // (Also more metastability events, which are a secondary noise source) - - // Each half-block (192 samples) takes approx 235 cycles, so 470 cycles/block. - uintptr_t trng = (uintptr_t)trng_hw; - sha256_hw_t *sha256 = sha256_hw; - uint32_t _counter; - // we use 0xff instead of -1 below to set all bits, so make sure there are no bits above bit 7 - static_assert(0xffffff00u == ( - (TRNG_TRNG_DEBUG_CONTROL_RESERVED_BITS | ~TRNG_TRNG_DEBUG_CONTROL_BITS) & - (TRNG_RND_SOURCE_ENABLE_RESERVED_BITS | ~TRNG_RND_SOURCE_ENABLE_BITS) & - (TRNG_RNG_ICR_RESERVED_BITS | ~TRNG_RNG_ICR_BITS) & ~0xffu), ""); - pico_default_asm_volatile ( - "movs r1, #1\n" - "str r1, [%[trng], %[offset_trng_sw_reset]]\n" - // Fixed delay is required after TRNG soft reset -- this plus - // following sha256 write is sufficient: - "ldr %[counter], [%[trng], %[offset_trng_sw_reset]]\n" - // (reads as 0 -- initialises counter to 0.) - // Initialise SHA internal state by writing START bit - "movw r1, %[sha256_init]\n" - "str r1, [%[sha256]]\n" - // This is out of the loop because writing to this register seems to - // restart the sampling, slowing things down. We don't care if this write - // is skipped as that would just make sampling take longer. - "str %[counter], [%[trng], %[offset_sample_cnt1]]\n" - "adds %[counter], %[iterations] + 1\n" - "2:\n" - // TRNG setup is inside loop in case it is skipped. Disable checks and - // bypass decorrelators, to stream raw TRNG ROSC samples: - "movs r1, #0xff\n" - "str r1, [%[trng], %[offset_debug_control]]\n" - // Start ROSC if it is not already started - "str r1, [%[trng], %[offset_rnd_source_enable]]\n" - // Clear all interrupts (including EHR_VLD) - "str r1, [%[trng], %[offset_rng_icr]]\n" - // (hoist above polling loop to reduce poll->read delay) - "movs r0, %[trng]\n" - "adds r0, %[offset_ehr_data0]\n" - // Wait for 192 ROSC samples to fill EHR, this should take constant time: - "movs r2, %[offset_trng_busy]\n" - "1:\n" - "ldr r1, [%[trng], r2]\n" - "cmp r1, #0\n" - "bne 1b\n" - // Check counter and bail out if done -- we always end with a full - // EHR, which is sufficient time for SHA to complete too. - "subs %[counter], #1\n" - "beq 3f\n" - // r1 should now be 0, and we "check" by using it as the base for the loop count. - "rnd_to_sha_start:\n" - // Copy 6 EHR words to SHA-256, plus garbage (RND_SOURCE_ENABLE and - // SAMPLE_CNT1) which pads us out to half of a SHA-256 block. This - // means we can avoid checking SHA-256 ready whilst reading EHR, so - // we restart sampling sooner. (SHA-256 becomes non-ready for 57 - // cycles after each 16 words written.). - "adds r1, #8\n" - "1:\n" - "ldmia r0!, {r2, r3}\n" - "str r2, [%[sha256], #4]\n" - "str r3, [%[sha256], #4]\n" - "subs r1, #2\n" - "bne 1b\n" - "rnd_to_sha_end:\n" - // TRNG is now sampling again, having started after we read the last - // EHR word. Grab some in-progress SHA bits and use them to modulate - // the chain length, to reduce chance of injection locking: - "ldr r2, [%[sha256], #8]\n" - "str r2, [%[trng], %[offset_trng_config]]\n" - // Repeat for all blocks - "b.n 2b\n" - "3:\n" - // Done -- turn off rand source as it's a waste of power, and wipe SHA - // bits left in TRNG config. r1 is known to be 0 (even on RISC-V), so - // use that. - "str r1, [%[trng], %[offset_trng_config]]\n" - "str r1, [%[trng], %[offset_rnd_source_enable]]\n" - - // Function of the actual TRNG pointer we used, and the number of loop iterations - : - [counter] "=&l" (_counter), - // Not actually written, but we tell the compiler to assume such: - [trng] "+l" (trng), - [sha256] "+l" (sha256) - : - [iterations] "i" (TRNG_SAMPLE_BLOCKS * 2), - [sha256_init] "i" (SHA256_CSR_RESET | SHA256_CSR_START_BITS), - [offset_trng_sw_reset] "i" (TRNG_TRNG_SW_RESET_OFFSET - TRNG_RNG_IMR_OFFSET), - [offset_sample_cnt1] "i" (TRNG_SAMPLE_CNT1_OFFSET - TRNG_RNG_IMR_OFFSET), - [offset_debug_control] "i" (TRNG_TRNG_DEBUG_CONTROL_OFFSET - TRNG_RNG_IMR_OFFSET), - [offset_rnd_source_enable] "i" (TRNG_RND_SOURCE_ENABLE_OFFSET - TRNG_RNG_IMR_OFFSET), - [offset_rng_icr] "i" (TRNG_RNG_ICR_OFFSET - TRNG_RNG_IMR_OFFSET), - [offset_trng_busy] "i" (TRNG_TRNG_BUSY_OFFSET - TRNG_RNG_IMR_OFFSET), - [offset_ehr_data0] "i" (TRNG_EHR_DATA0_OFFSET - TRNG_RNG_IMR_OFFSET), - [offset_trng_config] "i" (TRNG_TRNG_CONFIG_OFFSET - TRNG_RNG_IMR_OFFSET) - : "r0", "r1", "r2", "r3" - ); - // No need to wait for SHA, as we polled the EHR one extra time, which - // takes longer than the SHA. - for(i=0;i<4;i++) rstate_sha[i]=sha256->sum[i]; - - rosc_hw->random = rstate_sha[0]; -} - // These just have to be higher than the actual frequency, to prevent overclocking unused peripherals #define ROSC_HZ 300*MHZ #define OTHER_CLK_DIV 30 @@ -269,6 +140,15 @@ void runtime_init_clocks(void) { } +bi_decl(bi_ptr_int32(0, 0, otp_key_page, 30)); + +// The function lock_key() is called from decrypt() after key initialisation is complete and before decryption begins. +// That is a suitable point to lock the OTP area where key information is stored. +void lock_key() { + otp_hw->sw_lock[otp_key_page] = 0xf; +} + + int main() { // This works around E21 by locking down the page with a key, // as there is no way to enter a key over the picoboot interface. @@ -281,16 +161,11 @@ int main() { bi_decl(bi_ptr_int32(0, 0, data_start_addr, 0x20000000)); bi_decl(bi_ptr_int32(0, 0, data_size, 0x78000)); - bi_decl(bi_ptr_string(0, 0, iv, "0123456789abcdef", 17)) - bi_decl(bi_ptr_int32(0, 0, otp_key_page, 30)); - - // Initialise random state - init_rstate(); + bi_decl(bi_ptr_string(0, 0, iv, "0123456789abcdef", 17)); // Read key directly from OTP - guarded reads will throw a bus fault if there are any errors uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE; decrypt((uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & (otp_key_page * 0x40))]), (uint8_t*)iv, (void*)data_start_addr, data_size/16); - otp_hw->sw_lock[otp_key_page] = 0xf; // Increase stack limit by 0x100 pico_default_asm_volatile( From 2766a7d5ee8e85dd12ad4939a96767d1b0666b42 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 12 Feb 2025 16:03:19 +0000 Subject: [PATCH 31/80] Seed ROSC random using rstate_lfsr --- enc_bootloader/aes.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index f24f9718..c835cbfc 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -18,6 +18,7 @@ pointer is overwritten. #include "hardware/regs/clocks.h" #include "hardware/regs/sha256.h" #include "hardware/regs/resets.h" +#include "hardware/regs/rosc.h" #include "hardware/regs/trng.h" #include "hardware/rcp.h" @@ -291,6 +292,8 @@ init_rstate: mov r1,r5 @ give up and use the address of rstate_sha (which is non-zero); this can't really happen (2^{-96} probability) 1: str r1,[r5,#rstate_lfsr-rstate_sha] + ldr r2,=ROSC_RANDOM_OFFSET+ROSC_BASE + str r1,[r2,#0] CHK_COUNT 27,6 .if GEN_RAND_SHA .if SH_JITTER From 9f01c2c09df4dbdafbfd0e9472d1de4b53d168af Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 12 Feb 2025 16:21:22 +0000 Subject: [PATCH 32/80] Use CK_JITTER to enable/disable ROSC randomisation --- enc_bootloader/enc_bootloader.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 12d5d878..ce4d450c 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -28,6 +28,7 @@ extern void decrypt(uint8_t* key4way, uint8_t* iv, uint8_t(*buf)[16], int nblk); #define ROSC_HZ 300*MHZ #define OTHER_CLK_DIV 30 +#if CK_JITTER void runtime_init_clocks(void) { // Disable resus that may be enabled from previous software clocks_hw->resus.ctrl = 0; @@ -138,6 +139,7 @@ void runtime_init_clocks(void) { ROSC_HZ, OTHER_CLK_DIV); } +#endif bi_decl(bi_ptr_int32(0, 0, otp_key_page, 30)); From baf011fb12d0a61862bfd18971daf0dd5ed37e0d Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 19 Feb 2025 14:47:40 +0000 Subject: [PATCH 33/80] Add IV XOR code, but turn it off with IV0_XOR for now --- bintool/mbedtls_wrapper.c | 41 +++++++++++++++++++++++++++++++++++++++ bintool/mbedtls_wrapper.h | 10 ++++++++++ 2 files changed, 51 insertions(+) diff --git a/bintool/mbedtls_wrapper.c b/bintool/mbedtls_wrapper.c index b00bb7a1..0a2ba02d 100644 --- a/bintool/mbedtls_wrapper.c +++ b/bintool/mbedtls_wrapper.c @@ -40,15 +40,56 @@ void mb_sha256_buffer(const uint8_t *data, size_t len, message_digest_t *digest_ mbedtls_sha256(data, len, digest_out->bytes, 0); } +#if IV0_XOR +// Taken from mbedtls_aes_crypt_ctr, but with XOR instead of adding to IV0 +int mb_aes_crypt_ctr_xor(mbedtls_aes_context *ctx, + size_t length, + unsigned char iv0[16], + unsigned char nonce_xor[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output) +{ + int c; + int ret = 0; + size_t n = 0; + size_t counter = 0; + + while (length--) { + if (n == 0) { + for (int i = 16; i > 16 - sizeof(counter); i--) { + nonce_xor[i-1] == iv0[i-1] ^ (unsigned char)(counter >> (i*8)); + } + + ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, nonce_xor, stream_block); + if (ret != 0) { + break; + } + } + c = *input++; + *output++ = (unsigned char) (c ^ stream_block[n]); + + n = (n + 1) & 0x0F; + } + + return ret; +} +#endif + void mb_aes256_buffer(const uint8_t *data, size_t len, uint8_t *data_out, const aes_key_t *key, iv_t *iv) { mbedtls_aes_context aes; assert(len % 16 == 0); mbedtls_aes_setkey_enc(&aes, key->bytes, 256); + uint8_t xor_working_block[16] = {0}; uint8_t stream_block[16] = {0}; size_t nc_off = 0; +#if IV0_XOR + mb_aes_crypt_ctr_xor(&aes, len, iv->bytes, xor_working_block, stream_block, data, data_out); +#else mbedtls_aes_crypt_ctr(&aes, len, &nc_off, iv->bytes, stream_block, data, data_out); +#endif } void raw_to_der(signature_t *sig) { diff --git a/bintool/mbedtls_wrapper.h b/bintool/mbedtls_wrapper.h index f3c7101b..d5f6a29d 100644 --- a/bintool/mbedtls_wrapper.h +++ b/bintool/mbedtls_wrapper.h @@ -19,6 +19,16 @@ extern "C" { #include #include +/* + * Use XOR of counter with IV0 to generate the IV for each encrypted block + * + * ie IV = IV0 ^ block_number, rather than the default IV = IV0 + block_number + * + * The power signature for this calculation is easier to mask on RP2350 than + * adding the block number to the IV0 + */ +#define IV0_XOR 0 + #ifdef __cplusplus #define _Static_assert static_assert #endif From aa6a0ba8a862ad1b3cc717ee8052831e43e74963 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 20 Feb 2025 10:17:25 +0000 Subject: [PATCH 34/80] Disable calibration using XOSC by default --- enc_bootloader/enc_bootloader.c | 13 +++++++++---- enc_bootloader/enc_bootloader.elf | Bin 20600 -> 19924 bytes 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index ce4d450c..7f378039 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -28,17 +28,18 @@ extern void decrypt(uint8_t* key4way, uint8_t* iv, uint8_t(*buf)[16], int nblk); #define ROSC_HZ 300*MHZ #define OTHER_CLK_DIV 30 +// Enable calibration of the ROSC using the XOSC - if disabled it runs with the fixed ~110MHz ROSC configuration +#define XOSC_CALIBRATION 0 + #if CK_JITTER void runtime_init_clocks(void) { // Disable resus that may be enabled from previous software clocks_hw->resus.ctrl = 0; bi_decl(bi_ptr_int32(0, 0, rosc_div, 2)); // default divider 2 - bi_decl(bi_ptr_int32(0, 0, rosc_drive, 0x0000)); // default drives of 0 - bi_decl(bi_ptr_int32(0, 0, xosc_hz, 12000000)); // xosc freq in Hz - bi_decl(bi_ptr_int32(0, 0, clk_khz, 150 * KHZ)); // maximum clk_sys freq in KHz + bi_decl(bi_ptr_int32(0, 0, rosc_drive, 0x7777)); // default drives of 0b111 (0x7) - // Bump up ROSC speed to ~90MHz + // Bump up ROSC speed to ~110MHz rosc_hw->freqa = 0; // reset the drive strengths rosc_hw->div = rosc_div | ROSC_DIV_VALUE_PASS; // set divider // Increment the freqency range one step at a time - this is safe provided the current config is not TOOHIGH @@ -56,7 +57,10 @@ void runtime_init_clocks(void) { rosc_hw->freqb = (ROSC_FREQB_PASSWD_VALUE_PASS << ROSC_FREQB_PASSWD_LSB) | ROSC_FREQB_DS7_LSB | ROSC_FREQB_DS6_LSB | ROSC_FREQB_DS5_LSB | ROSC_FREQB_DS4_LSB; +#if XOSC_CALIBRATION // Calibrate ROSC frequency if XOSC present - otherwise just configure + bi_decl(bi_ptr_int32(0, 0, xosc_hz, 12000000)); // xosc freq in Hz + bi_decl(bi_ptr_int32(0, 0, clk_khz, 150 * KHZ)); // maximum clk_sys freq in KHz if (xosc_hz) { xosc_init(); // Switch away from ROSC to avoid overclocking @@ -95,6 +99,7 @@ void runtime_init_clocks(void) { drives = drives ? 0x0000 : 0x7777; } } +#endif // XOSC_CALIBRATION // CLK SYS = ROSC directly, as it's running slowly enough clock_configure_int_divider(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, diff --git a/enc_bootloader/enc_bootloader.elf b/enc_bootloader/enc_bootloader.elf index aafe5a6876e8bcd389f8c7a39dad7f7faf11ca38..08951671c8da067acb99dedccb3fb04ea2a448a9 100755 GIT binary patch delta 5879 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 delta 6617 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 Date: Thu, 20 Feb 2025 10:19:32 +0000 Subject: [PATCH 35/80] Update with latest aes.S From commit 6e6df5a5769 --- enc_bootloader/aes.S | 14 ++++++-------- enc_bootloader/enc_bootloader.elf | Bin 19924 -> 19924 bytes 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index c835cbfc..ec95c2d9 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -3,9 +3,9 @@ The "chaff" area must be located at the start of Y scratch RAM, 0x20081000: see the macro getchaffaddress. -The stack must be located at the end of Y scratch RAM: see the calls to memwipe -at the end of ctr_crypt_s where memory between __scratch_y_end__ and the stack -pointer is overwritten. +The stack must be located at the end of Y scratch RAM: see the memory +wiping at the end of ctr_crypt_s where memory between the start of Y +scratch RAM and the stack pointer is overwritten. */ .syntax unified @@ -23,7 +23,6 @@ pointer is overwritten. #include "hardware/rcp.h" .global decrypt -.global do_not_wipe .global chaff .extern lock_key @@ -53,7 +52,7 @@ pointer is overwritten. @ number of blocks from the TRNG processed to initialise rstate_sha #define TRNG_BLOCKS 25 -@ The lower the jitterpriorty the more the jitter +@ The lower jitterpriorty is, the more the jitter .macro SET_COUNT n,jitterpriority .if RC_COUNT .if RC_JITTER > \jitterpriority @@ -243,15 +242,14 @@ init_rstate: movw r1,#SHA256_CSR_RESET|SHA256_CSR_START_BITS @ initialise SHA internal state by writing START bit str r1,[r5,#SHA256_CSR_OFFSET] str r6,[r4,#TRNG_SAMPLE_CNT1_OFFSET -TRNG_RNG_IMR_OFFSET] - movs r6,TRNG_BLOCKS*2+1 @ odd so that we break out of the loop half-way through loading the SHA hardware, giving + movs r6,#TRNG_BLOCKS*2+1 @ odd so that we break out of the loop half-way through loading the SHA hardware, giving @ time for previous SHA computation to complete 2: movs r1,#0xff @ TRNG setup is inside loop in case it is skipped. str r1,[r4,#TRNG_TRNG_DEBUG_CONTROL_OFFSET-TRNG_RNG_IMR_OFFSET] @ disable checks and bypass decorrelators,to stream raw TRNG ROSC samples str r1,[r4,#TRNG_RND_SOURCE_ENABLE_OFFSET -TRNG_RNG_IMR_OFFSET] @ start ROSC if it is not already started str r1,[r4,#TRNG_RNG_ICR_OFFSET -TRNG_RNG_IMR_OFFSET] @ clear all interrupts (including EHR_VLD) - movs r0,r4 - adds r0,#TRNG_EHR_DATA0_OFFSET -TRNG_RNG_IMR_OFFSET + adds r0,r4,#TRNG_EHR_DATA0_OFFSET -TRNG_RNG_IMR_OFFSET movs r2,#TRNG_TRNG_BUSY_OFFSET -TRNG_RNG_IMR_OFFSET 1: ldr r1,[r4,r2] @ wait for 192 ROSC samples to fill EHR,should take constant time diff --git a/enc_bootloader/enc_bootloader.elf b/enc_bootloader/enc_bootloader.elf index 08951671c8da067acb99dedccb3fb04ea2a448a9..40fb553c1e9befcac76702abd7630a4f9efd9ec2 100755 GIT binary patch delta 19 bcmcaIoAJtQ#tk-FEFvF87&bd-O;H8_P45Qi delta 19 bcmcaIoAJtQ#tk-FED8)F2AdtUrYHjdNAm_V From d7aa177077e79820d79525a696397cff88f91045 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 20 Feb 2025 13:09:30 +0000 Subject: [PATCH 36/80] Add fast-rosc option Default to the 110MHz ROSC config, but can be increased to the 180MHz config by setting --fast-rosc --- main.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/main.cpp b/main.cpp index f7e5ba69..685ed61a 100644 --- a/main.cpp +++ b/main.cpp @@ -486,6 +486,7 @@ struct _settings { struct { bool embed = false; bool otp_key_page_set = false; + bool fast_rosc = false; uint16_t otp_key_page = 30; } encrypt; @@ -798,6 +799,7 @@ struct encrypt_command : public cmd { option("--quiet").set(settings.quiet) % "Don't print any output" + option("--verbose").set(settings.verbose) % "Print verbose output" + option("--embed").set(settings.encrypt.embed) % "Embed bootloader in output file" + + option("--fast-rosc").set(settings.encrypt.fast_rosc) % "Use ~180MHz ROSC configuration for embedded bootloader" + ( option("--otp-key-page").set(settings.encrypt.otp_key_page_set) % "Specify the OTP page storing the AES key" & integer("page").set(settings.encrypt.otp_key_page) % "OTP page (default 30)" @@ -5047,6 +5049,16 @@ bool encrypt_command::execute(device_map &devices) { config_guts(program); } + // fast rosc + if (settings.encrypt.fast_rosc) { + settings.config.key = "rosc_div"; + settings.config.value = "0x1"; + config_guts(program); + settings.config.key = "rosc_drive"; + settings.config.value = "0x0000"; + config_guts(program); + } + elf_file source_file(settings.verbose); elf_file *enc_elf = &source_file; enc_elf->read_file(tmp); From c8b03fda7e10502e39efa205b4d44b6ea78cef1c Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 24 Feb 2025 14:51:44 +0000 Subject: [PATCH 37/80] clock_configure_mhz no longer used --- enc_bootloader/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 7576d572..244ad30f 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -20,8 +20,7 @@ if (NOT USE_PRECOMPILED) # Temporary import handling, to ensure SDK branch with PICO_MINIMAL_VECTOR_TABLE is present file(READ ${PICO_SDK_PATH}/src/rp2_common/pico_crt0/crt0.S CRT0_S) - file(READ ${PICO_SDK_PATH}/src/rp2_common/hardware_clocks/clocks.c CLOCKS_C) - if ((CRT0_S MATCHES "PICO_MINIMAL_VECTOR_TABLE") AND (CLOCKS_C MATCHES "clock_configure_mhz")) + if (CRT0_S MATCHES "PICO_MINIMAL_VECTOR_TABLE") include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) else() # This clones the will-v-pi/pico-sdk repo on the self-decrypting branch From 854e1471810468b060df3676a7ff91877770e857 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 24 Feb 2025 15:06:23 +0000 Subject: [PATCH 38/80] Tidy up indents in CMakeLists.txt --- CMakeLists.txt | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b57945bb..d6041931 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,20 +63,20 @@ endif() # compile enc_bootloader.elf if (NOT DEFINED USE_PRECOMPILED) -set(USE_PRECOMPILED true) + set(USE_PRECOMPILED true) endif() ExternalProject_Add(enc_bootloader - PREFIX enc_bootloader - SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/enc_bootloader - BINARY_DIR ${CMAKE_BINARY_DIR}/enc_bootloader - CMAKE_ARGS - "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}" - "-DPICO_SDK_PATH:FILEPATH=${PICO_SDK_PATH}" - "-DUSE_PRECOMPILED:BOOL=${USE_PRECOMPILED}" - "-DPICO_DEBUG_INFO_IN_RELEASE=OFF" - BUILD_ALWAYS 1 # todo remove this - INSTALL_COMMAND "" - ) + PREFIX enc_bootloader + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/enc_bootloader + BINARY_DIR ${CMAKE_BINARY_DIR}/enc_bootloader + CMAKE_ARGS + "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}" + "-DPICO_SDK_PATH:FILEPATH=${PICO_SDK_PATH}" + "-DUSE_PRECOMPILED:BOOL=${USE_PRECOMPILED}" + "-DPICO_DEBUG_INFO_IN_RELEASE=OFF" + BUILD_ALWAYS 1 # todo remove this + INSTALL_COMMAND "" + ) set(ENC_BOOTLOADER_ELF ${CMAKE_BINARY_DIR}/enc_bootloader/enc_bootloader.elf) add_executable(enc_bootloader_elf IMPORTED) @@ -84,8 +84,8 @@ add_dependencies(enc_bootloader_elf enc_bootloader) set_property(TARGET enc_bootloader_elf PROPERTY IMPORTED_LOCATION ${ENC_BOOTLOADER_ELF}) # copy enc_bootloader.elf into build directory add_custom_command(TARGET enc_bootloader -COMMAND ${CMAKE_COMMAND} -E copy ${ENC_BOOTLOADER_ELF} ${CMAKE_BINARY_DIR}/enc_bootloader.elf -DEPENDS enc_bootloader + COMMAND ${CMAKE_COMMAND} -E copy ${ENC_BOOTLOADER_ELF} ${CMAKE_BINARY_DIR}/enc_bootloader.elf + DEPENDS enc_bootloader ) if (NOT PICOTOOL_NO_LIBUSB) @@ -371,8 +371,8 @@ install(FILES #Install enc_bootloader.elf install(FILES -${ENC_BOOTLOADER_ELF} -DESTINATION ${INSTALL_DATADIR} + ${ENC_BOOTLOADER_ELF} + DESTINATION ${INSTALL_DATADIR} ) if (NOT PICOTOOL_NO_LIBUSB) From 8af552dc43fd4c77d85ec523a3440c54e7cbf5be Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 24 Feb 2025 15:50:53 +0000 Subject: [PATCH 39/80] Remove CK_JITTER from aes.S, as this is now handled in the C code --- enc_bootloader/aes.S | 17 ----------------- enc_bootloader/enc_bootloader.elf | Bin 19924 -> 19924 bytes 2 files changed, 17 deletions(-) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index ec95c2d9..e0d65323 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -352,23 +352,6 @@ gen_rand_sha_nonpres: strb r3,[r2] @ save updated SUM register offset in bottom byte of rstate_sha[] bx r14 1: -.if CK_JITTER - ldr r3,=CLOCKS_BASE+CLOCKS_CLK_REF_CTRL_OFFSET - ldr r1,[r3,#0] - push {r1} - bic r1,r1,#3 - str r1,[r3,#0] @ switch ref_clk to ROSC - ldr r1,[r3,#CLOCKS_CLK_SYS_CTRL_OFFSET-CLOCKS_CLK_REF_CTRL_OFFSET] - push {r1} - bic r1,r1,#3 - str r1,[r3,#CLOCKS_CLK_SYS_CTRL_OFFSET-CLOCKS_CLK_REF_CTRL_OFFSET] @ switch sys_clk to ref_clk - @ run for a couple of cycles off ROSC (ca. 11MHz rather than 150MHz) to jitter the timing - @ could also insert a random delay here - pop {r1} - str r1,[r3,#CLOCKS_CLK_SYS_CTRL_OFFSET-CLOCKS_CLK_REF_CTRL_OFFSET] @ switch sys_clk back to clksrc_clk_sys_aux - pop {r1} - str r1,[r3,#0] -.endif movs r3,#SHA256_SUM6_OFFSET+1 strb r3,[r2] @ reset word counter: the +1 is compensated for later movw r1,#(1<?oMi}{_qWe%AnTA=AMK4e~5E4xi(?sbcXs>&;{ijxT@L*@=_n-aGe{Oa+)QdyC z_{u?D$c#|v;o(^e&LwqLM{c&y+K9sX$nb($;Qs8IN5g0&vxfKCT8%l}lu)16(o#+C zTLr8q%T@<5*?41TeA>S!Ea+tEDJigdxRf|KV5`7Q>(+sMaDw;x)+5F z5m|DJ-qcx)8%58$d|cX}u!d!{cM-YZ@(+4nM$ijc>Pb`x7Z(d_Wa(<6l8(HxbU9H) zM?P8VNL15Ntt_1rucgN@K2N$b=5ZQndvPQ2ID?k25&Y^wdGu4dzZptQir<|B z+ODx&v2WuCE#}4MyjqNV#KHUwQ{W>?b|Lt2$cPNkPb)@>XBcO}{1W3a4x#;6&Hn0W zrTJfv=67oX2>n(9^aO%%LzKDUEm*Hr6Xw7t7}FDLXG~A1k1^IFbXzf9-vZvGQ`g%N zfIuD-*aE)5cpEsVm@ZHb-ltbjtPWg~q?&HXshsHQBXlvQ8x$A^>S2LaCU6`)te6&T z2kV$;*a0>e)qg+>o?PQr#xA%&>cSmA4yRQjDo*0o&fvI>ihz@ opE2Fg;uJdn0~Q<-@A=*MFL>1V?f?J) delta 1231 zcmZA0ZAep57y#h&-c6^Q?Zc0BQ>PYUYL@JfsdJKl0_%?&1{Ekq1cu3)1VM;Y z2P{&;Ai_w0$W25_W}%^dfRYgY&hYdC$lDp0jPS9~S%J zeoIWyNJwz>%Bl$t(>imyGuLfyCV|-Cxr3F`W{ZO~w+VDnW%XeBvZ0iY!z3_Qg@`1^=f3PD{_YQ+_KaWsbala zS-LJ>O9=zqpslIv0Q6j1A^fDiG%M_<{b_|sv*FukWN9Ys!}t4eH@^LJDXm<0C#)l~ z4u(!+YySWhzZPU^Ox(7K0Dk%` z;}e{xt(pDMNaO7zruiUD4?9C&Q#`GhXHJ~4k88!jgP*lfCbk}`1$ZS+-PtyRp%o6y;uJJB}n?tC`hr|}Y!sRY;wMsR|in=pqi z;Jk+JoLRyt&QEbY zXD=DTz>4ytU=_bX_wG{@%%Jr~)!)! Date: Mon, 24 Feb 2025 17:53:06 +0000 Subject: [PATCH 40/80] Throw error when using AES key share file of incorrect size --- main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main.cpp b/main.cpp index 685ed61a..869efc29 100644 --- a/main.cpp +++ b/main.cpp @@ -4983,6 +4983,10 @@ bool encrypt_command::execute(device_map &devices) { aes_file->exceptions(std::iostream::failbit | std::iostream::badbit); aes_key_share_t aes_key_share; + aes_file->seekg(0, std::ios::end); + if (aes_file->tellg() != 128) { + fail(ERROR_INCOMPATIBLE, "The AES key share must be a 128 byte file (the supplied file is %d bytes)", aes_file->tellg()); + } aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); aes_key_t aes_key; From bd403b4a91857d34259670c696e31da78d74ee26 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 26 Feb 2025 12:22:10 +0000 Subject: [PATCH 41/80] Incorporate latest aes.S code, and add IV salt Now uses IV salt in OTP, and IV0_XOR is now enabled and fixed From commit 627274d21a2d6a30 --- bintool/mbedtls_wrapper.c | 12 +- bintool/mbedtls_wrapper.h | 2 +- enc_bootloader/aes.S | 204 +++++++++++++++----------------- enc_bootloader/enc_bootloader.c | 13 +- main.cpp | 59 +++++++-- 5 files changed, 163 insertions(+), 127 deletions(-) diff --git a/bintool/mbedtls_wrapper.c b/bintool/mbedtls_wrapper.c index 0a2ba02d..b5b8dbcb 100644 --- a/bintool/mbedtls_wrapper.c +++ b/bintool/mbedtls_wrapper.c @@ -53,18 +53,24 @@ int mb_aes_crypt_ctr_xor(mbedtls_aes_context *ctx, int c; int ret = 0; size_t n = 0; - size_t counter = 0; + uint32_t counter = 0; + + assert(length == (uint32_t)length); while (length--) { if (n == 0) { - for (int i = 16; i > 16 - sizeof(counter); i--) { - nonce_xor[i-1] == iv0[i-1] ^ (unsigned char)(counter >> (i*8)); + for (int i = 16; i > 0; i--) { + nonce_xor[i-1] = iv0[i-1]; + if (i - (int)(16 - sizeof(counter)) > (int)0) { + nonce_xor[i-1] ^= (unsigned char)(counter >> ((16-i)*8)); + } } ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, nonce_xor, stream_block); if (ret != 0) { break; } + counter++; } c = *input++; *output++ = (unsigned char) (c ^ stream_block[n]); diff --git a/bintool/mbedtls_wrapper.h b/bintool/mbedtls_wrapper.h index d5f6a29d..48c06f7b 100644 --- a/bintool/mbedtls_wrapper.h +++ b/bintool/mbedtls_wrapper.h @@ -27,7 +27,7 @@ extern "C" { * The power signature for this calculation is easier to mask on RP2350 than * adding the block number to the IV0 */ -#define IV0_XOR 0 +#define IV0_XOR 1 #ifdef __cplusplus #define _Static_assert static_assert diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index e0d65323..2014b5fd 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -40,7 +40,7 @@ scratch RAM and the stack pointer is overwritten. #define CTAG8 0x33 #define CTAG9 0x34 #define CTAG10 0x35 @ not used -#define CTAG11 0x36 +#define CTAG11 0x36 @ not used #define CTAG12 0x37 #define CTAG13 0x38 #define CTAG14 0x39 @@ -93,6 +93,8 @@ scratch RAM and the stack pointer is overwritten. .endif .endm +@ Clear internal stripe load registers, and r0-r3 +@ 0 <= offset <= 32 .macro clear03 offset=0 getchaffaddress r0,\offset ldmia r0,{r0-r3} @@ -158,6 +160,10 @@ RKshareC: @ Round key common share C; see comment at init_key .space 4 RKshareCchange: @ Temporary used by ref_roundkey_share_s .space 4 +IV0: @ 2-way share of IV for block 0 +.space 36 @ Considering IV0 as a word pointer, the format is IV = IV0[0,1,2,3] ^ (IV0[5,6,7,8],ror#16) + @ The gap at IV0[4] is to defeat unsharing by internal striped memory registers + @ I.e., there are implicit XORs IV0[0]^IV0[4], IV0[1]^IV0[5], ..., that the 1 word offset renders useless @ Regardless of configuration, the code uses a single 256-entry LUT, @ which is a simple S-box table. @@ -323,11 +329,11 @@ gen_rand_sha: ldr r2,=rstate_sha ldr r0,[r2,#jstate-rstate_sha] movs r1,#1 - movs r3,r0,lsl#2 - ands r3,r3,#31 - movs r3,r1,lsl r3 @ 1<<(4*(r0&7)) - udiv r3,r3,r1 @ Takes constant + (r0&7) cycles - lsrs r0,r0,#1 + ands r3,r0,#3 + movs r3,r3,lsl#2 + movs r3,r1,lsl r3 @ 1<<(4*(r0&3)) + udiv r3,r3,r1 @ Takes constant + (r0&3) cycles + lsrs r0,r0,#2 bne 1f bl gen_rand_sha_nonpres ldr r2,=rstate_sha @@ -352,6 +358,7 @@ gen_rand_sha_nonpres: strb r3,[r2] @ save updated SUM register offset in bottom byte of rstate_sha[] bx r14 1: +@ [CK_JITTER code was here] movs r3,#SHA256_SUM6_OFFSET+1 strb r3,[r2] @ reset word counter: the +1 is compensated for later movw r1,#(1<r5-r6->r7->r4 etc.) by an addtional amount +@ Cycle share registers r4-r7, r8-r11 (r4->r5-r6->r7->r4 etc.) by an addtional amount @ given in the bottom two bits of R0 and update the rotation recorded at statevperm. @ On entry R1 must point to statevperm. @ Trashes r0-r3,r12 @@ -901,46 +911,7 @@ addstatevperm_exit: @ label exit point to be to able to specify to ana bx r14 .endif -@ Switch from non-shared to shared state -@ Trashes r0-r3,r12 -.balign 4 -ns_to_s: - GET_CANARY r12,CTAG11,6 - push {r12,r14} -.if ST_SHAREC - bl gen_rand_sha_nonpres @ Create state share C; all bytes the same - ands r0,r0,#255 - orrs r0,r0,r0,lsl#8 - orrs r12,r0,r0,lsl#16 - ldr r1,=shareC - str r12,[r1] -.else - movs r12,#0 -.endif - bl gen_rand_sha_nonpres - eors r4,r4,r0 - eor r8,r12,r0,ror#16 - bl gen_rand_sha_nonpres - eors r5,r5,r0 - eor r9,r12,r0,ror#16 - bl gen_rand_sha_nonpres - eors r6,r6,r0 - eor r10,r12,r0,ror#16 - bl gen_rand_sha_nonpres - eors r7,r7,r0 - eor r11,r12,r0,ror#16 -.if ST_VPERM - bl gen_rand_sha_nonpres - ldr r1,=statevperm - movs r2,#0 - str r2,[r1] - bl addstatevperm @ Initialise state vperm with SHA RNG, refresh with LFSR RNG -.endif - pop {r12,r14} - CHK_CANARY r12,CTAG11,6 - bx r14 - -@ Conjugate lut_a, lut_b with shareC +@ Conjugate lut_a, lut_b with (state) shareC @ I.e., EOR the input and output with shareC. @ We need to pick one input for each share A and B, and one output for ONE of the shares A and B @ Arbitrarily choosing a0, b1 and d0 @@ -1653,36 +1624,57 @@ addrkey_s: .endif ctr_crypt_s: -@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks +@ r0=IV_shareA, r1=IV_shareB, r2=cipher/plaintext buffer, r3=number of blocks GET_CANARY r12,CTAG0,6 push {r0-r12,r14} @ save all registers so that when we restore we overwrite any secrets - push {r0-r2} + push {r0-r3} + SET_COUNT 93,6 .if CT_BPERM @ Initialise 32 random numbers (which fit in half-words) +@ r3=number of blocks ldr r4,=bperm_rand movs r5,#32 1: bl gen_rand_sha - umull r0,r3,r0,r2 @ Random number between 0 and n-1 (n=#blocks) - strh r3,[r4],#2 + umull r0,r2,r0,r3 @ Random number between 0 and n-1 (n=#blocks) + strh r2,[r4],#2 subs r5,r5,#1 bne 1b .endif bl randomisechaff - pop {r0-r2} + +@ Refresh IVshareA and IVshareB, convert to ror#16 format and store the result at IV0 +@ Not doing shareC or state vperm at this point + pop {r0} + ldmia r0,{r4-r7} @ r4-r7 = IVshareA + clear03 16 + pop {r1} + ldmia r1,{r8-r11} @ r8-r11 = IVshareB + clear03 32 + bl gen_rand_sha_nonpres; eors r4,r4,r0; mov r8, r8, ror#16; eor r8, r8, r0,ror#16 + bl gen_rand_sha_nonpres; eors r5,r5,r0; mov r9, r9, ror#16; eor r9, r9, r0,ror#16 + bl gen_rand_sha_nonpres; eors r6,r6,r0; mov r10,r10,ror#16; eor r10,r10,r0,ror#16 + bl gen_rand_sha_nonpres; eors r7,r7,r0; mov r11,r11,ror#16; eor r11,r11,r0,ror#16 + ldr r0,=IV0 + stmia r0,{r4-r7} + adds r0,r0,#20 + stmia r0,{r8-r11} + pop {r1,r2} +@ r1=cipher/plaintext buffer, r2=number of blocks + movs r3,#0 CHK_COUNT 93,6 ctr_crypt_mainloop: SET_COUNT 80,6 -@ here r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter +@ r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter @ Do as much preparatory stuff as possible that doesn't involve the IV (to reduce interaction with it) - push {r0-r3} + push {r1-r3} @ It's OK for execution time to depend on the block counter r3 ("public"), but not the block number (secret) tst r3,#(REFCHAFF_PERIOD-1) @@ -1690,7 +1682,7 @@ ctr_crypt_mainloop: bl refreshchaff_and_lfsr 1: - ldr r3,[r13,#12] @ get block count off the stack + ldr r3,[r13,#8] @ get block count off the stack tst r3,#(REMAP_PERIOD-1) bne 1f bl remap @ shuffle the LUTs; this preserves R3 @@ -1702,7 +1694,7 @@ ctr_crypt_mainloop: bl ref_roundkey_shares_s @ refresh the round key shares 1: - ldr r3,[r13,#12] @ get block count off the stack + ldr r3,[r13,#8] @ get block count off the stack tst r3,#(REFROUNDKEYHVPERMS_PERIOD-1) bne 1f bl ref_roundkey_hvperms_s @ refresh the round key vperms @@ -1710,13 +1702,13 @@ ctr_crypt_mainloop: CHK_COUNT 81,6 - pop {r0-r3} -@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter + pop {r1-r3} +@ r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter @ Now calculate r12 = block number-to-be-deciphered from r3 = block counter .if CT_BPERM @ Use a "swap-or-not" method to generate an "oblivious" permutation; see makeperm.py version 7 - push {r0,r1} + push {r1} ldr r0,=murmur3_constants ldmia r0,{r9-r12,r14} @ load five murmur3_32 hash constants ldr r0,=bperm_rand @@ -1752,57 +1744,53 @@ ctr_crypt_mainloop: adds r4,r4,r7 @ r4=j if top bit of r6, else i subs r1,r1,#1 bpl 1b - pop {r0,r1} + pop {r1} mov r12,r4 .else mov r12,r3 .endif CHK_COUNT 82,6 -@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter (monotonic), r12=block number (block to be deciphered) - push {r0-r3,r12} +@ r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter (monotonic), r12=block number (block to be deciphered) + push {r1-r3,r12} +@ r4-r11 = IV0, r12=block number processIV: @ non-target label to assist power analysis - -@ It is not clear if the following addition of the block number in r12 to the IV can usefully -@ be done in terms of shares. Instead we do an addition and subtraction whose overall effect -@ is the same, and which provides a small degree of masking. The IV is not traditionally a secret, -@ though it will make it harder for the attacker if it is obscured. - bl gen_rand_sha - movs r8,r0,lsr#16 @ only use 16 low bits so we don't get any overflows in the following, and so that a carry from the first word is rare - add r9,r8,r12 @ "masked" block number -@ r8=random, r9=(block number)+r8, stack=IV,... - - ldr r0,[r13] @ peek at stack to restore r0=IV ptr - ldmia r0,{r4-r7} @ load IV - clear03 @ barrier to remove traces of IV from internal CPU load registers - -@ Add in r9 in byte-big-endian, bit-little-endian (!) fashion, while trying to avoid rev operations -@ as far as possible as these tend to expose (via power fluctuations) byte-level hamming weights. -@ First do 128-bit addition of r9 to byte-reversed IV - rev r7,r7 - cmn r7,#MAX_NUM_BLOCKS @ Compare against maximum number of blocks - bcs 1f - add r7,r7,r9 @ This can temporarily overflow but it doesn't matter as we know that r7+r12 does not overflow - sub r7,r7,r8 - b 2f -1: - adds r7,r7,r9 - rev r6,r6; adcs r6,r6,#0 - rev r5,r5; adcs r5,r5,#0 - rev r4,r4; adcs r4,r4,#0 -@ Now do 128-bit subtraction of r8 from byte-reversed IV - subs r7,r7,r8 - sbcs r6,r6,#0; rev r6,r6 - sbcs r5,r5,#0; rev r5,r5 - sbcs r4,r4,#0; rev r4,r4 -2: - rev r7,r7 - clear01 16 + ldr r8,=IV0 + ldmia r8,{r4-r7} @ load IV0_A + clear03 16 + add r8,r8,#20 + ldmia r8,{r8-r11} @ load IV0_B + clear03 32 + rev r0,r12 + eor r7,r7,r0 @ XOR in block number to IV0. IV(block n) = IV0 ^ n, cf standard CTR mode IV0 + n. + @ XOR (vs addition) is compatible with XOR-shares, so stealthier/simpler because don't have to unshare to work out IV(block n) +@ r4-r11 = IV for the current block CHK_COUNT 83,6 +.if ST_SHAREC + bl gen_rand_sha_nonpres @ Create state share C; all bytes the same + ands r0,r0,#255 + orrs r0,r0,r0,lsl#8 + orrs r12,r0,r0,lsl#16 + ldr r1,=shareC + str r12,[r1] +.else + movs r12,#0 +.endif +@ r4-r11 = IV for the current block w/o shareC, r12=shareC +@ refresh state shares and mix in shareC + bl gen_rand_sha_nonpres; eors r4,r4,r0; eor r4,r4,r12; movs r1,#0; eor r8, r8, r0,ror#16 @ Barriers between shares to prevent implicit r4^r8 etc + bl gen_rand_sha_nonpres; eors r5,r5,r0; eor r5,r5,r12; movs r1,#0; eor r9, r9, r0,ror#16 + bl gen_rand_sha_nonpres; eors r6,r6,r0; eor r6,r6,r12; movs r1,#0; eor r10,r10,r0,ror#16 + bl gen_rand_sha_nonpres; eors r7,r7,r0; eor r7,r7,r12; movs r1,#0; eor r11,r11,r0,ror#16 +.if ST_VPERM + bl gen_rand_sha_nonpres + ldr r1,=statevperm + movs r2,#0 + str r2,[r1] + bl addstatevperm @ Initialise state vperm (use SHA RNG to start with, later refreshes are with LFSR RNG) +.endif -@ r4-r7 = IV for the current block - bl ns_to_s @ convert IV+x to shares, which includes choosing and incorporating a random shareC CHK_COUNT 84,6 bl conjshareC @ Add the effect of shareC to lut_a, lut_b CHK_COUNT 85,6 @@ -1849,9 +1837,9 @@ rounds_s_mainloop: bl addstatevperm .endif - pop {r0-r3,r12} - push {r0,r3} -@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter, r12=block to be deciphered + pop {r1-r3,r12} + push {r3} +@ r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter, r12=block to be deciphered decryption_start: @ Decrypt ciphertext using AES output in shares: r4-r11 @@ -1893,8 +1881,8 @@ decryption_start: sub r1,r1,r12,lsl#4 @ Restore r1 to point to start of buffer CHK_COUNT 90,6 - pop {r0,r3} @ Restore IV and block counter -@ r0=IV, r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter + pop {r3} @ Restore block counter +@ r1=cipher/plaintext buffer, r2=number of blocks, r3=block counter decryption_end: adds r3,r3,#1 diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 7f378039..009686d1 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -22,7 +22,7 @@ #include "config.h" -extern void decrypt(uint8_t* key4way, uint8_t* iv, uint8_t(*buf)[16], int nblk); +extern void decrypt(uint8_t* key4way, uint8_t* IV_OTPsalt, uint8_t* IV_public, uint8_t(*buf)[16], int nblk); // These just have to be higher than the actual frequency, to prevent overclocking unused peripherals #define ROSC_HZ 300*MHZ @@ -172,7 +172,16 @@ int main() { // Read key directly from OTP - guarded reads will throw a bus fault if there are any errors uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE; - decrypt((uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & (otp_key_page * 0x40))]), (uint8_t*)iv, (void*)data_start_addr, data_size/16); + decrypt( + (uint8_t*)&(otp_data[otp_key_page * 0x40]), + (uint8_t*)&(otp_data[(otp_key_page + 1) * 0x40]), + (uint8_t*)iv, + (void*)data_start_addr, + data_size/16 + ); + + // Lock the IV salt + otp_hw->sw_lock[otp_key_page + 1] = 0xf; // Increase stack limit by 0x100 pico_default_asm_volatile( diff --git a/main.cpp b/main.cpp index 869efc29..f964a2f7 100644 --- a/main.cpp +++ b/main.cpp @@ -402,8 +402,8 @@ struct multi_cmd : public cmd { }; struct _settings { - std::array filenames; - std::array file_types; + std::array filenames; + std::array file_types; uint32_t binary_start = FLASH_START; int bus=-1; int address=-1; @@ -801,7 +801,7 @@ struct encrypt_command : public cmd { option("--embed").set(settings.encrypt.embed) % "Embed bootloader in output file" + option("--fast-rosc").set(settings.encrypt.fast_rosc) % "Use ~180MHz ROSC configuration for embedded bootloader" + ( - option("--otp-key-page").set(settings.encrypt.otp_key_page_set) % "Specify the OTP page storing the AES key" & + option("--otp-key-page").set(settings.encrypt.otp_key_page_set) % "Specify the OTP page storing the AES key (IV salt is stored on the next page)" & integer("page").set(settings.encrypt.otp_key_page) % "OTP page (default 30)" ).force_expand_help(true) + ( @@ -815,8 +815,9 @@ struct encrypt_command : public cmd { ).force_expand_help(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + named_typed_file_selection_x("aes_key", 2, "bin") % "AES Key Share" + - optional_typed_file_selection_x("signing_key", 3, "pem") % "Signing Key file" + - optional_typed_file_selection_x("otp", 4, "json") % "File to save OTP to (will edit existing file if it exists)" + named_typed_file_selection_x("iv_otp", 3, "bin") % "IV OTP Salt" + + optional_typed_file_selection_x("signing_key", 4, "pem") % "Signing Key file" + + optional_typed_file_selection_x("otp", 5, "json") % "File to save OTP to (will edit existing file if it exists)" ); } @@ -4970,11 +4971,15 @@ bool encrypt_command::execute(device_map &devices) { fail(ERROR_ARGS, "Can only read AES key share from BIN file"); } - if (settings.seal.sign && settings.filenames[3].empty()) { + if (get_file_type_idx(3) != filetype::bin) { + fail(ERROR_ARGS, "Can only read IV OTP salt from BIN file"); + } + + if (settings.seal.sign && settings.filenames[4].empty()) { fail(ERROR_ARGS, "missing key file for signing after encryption"); } - if (!settings.filenames[3].empty() && get_file_type_idx(3) != filetype::pem) { + if (!settings.filenames[4].empty() && get_file_type_idx(4) != filetype::pem) { fail(ERROR_ARGS, "Can only read pem keys"); } @@ -4987,6 +4992,7 @@ bool encrypt_command::execute(device_map &devices) { if (aes_file->tellg() != 128) { fail(ERROR_INCOMPATIBLE, "The AES key share must be a 128 byte file (the supplied file is %d bytes)", aes_file->tellg()); } + aes_file->seekg(0, std::ios::beg); aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); aes_key_t aes_key; @@ -5001,7 +5007,18 @@ bool encrypt_command::execute(device_map &devices) { private_t private_key = {}; public_t public_key = {}; - if (settings.seal.sign) read_keys(settings.filenames[3], &public_key, &private_key); + if (settings.seal.sign) read_keys(settings.filenames[4], &public_key, &private_key); + + // Read IV Salt + auto iv_salt_file = get_file_idx(ios::in|ios::binary, 3); + iv_salt_file->exceptions(std::iostream::failbit | std::iostream::badbit); + uint8_t iv_salt[16]; + iv_salt_file->seekg(0, std::ios::end); + if (iv_salt_file->tellg() != 16) { + fail(ERROR_INCOMPATIBLE, "The IV OTP salt must be a 16 byte file (the supplied file is %d bytes)", iv_salt_file->tellg()); + } + iv_salt_file->seekg(0, std::ios::beg); + iv_salt_file->read((char*)iv_salt, sizeof(iv_salt)); if (isElf) { elf_file source_file(settings.verbose); @@ -5024,6 +5041,11 @@ bool encrypt_command::execute(device_map &devices) { uint32_t data_start_address = SRAM_START; encrypt_guts(elf, &new_block, aes_key, iv_data, enc_data); + // Salt IV + assert(iv_data.size() == sizeof(iv_salt)); + for (int i=0; i < iv_data.size(); i++) { + iv_data[i] ^= iv_salt[i]; + } auto tmp = std::make_shared(); auto file = get_enc_bootloader(); *tmp << file->rdbuf(); @@ -5040,7 +5062,7 @@ bool encrypt_command::execute(device_map &devices) { settings.config.value = hex_string(enc_data.size()); config_guts(program); // iv - for (int i=0; i < 4; i++) { + { string s((char*)iv_data.data(), iv_data.size()); settings.config.key = "iv"; settings.config.value = s; @@ -5145,18 +5167,18 @@ bool encrypt_command::execute(device_map &devices) { fail(ERROR_ARGS, "Must be ELF or BIN"); } - if (!settings.filenames[4].empty()) { - if (get_file_type_idx(4) != filetype::json) { + if (!settings.filenames[5].empty()) { + if (get_file_type_idx(5) != filetype::json) { fail(ERROR_ARGS, "Can only output OTP json"); } - auto check_json_file = std::ifstream(settings.filenames[4]); + auto check_json_file = std::ifstream(settings.filenames[5]); json otp_json; if (check_json_file.good()) { otp_json = json::parse(check_json_file); DEBUG_LOG("Appending to existing otp json\n"); check_json_file.close(); } - auto json_out = get_file_idx(ios::out, 4); + auto json_out = get_file_idx(ios::out, 5); // Add otp AES key page for (int i = 0; i < 128; ++i) { @@ -5166,11 +5188,22 @@ bool encrypt_command::execute(device_map &devices) { otp_json[ss.str()]["value"][i] = aes_key_share.bytes[i]; } + // Add otp IV salt page + for (int i = 0; i < sizeof(iv_salt); ++i) { + std::stringstream ss; + ss << settings.encrypt.otp_key_page + 1 << ":0"; + otp_json[ss.str()]["ecc"] = true; + otp_json[ss.str()]["value"][i] = iv_salt[i]; + } + // Add page locks to prevent BL and NS access, and only allow S reads { std::stringstream ss; ss << "PAGE" << settings.encrypt.otp_key_page << "_LOCK1"; otp_json[ss.str()] = "0x3d3d3d"; + ss.str(string()); + ss << "PAGE" << settings.encrypt.otp_key_page + 1 << "_LOCK1"; + otp_json[ss.str()] = "0x3d3d3d"; } *json_out << std::setw(4) << otp_json << std::endl; From aef73b184ba6ad73d68d392d55364f8298e31079 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 26 Feb 2025 13:53:27 +0000 Subject: [PATCH 42/80] Update enc_bootloader.elf with latest --- enc_bootloader/enc_bootloader.elf | Bin 19924 -> 19920 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/enc_bootloader/enc_bootloader.elf b/enc_bootloader/enc_bootloader.elf index b1a2f67b37b10c6b26bab846ca333e0bdd4f821a..b0ce0bfaf9c736febc66bc7df0435963962e37b2 100755 GIT binary patch delta 5075 zcmZvf4RjRM702()?q)a1lHGifkPkAO7?Kbo2>}fPvV;(`$zp;BT6zpNDAxFiwzg8M zXM=)9#AD*qx&^J!LkhIj0}UvpL4#VfT2Eu~oN8N(9`Tq&J>3{|^Tkg8cXoyua+p1D z-u&LZ@80|Fym{~K2fL+#-BOn#ec0_=nc(sexjfysi0ld7J8~Rh2T}jwgPT2$ zntYeiBJ#b9Jl=@SwwqEXY@@xCBcmN+qc>DNkm*vW!{a}T`r|R`r=w}HWnXNKWjQN` zBFz;;9xp{I#-vF1)PIA`5x+x?9udi@#f9Bddt=L;QY3N8(?^ke&r>8g*?g{}<@Ik| zj!<(c~fuJ-+b_s7Wh)Ufx) zlc>qPulM=PGD*5ayft&LXU%yv+B@}d{h6_29Vp_ytOBW2Je&1QF*{@@1Sjk;JyzqC zj=Gh118Vf-RI*P#qDEJUb=l>){`ej(RofnA(cF zJoOOns`x0oK)PLwXXiWLoOBW_iu_q4QiJg1tdfdx&lI=iY$_O>SmCeptI@L)wOakj z#7fk&Pf5%F_r4naR(zfFWDzHTV{Ai=M$DXy&a`-fzH5kz7jo~D{w1>V9t?SoY>(wT zj|Y`vL@~Z+(k5RvCYMXgqOkYYd8w~!FhVQ7@lyOX$$i~}RiMRz$A&90>e2Cf&{d5)hASJ@=vU*{fR;2pK3v)KLPH1aW>j|M!CXQg z74^iHIk(5W&XgnR5%NfI-g90S@8-?Qm3JF0+a`NrRnC+?`N-o9U4wK(r%>~L7?S(; z)%U~}VD_bW@xr!-FACT-lHm$p6!LQuJ0p({ld~sQ?Np<;k9#93wG5Mj5^M`#M)a%E zw(&auwqduOeDPte$FQ~!yB*|<53B_Pdt}(1K)xLzZEOt~+oQv7C;51EYScg85TL@O zHZ{5iK`(@(Zq$pM%@LB%j43DGU)JWizkJCHdXPMn?@yb}}e4V}C}Qtqs0$<%57 zmepvfI5Rt631i|!Frhk8^VijeNyL+jSAnlk4xhZ&-n*yBZ-1(EeV~OG4wNuT9mPw~ z6Ys#C>@)F<>+WlaI=`k*um~wG)U4l+a%UdOESGYN*jpO7zGl3g7UM0L|4YQKKSVGeM&~otzqFj?0JcK0&Xw!9J1Xr#&hDRG4&!r#DK~wPQtIalEXiEU977HQPtMo%AXxwnLEil zHgtI4f3);z1^?N?9K|UxjpFth%4w7mG-jbVQK-Yo=9Sqb8Y^Zif5~+zBPc!DE~N~r zd-)k9fae(~hfj7&LYdzc;y}5h4L`nW*cL7u*}2k!=h{Ztd2sPCxwaAZBwQwI{w?g9 zCQsCuXAnQwD2XdEztSfuVol}mr7OkxN`dF@s!%GYgZ)eJ`Vs0@;af0YS})ctC*RY&TPmZ2J<^MymQ7H zO#L~u6AIj5bYO*`sX)6R4~qAzmq^cx84H(7mxR~oW|FJKe6bqlj%Crc0j$uMkw$?r6B^k8_Wxu z!4H9rb%_JcgJ7Oz9&|xIPjoNJ^*x6tUe)3~lj$J%0Smt>-dnUh^Z{BoN^vb2AK_=< zS+dDP;0;M8N5NHYlWq7|K6jd&2L7qVo&r8<;d1cRx_x}~A*yBr7KZ`wps7Hcz;;{Q z0k0AvaLmHHz>?hz;4oO$nf2cTr&xFheE$UwC2MnY+l7W?Eu#l4?7*Bls556M9elaN z3}_Cx(!y2Xbvm>D)!_3M4uC&T&;t%Zf#>QeJ%t=G^V=5YSA5pOYhfVIWd?8?c%#nj za0~cgqM4CLz%S{q(6FDA9G8cThCIE2nH~BqybA`bybt`kJ_lI;74VN|m;s#x&rC7- zH28iCp9OyqXZY7goWmQhcEvN0H;oTiCiy4~7?+(!R1WxF$m;>I!EQ%Lr zh8(oy68-mC^#6@5gf}gGeAZ3lS#c@c${F>c z<1HK@Z&o~lpTj5roj9cPN^~#-N+kAl6FL|UuZ5gfC}tkOgW!FZN&Fc2pk>ni2Fxp- zq5lIg|6S=_>z^sx0^h6by|p$|?tP^0cmn=>t!OGpi4JjC{|C0buc!b3 delta 5051 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 Bv2g$Z From abb4e6323c0581fed8b140973b56985f0731e6c3 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 26 Feb 2025 13:50:35 +0000 Subject: [PATCH 43/80] Remove pico-sdk branch handling This will now fail CI checks until raspberrypi/pico-sdk#2233 is merged --- enc_bootloader/CMakeLists.txt | 10 +----- enc_bootloader/tmp_pico_sdk_import.cmake | 45 ------------------------ 2 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 enc_bootloader/tmp_pico_sdk_import.cmake diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 244ad30f..b8b8d498 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -17,15 +17,7 @@ if (NOT USE_PRECOMPILED) unset(ENV{LDFLAGS}) # Pull in SDK (must be before project) - - # Temporary import handling, to ensure SDK branch with PICO_MINIMAL_VECTOR_TABLE is present - file(READ ${PICO_SDK_PATH}/src/rp2_common/pico_crt0/crt0.S CRT0_S) - if (CRT0_S MATCHES "PICO_MINIMAL_VECTOR_TABLE") - include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) - else() - # This clones the will-v-pi/pico-sdk repo on the self-decrypting branch - include(tmp_pico_sdk_import.cmake) - endif() + include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) project(enc_bootloader C CXX ASM) set(CMAKE_C_STANDARD 11) diff --git a/enc_bootloader/tmp_pico_sdk_import.cmake b/enc_bootloader/tmp_pico_sdk_import.cmake deleted file mode 100644 index c7621184..00000000 --- a/enc_bootloader/tmp_pico_sdk_import.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# This is a modified copy of /external/pico_sdk_import.cmake to use a specific git repo and branch - -set(PICO_SDK_FETCH_FROM_GIT_URL "https://github.com/will-v-pi/pico-sdk") -set(PICO_SDK_FETCH_FROM_GIT_TAG "self-decrypting") - -include(FetchContent) -FetchContent_Declare( - pico_sdk - GIT_REPOSITORY ${PICO_SDK_FETCH_FROM_GIT_URL} - GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} -) - -if (NOT pico_sdk) - message("Downloading Raspberry Pi Pico SDK") - # GIT_SUBMODULES_RECURSE was added in 3.17 - if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") - FetchContent_Populate( - pico_sdk - QUIET - GIT_REPOSITORY ${PICO_SDK_FETCH_FROM_GIT_URL} - GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} - GIT_SUBMODULES_RECURSE FALSE - - SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src - BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build - SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild - ) - else () - FetchContent_Populate( - pico_sdk - QUIET - GIT_REPOSITORY ${PICO_SDK_FETCH_FROM_GIT_URL} - GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} - - SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src - BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build - SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild - ) - endif () - - set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) -endif () - - -include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) From e96ff1cf669a410665ac82469821f8326fdea402 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 26 Feb 2025 15:46:07 +0000 Subject: [PATCH 44/80] Update aes.S From commit d831bb59 --- enc_bootloader/aes.S | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index 2014b5fd..962ecabb 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -278,31 +278,39 @@ init_rstate: str r1,[r4,#TRNG_TRNG_CONFIG_OFFSET -TRNG_RNG_IMR_OFFSET] @ turn off rand source and wipe SHA bits left in TRNG config; r1=0 str r1,[r4,#TRNG_RND_SOURCE_ENABLE_OFFSET -TRNG_RNG_IMR_OFFSET] adds r5,r5,#SHA256_SUM0_OFFSET - ldmia r5!,{r0-r3} - ldr r5,=rstate_sha - stmia r5,{r0-r3} +@ r5=SHA256 SUM0 register (r5+4=SUM1, r4+8=SUM2, etc) + ldmia r5,{r0-r3} @ load first 4 words of the 8 word SHA256 output + ldr r6,=rstate_sha +@ r5=SHA256 SUM0 register (r5+4=SUM1, r4+8=SUM2, etc), r6=rstate_sha + stmia r6,{r0-r3} CHK_COUNT 26,6 - -@ r5=rstate_sha movs r0,#0 - strb r0,[r5] @ make sure rstate_sha[0] has byte 0 set to 0, representing "out of data" -@ try to find a non-zero initialiser to create a non-degenerate LFSR - ldr r1,[r5,#4] - cbnz r1,1f @ is word 1 non-zero? then use it - ldr r1,[r5,#8] - cbnz r1,1f @ otherwise, is word 2 non-zero? use it - ldr r1,[r5,#12] - cbnz r1,1f @ otherwise, is word 3 non-zero? use it - mov r1,r5 @ give up and use the address of rstate_sha (which is non-zero); this can't really happen (2^{-96} probability) + strb r0,[r6] @ make sure rstate_sha[0] has byte 0 set to 0, representing "out of data" + +@ try to find a non-zero initialiser to create a non-degenerate LFSR random state + ldr r1,[r5,#16] @ SHA SUM4 + cbnz r1,1f @ is word 4 non-zero? then use it + ldr r1,[r5,#20] @ SHA SUM5 + cbnz r1,1f @ otherwise, is word 5 non-zero? use it + mov r1,r6 @ give up and use the address of rstate_sha (which is non-zero); this can't really happen (2^{-64} probability) +1: + str r1,[r6,#rstate_lfsr-rstate_sha] + +@ try to find a non-zero initialiser to create a non-degenerate ROSC random state + ldr r1,[r5,#24] @ SHA SUM6 + cbnz r1,1f @ is word 6 non-zero? then use it + ldr r1,[r5,#28] @ SHA SUM7 + cbnz r1,1f @ otherwise, is word 7 non-zero? use it + mov r1,r6 @ give up and use the address of rstate_sha (which is non-zero); this can't really happen (2^{-64} probability) 1: - str r1,[r5,#rstate_lfsr-rstate_sha] ldr r2,=ROSC_RANDOM_OFFSET+ROSC_BASE - str r1,[r2,#0] + str r1,[r2,#0] @ Initialise ROSC LFSR CHK_COUNT 27,6 + .if GEN_RAND_SHA .if SH_JITTER movs r2,#0 - str r2,[r5,#jstate-rstate_sha] + str r2,[r6,#jstate-rstate_sha] .endif .endif From c29bad5eb12b902738925d364f2eb6de4c5d4cec Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 26 Feb 2025 18:16:09 +0000 Subject: [PATCH 45/80] Add USE_MBEDTLS variant This variant runs from xip_sram, and uses the MbedTLS AES code instead. This is much faster, but not secure against side channel attacks. Currently no way to enable it, but that can be added if it's going to be useful --- enc_bootloader/CMakeLists.txt | 29 ++- enc_bootloader/enc_bootloader.c | 2 +- enc_bootloader/mbedtls_aes.c | 73 +++++++ enc_bootloader/mbedtls_config.h | 10 + enc_bootloader/memmap_enc_bootloader.ld | 4 + enc_bootloader/memmap_mbedtls.ld | 263 ++++++++++++++++++++++++ main.cpp | 7 +- 7 files changed, 379 insertions(+), 9 deletions(-) create mode 100644 enc_bootloader/mbedtls_aes.c create mode 100644 enc_bootloader/mbedtls_config.h create mode 100644 enc_bootloader/memmap_mbedtls.ld diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index b8b8d498..7ae2b200 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -33,19 +33,39 @@ if (NOT USE_PRECOMPILED) # Encrypted Bootloader add_executable(enc_bootloader enc_bootloader.c - aes.S ) target_link_libraries(enc_bootloader pico_stdlib ) + if (USE_MBEDTLS) + target_sources(enc_bootloader PRIVATE mbedtls_aes.c) + + target_link_libraries(enc_bootloader pico_mbedtls) + + target_compile_definitions(enc_bootloader PRIVATE + PICO_STACK_SIZE=0x800 + # 0x20080000 -> 0x20081000 doesn't overlap the stack + ROM_CHAIN_WORKSPACE=0x20080000) + + target_include_directories(enc_bootloader PRIVATE ${CMAKE_CURRENT_LIST_DIR}) + + pico_set_linker_script(enc_bootloader ${CMAKE_CURRENT_LIST_DIR}/memmap_mbedtls.ld) + else() + target_sources(enc_bootloader PRIVATE aes.S) + + target_compile_definitions(enc_bootloader PRIVATE + PICO_STACK_SIZE=0x180 + # AES Code & workspace from 0x20080044 -> 0x20081604, so 0x20080200 -> 0x20081200 is inside that + ROM_CHAIN_WORKSPACE=0x20080200) + + pico_set_linker_script(enc_bootloader ${CMAKE_CURRENT_LIST_DIR}/memmap_enc_bootloader.ld) + endif() + target_compile_definitions(enc_bootloader PRIVATE - # increase startup delay for stability - PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64 # use stack guards, as AES variables are written near the stack PICO_USE_STACK_GUARDS=1 - PICO_STACK_SIZE=0x180 # The following are to reduce the size of the binary PICO_NO_PROGRAM_INFO=1 # No spinlocks used @@ -66,7 +86,6 @@ if (NOT USE_PRECOMPILED) pico_minimize_runtime(enc_bootloader) pico_set_binary_type(enc_bootloader no_flash) - pico_set_linker_script(enc_bootloader ${CMAKE_CURRENT_LIST_DIR}/memmap_enc_bootloader.ld) pico_add_dis_output(enc_bootloader) else() project(enc_bootloader C CXX ASM) diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 009686d1..1a7aa141 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -192,7 +192,7 @@ int main() { // Chain into decrypted image rom_chain_image( - (uint8_t*)0x20080200, // AES Code & workspace from 0x20080030 -> 0x20081500 + (uint8_t*)ROM_CHAIN_WORKSPACE, 4 * 1024, data_start_addr, data_size diff --git a/enc_bootloader/mbedtls_aes.c b/enc_bootloader/mbedtls_aes.c new file mode 100644 index 00000000..2f072ca4 --- /dev/null +++ b/enc_bootloader/mbedtls_aes.c @@ -0,0 +1,73 @@ +#include +#include "pico/stdlib.h" + +extern void lock_key(); + +int mb_aes_crypt_ctr_xor(mbedtls_aes_context *ctx, + size_t length, + unsigned char iv0[16], + unsigned char nonce_xor[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output) +{ + int c; + int ret = 0; + size_t n = 0; + uint32_t counter = 0; + + assert(length == (uint32_t)length); + + while (length--) { + if (n == 0) { + for (int i = 16; i > 0; i--) { + nonce_xor[i-1] = iv0[i-1]; + if (i - (int)(16 - sizeof(counter)) > (int)0) { + nonce_xor[i-1] ^= (unsigned char)(counter >> ((16-i)*8)); + } + } + + ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, nonce_xor, stream_block); + if (ret != 0) { + break; + } + counter++; + } + c = *input++; + *output++ = (unsigned char) (c ^ stream_block[n]); + + n = (n + 1) & 0x0F; + } + + return ret; +} + +void decrypt(uint8_t* key4way, uint8_t* IV_OTPsalt, uint8_t* IV_public, uint8_t(*buf)[16], int nblk) { + mbedtls_aes_context aes; + + uint32_t aes_key[8]; + uint32_t* key4waywords = (uint32_t*)key4way; + // Key is stored as a 4-way share of each word, ie X[0] = A[0] ^ B[0] ^ C[0] ^ D[0], stored as A[0], B[0], C[0], D[0] + for (int i=0; i < count_of(aes_key); i++) { + aes_key[i] = key4waywords[i*4] + ^ key4waywords[i*4 + 1] + ^ key4waywords[i*4 + 2] + ^ key4waywords[i*4 + 3]; + } + + uint8_t iv[16]; + for (int i=0; i < sizeof(iv); i++) { + iv[i] = IV_OTPsalt[i] ^ IV_public[i]; + } + + int len = nblk * 16; + + mbedtls_aes_setkey_enc(&aes, (uint8_t*)aes_key, 256); + + lock_key(); + + uint8_t xor_working_block[16] = {0}; + uint8_t stream_block[16] = {0}; + size_t nc_off = 0; + mb_aes_crypt_ctr_xor(&aes, len, (uint8_t*)iv, xor_working_block, stream_block, (uint8_t*)buf, (uint8_t*)buf); +} diff --git a/enc_bootloader/mbedtls_config.h b/enc_bootloader/mbedtls_config.h new file mode 100644 index 00000000..87486cfc --- /dev/null +++ b/enc_bootloader/mbedtls_config.h @@ -0,0 +1,10 @@ +#ifndef _MBEDTLS_CONFIG_H +#define _MBEDTLS_CONFIG_H + +#define MBEDTLS_HAVE_ASM +#define MBEDTLS_AES_C +#define MBEDTLS_AES_ROM_TABLES +// #define MBEDTLS_AES_FEWER_TABLES +#define MBEDTLS_CIPHER_MODE_CTR + +#endif diff --git a/enc_bootloader/memmap_enc_bootloader.ld b/enc_bootloader/memmap_enc_bootloader.ld index 439a95d9..6b08c5b3 100644 --- a/enc_bootloader/memmap_enc_bootloader.ld +++ b/enc_bootloader/memmap_enc_bootloader.ld @@ -257,4 +257,8 @@ SECTIONS /* todo assert on extra code */ ASSERT(chaff==0x20081000, "Chaff array must be located at 0x20081000") + + /* Provide symbols for picotool */ + __enc_bootloader_start = ORIGIN(RAM_START); + __enc_bootloader_end = ORIGIN(RAM) + LENGTH(RAM); } diff --git a/enc_bootloader/memmap_mbedtls.ld b/enc_bootloader/memmap_mbedtls.ld new file mode 100644 index 00000000..261e336b --- /dev/null +++ b/enc_bootloader/memmap_mbedtls.ld @@ -0,0 +1,263 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + RAM(rwx) : ORIGIN = 0x13ffc000, LENGTH = 16k + SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* Note unlike RP2040, we start the image with a vector table even for + NO_FLASH builds. On Arm, the bootrom expects a VT at the start of the + image by default; on RISC-V, the default is to enter the image at its + lowest address, so an IMAGEDEF item is required to specify the + nondefault entry point. */ + + .start_text : { + __logical_binary_start = .; + /* Vectors require 512-byte alignment on v8-M when >48 IRQs are used, + so we would waste RAM if the vector table were not at the + start. */ + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + } > RAM + + .text : { + __reset_start = .; + KEEP (*(.reset)) + __reset_end = .; + *(.time_critical*) + *(.text*) + . = ALIGN(4); + *(.init) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + } > RAM + + .rodata : { + . = ALIGN(4); + *(.rodata*) + *(.srodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > RAM + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > RAM + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > RAM + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > RAM + __binary_info_end = .; + . = ALIGN(4); + + .data : { + __data_start__ = .; + *(vtable) + *(.data*) + *(.sdata*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM + PROVIDE(__data_end__ = .); + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + PROVIDE(__global_pointer$ = . + 2K); + *(.sbss*) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + } > RAM + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary") + ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") + + /* todo assert on extra code */ + + /* Provide symbols for picotool */ + __enc_bootloader_start = ORIGIN(RAM); + __enc_bootloader_end = ORIGIN(RAM) + LENGTH(RAM); +} + diff --git a/main.cpp b/main.cpp index f964a2f7..dd47f1a9 100644 --- a/main.cpp +++ b/main.cpp @@ -5090,11 +5090,12 @@ bool encrypt_command::execute(device_map &devices) { enc_elf->read_file(tmp); // Bootloader size - auto bootloader_txt = enc_elf->get_section(".start_text"); - uint32_t bootloader_size = 0x20082000 - bootloader_txt->virtual_address(); + auto bootloader_start = enc_elf->get_symbol("__enc_bootloader_start"); + auto bootloader_end = enc_elf->get_symbol("__enc_bootloader_end"); + uint32_t bootloader_size = bootloader_end - bootloader_start; // Move bootloader down in physical space to start of SRAM (which will be start of flash once packaged) - enc_elf->move_all(data_start_address - bootloader_txt->virtual_address()); + enc_elf->move_all(data_start_address - bootloader_start); // Add encrypted blob enc_elf->append_segment(data_start_address, data_start_address + bootloader_size, enc_data.size(), ".enc_data"); From 7080fdf1b82b5515d874b689589b2f04812c29df Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 4 Mar 2025 13:17:50 +0000 Subject: [PATCH 46/80] Update with latest aes.S From commit 6e6c8ee --- enc_bootloader/aes.S | 17 +++++++++++++---- enc_bootloader/config.h | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index 962ecabb..093c4b0f 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -1663,14 +1663,23 @@ ctr_crypt_s: pop {r1} ldmia r1,{r8-r11} @ r8-r11 = IVshareB clear03 32 - bl gen_rand_sha_nonpres; eors r4,r4,r0; mov r8, r8, ror#16; eor r8, r8, r0,ror#16 - bl gen_rand_sha_nonpres; eors r5,r5,r0; mov r9, r9, ror#16; eor r9, r9, r0,ror#16 - bl gen_rand_sha_nonpres; eors r6,r6,r0; mov r10,r10,ror#16; eor r10,r10,r0,ror#16 - bl gen_rand_sha_nonpres; eors r7,r7,r0; mov r11,r11,ror#16; eor r11,r11,r0,ror#16 + bl gen_rand_sha_nonpres; eors r4,r4,r0; movs r1,#0; mov r8, r8, ror#16; eor r8, r8, r0,ror#16 @ Barriers between shares to prevent implicit r4^r8 etc + bl gen_rand_sha_nonpres; eors r5,r5,r0; movs r1,#0; mov r9, r9, ror#16; eor r9, r9, r0,ror#16 + bl gen_rand_sha_nonpres; eors r6,r6,r0; movs r1,#0; mov r10,r10,ror#16; eor r10,r10,r0,ror#16 + bl gen_rand_sha_nonpres; eors r7,r7,r0; movs r1,#0; mov r11,r11,ror#16; eor r11,r11,r0,ror#16 ldr r0,=IV0 stmia r0,{r4-r7} adds r0,r0,#20 stmia r0,{r8-r11} +@ "Decommission" IV0 so that it doesn't get stacked + bl gen_rand_sha_nonpres; movs r4,r0 + bl gen_rand_sha_nonpres; movs r5,r0 + bl gen_rand_sha_nonpres; movs r6,r0 + bl gen_rand_sha_nonpres; movs r7,r0 + bl gen_rand_sha_nonpres; mov r8,r0 + bl gen_rand_sha_nonpres; mov r9,r0 + bl gen_rand_sha_nonpres; mov r10,r0 + bl gen_rand_sha_nonpres; mov r11,r0 pop {r1,r2} @ r1=cipher/plaintext buffer, r2=number of blocks diff --git a/enc_bootloader/config.h b/enc_bootloader/config.h index b4e28544..1573fbff 100644 --- a/enc_bootloader/config.h +++ b/enc_bootloader/config.h @@ -51,7 +51,7 @@ #endif #ifndef CK_JITTER -#define CK_JITTER 1 // occasionally switch CPU clock to ROSC for extra timing variability +#define CK_JITTER 1 // Use the ROSC clock to make ARM timings unpredictable #endif From 8e9eabad778007a3265750671686822c4ec1490c Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 4 Mar 2025 14:33:19 +0000 Subject: [PATCH 47/80] Better fix for #210 Instead of emptying the range_map, just overwrite overlapping entries, like the bootrom would do when loading the load_map --- main.cpp | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/main.cpp b/main.cpp index dd47f1a9..4f618708 100644 --- a/main.cpp +++ b/main.cpp @@ -246,8 +246,39 @@ template struct range_map { } } - void empty() { - m.clear(); + void insert_overwrite(const range& r, T t) { + if (r.to != r.from) { + assert(r.to > r.from); + // insert overlapping entry, and overwrite any it overlaps + + // avoid modifying m while iterating through it + vector to_erase; + vector>> to_add; + + auto f = m.upper_bound(r.from); // first element that starts after r.from + if (f != m.begin()) f--; // back up, to catch element that starts on or before r.from + for(; f != m.end() && f->first < r.to; f++) { // loop till we can't possibly overlap + range r2(f->first, f->second.first); + T r2off = f->second.second; + if (r2.intersects(r)) { + // remove existing r2 + to_erase.push_back(r2.from); + if (r2.from < r.from) { + // add r2 which ends at start of r + to_add.push_back(std::make_pair(r2.from, std::make_pair(r.from, r2off))); + } + if (r2.to > r.to) { + // add r2 which starts at end of r + to_add.push_back(std::make_pair(r.to, std::make_pair(r2.to, r2off + (r.to - r2.from)))); + } + } + } + for (auto k : to_erase) m.erase(k); + for (auto v : to_add) m.insert(v); + + // finally, add the new entry + m.insert(std::make_pair(r.from, std::make_pair(r.to, t))); + } } pair get(uint32_t p) { @@ -2808,9 +2839,8 @@ void build_rmap_load_map(std::shared_ptrload_map, range_map Date: Tue, 4 Mar 2025 15:40:07 +0000 Subject: [PATCH 48/80] Add --use-mbedtls option to use mbedtls version of the decryption stage --- CMakeLists.txt | 71 ++++++++++++++++++---- enc_bootloader.cpp | 16 +++-- enc_bootloader.h | 2 +- enc_bootloader/CMakeLists.txt | 6 +- enc_bootloader/enc_bootloader.elf | Bin 19920 -> 19996 bytes enc_bootloader/enc_bootloader_mbedtls.elf | Bin 0 -> 28432 bytes main.cpp | 4 +- 7 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 enc_bootloader/enc_bootloader_mbedtls.elf diff --git a/CMakeLists.txt b/CMakeLists.txt index d6041931..d492e15a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,14 +57,18 @@ endif() list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) +# To configure mbedtls +# todo make the configuration better +set(MBEDTLS_CONFIG_FILE "mbedtls_config.h") +add_compile_options(-I${CMAKE_SOURCE_DIR}/lib/include) + +add_subdirectory(lib) + if (NOT DEFINED USE_PRECOMPILED) set(USE_PRECOMPILED true) endif() # compile enc_bootloader.elf -if (NOT DEFINED USE_PRECOMPILED) - set(USE_PRECOMPILED true) -endif() ExternalProject_Add(enc_bootloader PREFIX enc_bootloader SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/enc_bootloader @@ -73,6 +77,7 @@ ExternalProject_Add(enc_bootloader "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}" "-DPICO_SDK_PATH:FILEPATH=${PICO_SDK_PATH}" "-DUSE_PRECOMPILED:BOOL=${USE_PRECOMPILED}" + "-DUSE_MBEDTLS=0" "-DPICO_DEBUG_INFO_IN_RELEASE=OFF" BUILD_ALWAYS 1 # todo remove this INSTALL_COMMAND "" @@ -88,6 +93,32 @@ add_custom_command(TARGET enc_bootloader DEPENDS enc_bootloader ) +if (TARGET mbedtls) + ExternalProject_Add(enc_bootloader_mbedtls + PREFIX enc_bootloader_mbedtls + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/enc_bootloader + BINARY_DIR ${CMAKE_BINARY_DIR}/enc_bootloader_mbedtls + CMAKE_ARGS + "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}" + "-DPICO_SDK_PATH:FILEPATH=${PICO_SDK_PATH}" + "-DUSE_PRECOMPILED:BOOL=${USE_PRECOMPILED}" + "-DUSE_MBEDTLS=1" + "-DPICO_DEBUG_INFO_IN_RELEASE=OFF" + BUILD_ALWAYS 1 # todo remove this + INSTALL_COMMAND "" + ) + + set(ENC_BOOTLOADER_MBEDTLS_ELF ${CMAKE_BINARY_DIR}/enc_bootloader_mbedtls/enc_bootloader.elf) + add_executable(enc_bootloader_mbedtls_elf IMPORTED) + add_dependencies(enc_bootloader_mbedtls_elf enc_bootloader_mbedtls) + set_property(TARGET enc_bootloader_mbedtls_elf PROPERTY IMPORTED_LOCATION ${ENC_BOOTLOADER_MBEDTLS_ELF}) + # copy enc_bootloader_mbedtls.elf into build directory + add_custom_command(TARGET enc_bootloader_mbedtls + COMMAND ${CMAKE_COMMAND} -E copy ${ENC_BOOTLOADER_MBEDTLS_ELF} ${CMAKE_BINARY_DIR}/enc_bootloader_mbedtls.elf + DEPENDS enc_bootloader_mbedtls + ) +endif() + if (NOT PICOTOOL_NO_LIBUSB) # compile xip_ram_perms.elf ExternalProject_Add(xip_ram_perms @@ -197,8 +228,14 @@ if (NOT PICOTOOL_NO_LIBUSB) endif() endif() -add_custom_target(binary_data_no_libusb DEPENDS - ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h) +if (TARGET mbedtls) + add_custom_target(binary_data_no_libusb DEPENDS + ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h + ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_mbedtls_elf.h) +else() + add_custom_target(binary_data_no_libusb DEPENDS + ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h) +endif() add_custom_target(binary_data DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h @@ -227,6 +264,14 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h DEPENDS enc_bootloader COMMENT "Configuring enc_bootloader_elf.h" VERBATIM) +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_mbedtls_elf.h + COMMAND ${CMAKE_COMMAND} + -D BINARY_FILE=${ENC_BOOTLOADER_MBEDTLS_ELF} + -D OUTPUT_NAME=enc_bootloader_mbedtls_elf + -P ${CMAKE_CURRENT_LIST_DIR}/cmake/binh.cmake + DEPENDS enc_bootloader_mbedtls + COMMENT "Configuring enc_bootloader_mbedtls_elf.h" + VERBATIM) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/flash_id_bin.h COMMAND ${CMAKE_COMMAND} -D BINARY_FILE=${FLASH_ID_BIN} @@ -242,13 +287,6 @@ add_subdirectory(picoboot_connection) add_subdirectory(elf) add_subdirectory(elf2uf2) -# To configure mbedtls -# todo make the configuration better -set(MBEDTLS_CONFIG_FILE "mbedtls_config.h") -add_compile_options(-I${CMAKE_SOURCE_DIR}/lib/include) - -add_subdirectory(lib) - add_subdirectory(bintool) if (NOT PICOTOOL_NO_LIBUSB) @@ -375,6 +413,15 @@ install(FILES DESTINATION ${INSTALL_DATADIR} ) +if (TARGET mbedtls) + #Install enc_bootloader_mbedtls.elf + install(FILES + ${ENC_BOOTLOADER_MBEDTLS_ELF} + DESTINATION ${INSTALL_DATADIR} + RENAME enc_bootloader_mbedtls.elf + ) +endif() + if (NOT PICOTOOL_NO_LIBUSB) if (NOT PICOTOOL_CODE_OTP) #Install the otp json diff --git a/enc_bootloader.cpp b/enc_bootloader.cpp index 21b39333..5b28d6f6 100644 --- a/enc_bootloader.cpp +++ b/enc_bootloader.cpp @@ -7,13 +7,14 @@ #include "enc_bootloader.h" #include "enc_bootloader_elf.h" +#include "enc_bootloader_mbedtls_elf.h" #include "data_locs.h" #include "whereami++.h" -std::shared_ptr get_enc_bootloader() { +std::shared_ptr get_enc_bootloader(bool use_mbedtls) { // search same directory as executable whereami::whereami_path_t executablePath = whereami::getExecutablePath(); std::string local_loc = executablePath.dirname() + "/"; @@ -22,7 +23,10 @@ std::shared_ptr get_enc_bootloader() { } for (auto loc : data_locs) { - std::string filename = loc + "enc_bootloader.elf"; + std::string filename = loc + + "enc_bootloader" + + (use_mbedtls ? "_mbedtls" : "") + + ".elf"; std::ifstream i(filename); if (i.good()) { printf("Picking file %s\n", filename.c_str()); @@ -32,8 +36,12 @@ std::shared_ptr get_enc_bootloader() { } // fall back to embedded enc_bootloader.elf file - printf("Could not find enc_bootloader.elf file - using embedded binary\n"); + printf("Could not find enc_bootloader%s.elf file - using embedded binary\n", use_mbedtls ? "_mbedtls" : ""); auto tmp = std::make_shared(); - tmp->write(reinterpret_cast(enc_bootloader_elf), enc_bootloader_elf_SIZE); + if (use_mbedtls) { + tmp->write(reinterpret_cast(enc_bootloader_mbedtls_elf), enc_bootloader_mbedtls_elf_SIZE); + } else { + tmp->write(reinterpret_cast(enc_bootloader_elf), enc_bootloader_elf_SIZE); + } return tmp; } diff --git a/enc_bootloader.h b/enc_bootloader.h index dc942634..07684be7 100644 --- a/enc_bootloader.h +++ b/enc_bootloader.h @@ -9,4 +9,4 @@ #include #include -std::shared_ptr get_enc_bootloader(); +std::shared_ptr get_enc_bootloader(bool use_mbedtls = false); diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 7ae2b200..257b27dd 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -90,7 +90,11 @@ if (NOT USE_PRECOMPILED) else() project(enc_bootloader C CXX ASM) message("Using precompiled enc_bootloader.elf") - configure_file(${CMAKE_CURRENT_LIST_DIR}/enc_bootloader.elf ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader.elf COPYONLY) + if (USE_MBEDTLS) + configure_file(${CMAKE_CURRENT_LIST_DIR}/enc_bootloader_mbedtls.elf ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader.elf COPYONLY) + else() + configure_file(${CMAKE_CURRENT_LIST_DIR}/enc_bootloader.elf ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader.elf COPYONLY) + endif() # Use manually specified variables set(NULL ${CMAKE_MAKE_PROGRAM}) set(NULL ${PICO_SDK_PATH}) diff --git a/enc_bootloader/enc_bootloader.elf b/enc_bootloader/enc_bootloader.elf index b0ce0bfaf9c736febc66bc7df0435963962e37b2..d7eb8e8a8277f03900b3ddef276a5252bd64675b 100755 GIT binary patch delta 3187 zcmZ9Oe{5S<701tW?AT7d*lv^lpg-PoylVN;E>6;o{48FVx=G`VdOuL0Oik7V8d#IH z%UUrZ?9q1XprA~*Mb*|noM{nMYax}#rj8*JQM3y7qa^~VLNF#3wHP5XA@uT^&X4bT z_KUB4t^4l#+;h*l=bZcF`PysBxpT^-=D4@-m6{!{Za-1?E2AB*w2SEKrSs!{S9GIC z8^~-7)G5l5%&wr}^?b5e7Kepy-T-$8wpBw3=GRM7(js~Smzi#Tk z6HjaiCrzU}N}(@jTYFAsBR!_B8#^s->wP&J={5EL7%_|6cmE(8*=_2#j9nIY^!+d! z>3g#$b$fxTBB|TtH}xBalT7{3h6~Smqa4p`h8xchja1Cke{Z~i=ljO@@w{Rjip?pZq2McvtxovJ!e^%VHY271ZIaA+$*VHGL=v8;U^N*(9V6-7ctCyVbDb1Dj z&TG!MO#L$G(_7QI2i>16-h?-g<)(9AgpFOW)#~2bTXA5|4pYB-M@(!wTz6qnxpHC0 zo%z-AgUHHsfdX;T4#rq=cm9Rk8Yov}{OWVg%xw6qvwDD+95s#FP5F9K9IOoox#ueC za~nL`!F3*OTujyW?(H-hVl}Z)%&E~&XyE}+DCVf~Sag4ZycXS87+G$SRv@=U>kP-t zGry+a9DscyB}(ba=&;yXrz(SDr0y3gdo+leO09~19qrRR&f(Ff5VRWCL`(g{EoW}~ z$^XcUcuwWo-K0gHsT&Pm9vxLKH^mMOoyYq>t#|kC;NVoQ!%a2KVygZfB`v<%u-}z} z#k81f_}Im9rp1{pu_VuwMzkv^U)YNou&==E#Vj}ow#RS;?@Btu;NMEjQG5iZB|QFx z`WMtjbgHO+R7$zoytawN*S2obK3wn74Akkh9<2#O&*S|fY69=osF&taiqIM-lk6xf zmf^=G%C=C-$jJ?{^W2sZjyDR2$!!^xb0e`ezr>yrxz}Re3j9(=72ISRBcCckj-IDe zFv$(H!y?q&h&4|m0OMDX?@E7GA!&gdppoC|>V2^juf1M;gZVe`8rM|ncIR}0NwjB7e%|UEJG$;J*OL88c7ENucazrHB)TUFVGKpFDXA-mSq?l?`AG#p*E?`sgF>pZ2 z9a?P~IalHQvVvz0}O*@~}lu6Q1nS4qtBEfTXlA~DN*B!;|`_DCH( z;MDR~$ZxolC48>P;?4HW%2}aws0vP2huZWl$revOR>Bh!^IWIJp$;`^AAR29qOf!j zqPXa%CHyWpRrJ7Gdi$!e4?mw&OTBOz@JW@KPpZVt)=|%QpUeQ?HgvSiZ3W()Sa0J5|Qb*Vwwf)_imNhG@V1vx!9AQXgO|HkF{BQe+2P~8GK zU(mcPY~_ZR$Pmi*^~vWUv2V`+657fau9lBDKtAec4itGWcm)2eL^u0-6r5g;#YM-# zMSEVF1Upw3^|KjB3E9fyEzsw3f#jM$KABCVeE;;;T zILM#n84O!V-*nJ86p9(}63_H)DA*G|5BXUsqVwQ4mK}7^FTt1KAa5DY*hk>w()bhL zL?#j4>0UW9wtwGKaylb@9T4NA9|j=zuN eCC2(|T;Gin{ZE|f53fD68`m+SunWF$R{1}cW?!WM delta 3039 zcmZ9O4Qx|Y6vxl&Ue^{1YaK&@ZLeJ=`x3V?#g1*H3@Gi^>6Vy?aVVh37Da^kfnVto zKtV?`yv8&tkr@^tkqFj^LyW`Es4)&PekVvY5ep_;Fh06Lpa0YLtv7wm?d$KJd(Qpe z^X|E?uf3*>y{Zg)EI+mHt7x;@i-_#|`kJjVE79n&!&{21{xZ9#D_-WRR+LBLtKCV5 z`d>)z5TON+DZhx17VNn8M8vbu(2eAQsRGOOmo2qQtA5(hzYuqp`l8C@%D`<2e`t52 zK4j>V$yK1H@Sa3{*w8N}gP^7D&nN2J4gF$rHK@7cg+zVF>!JSh<20wf|2!2Ho+RIJ zs@1kD<+9Cww|Jb26ht4sYI(JIydeF1|KxD0-ey{^OtK|2YNu!{JEA-w&Xtu!*Ckg5 zb|#!Ec{5tpK+CR#(@Nfqnl7k$HsLHJZ$?iDdY(%-ZRBM|h8{?U!c;M1ouPM64X19h z8TvllZ?@ewPL{KmJ*S)(+De@lK6a|_8u~!W(0i^J`ioQaimgWd&d|fn$tEoMrYZHL zvUqllI>y6(1fBb9IJLxf_VS;jhQ2j5oVo>pHzKg#wkSNKx2w(2=TC{F<=cwBx~zQq zR@>y6xslC5L;plHX!De+c!&0=x^Ju{KI)69C0+ca&|+vSDbpfibCu7{J)>xwOVQ@p zJ)6W(RroG{vNl)|^afQAy-ec`AaBr8VNX*{mKK3a$P zL`r}T%ntO3Rn?lZMr^EpTVq9Q(E9s}1GnRTACI%xJwsSyS)^Pv)~u*4`es(=uJc7S zlv-*dPu0VHz21?-z3!2}dzF#uVBdOMyL;VGs@X;rHR5p1aU~|!*WPR0jz}@_W$m|C zUSCY?zabdq8Dd!FOv+q5`ysG1%RKx0U^mzt!^+>0e0sniNX+U#2h$~d{egA{trDHr zqS?@>-^SrR#ND))T0!0wqq8@|r&sm9PD{*4yK?^$TaPVH8Nr*1IHLfNqZAHMt? z3s(WDd?Oam_X0vS;^JlUy?_e&CUJUxiS2oIN1AyH@Q(tT!MAu_{W-l0hNKYe1}R)U2;yR3v`7F=|m7;@wLOCHk?`R5Fu}L&COMIX(rRJ3Gf`z)wp2 zC-{4bS?T%TJ+Pr-rpqVDB}`y6g68{JP+HQ60`k!iP>HyG1~(Y!|tR|u0#B~bYa6w z(;FhQt)9-~?Rh*Xlx9sqWi@LJ8*;JI;_lDm2PI}P17d5l7Bx$sH?qh+Iv8CHSo z0nS%o9yMn1bET%;vxfm!s?1!e5-(1ddZr_@;b?eRgb3x`` zkd@IXBp!jp~BTGVm5WX+A*Mz6jhUPPb@EL`<}3Za#L| zw*t0a*kZ!ls=4I||Kgae7TvA)xH&y25OPD;Pw3qxPPNvSlw*hT0nK}Yqa1M%O$cS# zg6KwA%+u3_TJrOO>&mRyg$)t}uf_b=pqsUA0LP}aTWJe8=kK7W!0Ozb|6XuTsY*lO zT>ObP<2~-cJDtB>DA-Z(ZfOyJgLg_?48goh%|(X9Kg+b}TJ+{*6;dCV{|}5JD$9Nd z+%I|U1+PUykj=FHIJinuF$Nxx#{UP-#RnjPxn<9Yk{um{*CM{L3@3dJ+5CEBnVj)r z*!izZ#y*oOdHgghw1nAX+MVQu<0eVq6PS}~TyimS zq{HXnC+lB?g^Tqa#o}Uz)5>+wC9cB8>SC~wI0jow#A!|Uh|Y))akfS(v%Ejzb8sp% mV>!-mkvWQ8Ohmr0{O%GbIxDO_0irL(xlW&TVLQ=L4){N9bRjAL diff --git a/enc_bootloader/enc_bootloader_mbedtls.elf b/enc_bootloader/enc_bootloader_mbedtls.elf new file mode 100644 index 0000000000000000000000000000000000000000..b3b2fbf501754f1d205df44e45d52f4d1affb0b2 GIT binary patch literal 28432 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 literal 0 HcmV?d00001 diff --git a/main.cpp b/main.cpp index 4f618708..c43a0ce8 100644 --- a/main.cpp +++ b/main.cpp @@ -518,6 +518,7 @@ struct _settings { bool embed = false; bool otp_key_page_set = false; bool fast_rosc = false; + bool use_mbedtls = false; uint16_t otp_key_page = 30; } encrypt; @@ -831,6 +832,7 @@ struct encrypt_command : public cmd { option("--verbose").set(settings.verbose) % "Print verbose output" + option("--embed").set(settings.encrypt.embed) % "Embed bootloader in output file" + option("--fast-rosc").set(settings.encrypt.fast_rosc) % "Use ~180MHz ROSC configuration for embedded bootloader" + + option("--use-mbedtls").set(settings.encrypt.use_mbedtls) % "Use MbedTLS implementation of embedded bootloader" + ( option("--otp-key-page").set(settings.encrypt.otp_key_page_set) % "Specify the OTP page storing the AES key (IV salt is stored on the next page)" & integer("page").set(settings.encrypt.otp_key_page) % "OTP page (default 30)" @@ -5077,7 +5079,7 @@ bool encrypt_command::execute(device_map &devices) { iv_data[i] ^= iv_salt[i]; } auto tmp = std::make_shared(); - auto file = get_enc_bootloader(); + auto file = get_enc_bootloader(settings.encrypt.use_mbedtls); *tmp << file->rdbuf(); auto program = get_iostream_memory_access(tmp, filetype::elf, true); From 15f059c4720fc730f541a7db04efc52ffcae3065 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 4 Mar 2025 15:44:34 +0000 Subject: [PATCH 49/80] Add enc_bootloader_mbedtls to bazel build --- BUILD.bazel | 8 ++++++++ enc_bootloader/BUILD.bazel | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/BUILD.bazel b/BUILD.bazel index 267a2d40..3195890e 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -24,6 +24,13 @@ picotool_binary_data_header( out = "enc_bootloader_elf.h", ) +# TODO: Make it possible to build the prebuilt from source. +picotool_binary_data_header( + name = "enc_bootloader_mbedtls_elf", + src = "//enc_bootloader:enc_bootloader_mbedtls_prebuilt", + out = "enc_bootloader_mbedtls_elf.h", +) + # TODO: Make it possible to build the prebuilt from source. picotool_binary_data_header( name = "flash_id_bin", @@ -50,6 +57,7 @@ cc_library( hdrs = [ "enc_bootloader.h", "enc_bootloader_elf.h", + "enc_bootloader_mbedtls_elf.h", ], deps = [ "//bazel:data_locs", diff --git a/enc_bootloader/BUILD.bazel b/enc_bootloader/BUILD.bazel index bf46b104..ba986770 100644 --- a/enc_bootloader/BUILD.bazel +++ b/enc_bootloader/BUILD.bazel @@ -5,6 +5,11 @@ filegroup( srcs = ["enc_bootloader.elf"], ) +filegroup( + name = "enc_bootloader_mbedtls_prebuilt", + srcs = ["enc_bootloader_mbedtls.elf"], +) + # TODO: Make this work. cc_library( name = "enc_bootloader", From 8c4a66c6222ecb4c270052a66a189a91fa0dafbd Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 4 Mar 2025 15:48:19 +0000 Subject: [PATCH 50/80] Fix build without mbedtls --- enc_bootloader.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/enc_bootloader.cpp b/enc_bootloader.cpp index 5b28d6f6..012335a6 100644 --- a/enc_bootloader.cpp +++ b/enc_bootloader.cpp @@ -7,7 +7,9 @@ #include "enc_bootloader.h" #include "enc_bootloader_elf.h" +#if HAS_MBEDTLS #include "enc_bootloader_mbedtls_elf.h" +#endif #include "data_locs.h" @@ -38,9 +40,12 @@ std::shared_ptr get_enc_bootloader(bool use_mbedtls) { // fall back to embedded enc_bootloader.elf file printf("Could not find enc_bootloader%s.elf file - using embedded binary\n", use_mbedtls ? "_mbedtls" : ""); auto tmp = std::make_shared(); +#if HAS_MBEDTLS if (use_mbedtls) { tmp->write(reinterpret_cast(enc_bootloader_mbedtls_elf), enc_bootloader_mbedtls_elf_SIZE); - } else { + } else +#endif + { tmp->write(reinterpret_cast(enc_bootloader_elf), enc_bootloader_elf_SIZE); } return tmp; From bbbd03224903ed4513c8b20f1f34c2acde324d64 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 13 Mar 2025 15:46:45 +0000 Subject: [PATCH 51/80] Review fixups --- bintool/mbedtls_wrapper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bintool/mbedtls_wrapper.c b/bintool/mbedtls_wrapper.c index b5b8dbcb..c623ebdc 100644 --- a/bintool/mbedtls_wrapper.c +++ b/bintool/mbedtls_wrapper.c @@ -61,7 +61,7 @@ int mb_aes_crypt_ctr_xor(mbedtls_aes_context *ctx, if (n == 0) { for (int i = 16; i > 0; i--) { nonce_xor[i-1] = iv0[i-1]; - if (i - (int)(16 - sizeof(counter)) > (int)0) { + if (i > 16 - sizeof(counter)) { nonce_xor[i-1] ^= (unsigned char)(counter >> ((16-i)*8)); } } @@ -88,10 +88,10 @@ void mb_aes256_buffer(const uint8_t *data, size_t len, uint8_t *data_out, const assert(len % 16 == 0); mbedtls_aes_setkey_enc(&aes, key->bytes, 256); - uint8_t xor_working_block[16] = {0}; uint8_t stream_block[16] = {0}; size_t nc_off = 0; #if IV0_XOR + uint8_t xor_working_block[16] = {0}; mb_aes_crypt_ctr_xor(&aes, len, iv->bytes, xor_working_block, stream_block, data, data_out); #else mbedtls_aes_crypt_ctr(&aes, len, &nc_off, iv->bytes, stream_block, data, data_out); From fce50e1c147a84e704bff72ca03ef36487d2f0c7 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 13 Mar 2025 16:07:12 +0000 Subject: [PATCH 52/80] Reword readme, to reflect word-wise aes file --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bc013250..89dbae46 100644 --- a/README.md +++ b/README.md @@ -586,8 +586,9 @@ The encrypted binary will have the following structure: - Padding to ensure the encrypted length is a multiple of 4 words - Signature metadata block -The AES key must be provided as a .bin file containing a 4-way share of the 256 bit AES key to be used for encryption. -Eg if the 256 bit key is X, the bin file should contain the 256 bit numbers A B C D where X = A ^ B ^ C ^ D. +The AES key must be provided as a 1024 bit .bin file containing 4-way shares of each word of the 256 bit AES key to be used for encryption. +This should usually be generated by creating a random file of length 128 bytes and deriving the AES key from that, rather than crafting a file to match a specific AES key, to ensure the shares are random. +With the words stored in the file as A[0], B[0], C[0], D[0], A[1], B[1], etc, C[7], D[7], word i of the key X is X[i] = A[i] ^ B[i] ^ C[i] ^ D[i]. ```text $ picotool help encrypt From cd21a1eaed1c07bf4b384e9a2184a8c5f49f5dac Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 20 Mar 2025 11:03:01 +0000 Subject: [PATCH 53/80] Add IV salt to regular encrypted binaries too --- bintool/bintool.cpp | 16 ++++++++++++++-- bintool/bintool.h | 4 ++-- main.cpp | 11 ++++++----- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/bintool/bintool.cpp b/bintool/bintool.cpp index 13c66c5a..f5900779 100644 --- a/bintool/bintool.cpp +++ b/bintool/bintool.cpp @@ -880,11 +880,17 @@ void encrypt_guts(elf_file *elf, block *new_block, const aes_key_t aes_key, std: } -int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign) { +int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, std::vector iv_salt, bool hash_value, bool sign) { std::vector iv_data; std::vector enc_data; encrypt_guts(elf, new_block, aes_key, iv_data, enc_data); + + // Salt IV + assert(iv_data.size() == iv_salt.size()); + for (int i=0; i < iv_data.size(); i++) { + iv_data[i] ^= iv_salt[i]; + } unsigned int i=0; for(const auto &seg : sorted_segs(elf)) { @@ -968,7 +974,7 @@ int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const publ } -std::vector encrypt(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign) { +std::vector encrypt(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, std::vector iv_salt, bool hash_value, bool sign) { std::random_device rand{}; assert(rand.max() - rand.min() >= 256); @@ -984,6 +990,12 @@ std::vector encrypt(std::vector bin, uint32_t storage_addr, ui std::vector iv_data(iv.bytes, iv.bytes + sizeof(iv.bytes)); + // Salt IV + assert(iv_data.size() == iv_salt.size()); + for (int i=0; i < iv_data.size(); i++) { + iv_data[i] ^= iv_salt[i]; + } + std::vector enc_data; enc_data.resize(bin.size()); diff --git a/bintool/bintool.h b/bintool/bintool.h index d7db52cf..c4ddbec2 100644 --- a/bintool/bintool.h +++ b/bintool/bintool.h @@ -26,7 +26,7 @@ block place_new_block(elf_file *elf, std::unique_ptr &first_block); #if HAS_MBEDTLS int hash_andor_sign(elf_file *elf, block *new_block, const public_t public_key, const private_t private_key, bool hash_value, bool sign, bool clear_sram = false); void encrypt_guts(elf_file *elf, block *new_block, const aes_key_t aes_key, std::vector &iv_data, std::vector &enc_data); - int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign); + int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, std::vector iv_salt, bool hash_value, bool sign); #endif // Bins @@ -38,6 +38,6 @@ block place_new_block(std::vector &bin, uint32_t storage_addr, std::uni uint32_t calc_checksum(std::vector bin); #if HAS_MBEDTLS std::vector hash_andor_sign(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const public_t public_key, const private_t private_key, bool hash_value, bool sign, bool clear_sram = false); - std::vector encrypt(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign); + std::vector encrypt(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, std::vector iv_salt, bool hash_value, bool sign); void verify_block(std::vector bin, uint32_t storage_addr, uint32_t runtime_addr, block *block, verified_t &hash_verified, verified_t &sig_verified, get_more_bin_cb more_cb = nullptr); #endif diff --git a/main.cpp b/main.cpp index c43a0ce8..e961c55b 100644 --- a/main.cpp +++ b/main.cpp @@ -5044,13 +5044,14 @@ bool encrypt_command::execute(device_map &devices) { // Read IV Salt auto iv_salt_file = get_file_idx(ios::in|ios::binary, 3); iv_salt_file->exceptions(std::iostream::failbit | std::iostream::badbit); - uint8_t iv_salt[16]; + std::vector iv_salt; + iv_salt.resize(16); iv_salt_file->seekg(0, std::ios::end); if (iv_salt_file->tellg() != 16) { fail(ERROR_INCOMPATIBLE, "The IV OTP salt must be a 16 byte file (the supplied file is %d bytes)", iv_salt_file->tellg()); } iv_salt_file->seekg(0, std::ios::beg); - iv_salt_file->read((char*)iv_salt, sizeof(iv_salt)); + iv_salt_file->read((char*)iv_salt.data(), iv_salt.size()); if (isElf) { elf_file source_file(settings.verbose); @@ -5074,7 +5075,7 @@ bool encrypt_command::execute(device_map &devices) { encrypt_guts(elf, &new_block, aes_key, iv_data, enc_data); // Salt IV - assert(iv_data.size() == sizeof(iv_salt)); + assert(iv_data.size() == iv_salt.size()); for (int i=0; i < iv_data.size(); i++) { iv_data[i] ^= iv_salt[i]; } @@ -5169,7 +5170,7 @@ bool encrypt_command::execute(device_map &devices) { enc_elf->write(out); out->close(); } else { - encrypt(elf, &new_block, aes_key, public_key, private_key, settings.seal.hash, settings.seal.sign); + encrypt(elf, &new_block, aes_key, public_key, private_key, iv_salt, settings.seal.hash, settings.seal.sign); auto out = get_file_idx(ios::out|ios::binary, 1); elf->write(out); out->close(); @@ -5191,7 +5192,7 @@ bool encrypt_command::execute(device_map &devices) { auto bin_cp = bin; block new_block = place_new_block(bin_cp, bin_start, first_block); - auto enc_data = encrypt(bin, bin_start, bin_start, &new_block, aes_key, public_key, private_key, settings.seal.hash, settings.seal.sign); + auto enc_data = encrypt(bin, bin_start, bin_start, &new_block, aes_key, public_key, private_key, iv_salt, settings.seal.hash, settings.seal.sign); auto out = get_file_idx(ios::out|ios::binary, 1); out->write((const char *)enc_data.data(), enc_data.size()); From fd7e89b4e7d25c7499cfb94da70e0bcf67c8d94d Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 20 Mar 2025 11:16:52 +0000 Subject: [PATCH 54/80] Rename xip_ram_perms.cpp and enc_bootloader.cpp to get_....cpp --- BUILD.bazel | 12 ++++++------ CMakeLists.txt | 4 ++-- enc_bootloader.cpp => get_enc_bootloader.cpp | 2 +- enc_bootloader.h => get_enc_bootloader.h | 0 xip_ram_perms.cpp => get_xip_ram_perms.cpp | 2 +- xip_ram_perms.h => get_xip_ram_perms.h | 0 main.cpp | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) rename enc_bootloader.cpp => get_enc_bootloader.cpp (98%) rename enc_bootloader.h => get_enc_bootloader.h (100%) rename xip_ram_perms.cpp => get_xip_ram_perms.cpp (97%) rename xip_ram_perms.h => get_xip_ram_perms.h (100%) diff --git a/BUILD.bazel b/BUILD.bazel index 3195890e..5944db8e 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -40,9 +40,9 @@ picotool_binary_data_header( cc_library( name = "xip_ram_perms", - srcs = ["xip_ram_perms.cpp"], + srcs = ["get_xip_ram_perms.cpp"], hdrs = [ - "xip_ram_perms.h", + "get_xip_ram_perms.h", "xip_ram_perms_elf.h", ], deps = [ @@ -53,9 +53,9 @@ cc_library( cc_library( name = "enc_bootloader", - srcs = ["enc_bootloader.cpp"], + srcs = ["get_enc_bootloader.cpp"], hdrs = [ - "enc_bootloader.h", + "get_enc_bootloader.h", "enc_bootloader_elf.h", "enc_bootloader_mbedtls_elf.h", ], @@ -89,8 +89,8 @@ cc_binary( "otp.cpp", "otp.h", "rp2350.rom.h", - "xip_ram_perms.cpp", - "enc_bootloader.cpp", + "get_xip_ram_perms.cpp", + "get_enc_bootloader.cpp", ] + select({ # MSVC can't handle long strings, so use this manually generated # header instead. diff --git a/CMakeLists.txt b/CMakeLists.txt index d492e15a..e1fd8873 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,12 +310,12 @@ target_include_directories(regs_headers INTERFACE ${PICO_SDK_PATH}/src/rp2350/ha # Main picotool executable add_executable(picotool data_locs.cpp - enc_bootloader.cpp + get_enc_bootloader.cpp ${OTP_EXE} main.cpp) add_dependencies(picotool enc_bootloader_elf binary_data_no_libusb) if (NOT PICOTOOL_NO_LIBUSB) - target_sources(picotool PRIVATE xip_ram_perms.cpp) + target_sources(picotool PRIVATE get_xip_ram_perms.cpp) add_dependencies(picotool generate_otp_header xip_ram_perms_elf binary_data) endif() set(PROJECT_VERSION 2.1.2-develop) diff --git a/enc_bootloader.cpp b/get_enc_bootloader.cpp similarity index 98% rename from enc_bootloader.cpp rename to get_enc_bootloader.cpp index 012335a6..bac91d50 100644 --- a/enc_bootloader.cpp +++ b/get_enc_bootloader.cpp @@ -5,7 +5,7 @@ #include #include -#include "enc_bootloader.h" +#include "get_enc_bootloader.h" #include "enc_bootloader_elf.h" #if HAS_MBEDTLS #include "enc_bootloader_mbedtls_elf.h" diff --git a/enc_bootloader.h b/get_enc_bootloader.h similarity index 100% rename from enc_bootloader.h rename to get_enc_bootloader.h diff --git a/xip_ram_perms.cpp b/get_xip_ram_perms.cpp similarity index 97% rename from xip_ram_perms.cpp rename to get_xip_ram_perms.cpp index 1f6260d2..b940bb65 100644 --- a/xip_ram_perms.cpp +++ b/get_xip_ram_perms.cpp @@ -5,7 +5,7 @@ #include #include -#include "xip_ram_perms.h" +#include "get_xip_ram_perms.h" #include "xip_ram_perms_elf.h" #include "data_locs.h" diff --git a/xip_ram_perms.h b/get_xip_ram_perms.h similarity index 100% rename from xip_ram_perms.h rename to get_xip_ram_perms.h diff --git a/main.cpp b/main.cpp index e961c55b..b25870b7 100644 --- a/main.cpp +++ b/main.cpp @@ -33,11 +33,11 @@ #include "boot/uf2.h" #include "boot/picobin.h" -#include "enc_bootloader.h" +#include "get_enc_bootloader.h" #if HAS_LIBUSB #include "picoboot_connection_cxx.h" #include "rp2350.rom.h" - #include "xip_ram_perms.h" + #include "get_xip_ram_perms.h" #else #include "picoboot_connection.h" #endif From d26ccd05bbbafed21489b45c0b43c6b6e42591d1 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 20 Mar 2025 11:23:31 +0000 Subject: [PATCH 55/80] Use include_directories for lib/include --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e1fd8873..f6b0f63d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) # To configure mbedtls # todo make the configuration better set(MBEDTLS_CONFIG_FILE "mbedtls_config.h") -add_compile_options(-I${CMAKE_SOURCE_DIR}/lib/include) +include_directories(${CMAKE_SOURCE_DIR}/lib/include) add_subdirectory(lib) From 5030525f79b503c40e7e320efbdf8ae0a4da4593 Mon Sep 17 00:00:00 2001 From: Graham Sanderson Date: Sat, 22 Mar 2025 13:26:07 -0500 Subject: [PATCH 56/80] Use newer VTABLE related variables --- enc_bootloader/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 257b27dd..aecf059e 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -78,7 +78,9 @@ if (NOT USE_PRECOMPILED) PICO_BOOTROM_LOCKING_ENABLED=0 # PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1 # Don't need any vtor irqs - PICO_MINIMAL_VECTOR_TABLE=1) + PICO_MINIMAL_STORED_VECTOR_TABLE=1 + PICO_NUM_VTABLE_IRQS=0 + ) # print memory usage target_link_options(enc_bootloader PUBLIC -Wl,--print-memory-usage) From d838946c8134aec13332832ededcc64761fdf99e Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 24 Mar 2025 12:59:12 +0000 Subject: [PATCH 57/80] Copy last block when signing/encrypting, if it has an image_def If the last block has an image_def, then it is a better choice of block than the first block, so the items for the new block should be copied from it instead --- bintool/bintool.cpp | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/bintool/bintool.cpp b/bintool/bintool.cpp index f5900779..b6a29bc9 100644 --- a/bintool/bintool.cpp +++ b/bintool/bintool.cpp @@ -260,6 +260,7 @@ block place_new_block(elf_file *elf, std::unique_ptr &first_block) { int32_t loop_start_rel = 0; uint32_t new_block_addr = 0; + std::unique_ptr new_first_block; if (!first_block->next_block_rel) { set_next_block(elf, first_block, highest_address); loop_start_rel = -first_block->next_block_rel; @@ -267,7 +268,6 @@ block place_new_block(elf_file *elf, std::unique_ptr &first_block) { } else { DEBUG_LOG("There is already a block loop\n"); uint32_t next_block_addr = first_block->physical_addr + first_block->next_block_rel; - std::unique_ptr new_first_block; while (true) { auto segment = elf->segment_from_physical_address(next_block_addr); if (segment == nullptr) { @@ -314,10 +314,18 @@ block place_new_block(elf_file *elf, std::unique_ptr &first_block) { // loop back to first block block new_block(new_block_addr, loop_start_rel); - // copt the existing block - std::copy(first_block->items.begin(), - first_block->items.end(), - std::back_inserter(new_block.items)); + // check if last block has an image_def + if (new_first_block != nullptr && new_first_block->get_item() != nullptr) { + // copy the last block items + std::copy(new_first_block->items.begin(), + new_first_block->items.end(), + std::back_inserter(new_block.items)); + } else { + // copy the first block items + std::copy(first_block->items.begin(), + first_block->items.end(), + std::back_inserter(new_block.items)); + } return new_block; } @@ -404,13 +412,14 @@ block place_new_block(std::vector &bin, uint32_t storage_addr, std::uni int32_t loop_start_rel = 0; uint32_t new_block_addr = 0; + std::unique_ptr new_first_block; if (!first_block->next_block_rel) { set_next_block(bin, storage_addr, first_block, highest_address); loop_start_rel = -first_block->next_block_rel; new_block_addr = first_block->physical_addr + first_block->next_block_rel; } else { DEBUG_LOG("Ooh, there is already a block loop - lets find it's end\n"); - auto new_first_block = get_last_block(bin, storage_addr, first_block); + new_first_block = get_last_block(bin, storage_addr, first_block); set_next_block(bin, storage_addr, new_first_block, highest_address); new_block_addr = new_first_block->physical_addr + new_first_block->next_block_rel; loop_start_rel = first_block->physical_addr - new_block_addr; @@ -421,10 +430,18 @@ block place_new_block(std::vector &bin, uint32_t storage_addr, std::uni // loop back to first block block new_block(new_block_addr, loop_start_rel); - // copt the existing block - std::copy(first_block->items.begin(), - first_block->items.end(), - std::back_inserter(new_block.items)); + // check if last block has an image_def + if (new_first_block != nullptr && new_first_block->get_item() != nullptr) { + // copy the last block items + std::copy(new_first_block->items.begin(), + new_first_block->items.end(), + std::back_inserter(new_block.items)); + } else { + // copy the first block items + std::copy(first_block->items.begin(), + first_block->items.end(), + std::back_inserter(new_block.items)); + } return new_block; } From e0db2437c3b518a7ddbff6688d7f2d611d7d71b5 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 24 Mar 2025 13:23:12 +0000 Subject: [PATCH 58/80] Review fixups enc_bootloader requires SDK 2.1.2 rename binary_data to embedded_data move mbedtls TODO into header --- CMakeLists.txt | 11 +++++------ enc_bootloader/CMakeLists.txt | 2 +- lib/include/mbedtls_config.h | 2 ++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6b0f63d..077b4f46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,6 @@ endif() list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) # To configure mbedtls -# todo make the configuration better set(MBEDTLS_CONFIG_FILE "mbedtls_config.h") include_directories(${CMAKE_SOURCE_DIR}/lib/include) @@ -229,15 +228,15 @@ if (NOT PICOTOOL_NO_LIBUSB) endif() if (TARGET mbedtls) - add_custom_target(binary_data_no_libusb DEPENDS + add_custom_target(embedded_data_no_libusb DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_mbedtls_elf.h) else() - add_custom_target(binary_data_no_libusb DEPENDS + add_custom_target(embedded_data_no_libusb DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/enc_bootloader_elf.h) endif() -add_custom_target(binary_data DEPENDS +add_custom_target(embedded_data DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h ${CMAKE_CURRENT_BINARY_DIR}/xip_ram_perms_elf.h ${CMAKE_CURRENT_BINARY_DIR}/flash_id_bin.h) @@ -313,10 +312,10 @@ add_executable(picotool get_enc_bootloader.cpp ${OTP_EXE} main.cpp) -add_dependencies(picotool enc_bootloader_elf binary_data_no_libusb) +add_dependencies(picotool enc_bootloader_elf embedded_data_no_libusb) if (NOT PICOTOOL_NO_LIBUSB) target_sources(picotool PRIVATE get_xip_ram_perms.cpp) - add_dependencies(picotool generate_otp_header xip_ram_perms_elf binary_data) + add_dependencies(picotool generate_otp_header xip_ram_perms_elf embedded_data) endif() set(PROJECT_VERSION 2.1.2-develop) set(PICOTOOL_VERSION 2.1.2-develop) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index aecf059e..dd669eaf 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -23,7 +23,7 @@ if (NOT USE_PRECOMPILED) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) - if (PICO_SDK_VERSION_STRING VERSION_LESS "2.0.0") + if (PICO_SDK_VERSION_STRING VERSION_LESS "2.1.2") message(FATAL_ERROR "Raspberry Pi Pico SDK version 2.0.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") endif() diff --git a/lib/include/mbedtls_config.h b/lib/include/mbedtls_config.h index 5bdd142c..5937028a 100644 --- a/lib/include/mbedtls_config.h +++ b/lib/include/mbedtls_config.h @@ -15,6 +15,8 @@ #ifndef MBEDTLS_CONFIG_H #define MBEDTLS_CONFIG_H +// todo make the configuration better + #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) #define _CRT_SECURE_NO_DEPRECATE 1 #endif From 3fdc5561be6c821774417b4cf62d070459673067 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 25 Mar 2025 09:44:29 +0000 Subject: [PATCH 59/80] Review fixups expand comment on configuring mbedtls 2.0.0->2.1.2 message reword mbedtls todo --- CMakeLists.txt | 2 +- enc_bootloader/CMakeLists.txt | 2 +- lib/include/mbedtls_config.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 077b4f46..e49dd1d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,7 +57,7 @@ endif() list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) -# To configure mbedtls +# To configure mbedtls, without modifying mbedtls CMake files set(MBEDTLS_CONFIG_FILE "mbedtls_config.h") include_directories(${CMAKE_SOURCE_DIR}/lib/include) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index dd669eaf..1d01dbee 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -24,7 +24,7 @@ if (NOT USE_PRECOMPILED) set(CMAKE_CXX_STANDARD 17) if (PICO_SDK_VERSION_STRING VERSION_LESS "2.1.2") - message(FATAL_ERROR "Raspberry Pi Pico SDK version 2.0.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") + message(FATAL_ERROR "Raspberry Pi Pico SDK version 2.1.2 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") endif() # Initialize the SDK diff --git a/lib/include/mbedtls_config.h b/lib/include/mbedtls_config.h index 5937028a..e77d5d69 100644 --- a/lib/include/mbedtls_config.h +++ b/lib/include/mbedtls_config.h @@ -15,7 +15,7 @@ #ifndef MBEDTLS_CONFIG_H #define MBEDTLS_CONFIG_H -// todo make the configuration better +// todo fine tune this config file #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) #define _CRT_SECURE_NO_DEPRECATE 1 From 64fe3d471b06310ae0379be59e620f9a46eef789 Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Tue, 25 Mar 2025 10:22:07 -0500 Subject: [PATCH 60/80] don't include RAM vector table --- enc_bootloader/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 1d01dbee..661a78cd 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -79,7 +79,7 @@ if (NOT USE_PRECOMPILED) # PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1 # Don't need any vtor irqs PICO_MINIMAL_STORED_VECTOR_TABLE=1 - PICO_NUM_VTABLE_IRQS=0 + PICO_NO_RAM_VECTOR_TABLE=1 ) # print memory usage From 091cf6f220fa6630f437bcfe9e0d79512c5f4c9c Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 31 Mar 2025 10:20:16 +0100 Subject: [PATCH 61/80] Mention file size in bits & bytes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 89dbae46..29dd4244 100644 --- a/README.md +++ b/README.md @@ -586,7 +586,7 @@ The encrypted binary will have the following structure: - Padding to ensure the encrypted length is a multiple of 4 words - Signature metadata block -The AES key must be provided as a 1024 bit .bin file containing 4-way shares of each word of the 256 bit AES key to be used for encryption. +The AES key must be provided as a 1024 bit (128 byte) .bin file containing 4-way shares of each word of the 256 bit AES key to be used for encryption. This should usually be generated by creating a random file of length 128 bytes and deriving the AES key from that, rather than crafting a file to match a specific AES key, to ensure the shares are random. With the words stored in the file as A[0], B[0], C[0], D[0], A[1], B[1], etc, C[7], D[7], word i of the key X is X[i] = A[i] ^ B[i] ^ C[i] ^ D[i]. From 04d881947057b613e0623c8ddbedc77192f0f845 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 31 Mar 2025 10:42:03 +0100 Subject: [PATCH 62/80] Remove OTP key workaround --- enc_bootloader/enc_bootloader.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 1a7aa141..1a68090c 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -157,15 +157,6 @@ void lock_key() { int main() { - // This works around E21 by locking down the page with a key, - // as there is no way to enter a key over the picoboot interface. - // Unlocks the OTP page with hardcoded key [0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7] - for (int i=0; i<4; i++) { - uint32_t key_i = ((i*2+1) << 24) | ((i*2+1) << 16) | - (i*2 << 8) | i*2; - otp_hw->crt_key_w[i] = key_i; - } - bi_decl(bi_ptr_int32(0, 0, data_start_addr, 0x20000000)); bi_decl(bi_ptr_int32(0, 0, data_size, 0x78000)); bi_decl(bi_ptr_string(0, 0, iv, "0123456789abcdef", 17)); From 90ddbc4d12cb371b38aa6ee6df461ba134f6bd1e Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 31 Mar 2025 11:30:04 +0100 Subject: [PATCH 63/80] Remove load_map, hash and signature from chosen ELF blocks These would mess up signing/encryption of an already signed/encrypted binary --- bintool/bintool.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/bintool/bintool.cpp b/bintool/bintool.cpp index b6a29bc9..8b01a2fc 100644 --- a/bintool/bintool.cpp +++ b/bintool/bintool.cpp @@ -327,6 +327,24 @@ block place_new_block(elf_file *elf, std::unique_ptr &first_block) { std::back_inserter(new_block.items)); } + // Delete existing load_map, signature and hash as these will be replaced with new ones + std::shared_ptr load_map = new_block.get_item(); + if (load_map != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), load_map), new_block.items.end()); + } + std::shared_ptr signature = new_block.get_item(); + if (signature != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), signature), new_block.items.end()); + } + std::shared_ptr hash_value = new_block.get_item(); + if (hash_value != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), hash_value), new_block.items.end()); + } + std::shared_ptr hash_def = new_block.get_item(); + if (hash_def != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), hash_def), new_block.items.end()); + } + return new_block; } @@ -443,6 +461,24 @@ block place_new_block(std::vector &bin, uint32_t storage_addr, std::uni std::back_inserter(new_block.items)); } + // Delete existing load_map, signature and hash as these will be replaced with new ones + std::shared_ptr load_map = new_block.get_item(); + if (load_map != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), load_map), new_block.items.end()); + } + std::shared_ptr signature = new_block.get_item(); + if (signature != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), signature), new_block.items.end()); + } + std::shared_ptr hash_value = new_block.get_item(); + if (hash_value != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), hash_value), new_block.items.end()); + } + std::shared_ptr hash_def = new_block.get_item(); + if (hash_def != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), hash_def), new_block.items.end()); + } + return new_block; } @@ -908,7 +944,7 @@ int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const publ for (int i=0; i < iv_data.size(); i++) { iv_data[i] ^= iv_salt[i]; } - + unsigned int i=0; for(const auto &seg : sorted_segs(elf)) { if (!seg->is_load()) continue; From e370a20aedad72587187cad1febc862ffdb71395 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 31 Mar 2025 11:44:13 +0100 Subject: [PATCH 64/80] Clear SRAM in load_map of self-decrypting binary --- main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/main.cpp b/main.cpp index b25870b7..641a510e 100644 --- a/main.cpp +++ b/main.cpp @@ -5164,6 +5164,7 @@ bool encrypt_command::execute(device_map &devices) { } // Sign the final thing + settings.seal.clear_sram = true; sign_guts_elf(enc_elf, private_key, public_key); auto out = get_file_idx(ios::out|ios::binary, 1); From 33419cf875bce67067695ace24146d0af8e95bc3 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 31 Mar 2025 12:28:54 +0100 Subject: [PATCH 65/80] Add ability to generate key share from 256 bit key --- README.md | 6 ++++-- main.cpp | 28 ++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 29dd4244..ae133287 100644 --- a/README.md +++ b/README.md @@ -586,9 +586,11 @@ The encrypted binary will have the following structure: - Padding to ensure the encrypted length is a multiple of 4 words - Signature metadata block -The AES key must be provided as a 1024 bit (128 byte) .bin file containing 4-way shares of each word of the 256 bit AES key to be used for encryption. -This should usually be generated by creating a random file of length 128 bytes and deriving the AES key from that, rather than crafting a file to match a specific AES key, to ensure the shares are random. +The AES key can either be provided as a 1024 bit (128 byte) .bin file containing 4-way shares of each word of the 256 bit AES key to be used for encryption, or as a 256 bit (32 byte) .bin file containing the 256 bit AES key. +The key share should usually be generated by creating a random file of length 128 bytes and deriving the AES key from that, rather than crafting a file to match a specific AES key, to ensure the shares are random. +When passing a 256 bit AES key (as opposed to a 1028 bit 4-way AES key share), picotool will generate a random key share to match that key, by generating 3 random shares (A, B & C) and calculating a 4th share (D) to match the provided key. With the words stored in the file as A[0], B[0], C[0], D[0], A[1], B[1], etc, C[7], D[7], word i of the key X is X[i] = A[i] ^ B[i] ^ C[i] ^ D[i]. +When generating a key share from a key, picotool calculates it as D[i] = X[i] ^ A[i] ^ B[i] ^ C[i]. ```text $ picotool help encrypt diff --git a/main.cpp b/main.cpp index 641a510e..c6ed1fb4 100644 --- a/main.cpp +++ b/main.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #if !defined(__APPLE__) && !defined(__FreeBSD__) #include #endif @@ -847,7 +848,7 @@ struct encrypt_command : public cmd { hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)" ).force_expand_help(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + - named_typed_file_selection_x("aes_key", 2, "bin") % "AES Key Share" + + named_typed_file_selection_x("aes_key", 2, "bin") % "AES Key Share or AES Key" + named_typed_file_selection_x("iv_otp", 3, "bin") % "IV OTP Salt" + optional_typed_file_selection_x("signing_key", 4, "pem") % "Signing Key file" + optional_typed_file_selection_x("otp", 5, "json") % "File to save OTP to (will edit existing file if it exists)" @@ -5022,10 +5023,29 @@ bool encrypt_command::execute(device_map &devices) { aes_key_share_t aes_key_share; aes_file->seekg(0, std::ios::end); if (aes_file->tellg() != 128) { - fail(ERROR_INCOMPATIBLE, "The AES key share must be a 128 byte file (the supplied file is %d bytes)", aes_file->tellg()); + // Generate a random key share from 256-bit key + if (aes_file->tellg() != 32) { + fail(ERROR_INCOMPATIBLE, "The AES key share must be a 128 byte key share, or a 32 byte key (the supplied file is %d bytes)", aes_file->tellg()); + } + aes_key_t tmp_key; + aes_file->seekg(0, std::ios::beg); + aes_file->read((char*)tmp_key.bytes, sizeof(tmp_key.bytes)); + + std::random_device rand{}; + assert(rand.max() - rand.min() >= 256); + for(int i=0; i < 8; i++) { + for (int j=0; j < 12; j++) { + aes_key_share.bytes[i*16 + j] = rand(); + } + aes_key_share.words[i*4 + 3] = tmp_key.words[i] + ^ aes_key_share.words[i*4 + 2] + ^ aes_key_share.words[i*4 + 1] + ^ aes_key_share.words[i*4]; + } + } else { + aes_file->seekg(0, std::ios::beg); + aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); } - aes_file->seekg(0, std::ios::beg); - aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); aes_key_t aes_key; // Key is stored as a 4-way share of each word, ie X[0] = A[0] ^ B[0] ^ C[0] ^ D[0], stored as A[0], B[0], C[0], D[0] From b35d9a9addf1980bb261b5a8cb0818cc194f1d33 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 31 Mar 2025 13:30:42 +0100 Subject: [PATCH 66/80] Remove -t argument when not needed Remove they -t argument from all key files (bin & pem) and JSON files, as they should always have the correct extension so it's not necessary --- main.cpp | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/main.cpp b/main.cpp index c6ed1fb4..f2d83b5f 100644 --- a/main.cpp +++ b/main.cpp @@ -606,6 +606,13 @@ auto device_selection = named_file_types_x(types, i)\ ) +#define named_untyped_file_selection_x(name, i)\ +(\ + value(name).with_exclusion_filter([](const string &value) {\ + return value.find_first_of('-') == 0;\ + }).set(settings.filenames[i]) % "The file name"\ +) + #define optional_file_selection_x(name, i)\ (\ value(name).with_exclusion_filter([](const string &value) {\ @@ -622,6 +629,13 @@ auto device_selection = named_file_types_x(types, i)\ ).min(0).doc_non_optional(true) +#define optional_untyped_file_selection_x(name, i)\ +(\ + value(name).with_exclusion_filter([](const string &value) {\ + return value.find_first_of('-') == 0;\ + }).set(settings.filenames[i]).min(0) % "The file name"\ +).min(0).doc_non_optional(true) + #define option_file_selection_x(option, i)\ (\ option & value("filename").with_exclusion_filter([](const string &value) {\ @@ -848,10 +862,10 @@ struct encrypt_command : public cmd { hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)" ).force_expand_help(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + - named_typed_file_selection_x("aes_key", 2, "bin") % "AES Key Share or AES Key" + - named_typed_file_selection_x("iv_otp", 3, "bin") % "IV OTP Salt" + - optional_typed_file_selection_x("signing_key", 4, "pem") % "Signing Key file" + - optional_typed_file_selection_x("otp", 5, "json") % "File to save OTP to (will edit existing file if it exists)" + named_untyped_file_selection_x("aes_key", 2) % "AES Key Share or AES Key" + + named_untyped_file_selection_x("iv_otp", 3) % "IV OTP Salt" + + optional_untyped_file_selection_x("signing_key", 4) % "Signing Key file" + + optional_untyped_file_selection_x("otp", 5) % "File to save OTP to (will edit existing file if it exists)" ); } @@ -880,8 +894,8 @@ struct seal_command : public cmd { hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)" ).force_expand_help(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + - optional_typed_file_selection_x("key", 2, "pem") % "Key file" + - optional_typed_file_selection_x("otp", 3, "json") % "File to save OTP to (will edit existing file if it exists)" + + optional_untyped_file_selection_x("key", 2) % "Key file" + + optional_untyped_file_selection_x("otp", 3) % "File to save OTP to (will edit existing file if it exists)" + ( option("--major") & integer("major").set(settings.seal.major_version) @@ -953,7 +967,7 @@ struct partition_create_command : public cmd { return ( option("--quiet").set(settings.quiet) % "Don't print any output" + option("--verbose").set(settings.verbose) % "Print verbose output" + - named_typed_file_selection_x("infile", 0, "json") % "partition table JSON" + + named_untyped_file_selection_x("infile", 0) % "partition table JSON" + (named_file_selection_x("outfile", 1) % "output file" + ( (option('o', "--offset").set(settings.offset_set) % "Specify the load address for UF2 file output" & @@ -1163,12 +1177,12 @@ struct otp_permissions_command : public cmd { group get_cli() override { return ( - named_typed_file_selection_x("filename", 0, "json") % "File to load permissions from" + + named_untyped_file_selection_x("filename", 0) % "File to load permissions from" + (option("--led") & integer("pin").set(settings.otp.led_pin)) % "LED Pin to flash; default 25" + ( option("--hash").set(settings.seal.hash) % "Hash the executable" + option("--sign").set(settings.seal.sign) % "Sign the executable" + - optional_typed_file_selection_x("key", 2, "pem") % "Key file" + optional_untyped_file_selection_x("key", 2) % "Key file" ).min(0).doc_non_optional(true) % "Signing Configuration" + device_selection % "Target device selection" ); @@ -1190,7 +1204,7 @@ struct otp_white_label_command : public cmd { ( (option('s', "--start_row") & integer("row").set(settings.otp.row)) % "Start row for white label struct (default 0x100) (note use 0x for hex)" ).min(0).doc_non_optional(true) % "Row options" + - named_typed_file_selection_x("filename", 0, "json") % "File with white labelling values" + + named_untyped_file_selection_x("filename", 0) % "File with white labelling values" + device_selection % "Target device selection" ); } @@ -5017,18 +5031,18 @@ bool encrypt_command::execute(device_map &devices) { } - auto aes_file = get_file_idx(ios::in|ios::binary, 2); - aes_file->exceptions(std::iostream::failbit | std::iostream::badbit); + auto aes_file = get_file_idx(ios::in|ios::binary, 2); + aes_file->exceptions(std::iostream::failbit | std::iostream::badbit); aes_key_share_t aes_key_share; - aes_file->seekg(0, std::ios::end); + aes_file->seekg(0, std::ios::end); if (aes_file->tellg() != 128) { // Generate a random key share from 256-bit key if (aes_file->tellg() != 32) { fail(ERROR_INCOMPATIBLE, "The AES key share must be a 128 byte key share, or a 32 byte key (the supplied file is %d bytes)", aes_file->tellg()); } aes_key_t tmp_key; - aes_file->seekg(0, std::ios::beg); + aes_file->seekg(0, std::ios::beg); aes_file->read((char*)tmp_key.bytes, sizeof(tmp_key.bytes)); std::random_device rand{}; From 4adbf75e9d53665256d02c9efefe87af2951a090 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 31 Mar 2025 13:57:29 +0100 Subject: [PATCH 67/80] Support passing AES key and IV salt as hex strings instead of files --- README.md | 6 ++-- main.cpp | 95 ++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 70 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index ae133287..5e882d90 100644 --- a/README.md +++ b/README.md @@ -586,9 +586,11 @@ The encrypted binary will have the following structure: - Padding to ensure the encrypted length is a multiple of 4 words - Signature metadata block -The AES key can either be provided as a 1024 bit (128 byte) .bin file containing 4-way shares of each word of the 256 bit AES key to be used for encryption, or as a 256 bit (32 byte) .bin file containing the 256 bit AES key. +The AES key can either be provided as a 1024 bit (128 byte) .bin file containing 4-way shares of each word of the 256 bit AES key to be used for encryption, as a 256 bit (32 byte) .bin file containing the 256 bit AES key, or a 256 bit hex string of the AES key (eg 0x0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210). + The key share should usually be generated by creating a random file of length 128 bytes and deriving the AES key from that, rather than crafting a file to match a specific AES key, to ensure the shares are random. -When passing a 256 bit AES key (as opposed to a 1028 bit 4-way AES key share), picotool will generate a random key share to match that key, by generating 3 random shares (A, B & C) and calculating a 4th share (D) to match the provided key. +However, if you wish to use a specific 256 bit AES key, then picotool will generate a random key share to match that key, by generating 3 random shares (A, B & C) and calculating a 4th share (D) to match the provided key. + With the words stored in the file as A[0], B[0], C[0], D[0], A[1], B[1], etc, C[7], D[7], word i of the key X is X[i] = A[i] ^ B[i] ^ C[i] ^ D[i]. When generating a key share from a key, picotool calculates it as D[i] = X[i] ^ A[i] ^ B[i] ^ C[i]. diff --git a/main.cpp b/main.cpp index f2d83b5f..d484cfc2 100644 --- a/main.cpp +++ b/main.cpp @@ -4999,6 +4999,16 @@ vector sign_guts_bin(iostream_memory_access in, private_t private_key, bool encrypt_command::execute(device_map &devices) { bool isElf = false; bool isBin = false; + + bool keyFromFile = true; + bool keyIsShare = false; + bool ivFromFile = true; + + aes_key_t aes_key; + aes_key_share_t aes_key_share; + std::vector iv_salt; + iv_salt.resize(16); + if (get_file_type() == filetype::elf) { isElf = true; } else if (get_file_type() == filetype::bin) { @@ -5014,11 +5024,37 @@ bool encrypt_command::execute(device_map &devices) { fail(ERROR_ARGS, "Can only sign to same file type"); } - if (get_file_type_idx(2) != filetype::bin) { + if (!settings.filenames[2].empty() && settings.filenames[2].find("0x") == 0) { + // Hex string instead of file + if (settings.filenames[2].size() != 64 + 2) { + fail(ERROR_ARGS, "AES key hex string must be 32 bytes long"); + } + keyFromFile = false; + for (int i=0; i < count_of(aes_key.bytes); i++) { + auto value = "0x" + settings.filenames[2].substr(2 + i*2, 2); + auto ret = integer::parse_string(value, aes_key.bytes[i]); + if (!ret.empty()) { + fail(ERROR_ARGS, "Invalid hex string: %s %s", value.c_str(), ret.c_str()); + } + } + } else if (get_file_type_idx(2) != filetype::bin) { fail(ERROR_ARGS, "Can only read AES key share from BIN file"); } - if (get_file_type_idx(3) != filetype::bin) { + if (!settings.filenames[3].empty() && settings.filenames[3].find("0x") == 0) { + // Hex string instead of file + if (settings.filenames[3].size() != 34) { + fail(ERROR_ARGS, "IV OTP salt hex string must be 16 bytes long"); + } + ivFromFile = false; + for (int i=0; i < iv_salt.size(); i++) { + auto value = "0x" + settings.filenames[3].substr(2 + i*2, 2); + auto ret = integer::parse_string(value, iv_salt[i]); + if (!ret.empty()) { + fail(ERROR_ARGS, "Invalid hex string: %s %s", value.c_str(), ret.c_str()); + } + } + } else if (get_file_type_idx(3) != filetype::bin) { fail(ERROR_ARGS, "Can only read IV OTP salt from BIN file"); } @@ -5030,38 +5066,39 @@ bool encrypt_command::execute(device_map &devices) { fail(ERROR_ARGS, "Can only read pem keys"); } - + if (keyFromFile) { auto aes_file = get_file_idx(ios::in|ios::binary, 2); aes_file->exceptions(std::iostream::failbit | std::iostream::badbit); - - aes_key_share_t aes_key_share; aes_file->seekg(0, std::ios::end); - if (aes_file->tellg() != 128) { - // Generate a random key share from 256-bit key - if (aes_file->tellg() != 32) { - fail(ERROR_INCOMPATIBLE, "The AES key share must be a 128 byte key share, or a 32 byte key (the supplied file is %d bytes)", aes_file->tellg()); - } - aes_key_t tmp_key; + auto aes_key_file_size = aes_file->tellg(); + if (aes_key_file_size == 32) { + keyIsShare = false; aes_file->seekg(0, std::ios::beg); - aes_file->read((char*)tmp_key.bytes, sizeof(tmp_key.bytes)); + aes_file->read((char*)aes_key.bytes, sizeof(aes_key.bytes)); + } else if (aes_key_file_size == 128) { + keyIsShare = true; + aes_file->seekg(0, std::ios::beg); + aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); + } else { + fail(ERROR_INCOMPATIBLE, "The AES key file must be a 128 byte key share, or a 32 byte key (the supplied file is %d bytes)", aes_key_file_size); + } + } + if (!keyIsShare) { + // Generate a random key share from 256-bit key std::random_device rand{}; assert(rand.max() - rand.min() >= 256); for(int i=0; i < 8; i++) { for (int j=0; j < 12; j++) { aes_key_share.bytes[i*16 + j] = rand(); } - aes_key_share.words[i*4 + 3] = tmp_key.words[i] - ^ aes_key_share.words[i*4 + 2] + aes_key_share.words[i*4 + 3] = aes_key.words[i] + ^ aes_key_share.words[i*4] ^ aes_key_share.words[i*4 + 1] - ^ aes_key_share.words[i*4]; + ^ aes_key_share.words[i*4 + 2]; } - } else { - aes_file->seekg(0, std::ios::beg); - aes_file->read((char*)aes_key_share.bytes, sizeof(aes_key_share.bytes)); } - aes_key_t aes_key; // Key is stored as a 4-way share of each word, ie X[0] = A[0] ^ B[0] ^ C[0] ^ D[0], stored as A[0], B[0], C[0], D[0] for (int i=0; i < count_of(aes_key.words); i++) { aes_key.words[i] = aes_key_share.words[i*4] @@ -5076,16 +5113,16 @@ bool encrypt_command::execute(device_map &devices) { if (settings.seal.sign) read_keys(settings.filenames[4], &public_key, &private_key); // Read IV Salt - auto iv_salt_file = get_file_idx(ios::in|ios::binary, 3); - iv_salt_file->exceptions(std::iostream::failbit | std::iostream::badbit); - std::vector iv_salt; - iv_salt.resize(16); - iv_salt_file->seekg(0, std::ios::end); - if (iv_salt_file->tellg() != 16) { - fail(ERROR_INCOMPATIBLE, "The IV OTP salt must be a 16 byte file (the supplied file is %d bytes)", iv_salt_file->tellg()); + if (ivFromFile) { + auto iv_salt_file = get_file_idx(ios::in|ios::binary, 3); + iv_salt_file->exceptions(std::iostream::failbit | std::iostream::badbit); + iv_salt_file->seekg(0, std::ios::end); + if (iv_salt_file->tellg() != 16) { + fail(ERROR_INCOMPATIBLE, "The IV OTP salt must be a 16 byte file (the supplied file is %d bytes)", iv_salt_file->tellg()); + } + iv_salt_file->seekg(0, std::ios::beg); + iv_salt_file->read((char*)iv_salt.data(), iv_salt.size()); } - iv_salt_file->seekg(0, std::ios::beg); - iv_salt_file->read((char*)iv_salt.data(), iv_salt.size()); if (isElf) { elf_file source_file(settings.verbose); @@ -5258,7 +5295,7 @@ bool encrypt_command::execute(device_map &devices) { } // Add otp IV salt page - for (int i = 0; i < sizeof(iv_salt); ++i) { + for (int i = 0; i < iv_salt.size(); ++i) { std::stringstream ss; ss << settings.encrypt.otp_key_page + 1 << ":0"; otp_json[ss.str()]["ecc"] = true; From cbfab7c695e9d46eb2e5def1ed4b90a2fb305735 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 1 Apr 2025 12:26:22 +0100 Subject: [PATCH 68/80] Improve encrypt docs Implement suggested changes, and add note about IV salt --- README.md | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 5e882d90..51a0a4d0 100644 --- a/README.md +++ b/README.md @@ -576,7 +576,7 @@ OPTIONS: ## encrypt -`encrypt` allows you to encrypt and sign a binary for use on the RP2350. By default, it will sign the encrypted binary, but that can be configured similarly to `picotool seal`. +`encrypt` allows you to encrypt and sign a binary for use on the RP2350. By default, it will sign the encrypted binary, but that can be configured similarly to `picotool seal`. You can either provide your own bootloader to decrypt the binary (see pico-examples/bootloaders/encrypted), or embed a decrypting bootloader into the binary with the `--embed` argument, to create a self-decrypting binary. The encrypted binary will have the following structure: @@ -586,13 +586,13 @@ The encrypted binary will have the following structure: - Padding to ensure the encrypted length is a multiple of 4 words - Signature metadata block -The AES key can either be provided as a 1024 bit (128 byte) .bin file containing 4-way shares of each word of the 256 bit AES key to be used for encryption, as a 256 bit (32 byte) .bin file containing the 256 bit AES key, or a 256 bit hex string of the AES key (eg 0x0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210). +The AES key can either be provided as a 1024-bit (128 byte) 4-way key share or as a 256-bit (32 byte) AES key. In the latter case, a key share will be generated for you. -The key share should usually be generated by creating a random file of length 128 bytes and deriving the AES key from that, rather than crafting a file to match a specific AES key, to ensure the shares are random. -However, if you wish to use a specific 256 bit AES key, then picotool will generate a random key share to match that key, by generating 3 random shares (A, B & C) and calculating a 4th share (D) to match the provided key. +The 4-way key share should be generated by creating a random .bin file of length 128 bytes and the AES key will be derived from that. With the words stored in the file as A[0], B[0], C[0], D[0], A[1], B[1], etc., C[7], D[7], word i of the key X is X[i] = A[i] ^ B[i] ^ C[i] ^ D[i]. -With the words stored in the file as A[0], B[0], C[0], D[0], A[1], B[1], etc, C[7], D[7], word i of the key X is X[i] = A[i] ^ B[i] ^ C[i] ^ D[i]. -When generating a key share from a key, picotool calculates it as D[i] = X[i] ^ A[i] ^ B[i] ^ C[i]. +Alternatively an AES key can be provided directly, as either a 32 byte .bin file or as a string of 64 hexadecimal characters (eg 0x0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210). Picotool will generate a random key share for that key, by generating 3 random shares (A, B & C) and calculating a 4th share (D) to match the provided key (X), with each word i of the 4th share calculated as D[i] = X[i] ^ A[i] ^ B[i] ^ C[i]. + +The encryption also requires a per-device IV salt in OTP, which should be a 128-bit (16 byte) random file - this can be provided as either a 16 byte .bin file, or a string of 32 hexadecimal characters, similar to the AES key. This per-device salt will be XORed with the IV stored in the binary, to give the IV used by the decryption code. ```text $ picotool help encrypt @@ -600,14 +600,32 @@ ENCRYPT: Encrypt the program. SYNOPSIS: - picotool encrypt [--quiet] [--verbose] [--hash] [--sign] [-t ] [-o ] [-t ] [-t ] - [] [-t ] + picotool encrypt [--quiet] [--verbose] [--embed] [--fast-rosc] [--use-mbedtls] [--otp-key-page ] [--hash] [--sign] + [-t ] [-o ] [-t ] OPTIONS: --quiet Don't print any output --verbose Print verbose output + --embed + Embed bootloader in output file + --fast-rosc + Use ~180MHz ROSC configuration for embedded bootloader + --use-mbedtls + Use MbedTLS implementation of embedded bootloader + --otp-key-page + Specify the OTP page storing the AES key (IV salt is stored on the next page) + + OTP page (default 30) + + AES Key Share or AES Key + + IV OTP Salt + + Signing Key file + + File to save OTP to (will edit existing file if it exists) Signing Configuration --hash Hash the encrypted file @@ -628,16 +646,6 @@ OPTIONS: The file name -t Specify file type (uf2 | elf | bin) explicitly, ignoring file extension - AES Key - - The file name - -t - Specify file type (bin) explicitly, ignoring file extension - Signing Key file - - The file name - -t - Specify file type (pem) explicitly, ignoring file extension ``` ## partition From eb5c9811343e18cc462501738dbfe7ddc7f10bd6 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 1 Apr 2025 13:56:50 +0100 Subject: [PATCH 69/80] Review fixups Specify file types where useful for untyped files (json, pem, bin) Expand IV salt description Abstract filename_to_hex_array into separate function --- README.md | 2 +- main.cpp | 64 +++++++++++++++++++++++++++---------------------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 51a0a4d0..e4203072 100644 --- a/README.md +++ b/README.md @@ -592,7 +592,7 @@ The 4-way key share should be generated by creating a random .bin file of length Alternatively an AES key can be provided directly, as either a 32 byte .bin file or as a string of 64 hexadecimal characters (eg 0x0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210). Picotool will generate a random key share for that key, by generating 3 random shares (A, B & C) and calculating a 4th share (D) to match the provided key (X), with each word i of the 4th share calculated as D[i] = X[i] ^ A[i] ^ B[i] ^ C[i]. -The encryption also requires a per-device IV salt in OTP, which should be a 128-bit (16 byte) random file - this can be provided as either a 16 byte .bin file, or a string of 32 hexadecimal characters, similar to the AES key. This per-device salt will be XORed with the IV stored in the binary, to give the IV used by the decryption code. +The encryption/decryption code also salts the IV (initialisation vector) so it's not stored in plaintext in the binary. This requires a per-device IV salt in OTP, which should be a 128-bit (16 byte) random value - this can be provided as either a 16 byte .bin file, or a string of 32 hexadecimal characters, similar to the AES key. This per-device salt will be XORed with the IV stored in the binary, to give the IV used by the decryption code. ```text $ picotool help encrypt diff --git a/main.cpp b/main.cpp index d484cfc2..29e79327 100644 --- a/main.cpp +++ b/main.cpp @@ -863,9 +863,9 @@ struct encrypt_command : public cmd { ).force_expand_help(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + named_untyped_file_selection_x("aes_key", 2) % "AES Key Share or AES Key" + - named_untyped_file_selection_x("iv_otp", 3) % "IV OTP Salt" + - optional_untyped_file_selection_x("signing_key", 4) % "Signing Key file" + - optional_untyped_file_selection_x("otp", 5) % "File to save OTP to (will edit existing file if it exists)" + named_untyped_file_selection_x("iv_salt", 3) % "IV Salt" + + optional_untyped_file_selection_x("signing_key", 4) % "Signing Key file (.pem)" + + optional_untyped_file_selection_x("otp", 5) % "JSON file to save OTP to (will edit existing file if it exists)" ); } @@ -894,8 +894,8 @@ struct seal_command : public cmd { hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)" ).force_expand_help(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + - optional_untyped_file_selection_x("key", 2) % "Key file" + - optional_untyped_file_selection_x("otp", 3) % "File to save OTP to (will edit existing file if it exists)" + + optional_untyped_file_selection_x("key", 2) % "Key file (.pem)" + + optional_untyped_file_selection_x("otp", 3) % "JSON file to save OTP to (will edit existing file if it exists)" + ( option("--major") & integer("major").set(settings.seal.major_version) @@ -1177,12 +1177,12 @@ struct otp_permissions_command : public cmd { group get_cli() override { return ( - named_untyped_file_selection_x("filename", 0) % "File to load permissions from" + + named_untyped_file_selection_x("filename", 0) % "JSON file to load permissions from" + (option("--led") & integer("pin").set(settings.otp.led_pin)) % "LED Pin to flash; default 25" + ( option("--hash").set(settings.seal.hash) % "Hash the executable" + option("--sign").set(settings.seal.sign) % "Sign the executable" + - optional_untyped_file_selection_x("key", 2) % "Key file" + optional_untyped_file_selection_x("key", 2) % "Key file (.pem)" ).min(0).doc_non_optional(true) % "Signing Configuration" + device_selection % "Target device selection" ); @@ -1204,7 +1204,7 @@ struct otp_white_label_command : public cmd { ( (option('s', "--start_row") & integer("row").set(settings.otp.row)) % "Start row for white label struct (default 0x100) (note use 0x for hex)" ).min(0).doc_non_optional(true) % "Row options" + - named_untyped_file_selection_x("filename", 0) % "File with white labelling values" + + named_untyped_file_selection_x("filename", 0) % "JSON file with white labelling values" + device_selection % "Target device selection" ); } @@ -2717,6 +2717,28 @@ uint32_t guess_flash_size(memory_access &access) { return size * 2; } +// returns true if filename is a hex string, and fills array with the values +bool filename_to_hex_array(uint8_t idx, uint8_t *array, size_t size) { + auto filename = settings.filenames[idx]; + + if (!filename.empty() && filename.find("0x") == 0) { + // Hex string instead of file + if (filename.size() != size*2 + 2) { + fail(ERROR_ARGS, "Hex string must be %d characters long (the supplied string is %d characters)", size*2, filename.size() - 2); + } + for (size_t i=0; i < size; i++) { + auto value = "0x" + filename.substr(2 + i*2, 2); + auto ret = integer::parse_string(value, array[i]); + if (!ret.empty()) { + fail(ERROR_ARGS, "Invalid hex string: %s %s", value.c_str(), ret.c_str()); + } + } + return true; + } + + return false; +} + std::shared_ptr get_file_idx(ios::openmode mode, uint8_t idx) { auto filename = settings.filenames[idx]; auto file = std::make_shared(filename, mode); @@ -5024,36 +5046,14 @@ bool encrypt_command::execute(device_map &devices) { fail(ERROR_ARGS, "Can only sign to same file type"); } - if (!settings.filenames[2].empty() && settings.filenames[2].find("0x") == 0) { - // Hex string instead of file - if (settings.filenames[2].size() != 64 + 2) { - fail(ERROR_ARGS, "AES key hex string must be 32 bytes long"); - } + if (filename_to_hex_array(2, aes_key.bytes, sizeof(aes_key.bytes))) { keyFromFile = false; - for (int i=0; i < count_of(aes_key.bytes); i++) { - auto value = "0x" + settings.filenames[2].substr(2 + i*2, 2); - auto ret = integer::parse_string(value, aes_key.bytes[i]); - if (!ret.empty()) { - fail(ERROR_ARGS, "Invalid hex string: %s %s", value.c_str(), ret.c_str()); - } - } } else if (get_file_type_idx(2) != filetype::bin) { fail(ERROR_ARGS, "Can only read AES key share from BIN file"); } - if (!settings.filenames[3].empty() && settings.filenames[3].find("0x") == 0) { - // Hex string instead of file - if (settings.filenames[3].size() != 34) { - fail(ERROR_ARGS, "IV OTP salt hex string must be 16 bytes long"); - } + if (filename_to_hex_array(3, iv_salt.data(), iv_salt.size())) { ivFromFile = false; - for (int i=0; i < iv_salt.size(); i++) { - auto value = "0x" + settings.filenames[3].substr(2 + i*2, 2); - auto ret = integer::parse_string(value, iv_salt[i]); - if (!ret.empty()) { - fail(ERROR_ARGS, "Invalid hex string: %s %s", value.c_str(), ret.c_str()); - } - } } else if (get_file_type_idx(3) != filetype::bin) { fail(ERROR_ARGS, "Can only read IV OTP salt from BIN file"); } From ba3bf03076c6cceebb8d261e30be59d4ab37e655 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 2 Apr 2025 12:03:57 +0100 Subject: [PATCH 70/80] Improve external project inclusion Remove uneccessary extra setup, which isn't actually required and throws deprecation warnings in CMake 4.0.0 --- CMakeLists.txt | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e49dd1d8..c9dfcdc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,6 @@ endif() # todo better install paths for this set(DATA_LOCS "./" "${CMAKE_INSTALL_PREFIX}/${INSTALL_DATADIR}/") -message(${DATA_LOCS}) string(REGEX REPLACE ";" "\",\"" DATA_LOCS_VEC "${DATA_LOCS}") configure_file(data_locs.template.cpp ${CMAKE_CURRENT_BINARY_DIR}/data_locs.cpp) @@ -83,14 +82,6 @@ ExternalProject_Add(enc_bootloader ) set(ENC_BOOTLOADER_ELF ${CMAKE_BINARY_DIR}/enc_bootloader/enc_bootloader.elf) -add_executable(enc_bootloader_elf IMPORTED) -add_dependencies(enc_bootloader_elf enc_bootloader) -set_property(TARGET enc_bootloader_elf PROPERTY IMPORTED_LOCATION ${ENC_BOOTLOADER_ELF}) -# copy enc_bootloader.elf into build directory -add_custom_command(TARGET enc_bootloader - COMMAND ${CMAKE_COMMAND} -E copy ${ENC_BOOTLOADER_ELF} ${CMAKE_BINARY_DIR}/enc_bootloader.elf - DEPENDS enc_bootloader -) if (TARGET mbedtls) ExternalProject_Add(enc_bootloader_mbedtls @@ -108,14 +99,6 @@ if (TARGET mbedtls) ) set(ENC_BOOTLOADER_MBEDTLS_ELF ${CMAKE_BINARY_DIR}/enc_bootloader_mbedtls/enc_bootloader.elf) - add_executable(enc_bootloader_mbedtls_elf IMPORTED) - add_dependencies(enc_bootloader_mbedtls_elf enc_bootloader_mbedtls) - set_property(TARGET enc_bootloader_mbedtls_elf PROPERTY IMPORTED_LOCATION ${ENC_BOOTLOADER_MBEDTLS_ELF}) - # copy enc_bootloader_mbedtls.elf into build directory - add_custom_command(TARGET enc_bootloader_mbedtls - COMMAND ${CMAKE_COMMAND} -E copy ${ENC_BOOTLOADER_MBEDTLS_ELF} ${CMAKE_BINARY_DIR}/enc_bootloader_mbedtls.elf - DEPENDS enc_bootloader_mbedtls - ) endif() if (NOT PICOTOOL_NO_LIBUSB) @@ -134,14 +117,6 @@ if (NOT PICOTOOL_NO_LIBUSB) ) set(XIP_RAM_PERMS_ELF ${CMAKE_BINARY_DIR}/xip_ram_perms/xip_ram_perms.elf) - add_executable(xip_ram_perms_elf IMPORTED) - add_dependencies(xip_ram_perms_elf xip_ram_perms) - set_property(TARGET xip_ram_perms_elf PROPERTY IMPORTED_LOCATION ${XIP_RAM_PERMS_ELF}) - # copy xip_ram_perms.elf into build directory - add_custom_command(TARGET xip_ram_perms - COMMAND ${CMAKE_COMMAND} -E copy ${XIP_RAM_PERMS_ELF} ${CMAKE_BINARY_DIR}/xip_ram_perms.elf - DEPENDS xip_ram_perms - ) # compile flash_id ExternalProject_Add(flash_id @@ -158,14 +133,6 @@ if (NOT PICOTOOL_NO_LIBUSB) ) set(FLASH_ID_BIN ${CMAKE_BINARY_DIR}/picoboot_flash_id/flash_id.bin) - add_executable(flash_id_bin IMPORTED) - add_dependencies(flash_id_bin flash_id) - set_property(TARGET flash_id_bin PROPERTY IMPORTED_LOCATION ${FLASH_ID_BIN}) - # copy flash_id.bin into build directory - add_custom_command(TARGET flash_id - COMMAND ${CMAKE_COMMAND} -E copy ${FLASH_ID_BIN} ${CMAKE_BINARY_DIR}/flash_id.bin - DEPENDS flash_id - ) # We want to generate headers from WELCOME.HTM etc. ExternalProject_Add(otp_header_parser @@ -312,10 +279,10 @@ add_executable(picotool get_enc_bootloader.cpp ${OTP_EXE} main.cpp) -add_dependencies(picotool enc_bootloader_elf embedded_data_no_libusb) +add_dependencies(picotool embedded_data_no_libusb) if (NOT PICOTOOL_NO_LIBUSB) target_sources(picotool PRIVATE get_xip_ram_perms.cpp) - add_dependencies(picotool generate_otp_header xip_ram_perms_elf embedded_data) + add_dependencies(picotool generate_otp_header embedded_data) endif() set(PROJECT_VERSION 2.1.2-develop) set(PICOTOOL_VERSION 2.1.2-develop) From 954120fb654618f03f9e32e2d8e8511fa9178d2d Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 3 Apr 2025 14:05:25 +0100 Subject: [PATCH 71/80] Use named_untyped_file_selection for coprodis as well --- main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 29e79327..880ad464 100644 --- a/main.cpp +++ b/main.cpp @@ -1307,8 +1307,8 @@ struct coprodis_command : public cmd { return ( option("--quiet").set(settings.quiet) % "Don't print any output" + option("--verbose").set(settings.verbose) % "Print verbose output" + - named_file_selection_x("infile", 0) % "Input DIS" + - named_file_selection_x("outfile", 1) % "Output DIS" + named_untyped_file_selection_x("infile", 0) % "Input DIS" + + named_untyped_file_selection_x("outfile", 1) % "Output DIS" ); } From 1ff20e01b3e83fb5ba4730e71d6628364c41ce5d Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 3 Apr 2025 15:46:40 +0100 Subject: [PATCH 72/80] Implement FIB workaround by storing inverse of row n in row n+32 of each OTP page Disabled for now behind `#if FIB_WORKAROUND`, until the bootloader change to handle this is implemented --- main.cpp | 127 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 106 insertions(+), 21 deletions(-) diff --git a/main.cpp b/main.cpp index 880ad464..01a505cf 100644 --- a/main.cpp +++ b/main.cpp @@ -520,7 +520,7 @@ struct _settings { bool otp_key_page_set = false; bool fast_rosc = false; bool use_mbedtls = false; - uint16_t otp_key_page = 30; + uint16_t otp_key_page = 29; } encrypt; struct { @@ -4879,6 +4879,27 @@ bool load_command::execute(device_map &devices) { } #endif + +static uint32_t even_parity(uint32_t input) { + return __builtin_popcount(input) & 1; +} + +// In: 16-bit unsigned integer. Out: 22-bit unsigned integer. +uint32_t __noinline otp_calculate_ecc(uint16_t x) { + // Source: db_shf40_ap_ab.pdf, page 25, "TABLE 9: PARITY BIT GENERATION MAP + // FOR 16 BIT USER DATA (X24 SHF MACROCELL)" + // https://drive.google.com/drive/u/1/folders/1jgU3tZt2BDeGkWUFhi6KZAlaYUpGrFaG + uint32_t p0 = even_parity(x & 0b1010110101011011); + uint32_t p1 = even_parity(x & 0b0011011001101101); + uint32_t p2 = even_parity(x & 0b1100011110001110); + uint32_t p3 = even_parity(x & 0b0000011111110000); + uint32_t p4 = even_parity(x & 0b1111100000000000); + uint32_t p5 = even_parity(x) ^ p0 ^ p1 ^ p2 ^ p3 ^ p4; + uint32_t p = p0 | (p1 << 1) | (p2 << 2) | (p3 << 3) | (p4 << 4) | (p5 << 5); + return x | (p << 16); +} + + #if HAS_MBEDTLS void sign_guts_elf(elf_file* elf, private_t private_key, public_t public_key) { std::unique_ptr first_block = find_first_block(elf); @@ -5286,6 +5307,86 @@ bool encrypt_command::execute(device_map &devices) { } auto json_out = get_file_idx(ios::out, 5); + #if FIB_WORKAROUND + // Make inverse pages to work around OTP FIB attack + vector page0_data; + page0_data.resize(64); + vector page1_data; + page1_data.resize(64); + vector page2_data; + page2_data.resize(iv_salt.size()); + + // Inverse pages need to be raw, to invert the ECC bits too + vector page0_inverse; + page0_inverse.resize(page0_data.size()*2); + vector page1_inverse; + page1_inverse.resize(page1_data.size()*2); + vector page2_inverse; + page2_inverse.resize(page2_data.size()*2); + + memcpy(page0_data.data(), aes_key_share.bytes, 64); + memcpy(page1_data.data(), aes_key_share.bytes + 64, 64); + memcpy(page2_data.data(), iv_salt.data(), iv_salt.size()); + + // The bits in rows 32-63 must be the inverse of the bits in rows 0-31 + for (int i = 0; i < page0_data.size(); i += 2) { + page0_inverse[i*2] = ~page0_data[i]; + page0_inverse[i*2+1] = ~page0_data[i+1]; + page0_inverse[i*2+2] = ~otp_calculate_ecc(*(uint16_t*)&page0_data[i]) >> 16; + } + for (int i = 0; i < page1_data.size(); i += 2) { + page1_inverse[i*2] = ~page1_data[i]; + page1_inverse[i*2+1] = ~page1_data[i+1]; + page1_inverse[i*2+2] = ~otp_calculate_ecc(*(uint16_t*)&page1_data[i]) >> 16; + } + for (int i = 0; i < page2_data.size(); i += 2) { + page2_inverse[i*2] = ~page2_data[i]; + page2_inverse[i*2+1] = ~page2_data[i+1]; + page2_inverse[i*2+2] = ~otp_calculate_ecc(*(uint16_t*)&page2_data[i]) >> 16; + } + + // Add otp AES key pages + for (int i = 0; i < page0_data.size(); i++) { + std::stringstream ss; + ss << settings.encrypt.otp_key_page << ":0"; + otp_json[ss.str()]["ecc"] = true; + otp_json[ss.str()]["value"][i] = page0_data[i]; + } + for (int i = 0; i < page1_data.size(); i++) { + std::stringstream ss; + ss << settings.encrypt.otp_key_page + 1 << ":0"; + otp_json[ss.str()]["ecc"] = true; + otp_json[ss.str()]["value"][i] = page1_data[i]; + } + + // Add otp IV salt page + for (int i = 0; i < page2_data.size(); i++) { + std::stringstream ss; + ss << settings.encrypt.otp_key_page + 2 << ":0"; + otp_json[ss.str()]["ecc"] = true; + otp_json[ss.str()]["value"][i] = page2_data[i]; + } + + // Add inverse pages + for (int i = 0; i < page0_inverse.size(); i++) { + std::stringstream ss; + ss << settings.encrypt.otp_key_page << ":32"; + otp_json[ss.str()]["ecc"] = false; + otp_json[ss.str()]["value"][i] = page0_inverse[i]; + } + for (int i = 0; i < page1_inverse.size(); i++) { + std::stringstream ss; + ss << settings.encrypt.otp_key_page + 1 << ":32"; + otp_json[ss.str()]["ecc"] = false; + otp_json[ss.str()]["value"][i] = page1_inverse[i]; + } + for (int i = 0; i < page2_inverse.size(); i++) { + std::stringstream ss; + ss << settings.encrypt.otp_key_page + 2 << ":32"; + otp_json[ss.str()]["ecc"] = false; + otp_json[ss.str()]["value"][i] = page2_inverse[i]; + } + #else // Add otp AES key page for (int i = 0; i < 128; ++i) { std::stringstream ss; @@ -5301,6 +5402,7 @@ bool encrypt_command::execute(device_map &devices) { otp_json[ss.str()]["ecc"] = true; otp_json[ss.str()]["value"][i] = iv_salt[i]; } + #endif // Add page locks to prevent BL and NS access, and only allow S reads { @@ -5310,6 +5412,9 @@ bool encrypt_command::execute(device_map &devices) { ss.str(string()); ss << "PAGE" << settings.encrypt.otp_key_page + 1 << "_LOCK1"; otp_json[ss.str()] = "0x3d3d3d"; + ss.str(string()); + ss << "PAGE" << settings.encrypt.otp_key_page + 2 << "_LOCK1"; + otp_json[ss.str()] = "0x3d3d3d"; } *json_out << std::setw(4) << otp_json << std::endl; @@ -5960,26 +6065,6 @@ std::map, otp_match> filter_otp(std::vector return matches; } -// todo we could make this popcount at the cost of having this not be Armv6m or adding the popcount instruction to varmulet for bootrom -static uint32_t even_parity(uint32_t input) { - return __builtin_popcount(input) & 1; -} - -// In: 16-bit unsigned integer. Out: 22-bit unsigned integer. -uint32_t __noinline otp_calculate_ecc(uint16_t x) { - // Source: db_shf40_ap_ab.pdf, page 25, "TABLE 9: PARITY BIT GENERATION MAP - // FOR 16 BIT USER DATA (X24 SHF MACROCELL)" - // https://drive.google.com/drive/u/1/folders/1jgU3tZt2BDeGkWUFhi6KZAlaYUpGrFaG - uint32_t p0 = even_parity(x & 0b1010110101011011); - uint32_t p1 = even_parity(x & 0b0011011001101101); - uint32_t p2 = even_parity(x & 0b1100011110001110); - uint32_t p3 = even_parity(x & 0b0000011111110000); - uint32_t p4 = even_parity(x & 0b1111100000000000); - uint32_t p5 = even_parity(x) ^ p0 ^ p1 ^ p2 ^ p3 ^ p4; - uint32_t p = p0 | (p1 << 1) | (p2 << 2) | (p3 << 3) | (p4 << 4) | (p5 << 5); - return x | (p << 16); -} - #if HAS_LIBUSB static void hack_init_otp_regs(picoboot::connection& con) { // build map of OTP regs by offset From d6c5171e297affda299c6d7a8e7820a7e3f77c01 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 7 Apr 2025 17:52:16 +0100 Subject: [PATCH 73/80] Only delete existing load_maps when encrypting These only cause issues when encrypting, as the old block needs to be included in the new load_map When signing, the old load_map can be used again without issue --- bintool/bintool.cpp | 12 ++---------- main.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/bintool/bintool.cpp b/bintool/bintool.cpp index 8b01a2fc..5df18d24 100644 --- a/bintool/bintool.cpp +++ b/bintool/bintool.cpp @@ -327,11 +327,7 @@ block place_new_block(elf_file *elf, std::unique_ptr &first_block) { std::back_inserter(new_block.items)); } - // Delete existing load_map, signature and hash as these will be replaced with new ones - std::shared_ptr load_map = new_block.get_item(); - if (load_map != nullptr) { - new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), load_map), new_block.items.end()); - } + // Delete existing signature and hash as these will be replaced with new ones std::shared_ptr signature = new_block.get_item(); if (signature != nullptr) { new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), signature), new_block.items.end()); @@ -461,11 +457,7 @@ block place_new_block(std::vector &bin, uint32_t storage_addr, std::uni std::back_inserter(new_block.items)); } - // Delete existing load_map, signature and hash as these will be replaced with new ones - std::shared_ptr load_map = new_block.get_item(); - if (load_map != nullptr) { - new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), load_map), new_block.items.end()); - } + // Delete existing signature and hash as these will be replaced with new ones std::shared_ptr signature = new_block.get_item(); if (signature != nullptr) { new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), signature), new_block.items.end()); diff --git a/main.cpp b/main.cpp index 01a505cf..ce08b939 100644 --- a/main.cpp +++ b/main.cpp @@ -5160,6 +5160,12 @@ bool encrypt_command::execute(device_map &devices) { block new_block = place_new_block(elf, first_block); elf->editable = true; + // Delete existing load_map, as it will be invalid after encryption + std::shared_ptr load_map = new_block.get_item(); + if (load_map != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), load_map), new_block.items.end()); + } + if (settings.encrypt.embed) { std::vector iv_data; std::vector enc_data; @@ -5285,6 +5291,12 @@ bool encrypt_command::execute(device_map &devices) { auto bin_cp = bin; block new_block = place_new_block(bin_cp, bin_start, first_block); + // Delete existing load_map, as it will be invalid after encryption + std::shared_ptr load_map = new_block.get_item(); + if (load_map != nullptr) { + new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), load_map), new_block.items.end()); + } + auto enc_data = encrypt(bin, bin_start, bin_start, &new_block, aes_key, public_key, private_key, iv_salt, settings.seal.hash, settings.seal.sign); auto out = get_file_idx(ios::out|ios::binary, 1); From 61bf7c3ebdc19a2722ba48f07701597e4f25cb61 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 15 Apr 2025 15:26:31 +0100 Subject: [PATCH 74/80] Tidy up string_to_hex_array function --- main.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/main.cpp b/main.cpp index ce08b939..08bb3ab3 100644 --- a/main.cpp +++ b/main.cpp @@ -2717,17 +2717,16 @@ uint32_t guess_flash_size(memory_access &access) { return size * 2; } -// returns true if filename is a hex string, and fills array with the values -bool filename_to_hex_array(uint8_t idx, uint8_t *array, size_t size) { - auto filename = settings.filenames[idx]; +// returns true if string is a hex string, and fills array with the values +bool string_to_hex_array(const string& str, uint8_t *array, size_t size, const string& error_msg) { - if (!filename.empty() && filename.find("0x") == 0) { + if (!str.empty() && str.find("0x") == 0) { // Hex string instead of file - if (filename.size() != size*2 + 2) { - fail(ERROR_ARGS, "Hex string must be %d characters long (the supplied string is %d characters)", size*2, filename.size() - 2); + if (str.size() != size*2 + 2) { + fail(ERROR_ARGS, "%s hex string must be %d characters long (the supplied string is %d characters)", error_msg.c_str(), size*2, str.size() - 2); } for (size_t i=0; i < size; i++) { - auto value = "0x" + filename.substr(2 + i*2, 2); + auto value = "0x" + str.substr(2 + i*2, 2); auto ret = integer::parse_string(value, array[i]); if (!ret.empty()) { fail(ERROR_ARGS, "Invalid hex string: %s %s", value.c_str(), ret.c_str()); @@ -5067,13 +5066,13 @@ bool encrypt_command::execute(device_map &devices) { fail(ERROR_ARGS, "Can only sign to same file type"); } - if (filename_to_hex_array(2, aes_key.bytes, sizeof(aes_key.bytes))) { + if (string_to_hex_array(settings.filenames[2], aes_key.bytes, sizeof(aes_key.bytes), "AES key")) { keyFromFile = false; } else if (get_file_type_idx(2) != filetype::bin) { fail(ERROR_ARGS, "Can only read AES key share from BIN file"); } - if (filename_to_hex_array(3, iv_salt.data(), iv_salt.size())) { + if (string_to_hex_array(settings.filenames[3], iv_salt.data(), iv_salt.size(), "IV OTP salt")) { ivFromFile = false; } else if (get_file_type_idx(3) != filetype::bin) { fail(ERROR_ARGS, "Can only read IV OTP salt from BIN file"); From f018f8994b738f697a44d6ed8407f458ee81aaad Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 7 Apr 2025 14:34:04 +0100 Subject: [PATCH 75/80] Add permission handling to OTP dump Prints XXs for any rows with permissions failures Also, add `--pages` option to index by page & row (eg 63:56) rather than hex (eg 0ff8) --- main.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/main.cpp b/main.cpp index 08bb3ab3..be6f2b93 100644 --- a/main.cpp +++ b/main.cpp @@ -82,6 +82,7 @@ static __forceinline int __builtin_ctz(unsigned x) { #define OTP_PAGE_COUNT 64 #define OTP_PAGE_ROWS 64 #define OTP_ROW_COUNT (OTP_PAGE_COUNT * OTP_PAGE_ROWS) +#define OTP_SPECIAL_PAGES 3 using std::string; using std::vector; @@ -476,6 +477,7 @@ struct _settings { std::vector selectors; uint32_t row = 0; std::vector extra_files; + bool dump_pages = false; } otp; struct { @@ -1098,7 +1100,8 @@ struct otp_dump_command : public cmd { return ( ( option('r', "--raw").set(settings.otp.raw) % "Get raw 24 bit values" + - option('e', "--ecc").set(settings.otp.ecc) % "Use error correction" + option('e', "--ecc").set(settings.otp.ecc) % "Use error correction" + + option('p', "--pages").set(settings.otp.dump_pages) % "Index by page number & row number" ).min(0).doc_non_optional(true) % "Row/field options" + ( device_selection % "Target device selection" @@ -6108,7 +6111,7 @@ bool otp_get_command::execute(device_map &devices) { if (m.reg_row / OTP_PAGE_ROWS != last_page) { // todo pre-check page lock struct picoboot_otp_cmd otp_cmd; - if (m.reg_row / OTP_PAGE_ROWS >= 62) { + if (m.reg_row / OTP_PAGE_ROWS >= OTP_PAGE_COUNT - OTP_SPECIAL_PAGES) { // Read individual rows for lock words otp_cmd.wRow = m.reg_row; otp_cmd.wRowCount = 1; @@ -6262,20 +6265,59 @@ bool otp_dump_command::execute(device_map &devices) { auto con = get_single_rp2350_bootsel_device_connection(devices, false); // todo pre-check page lock struct picoboot_otp_cmd otp_cmd; - otp_cmd.wRow = 0; - otp_cmd.wRowCount = OTP_ROW_COUNT; otp_cmd.bEcc = settings.otp.ecc && !settings.otp.raw; vector raw_buffer; - raw_buffer.resize(otp_cmd.wRowCount * (otp_cmd.bEcc ? 2 : 4)); + uint8_t row_size = otp_cmd.bEcc ? 2 : 4; + raw_buffer.resize(OTP_ROW_COUNT * row_size); picoboot_memory_access raw_access(con); - con.otp_read(&otp_cmd, raw_buffer.data(), raw_buffer.size()); + std::map page_errors; + std::map row_errors; + + // Read most pages by page, as permissions are per page + otp_cmd.wRowCount = OTP_PAGE_ROWS; + for (int i=0; i < OTP_PAGE_COUNT - OTP_SPECIAL_PAGES; i++) { + otp_cmd.wRow = i * OTP_PAGE_ROWS; + try { + con.otp_read(&otp_cmd, raw_buffer.data() + i*(raw_buffer.size() / OTP_PAGE_COUNT), raw_buffer.size() / OTP_PAGE_COUNT); + } catch (picoboot::command_failure& e) { + if (e.get_code() == PICOBOOT_NOT_PERMITTED) { + page_errors[i] = e.what(); + } else { + throw e; + } + } + } + + // Read special pages by row, as permissions are special + otp_cmd.wRowCount = 1; + for (int i=(OTP_PAGE_COUNT - OTP_SPECIAL_PAGES) * OTP_PAGE_ROWS; i < OTP_PAGE_COUNT * OTP_PAGE_ROWS; i++) { + otp_cmd.wRow = i; + try { + con.otp_read(&otp_cmd, raw_buffer.data() + i * row_size, row_size); + } catch (picoboot::command_failure& e) { + if (e.get_code() == PICOBOOT_NOT_PERMITTED) { + row_errors[i] = e.what(); + } else { + throw e; + } + } + } + fos.first_column(0); char buf[256]; for(int i=0;i Date: Wed, 16 Apr 2025 15:10:00 +0100 Subject: [PATCH 76/80] Modify bootloader to work with FIB workaround Modify aes.S init_key_4way to skip the 64 byte gap in the middle of the otp key share Uses 5 words of space --- enc_bootloader/aes.S | 8 +++++++- enc_bootloader/enc_bootloader.c | 5 +++-- main.cpp | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/enc_bootloader/aes.S b/enc_bootloader/aes.S index 093c4b0f..e3ef4644 100644 --- a/enc_bootloader/aes.S +++ b/enc_bootloader/aes.S @@ -1441,7 +1441,7 @@ storeroundkey: .balign 4 .thumb_func init_key_4way: -@ On entry, r0 points to 4-way shared raw key data (128 bytes) +@ On entry, r0 points to 4-way shared raw key data (64 bytes, 64 byte gap for FIB workaround, then other 64 bytes) @ The format is a0 b0 c0 d0 a1 b1 c1 d1 ... a7 b7 c7 d7 @ That is, each word, K, of the original 256-bit key is expanded into four words whose exclusive OR is K. @ @@ -1464,12 +1464,18 @@ init_key_4way: bl randomisechaff ldr r6,=rkey4way movs r7,#8 + b 1f +2: + adds r5,#64 @ Skip 64 byte gap for FIB workaround + subs r7,r7,#1 1: ldmia r5!,{r1-r4} bl gen_rand_sha; eors r1,r1,r0; eors r4,r4,r0 bl gen_rand_sha; eors r2,r2,r0; eors r4,r4,r0 bl gen_rand_sha; eors r3,r3,r0; eors r4,r4,r0 stmia r6!,{r1-r4} + cmp r7,#5 + beq 2b subs r7,r7,#1 bne 1b diff --git a/enc_bootloader/enc_bootloader.c b/enc_bootloader/enc_bootloader.c index 1a68090c..60b0f427 100644 --- a/enc_bootloader/enc_bootloader.c +++ b/enc_bootloader/enc_bootloader.c @@ -153,6 +153,7 @@ bi_decl(bi_ptr_int32(0, 0, otp_key_page, 30)); // That is a suitable point to lock the OTP area where key information is stored. void lock_key() { otp_hw->sw_lock[otp_key_page] = 0xf; + otp_hw->sw_lock[otp_key_page + 1] = 0xf; } @@ -165,14 +166,14 @@ int main() { uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE; decrypt( (uint8_t*)&(otp_data[otp_key_page * 0x40]), - (uint8_t*)&(otp_data[(otp_key_page + 1) * 0x40]), + (uint8_t*)&(otp_data[(otp_key_page + 2) * 0x40]), (uint8_t*)iv, (void*)data_start_addr, data_size/16 ); // Lock the IV salt - otp_hw->sw_lock[otp_key_page + 1] = 0xf; + otp_hw->sw_lock[otp_key_page + 2] = 0xf; // Increase stack limit by 0x100 pico_default_asm_volatile( diff --git a/main.cpp b/main.cpp index be6f2b93..ddb5d3dd 100644 --- a/main.cpp +++ b/main.cpp @@ -5321,6 +5321,7 @@ bool encrypt_command::execute(device_map &devices) { } auto json_out = get_file_idx(ios::out, 5); + #define FIB_WORKAROUND 1 #if FIB_WORKAROUND // Make inverse pages to work around OTP FIB attack vector page0_data; From f9bf5e7f630e0e2448168ccf2e48179724309da1 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 24 Apr 2025 10:38:08 +0100 Subject: [PATCH 77/80] Review fixups Add more notes to AES readme, fix error message, and remove commented CMake line --- README.md | 4 +++- enc_bootloader/CMakeLists.txt | 1 - main.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e4203072..26ab9b79 100644 --- a/README.md +++ b/README.md @@ -586,12 +586,14 @@ The encrypted binary will have the following structure: - Padding to ensure the encrypted length is a multiple of 4 words - Signature metadata block -The AES key can either be provided as a 1024-bit (128 byte) 4-way key share or as a 256-bit (32 byte) AES key. In the latter case, a key share will be generated for you. +The AES key can either be provided as a 1024-bit (128 byte) 4-way key share or as a 256-bit (32 byte) AES key. In the latter case, a key share will be generated for you, as the decryption code needs a 4-way key share in OTP rather than a plain key. The 4-way key share should be generated by creating a random .bin file of length 128 bytes and the AES key will be derived from that. With the words stored in the file as A[0], B[0], C[0], D[0], A[1], B[1], etc., C[7], D[7], word i of the key X is X[i] = A[i] ^ B[i] ^ C[i] ^ D[i]. Alternatively an AES key can be provided directly, as either a 32 byte .bin file or as a string of 64 hexadecimal characters (eg 0x0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210). Picotool will generate a random key share for that key, by generating 3 random shares (A, B & C) and calculating a 4th share (D) to match the provided key (X), with each word i of the 4th share calculated as D[i] = X[i] ^ A[i] ^ B[i] ^ C[i]. +When using a .bin file, picotool will use the file size to determine if a 128 byte key share or a 32 byte key has been provided - any other file size will throw an error. + The encryption/decryption code also salts the IV (initialisation vector) so it's not stored in plaintext in the binary. This requires a per-device IV salt in OTP, which should be a 128-bit (16 byte) random value - this can be provided as either a 16 byte .bin file, or a string of 32 hexadecimal characters, similar to the AES key. This per-device salt will be XORed with the IV stored in the binary, to give the IV used by the decryption code. ```text diff --git a/enc_bootloader/CMakeLists.txt b/enc_bootloader/CMakeLists.txt index 661a78cd..45f3cc16 100644 --- a/enc_bootloader/CMakeLists.txt +++ b/enc_bootloader/CMakeLists.txt @@ -76,7 +76,6 @@ if (NOT USE_PRECOMPILED) PICO_RUNTIME_SKIP_INIT_SPIN_LOCKS_RESET=1 PICO_RUNTIME_SKIP_INIT_PER_CORE_IRQ_PRIORITIES=1 PICO_BOOTROM_LOCKING_ENABLED=0 - # PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE=1 # Don't need any vtor irqs PICO_MINIMAL_STORED_VECTOR_TABLE=1 PICO_NO_RAM_VECTOR_TABLE=1 diff --git a/main.cpp b/main.cpp index ddb5d3dd..7a3fa4af 100644 --- a/main.cpp +++ b/main.cpp @@ -5072,7 +5072,7 @@ bool encrypt_command::execute(device_map &devices) { if (string_to_hex_array(settings.filenames[2], aes_key.bytes, sizeof(aes_key.bytes), "AES key")) { keyFromFile = false; } else if (get_file_type_idx(2) != filetype::bin) { - fail(ERROR_ARGS, "Can only read AES key share from BIN file"); + fail(ERROR_ARGS, "Can only read AES key or AES key share from BIN file"); } if (string_to_hex_array(settings.filenames[3], iv_salt.data(), iv_salt.size(), "IV OTP salt")) { From 861a153cc518e1373316449968a9f7c17a309cda Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 24 Apr 2025 10:46:50 +0100 Subject: [PATCH 78/80] Mention mbedtls is "faster but less secure" in help text More details will be in the C SDK book --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 7a3fa4af..89c82f4a 100644 --- a/main.cpp +++ b/main.cpp @@ -849,7 +849,7 @@ struct encrypt_command : public cmd { option("--verbose").set(settings.verbose) % "Print verbose output" + option("--embed").set(settings.encrypt.embed) % "Embed bootloader in output file" + option("--fast-rosc").set(settings.encrypt.fast_rosc) % "Use ~180MHz ROSC configuration for embedded bootloader" + - option("--use-mbedtls").set(settings.encrypt.use_mbedtls) % "Use MbedTLS implementation of embedded bootloader" + + option("--use-mbedtls").set(settings.encrypt.use_mbedtls) % "Use MbedTLS implementation of embedded bootloader (faster but less secure)" + ( option("--otp-key-page").set(settings.encrypt.otp_key_page_set) % "Specify the OTP page storing the AES key (IV salt is stored on the next page)" & integer("page").set(settings.encrypt.otp_key_page) % "OTP page (default 30)" From b6374218774f89643502c8b6ed6f6ef3ac4b79bb Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 28 Apr 2025 14:06:19 +0100 Subject: [PATCH 79/80] Update README help text --- README.md | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index fd5ea22a..376d3da5 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ SYNOPSIS: picotool config [-s ] [-g ] [-t ] picotool load [--ignore-partitions] [--family ] [-p ] [-n] [-N] [-u] [-v] [-x] [-t ] [-o ] [device-selection] - picotool encrypt [--quiet] [--verbose] [--hash] [--sign] [-t ] [-o ] [-t ] [-t ] - [] [-t ] - picotool seal [--quiet] [--verbose] [--hash] [--sign] [--clear] [-t ] [-o ] [-t ] [] [-t - ] [] [-t ] [--major ] [--minor ] [--rollback [..]] + picotool encrypt [--quiet] [--verbose] [--embed] [--fast-rosc] [--use-mbedtls] [--otp-key-page ] [--hash] [--sign] [-t + ] [-o ] [-t ] + picotool seal [--quiet] [--verbose] [--hash] [--sign] [--clear] [-t ] [-o ] [-t ] + [--major ] [--minor ] [--rollback [..]] picotool link [--quiet] [--verbose] [-t ] [-t ] [-t ] [] [-t ] [-p ] picotool save [-p] [-v] [--family ] [-t ] [device-selection] picotool save -a [-v] [--family ] [-t ] [device-selection] @@ -33,7 +33,7 @@ SYNOPSIS: picotool partition info|create picotool uf2 info|convert picotool version [-s] [] - picotool coprodis [--quiet] [--verbose] [-t ] [-t ] + picotool coprodis [--quiet] [--verbose] picotool help [] COMMANDS: @@ -578,14 +578,18 @@ SEAL: Add final metadata to a binary, optionally including a hash and/or signature. SYNOPSIS: - picotool seal [--quiet] [--verbose] [--hash] [--sign] [--clear] [-t ] [-o ] [-t ] [] [-t - ] [] [-t ] [--major ] [--minor ] [--rollback [..]] + picotool seal [--quiet] [--verbose] [--hash] [--sign] [--clear] [-t ] [-o ] [-t ] + [--major ] [--minor ] [--rollback [..]] OPTIONS: --quiet Don't print any output --verbose Print verbose output + + Key file (.pem) + + JSON file to save OTP to (will edit existing file if it exists) --major Add Major Version --minor @@ -614,16 +618,6 @@ OPTIONS: The file name -t Specify file type (uf2 | elf | bin) explicitly, ignoring file extension - Key file - - The file name - -t - Specify file type (pem) explicitly, ignoring file extension - File to save OTP to (will edit existing file if it exists) - - The file name - -t - Specify file type (json) explicitly, ignoring file extension ``` ## encrypt @@ -654,8 +648,8 @@ ENCRYPT: Encrypt the program. SYNOPSIS: - picotool encrypt [--quiet] [--verbose] [--embed] [--fast-rosc] [--use-mbedtls] [--otp-key-page ] [--hash] [--sign] - [-t ] [-o ] [-t ] + picotool encrypt [--quiet] [--verbose] [--embed] [--fast-rosc] [--use-mbedtls] [--otp-key-page ] [--hash] [--sign] [-t + ] [-o ] [-t ] OPTIONS: --quiet @@ -667,19 +661,19 @@ OPTIONS: --fast-rosc Use ~180MHz ROSC configuration for embedded bootloader --use-mbedtls - Use MbedTLS implementation of embedded bootloader + Use MbedTLS implementation of embedded bootloader (faster but less secure) --otp-key-page Specify the OTP page storing the AES key (IV salt is stored on the next page) OTP page (default 30) AES Key Share or AES Key - - IV OTP Salt + + IV Salt - Signing Key file + Signing Key file (.pem) - File to save OTP to (will edit existing file if it exists) + JSON file to save OTP to (will edit existing file if it exists) Signing Configuration --hash Hash the encrypted file @@ -768,19 +762,16 @@ PARTITION CREATE: Create a partition table from json SYNOPSIS: - picotool partition create [--quiet] [--verbose] [-t ] [-t ] [[-o ] [--family ]] - [] [-t ] [[--sign ] [-t ] [--no-hash] [--singleton]] [[--abs-block] []] + picotool partition create [--quiet] [--verbose] [-t ] [[-o ] [--family ]] [] [-t + ] [[--sign ] [-t ] [--no-hash] [--singleton]] [[--abs-block] []] OPTIONS: --quiet Don't print any output --verbose Print verbose output - partition table JSON - The file name - -t - Specify file type (json) explicitly, ignoring file extension + partition table JSON output file The file name @@ -915,9 +906,9 @@ SYNOPSIS: picotool otp get [-c ] [-r] [-e] [-n] [-i ] [device-selection] [-z] [..] picotool otp set [-c ] [-r] [-e] [-s] [-i ] [-z] [device-selection] picotool otp load [-r] [-e] [-s ] [-i ] [-t ] [device-selection] - picotool otp dump [-r] [-e] [device-selection] - picotool otp permissions [-t ] [--led ] [--hash] [--sign] [] [-t ] [device-selection] - picotool otp white-label -s [-t ] [device-selection] + picotool otp dump [-r] [-e] [-p] [device-selection] + picotool otp permissions [--led ] [--hash] [--sign] [device-selection] + picotool otp white-label -s [device-selection] SUB COMMANDS: list List matching known registers/fields @@ -1120,14 +1111,11 @@ OTP WHITE-LABEL: Set the white labelling values in OTP SYNOPSIS: - picotool otp white-label -s [-t ] [device-selection] + picotool otp white-label -s [device-selection] OPTIONS: - File with white labelling values - The file name - -t - Specify file type (json) explicitly, ignoring file extension + JSON file with white labelling values Target device selection --bus Filter devices by USB bus number @@ -1199,14 +1187,11 @@ OTP PERMISSIONS: Set the OTP access permissions SYNOPSIS: - picotool otp permissions [-t ] [--led ] [--hash] [--sign] [] [-t ] [device-selection] + picotool otp permissions [--led ] [--hash] [--sign] [device-selection] OPTIONS: - File to load permissions from - The file name - -t - Specify file type (json) explicitly, ignoring file extension + JSON file to load permissions from --led LED Pin to flash; default 25 Signing Configuration @@ -1214,11 +1199,8 @@ OPTIONS: Hash the executable --sign Sign the executable - Key file - The file name - -t - Specify file type (pem) explicitly, ignoring file extension + Key file (.pem) Target device selection --bus Filter devices by USB bus number @@ -1269,7 +1251,7 @@ OTP DUMP: Dump entire OTP SYNOPSIS: - picotool otp dump [-r] [-e] [device-selection] + picotool otp dump [-r] [-e] [-p] [device-selection] OPTIONS: Row/field options @@ -1277,6 +1259,8 @@ OPTIONS: Get raw 24-bit values. This is the default -e, --ecc Use error correction + -p, --pages + Index by page number & row number TARGET SELECTION: Target device selection @@ -1348,23 +1332,17 @@ COPRODIS: Post-process coprocessor instructions in disassembly files. SYNOPSIS: - picotool coprodis [--quiet] [--verbose] [-t ] [-t ] + picotool coprodis [--quiet] [--verbose] OPTIONS: --quiet Don't print any output --verbose Print verbose output - Input DIS - The file name - -t - Specify file type (uf2 | elf | bin) explicitly, ignoring file extension - Output DIS + Input DIS - The file name - -t - Specify file type (uf2 | elf | bin) explicitly, ignoring file extension + Output DIS ``` ## link From b89a7e897a1bfc04b445d16496e70ff45acb9985 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 28 Apr 2025 14:34:58 +0100 Subject: [PATCH 80/80] Prevent PRs running test-examples twice, and give it a name --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1d2c71f3..edd7e4f2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,6 +73,9 @@ jobs: picotool info -a flash_nuke.uf2 test-examples: + # Prevent running twice for PRs from same repo + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + name: Test Build Examples runs-on: ubuntu-latest steps: - name: Checkout