From 858bd4a87859d42b5a1eab6e992364b46b729c04 Mon Sep 17 00:00:00 2001 From: Ayoub Zaki Date: Fri, 13 Dec 2024 20:32:39 +0100 Subject: [PATCH] add support for pkcs11 engine signing --- .github/workflows/cmake-single-platform.yml | 2 +- stm32mp-sign-tool.cpp | 90 ++++++++++++++++----- stm32mp-sign-tool_test.sh | 22 ++++- 3 files changed, 94 insertions(+), 20 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 359cb7e..b8a73b1 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v4 - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y openssl libssl-dev python3 + run: sudo apt-get update && sudo apt-get install -y openssl libssl-dev python3 softhsm2 - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type diff --git a/stm32mp-sign-tool.cpp b/stm32mp-sign-tool.cpp index 9c20872..0a89f2e 100644 --- a/stm32mp-sign-tool.cpp +++ b/stm32mp-sign-tool.cpp @@ -32,8 +32,10 @@ #include #include #include +#include static bool verbose = false; +static ENGINE* engine = nullptr; struct STM32Header { char magic[4]; @@ -108,23 +110,70 @@ void print_hex(const std::string& label, const std::vector& data) std::cout << std::endl; } -EC_KEY* load_key(const char* key_file) { - FILE* key_fp = fopen(key_file, "r"); - if (!key_fp) { - throw std::runtime_error("Failed to open key file"); - } +EC_KEY* load_key(const char* key_file, const char* passphrase) { + if (std::strncmp(key_file, "pkcs11:", 7) == 0) { + // Load key using PKCS#11 + EC_KEY* ec_key = nullptr; - EC_KEY* key = PEM_read_ECPrivateKey(key_fp, nullptr, nullptr, nullptr); - fclose(key_fp); - if (!key) { - throw std::runtime_error("Failed to read key"); - } + // Load the engine + ENGINE_load_builtin_engines(); + engine = ENGINE_by_id("pkcs11"); + if (!engine) { + throw std::runtime_error("Failed to load PKCS#11 engine"); + } + + // Initialize the engine + if (!ENGINE_init(engine)) { + ENGINE_free(engine); + throw std::runtime_error("Failed to initialize PKCS#11 engine"); + } + + // Set the PIN + if (passphrase && !ENGINE_ctrl_cmd_string(engine, "PIN", passphrase, 0)) { + ENGINE_finish(engine); + ENGINE_free(engine); + throw std::runtime_error("Failed to set PKCS#11 PIN"); + } + + // Load the private key + EVP_PKEY* pkey = ENGINE_load_private_key(engine, key_file, nullptr, (void*)passphrase); + if (!pkey) { + ENGINE_finish(engine); + ENGINE_free(engine); + throw std::runtime_error("Failed to load private key from PKCS#11"); + } + + // Extract the EC_KEY from the EVP_PKEY + ec_key = EVP_PKEY_get1_EC_KEY(pkey); + EVP_PKEY_free(pkey); - return key; + if (!ec_key) { + ENGINE_finish(engine); + ENGINE_free(engine); + throw std::runtime_error("Failed to extract EC_KEY from EVP_PKEY"); + } + + return ec_key; + } + else { + // Load key from file + FILE* key_fp = fopen(key_file, "r"); + if (!key_fp) { + throw std::runtime_error("Failed to open key file"); + } + + EC_KEY* key = PEM_read_ECPrivateKey(key_fp, nullptr, nullptr, (void*)passphrase); + fclose(key_fp); + if (!key) { + throw std::runtime_error("Failed to read key from file"); + } + + return key; + } } -int verify_stm32_image(const std::vector& image, const char* key_file) { - EC_KEY* key = load_key(key_file); +int verify_stm32_image(const std::vector& image, const char* key_file, const char* passphrase) { + EC_KEY* key = load_key(key_file, passphrase); STM32Header header = unpack_stm32_header(image); if (std::strncmp(header.magic, "STM2", sizeof(header.magic)) != 0) { @@ -196,8 +245,8 @@ int verify_stm32_image(const std::vector& image, const char* key_ } } -int sign_stm32_image(std::vector& image, const char* key_file) { - EC_KEY* key = load_key(key_file); +int sign_stm32_image(std::vector& image, const char* key_file, const char* passphrase) { + EC_KEY* key = load_key(key_file, passphrase); STM32Header header = unpack_stm32_header(image); if (std::strncmp(header.magic, "STM2", sizeof(header.magic)) != 0) { @@ -264,7 +313,7 @@ int sign_stm32_image(std::vector& image, const char* key_file) { EC_KEY_free(key); // Verify the signature - if (verify_stm32_image(image, key_file)) { + if (verify_stm32_image(image, key_file, passphrase)) { return 1; } @@ -296,7 +345,7 @@ int main(int argc, char* argv[]) { output_file = optarg; break; default: - std::cerr << "Usage: " << argv[0] << " -k key_file [-p passphrase] [-v] [-i input_file] [-o output_file]" << std::endl; + std::cerr << "Usage: " << argv[0] << " -k key_file [-p passphrase/pin] [-v] [-i input_file] [-o output_file]" << std::endl; return 1; } } @@ -311,7 +360,7 @@ int main(int argc, char* argv[]) { std::vector image((std::istreambuf_iterator(image_file)), std::istreambuf_iterator()); image_file.close(); - if (sign_stm32_image(image, key_file) != 0) { + if (sign_stm32_image(image, key_file, passphrase) != 0) { return 1; } @@ -322,5 +371,10 @@ int main(int argc, char* argv[]) { } } + if (engine) { + ENGINE_finish(engine); + ENGINE_free(engine); + } + return 0; } diff --git a/stm32mp-sign-tool_test.sh b/stm32mp-sign-tool_test.sh index a1f08ce..088b316 100755 --- a/stm32mp-sign-tool_test.sh +++ b/stm32mp-sign-tool_test.sh @@ -1,4 +1,8 @@ -#!/bin/bash -e +#!/bin/sh -e +# +# Copyright (c) 2024 +# Embetrix Embedded Systems Solutions, ayoub.zaki@embetrix.com +# dd if=/dev/urandom of=image.bin bs=1M count=1 > /dev/null 2>&1 @@ -8,4 +12,20 @@ python3 stm32mp-gen-image.py image.stm32 image.bin openssl ecparam -name prime256v1 -genkey -out private_key.pem openssl ec -in private_key.pem -pubout -out public_key.pem +# test plain key file ./stm32mp-sign-tool -v -k private_key.pem -i image.stm32 -o image.stm32.signed + +# test pkcs11 key +export PKCS11_MODULE_PATH=/usr/lib/softhsm/libsofthsm2.so +export PIN="12345" +export SO_PIN="1234" +export SOFTHSM2_CONF=$PWD/.softhsm/softhsm2.conf +export TOKEN_NAME="token0" + +mkdir -p .softhsm/tokens +echo "directories.tokendir = $PWD/.softhsm/tokens" > .softhsm/softhsm2.conf +pkcs11-tool --pin $PIN --module $PKCS11_MODULE_PATH --slot-index=0 --init-token --label=$TOKEN_NAME --so-pin $SO_PIN --init-pin +pkcs11-tool --pin $PIN --module $PKCS11_MODULE_PATH --keypairgen --key-type EC:prime256v1 --id 1 --label "testkeyECp256" +./stm32mp-sign-tool -v -k "pkcs11:object=testkeyECp256" -i image.stm32 -p 12345 -o image.stm32.signed + +