Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for creating self-decrypting binaries, and use 4-way AES key shares instead of just the AES key #207

Open
wants to merge 46 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
89b792e
Use 4-way key shares for AES private keys
will-v-pi Jan 14, 2025
011111d
Remove check that ELF segments are between metadata blocks
will-v-pi Jan 15, 2025
90e2216
Fix overlapping memory ranges issue for encrypted binaries
will-v-pi Jan 15, 2025
c02717f
Update share as word-wise
will-v-pi Jan 20, 2025
8e5464a
Add enc_bootloader binary
will-v-pi Jan 20, 2025
4220510
Apply encrypted-example 6de8084b6eda
will-v-pi Jan 20, 2025
6186b68
Fixup no libusb build
will-v-pi Jan 20, 2025
3d0857c
Remove debug prints, and add USE_USB_DPRAM option
will-v-pi Jan 20, 2025
1cd9a30
Reduce binary size
will-v-pi Jan 20, 2025
3242ec5
Remove pico_rand and use boot random instead
will-v-pi Jan 21, 2025
a00a9a1
Update precompiled enc_bootloader.elf
will-v-pi Jan 21, 2025
1a585d2
Remove unused use_usb_dpram
will-v-pi Jan 21, 2025
02b7afa
Re-jig to create more space for the AES code
will-v-pi Jan 21, 2025
0cd7f74
Integrate new aes.S
will-v-pi Jan 22, 2025
16b7006
Use minimal vector table
will-v-pi Jan 22, 2025
f5a09f4
Pass TBYB and version from encrypted binary metadata to the decryptio…
will-v-pi Jan 22, 2025
b76f4e4
Add --otp-key-page argument
will-v-pi Jan 23, 2025
00db02b
Update aes.S
will-v-pi Jan 23, 2025
857fa02
Shrink enc_bootloader.c code further
will-v-pi Jan 29, 2025
684b6f6
Add otp file to enc
will-v-pi Jan 29, 2025
bb62382
Increase stack size by 0x80 to provide some headroom
will-v-pi Jan 29, 2025
f19f0ce
Integrate latest aes.S code
will-v-pi Feb 4, 2025
952dbd4
Use ROSC randomisation instead of CK_JITTER
will-v-pi Feb 7, 2025
956f514
Fix SDK branch for enc_bootloader
will-v-pi Feb 7, 2025
713f9e9
Add comments and slight size reduction
will-v-pi Feb 7, 2025
7bb027f
Uncomment chaff assertion
will-v-pi Feb 10, 2025
471efe9
Shrink IV storage size
will-v-pi Feb 10, 2025
242f189
Update enc_bootloader.elf with latest
will-v-pi Feb 10, 2025
9a3e4fe
Rejig clock init to prevent overclocking
will-v-pi Feb 10, 2025
081cb62
Update with latest aes.S
will-v-pi Feb 12, 2025
594b87c
Seed ROSC random using rstate_lfsr
will-v-pi Feb 12, 2025
34c4383
Use CK_JITTER to enable/disable ROSC randomisation
will-v-pi Feb 12, 2025
96f9ce1
Add IV XOR code, but turn it off with IV0_XOR for now
will-v-pi Feb 19, 2025
f6a0fd7
Disable calibration using XOSC by default
will-v-pi Feb 20, 2025
7eae923
Update with latest aes.S
will-v-pi Feb 20, 2025
6657416
Add fast-rosc option
will-v-pi Feb 20, 2025
6829265
clock_configure_mhz no longer used
will-v-pi Feb 24, 2025
e3fcac7
Tidy up indents in CMakeLists.txt
will-v-pi Feb 24, 2025
333d571
Remove CK_JITTER from aes.S, as this is now handled in the C code
will-v-pi Feb 24, 2025
a8bfde0
Throw error when using AES key share file of incorrect size
will-v-pi Feb 24, 2025
28c0335
Incorporate latest aes.S code, and add IV salt
will-v-pi Feb 26, 2025
401be66
Update enc_bootloader.elf with latest
will-v-pi Feb 26, 2025
92ffec2
Remove pico-sdk branch handling
will-v-pi Feb 26, 2025
c68be91
Update aes.S
will-v-pi Feb 26, 2025
e8ef20a
Add USE_MBEDTLS variant
will-v-pi Feb 26, 2025
043f7ab
Update with latest aes.S
will-v-pi Mar 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"],
Expand All @@ -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.
Expand Down Expand Up @@ -97,6 +118,7 @@ cc_binary(
}),
deps = [
":xip_ram_perms",
":enc_bootloader",
"//bazel:data_locs",
"//bintool",
"//elf",
Expand Down
53 changes: 50 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -169,6 +197,9 @@ 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
Expand All @@ -188,6 +219,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}
Expand Down Expand Up @@ -233,8 +272,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 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)
Expand Down Expand Up @@ -328,6 +369,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
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,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
Expand Down
19 changes: 13 additions & 6 deletions bintool/bintool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ std::vector<uint8_t> 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(
Expand Down Expand Up @@ -869,8 +869,7 @@ void verify_block(std::vector<uint8_t> 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) {

void encrypt_guts(elf_file *elf, block *new_block, const aes_key_t aes_key, std::vector<uint8_t> &iv_data, std::vector<uint8_t> &enc_data) {
std::vector<uint8_t> to_enc = get_lm_hash_data(elf, new_block);

std::random_device rand{};
Expand All @@ -886,13 +885,21 @@ int encrypt(elf_file *elf, block *new_block, const private_t aes_key, const publ
e = rand();
}

std::vector<uint8_t> 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<uint8_t> 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<uint8_t> iv_data;
std::vector<uint8_t> 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;
Expand Down Expand Up @@ -975,7 +982,7 @@ int encrypt(elf_file *elf, block *new_block, const private_t aes_key, const publ
}


std::vector<uint8_t> encrypt(std::vector<uint8_t> 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<uint8_t> encrypt(std::vector<uint8_t> 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);

Expand Down
5 changes: 3 additions & 2 deletions bintool/bintool.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ std::unique_ptr<block> find_first_block(elf_file *elf);
block place_new_block(elf_file *elf, std::unique_ptr<block> &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);
void encrypt_guts(elf_file *elf, block *new_block, const aes_key_t aes_key, std::vector<uint8_t> &iv_data, std::vector<uint8_t> &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

// Bins
Expand All @@ -37,6 +38,6 @@ block place_new_block(std::vector<uint8_t> &bin, uint32_t storage_addr, std::uni
uint32_t calc_checksum(std::vector<uint8_t> bin);
#if HAS_MBEDTLS
std::vector<uint8_t> hash_andor_sign(std::vector<uint8_t> 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<uint8_t> encrypt(std::vector<uint8_t> 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<uint8_t> encrypt(std::vector<uint8_t> 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<uint8_t> bin, uint32_t storage_addr, uint32_t runtime_addr, block *block, verified_t &hash_verified, verified_t &sig_verified);
#endif
49 changes: 48 additions & 1 deletion bintool/mbedtls_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,62 @@ 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) {
#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;
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;
}
#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) {
Expand Down
28 changes: 27 additions & 1 deletion bintool/mbedtls_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ extern "C" {
#include <mbedtls/ecp.h>
#include <mbedtls/aes.h>

/*
* 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 1

#ifdef __cplusplus
#define _Static_assert static_assert
#endif
Expand All @@ -38,11 +48,27 @@ 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 {
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;

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(
Expand Down
39 changes: 39 additions & 0 deletions enc_bootloader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

#include <algorithm>
#include <memory>
#include <iostream>
#include <sstream>
#include <fstream>

#include "enc_bootloader.h"
#include "enc_bootloader_elf.h"

#include "data_locs.h"

#include "whereami++.h"


std::shared_ptr<std::iostream> 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<std::fstream>(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<std::stringstream>();
tmp->write(reinterpret_cast<const char*>(enc_bootloader_elf), enc_bootloader_elf_SIZE);
return tmp;
}
12 changes: 12 additions & 0 deletions enc_bootloader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#pragma once

#include <memory>
#include <fstream>

std::shared_ptr<std::iostream> get_enc_bootloader();
Loading
Loading