wolfBoot/src/image.c

2961 lines
97 KiB
C

/* image.c
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/**
* @file image.c
* @brief This file contains functions related to image handling and
* verification.
*/
#ifdef UNIT_TEST
#include <stdio.h>
#endif
#include <wolfssl/wolfcrypt/settings.h> /* for wolfCrypt hash/sign routines */
#ifdef WOLFBOOT_KEYTOOLS
/* this code needs to use the Use ./include/user_settings.h, not keytools */
#error "The wrong user_settings.h has been included."
#endif
#include <stddef.h>
#include <string.h>
#include "loader.h"
#include "image.h"
#include "wolfboot/wolfboot.h"
#include "hal.h"
#include "spi_drv.h"
#include "printf.h"
#ifdef WOLFBOOT_TPM
#include "tpm.h"
#endif
#ifdef WOLFBOOT_HASH_SHA256
#include <wolfssl/wolfcrypt/sha256.h>
#endif
#ifdef WOLFBOOT_HASH_SHA384
#include <wolfssl/wolfcrypt/sha512.h>
#endif
#ifdef WOLFBOOT_HASH_SHA3_384
#include <wolfssl/wolfcrypt/sha3.h>
#endif
/* Globals */
static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE] XALIGNED(4);
#ifdef WOLFBOOT_ARMORED
/* Accumulator seed. Low byte clear so byte differences are never masked. */
#define CT_SENTINEL 0xA5C3F000U
/**
* Constant-time buffer comparison, hardened against instruction skips.
* Returns 0 when equal, non-zero otherwise.
*/
int NOINLINEFUNCTION image_CT_compare(
const uint8_t *expected, const uint8_t *actual, uint32_t len)
{
volatile uint32_t diff = CT_SENTINEL;
volatile uint32_t witness = 0U;
volatile uint32_t count = 0U;
volatile uint32_t i = 0U;
volatile uint32_t budget = len;
volatile uint32_t res = 0U;
uint32_t expected_witness;
uint32_t len_is_zero;
/* Two counters bound the loop, so either one can end it. */
for (i = 0; (i < len) && (budget != 0U); i++) {
diff |= (uint32_t)(expected[i] ^ actual[i]);
witness += i + 1U;
count++;
budget--;
}
expected_witness = (len * (len + 1U)) / 2U; /* sum(1..len) */
len_is_zero = 1U ^ ((len | (0U - len)) >> 31);
/* Folded twice, branch-free. */
res = (diff ^ CT_SENTINEL);
res |= (witness ^ expected_witness);
res |= (count ^ len);
res |= (i ^ len);
res |= len_is_zero;
res |= (diff ^ CT_SENTINEL);
res |= (witness ^ expected_witness);
res |= (count ^ len);
res |= (i ^ len);
res |= len_is_zero;
return (int)res;
}
#undef CT_SENTINEL
#else
int NOINLINEFUNCTION image_CT_compare(
const uint8_t *expected, const uint8_t *actual, uint32_t len)
{
volatile uint32_t diff = 0U;
uint32_t i;
for (i = 0; i < len; i++) {
diff |= (uint32_t)(expected[i] ^ actual[i]);
}
return (diff != 0U) ? 1 : 0;
}
#endif /* WOLFBOOT_ARMORED */
/**
* Fault-hardened equality check around image_CT_compare(): the constant-time
* comparison is run twice and a match is reported only when both independent
* calls agree. A single instruction-skip fault can subvert at most one of the
* two calls (or one of the two result checks), so a genuine mismatch is still
* detected. Returns 0 when equal, non-zero otherwise.
*/
int NOINLINEFUNCTION wolfBoot_hardened_CT_compare(
const uint8_t *expected, const uint8_t *actual, uint32_t len)
{
volatile int r1 = image_CT_compare(expected, actual, len);
volatile int r2 = image_CT_compare(expected, actual, len);
/* Combine both results without branching: non-zero if either independent
* comparison reported a mismatch. */
return (r1 | r2);
}
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \
(defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER))
static whKeyId g_certLeafKeyId = WH_KEYID_ERASED;
static int g_leafKeyIdValid = 0;
#endif
/* TPM based verify */
#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_VERIFY)
#ifdef ECC_IMAGE_SIGNATURE_SIZE
#define IMAGE_SIGNATURE_SIZE ECC_IMAGE_SIGNATURE_SIZE
#else
#define IMAGE_SIGNATURE_SIZE RSA_IMAGE_SIGNATURE_SIZE
#endif
static void wolfBoot_verify_signature_tpm(uint8_t key_slot,
struct wolfBoot_image *img, uint8_t *sig)
{
int ret = 0, verify_res = 0;
WOLFTPM2_KEY tpmKey;
TPM_ALG_ID alg, sigAlg;
uint8_t *hdr;
uint16_t hdrSz;
/* Load public key into TPM */
memset(&tpmKey, 0, sizeof(tpmKey));
/* get public key for policy authorization */
hdrSz = wolfBoot_get_header(img, HDR_PUBKEY, &hdr);
if (hdrSz != WOLFBOOT_SHA_DIGEST_SIZE) {
ret = -1;
}
if (ret == 0) {
ret = wolfBoot_load_pubkey(hdr /* pubkey_hint */, &tpmKey, &alg);
}
if (ret == 0) {
sigAlg = (alg == TPM_ALG_RSA) ? TPM_ALG_RSASSA : TPM_ALG_ECDSA;
ret = wolfTPM2_VerifyHashScheme(&wolftpm_dev, &tpmKey,
sig, /* Signature */
IMAGE_SIGNATURE_SIZE, /* Signature size */
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, /* Hash */
sigAlg, WOLFBOOT_TPM_HASH_ALG);
}
/* unload handle regardless of result */
wolfTPM2_UnloadHandle(&wolftpm_dev, &tpmKey.handle);
if (ret == 0) {
verify_res = 1; /* TPM does hash verify compare */
if ((~(uint32_t)ret == 0xFFFFFFFF) && (verify_res == 1) &&
(~(uint32_t)verify_res == 0xFFFFFFFE)) {
wolfBoot_image_confirm_signature_ok(img);
}
}
else {
wolfBoot_printf("TPM verify signature error %d (%s)\n",
ret, wolfTPM2_GetRCString(ret));
}
(void)key_slot;
}
#else
/* wolfCrypt software verify */
#ifdef WOLFBOOT_SIGN_ED25519
#include <wolfssl/wolfcrypt/ed25519.h>
static void wolfBoot_verify_signature_ed25519(uint8_t key_slot,
struct wolfBoot_image *img, uint8_t *sig)
{
int ret, res;
ed25519_key ed;
ret = wc_ed25519_init_ex(&ed, NULL, WOLFBOOT_DEVID_PUBKEY);
if (ret < 0) {
/* Failed to initialize key */
return;
}
ret = wc_ed25519_import_public(keystore_get_buffer(key_slot),
KEYSTORE_PUBKEY_SIZE, &ed);
if (ret < 0) {
/* Failed to import ed25519 key */
return;
}
VERIFY_FN(img, &res, wc_ed25519_verify_msg, sig, ED25519_IMAGE_SIGNATURE_SIZE,
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, &res, &ed);
}
#endif /* WOLFBOOT_SIGN_ED25519 */
#ifdef WOLFBOOT_SIGN_ED448
#include <wolfssl/wolfcrypt/ed448.h>
static void wolfBoot_verify_signature_ed448(uint8_t key_slot,
struct wolfBoot_image *img, uint8_t *sig)
{
int ret, res;
ed448_key ed;
ret = wc_ed448_init_ex(&ed, NULL, WOLFBOOT_DEVID_PUBKEY);
if (ret < 0) {
/* Failed to initialize key */
return;
}
ret = wc_ed448_import_public(keystore_get_buffer(key_slot),
KEYSTORE_PUBKEY_SIZE, &ed);
if (ret < 0) {
/* Failed to import ed448 key */
return;
}
VERIFY_FN(img, &res, wc_ed448_verify_msg, sig, ED448_IMAGE_SIGNATURE_SIZE,
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, &res, &ed, NULL, 0);
}
#endif
#if defined(WOLFBOOT_SIGN_ECC256) || \
defined(WOLFBOOT_SIGN_ECC384) || \
defined(WOLFBOOT_SIGN_ECC521) || \
defined(WOLFBOOT_SIGN_SECONDARY_ECC256) || \
defined(WOLFBOOT_SIGN_SECONDARY_ECC384) || \
defined(WOLFBOOT_SIGN_SECONDARY_ECC521)
#include <wolfssl/wolfcrypt/ecc.h>
#if defined(WOLFBOOT_SIGN_ECC256) || defined(WOLFBOOT_SIGN_SECONDARY_ECC256)
#define ECC_KEY_TYPE ECC_SECP256R1
#elif defined(WOLFBOOT_SIGN_ECC384) || defined(WOLFBOOT_SIGN_SECONDARY_ECC384)
#define ECC_KEY_TYPE ECC_SECP384R1
#elif defined(WOLFBOOT_SIGN_ECC521) || defined(WOLFBOOT_SIGN_SECONDARY_ECC521)
#define ECC_KEY_TYPE ECC_SECP521R1
#endif
/**
* @brief Verify the signature of the image using the provided key slot
* and signature.
*
* @param key_slot The key slot ID to use for verification.
* @param img The image to verify.
* @param sig The signature to use for verification.
*/
static void wolfBoot_verify_signature_ecc(uint8_t key_slot,
struct wolfBoot_image *img, uint8_t *sig)
{
int ret, verify_res = 0;
#if defined(__TMS320C28XX__) || defined(WOLFBOOT_ARCH_C2000)
/* C28x: the ecc_key struct is large relative to the 16-bit-SP low-RAM stack
* (WOLFSSL_NO_MALLOC keeps SP-256 verify temporaries on the stack too), so
* keep it in .bss to avoid overflowing the stack into adjacent RAM during
* verify. wolfBoot verifies images sequentially and wc_ecc_init_ex/
* wc_ecc_free bracket each use, so a single shared instance is safe. The
* mp_ints r/s are small and stay on the stack, freshly mp_init'd per call. */
static ecc_key ecc;
#else
ecc_key ecc;
#endif
#if !defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \
!defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
uint8_t* pubkey = keystore_get_buffer(key_slot);
int pubkey_sz = keystore_get_size(key_slot);
int point_sz = pubkey_sz / 2;
if (pubkey == NULL || pubkey_sz <= 0) {
return;
}
#endif
ret = wc_ecc_init_ex(&ecc, NULL, WOLFBOOT_DEVID_PUBKEY);
if (ret == 0) {
#if defined(WOLFBOOT_RENESAS_SCEPROTECT) || \
defined(WOLFBOOT_RENESAS_TSIP) || \
defined(WOLFBOOT_RENESAS_RSIP)
/* The public key is wrapped and cannot be imported.
* Key must be loaded to TSIP and unwrapped.
* Then ECDSA crypto callback will perform verify on TSIP hardware */
wc_ecc_set_curve(&ecc, 0, ECC_KEY_TYPE);
/* The wc_ecc_verify_hash must be used since _ex version does not
* trigger crypto callback. Building with NO_ASN allows us to send R+S
* directly without ASN.1 encoded DSA header */
VERIFY_FN(img, &verify_res, wc_ecc_verify_hash,
sig, ECC_IMAGE_SIGNATURE_SIZE,
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, &verify_res, &ecc)
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
uint8_t tmpSigBuf[ECC_MAX_SIG_SIZE] = {0};
word32 tmpSigSz = sizeof(tmpSigBuf);
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
(defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) && \
defined(WOLFBOOT_CERT_CHAIN_VERIFY))
(void)key_slot;
/* hardcoded, since not using keystore */
const int point_sz = ECC_IMAGE_SIGNATURE_SIZE / 2;
/* Use the public key ID to verify the signature */
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
/* If using certificate chain verification and we have a verified leaf
* key ID */
if (g_leafKeyIdValid) {
/* Use the leaf key ID from certificate verification */
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
ret = wh_Client_EccSetKeyId(&ecc, g_certLeafKeyId);
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
ret = wh_Server_EccKeyCacheExport(&hsmServerCtx, g_certLeafKeyId,
&ecc);
#endif
wolfBoot_printf(
"Using leaf cert public key (ID: %08x) for ECC verification\n",
(unsigned int)g_certLeafKeyId);
}
else {
/* Default behavior: use the pre-configured public key ID */
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
ret = wh_Client_EccSetKeyId(&ecc, hsmKeyIdPubKey);
#endif
}
#else /* WOLFBOOT_CERT_CHAIN_VERIFY */
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
ret = wh_Client_EccSetKeyId(&ecc, hsmKeyIdPubKey);
#endif
#endif /* !WOLFBOOT_CERT_CHAIN_VERIFY */
if (ret != 0) {
wc_ecc_free(&ecc);
return;
}
#else
/* First, import public key from the keystore to the local wolfCrypt
* struct, then import into wolfHSM key cache for subsequent
* verification */
ret = wc_ecc_import_unsigned(&ecc, pubkey, pubkey + point_sz, NULL,
ECC_KEY_TYPE);
if (ret != 0) {
wc_ecc_free(&ecc);
return;
}
#endif /* WOLFBOOT_ENABLE_WOLFHSM_CLIENT || (SERVER && CERT_CHAIN) */
/* wc_ecc_verify_hash_ex() doesn't trigger a crypto callback, so we need
to use wc_ecc_verify_hash instead. Unfortunately, that requires
converting the signature to intermediate DER format first. Both
fields are passed at full width: the raw signature is fixed-width
and left-zero-padded, and the conversion strips the padding. */
ret = wc_ecc_rs_raw_to_sig(sig, (word32)point_sz, &sig[point_sz],
(word32)point_sz,
(byte*)&tmpSigBuf, &tmpSigSz);
/* Verify the (temporary) DER representation of the signature */
if (ret == 0) {
VERIFY_FN(img, &verify_res, wc_ecc_verify_hash, tmpSigBuf, tmpSigSz,
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, &verify_res,
&ecc);
}
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
if (g_leafKeyIdValid) {
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
(void)wh_Client_KeyEvict(&hsmClientCtx, g_certLeafKeyId);
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
(void)wh_Server_KeystoreEvictKey(&hsmServerCtx, g_certLeafKeyId);
#endif
g_leafKeyIdValid = 0;
}
#endif
#else
mp_int r, s;
/* Import public key */
ret = wc_ecc_import_unsigned(&ecc, pubkey, pubkey + point_sz, NULL,
ECC_KEY_TYPE);
if (ret == 0 && ecc.type == ECC_PUBLICKEY) {
/* Import signature into r,s */
mp_init(&r);
mp_init(&s);
mp_read_unsigned_bin(&r, sig, point_sz);
mp_read_unsigned_bin(&s, sig + point_sz, point_sz);
VERIFY_FN(img, &verify_res, wc_ecc_verify_hash_ex, &r, &s,
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, &verify_res, &ecc);
}
#endif
}
wc_ecc_free(&ecc);
}
#endif /* WOLFBOOT_SIGN_ECC256 || WOLFBOOT_SIGN_ECC384 || WOLFBOOT_SIGN_ECC521 ||
* WOLFBOOT_SIGN_SECONDARY_ECC256 || WOLFBOOT_SIGN_SECONDARY_ECC384 ||
* WOLFBOOT_SIGN_SECONDARY_ECC521 */
#ifdef WOLFBOOT_SIGN_RSA_ANY
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/rsa.h>
#if defined(WOLFBOOT_SIGN_RSA4096) && \
(defined(USE_FAST_MATH) && \
!defined(WOLFSSL_SMALL_STACK) && !defined(WOLFBOOT_HUGE_STACK))
#error "TFM will allocate 70+ KB in the stack with this configuration." \
"If this is OK, please compile with WOLFBOOT_HUGE_STACK=1"
#endif
#ifndef NO_RSA_SIG_ENCODING /* option to reduce code size */
static inline int DecodeAsn1Tag(const uint8_t* input, int inputSz, int* inOutIdx,
int* tag_len, uint8_t tag)
{
if (*inOutIdx < 0 || *inOutIdx >= inputSz || (*inOutIdx + 1) >= inputSz) {
return -1;
}
if (input[*inOutIdx] != tag) {
return -1;
}
(*inOutIdx)++;
*tag_len = input[*inOutIdx];
(*inOutIdx)++;
if (*tag_len + *inOutIdx > inputSz) {
return -1;
}
return 0;
}
static int RsaDecodeSignature(uint8_t** pInput, int inputSz)
{
uint8_t* input = *pInput;
int idx = 0;
int digest_len = 0, algo_len, tot_len;
/* sequence - total size */
if (DecodeAsn1Tag(input, inputSz, &idx, &tot_len,
ASN_SEQUENCE | ASN_CONSTRUCTED) != 0) {
return -1;
}
/* sequence - algoid */
if (DecodeAsn1Tag(input, inputSz, &idx, &algo_len,
ASN_SEQUENCE | ASN_CONSTRUCTED) != 0) {
return -1;
}
idx += algo_len; /* skip algoid */
/* digest */
if (DecodeAsn1Tag(input, inputSz, &idx, &digest_len,
ASN_OCTET_STRING) != 0) {
return -1;
}
/* return digest buffer pointer */
*pInput = &input[idx];
return digest_len;
}
#endif /* !NO_RSA_SIG_ENCODING */
static void wolfBoot_verify_signature_rsa_common(uint8_t key_slot,
struct wolfBoot_image *img, uint8_t *sig, int is_pss)
{
int ret;
uint8_t output[RSA_IMAGE_SIGNATURE_SIZE];
uint8_t* digest_out = NULL;
word32 inOutIdx = 0;
struct RsaKey rsa;
(void)inOutIdx;
(void)is_pss;
#ifdef WOLFBOOT_SIGN_RSAPSS_ANY
enum wc_HashType hash_type;
int mgf;
#if defined(WOLFBOOT_HASH_SHA256)
hash_type = WC_HASH_TYPE_SHA256;
mgf = WC_MGF1SHA256;
#elif defined(WOLFBOOT_HASH_SHA384)
hash_type = WC_HASH_TYPE_SHA384;
mgf = WC_MGF1SHA384;
#else
#error "RSA-PSS requires SHA-256 or SHA-384"
#endif
#endif /* WOLFBOOT_SIGN_RSAPSS_ANY */
#if !defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \
!defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
uint8_t *pubkey = keystore_get_buffer(key_slot);
int pubkey_sz = keystore_get_size(key_slot);
if (pubkey == NULL || pubkey_sz < 0) {
return;
}
#endif
#if defined(WOLFBOOT_RENESAS_SCEPROTECT) || \
defined(WOLFBOOT_RENESAS_TSIP) || \
defined(WOLFBOOT_RENESAS_RSIP)
/* Renesas crypto callback supports RSA PKCS#1 v1.5 only */
#ifdef WOLFBOOT_SIGN_RSAPSS_ANY
#error "RSA-PSS is not yet supported with Renesas crypto callbacks"
#endif
ret = wc_InitRsaKey_ex(&rsa, NULL, WOLFBOOT_DEVID_PUBKEY);
if (ret == 0) {
XMEMCPY(output, sig, RSA_IMAGE_SIGNATURE_SIZE);
RSA_VERIFY_FN(ret,
wc_RsaSSL_Verify, img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE,
output, RSA_IMAGE_SIGNATURE_SIZE, &rsa);
/* The crypto callback success also verifies hash */
if (ret == 0)
wolfBoot_image_confirm_signature_ok(img);
}
(void)digest_out;
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
ret = wc_InitRsaKey_ex(&rsa, NULL, WOLFBOOT_DEVID_PUBKEY);
if (ret != 0) {
return;
}
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
(defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) && \
defined(WOLFBOOT_CERT_CHAIN_VERIFY))
(void)key_slot;
/* public key is stored on server at hsmKeyIdPubKey*/
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
/* If using certificate chain verification and we have a verified leaf key
* ID */
if (g_leafKeyIdValid) {
/* Use the leaf key ID from certificate verification */
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
ret = wh_Client_RsaSetKeyId(&rsa, g_certLeafKeyId);
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
ret = wh_Server_CacheExportRsaKey(&hsmServerCtx, g_certLeafKeyId, &rsa);
#endif
wolfBoot_printf(
"Using leaf cert public key (ID: %08x) for RSA verification\n",
(unsigned int)g_certLeafKeyId);
}
else {
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
/* Default behavior: use the pre-configured public key ID */
ret = wh_Client_RsaSetKeyId(&rsa, hsmKeyIdPubKey);
#endif
}
#else
ret = wh_Client_RsaSetKeyId(&rsa, hsmKeyIdPubKey);
#endif
if (ret != 0) {
wc_FreeRsaKey(&rsa);
return;
}
#else
whKeyId hsmKeyId = WH_KEYID_ERASED;
/* Cache the public key on the server */
ret = wh_Client_KeyCache(&hsmClientCtx, WH_NVM_FLAGS_USAGE_VERIFY, NULL, 0,
pubkey, pubkey_sz, &hsmKeyId);
if (ret != WH_ERROR_OK) {
wc_FreeRsaKey(&rsa);
return;
}
/* Associate this RSA struct with the keyId of the cached key */
ret = wh_Client_RsaSetKeyId(&rsa, hsmKeyId);
if (ret != WH_ERROR_OK) {
wc_FreeRsaKey(&rsa);
return;
}
#endif /* WOLFBOOT_ENABLE_WOLFHSM_CLIENT || (SERVER && CERT_CHAIN) */
XMEMCPY(output, sig, RSA_IMAGE_SIGNATURE_SIZE);
#ifdef WOLFBOOT_SIGN_RSAPSS_ANY
if (is_pss) {
RSA_VERIFY_FN(ret, wc_RsaPSS_VerifyInline, output,
RSA_IMAGE_SIGNATURE_SIZE, &digest_out, hash_type, mgf,
&rsa);
} else
#endif
{
RSA_VERIFY_FN(ret, wc_RsaSSL_VerifyInline, output,
RSA_IMAGE_SIGNATURE_SIZE, &digest_out, &rsa);
}
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
if (g_leafKeyIdValid) {
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
(void)wh_Client_KeyEvict(&hsmClientCtx, g_certLeafKeyId);
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
(void)wh_Server_KeystoreEvictKey(&hsmServerCtx, g_certLeafKeyId);
#endif
g_leafKeyIdValid = 0;
}
#endif /* WOLFBOOT_CERT_CHAIN_VERIFY */
#else
/* wolfCrypt software RSA verify */
ret = wc_InitRsaKey_ex(&rsa, NULL, WOLFBOOT_DEVID_PUBKEY);
if (ret == 0) {
/* Import public key */
ret = wc_RsaPublicKeyDecode((byte*)pubkey, &inOutIdx, &rsa, pubkey_sz);
if (ret >= 0) {
XMEMCPY(output, sig, RSA_IMAGE_SIGNATURE_SIZE);
#ifdef WOLFBOOT_SIGN_RSAPSS_ANY
if (is_pss) {
RSA_VERIFY_FN(ret,
wc_RsaPSS_VerifyInline, output, RSA_IMAGE_SIGNATURE_SIZE,
&digest_out, hash_type, mgf, &rsa);
} else
#endif
{
RSA_VERIFY_FN(ret,
wc_RsaSSL_VerifyInline, output, RSA_IMAGE_SIGNATURE_SIZE,
&digest_out, &rsa);
}
}
}
#endif /* SCE || TSIP */
wc_FreeRsaKey(&rsa);
#ifdef WOLFBOOT_SIGN_RSAPSS_ANY
if (is_pss) {
if (ret >= WOLFBOOT_SHA_DIGEST_SIZE && img && digest_out) {
RSA_PSS_VERIFY_HASH(img, digest_out, ret, hash_type);
}
} else
#endif
{
#ifndef NO_RSA_SIG_ENCODING
if (ret > WOLFBOOT_SHA_DIGEST_SIZE) {
/* larger result indicates it might have an ASN.1 encoded header */
ret = RsaDecodeSignature(&digest_out, ret);
}
#endif
if (ret == WOLFBOOT_SHA_DIGEST_SIZE && img && digest_out) {
RSA_VERIFY_HASH(img, digest_out);
}
}
}
#endif /* WOLFBOOT_SIGN_RSA_ANY */
#ifdef WOLFBOOT_SIGN_LMS
#ifdef HAVE_LIBLMS
#include <wolfssl/wolfcrypt/ext_lms.h>
#else
#include <wolfssl/wolfcrypt/wc_lms.h>
#endif
static void wolfBoot_verify_signature_lms(uint8_t key_slot,
struct wolfBoot_image *img, uint8_t *sig)
{
int ret = 0;
LmsKey lms;
uint8_t * pubkey = NULL;
wolfBoot_printf("info: LMS wolfBoot_verify_signature\n");
pubkey = keystore_get_buffer(key_slot);
if (pubkey == NULL) {
wolfBoot_printf("error: Lms pubkey not found\n");
return;
}
ret = wc_LmsKey_Init(&lms, NULL, WOLFBOOT_DEVID_PUBKEY);
if (ret != 0) {
wolfBoot_printf("error: wc_LmsKey_Init returned %d\n", ret);
return;
}
/* Set the LMS parameters. */
ret = wc_LmsKey_SetParameters(&lms, LMS_LEVELS, LMS_HEIGHT,
LMS_WINTERNITZ);
if (ret != 0) {
/* Something is wrong with the pub key or LMS parameters. */
wolfBoot_printf("error: wc_LmsKey_SetParameters(%d, %d, %d)" \
" returned %d\n", LMS_LEVELS, LMS_HEIGHT,
LMS_WINTERNITZ, ret);
wc_LmsKey_Free(&lms);
return;
}
wolfBoot_printf("info: using LMS parameters: L%d-H%d-W%d\n", LMS_LEVELS,
LMS_HEIGHT, LMS_WINTERNITZ);
/* Set the public key. */
ret = wc_LmsKey_ImportPubRaw(&lms, pubkey, KEYSTORE_PUBKEY_SIZE);
if (ret != 0) {
/* Something is wrong with the pub key or LMS parameters. */
wolfBoot_printf("error: wc_LmsKey_ImportPubRaw" \
" returned %d\n", ret);
wc_LmsKey_Free(&lms);
return;
}
ret = wc_LmsKey_Verify(&lms, sig, LMS_IMAGE_SIGNATURE_SIZE, img->sha_hash,
WOLFBOOT_SHA_DIGEST_SIZE);
if (ret == 0) {
wolfBoot_printf("info: wc_LmsKey_Verify returned OK\n");
wolfBoot_image_confirm_signature_ok(img);
}
else {
wolfBoot_printf("error: wc_LmsKey_Verify returned %d\n", ret);
}
wc_LmsKey_Free(&lms);
}
#endif /* WOLFBOOT_SIGN_LMS */
#ifdef WOLFBOOT_SIGN_XMSS
#ifdef HAVE_LIBXMSS
#include <wolfssl/wolfcrypt/ext_xmss.h>
#else
#include <wolfssl/wolfcrypt/wc_xmss.h>
#endif
static void wolfBoot_verify_signature_xmss(uint8_t key_slot,
struct wolfBoot_image *img, uint8_t *sig)
{
int ret = 0;
XmssKey xmss;
uint8_t * pubkey = NULL;
wolfBoot_printf("info: XMSS wolfBoot_verify_signature\n");
pubkey = keystore_get_buffer(key_slot);
if (pubkey == NULL) {
wolfBoot_printf("error: Xmss pubkey not found\n");
return;
}
ret = wc_XmssKey_Init(&xmss, NULL, WOLFBOOT_DEVID_PUBKEY);
if (ret != 0) {
wolfBoot_printf("error: wc_XmssKey_Init returned %d\n", ret);
return;
}
/* Set the XMSS parameters. */
ret = wc_XmssKey_SetParamStr(&xmss, WOLFBOOT_XMSS_PARAMS);
if (ret != 0) {
/* Something is wrong with the pub key or XMSS parameters. */
wolfBoot_printf("error: wc_XmssKey_SetParamStr(%s)" \
" returned %d\n", WOLFBOOT_XMSS_PARAMS, ret);
return;
}
wolfBoot_printf("info: using XMSS parameters: %s\n", WOLFBOOT_XMSS_PARAMS);
/* Set the public key. */
ret = wc_XmssKey_ImportPubRaw(&xmss, pubkey, KEYSTORE_PUBKEY_SIZE);
if (ret != 0) {
/* Something is wrong with the pub key or LMS parameters. */
wolfBoot_printf("error: wc_XmssKey_ImportPubRaw" \
" returned %d\n", ret);
return;
}
ret = wc_XmssKey_Verify(&xmss, sig, XMSS_IMAGE_SIGNATURE_SIZE, img->sha_hash,
WOLFBOOT_SHA_DIGEST_SIZE);
if (ret == 0) {
wolfBoot_printf("info: wc_XmssKey_Verify returned OK\n");
wolfBoot_image_confirm_signature_ok(img);
}
else {
wolfBoot_printf("error: wc_XmssKey_Verify returned %d\n", ret);
}
wc_XmssKey_Free(&xmss);
}
#endif /* WOLFBOOT_SIGN_XMSS */
#ifdef WOLFBOOT_SIGN_ML_DSA
#include <wolfssl/wolfcrypt/wc_mldsa.h>
static void wolfBoot_verify_signature_ml_dsa(uint8_t key_slot,
struct wolfBoot_image *img, uint8_t *sig)
{
int ret = 0;
int key_inited = 0;
wc_MlDsaKey ml_dsa;
#if !defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \
!defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
uint8_t * pubkey = NULL;
int pub_len = 0;
#endif
int sig_len = 0;
int verify_res = 0;
wolfBoot_printf("info: ML-DSA %d verify_signature: pubkey %d, sig %d\n",
ML_DSA_LEVEL, KEYSTORE_PUBKEY_SIZE, ML_DSA_IMAGE_SIGNATURE_SIZE);
#if !defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \
!defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
pubkey = keystore_get_buffer(key_slot);
if (pubkey == NULL) {
wolfBoot_printf("error: ML-DSA pubkey not found\n");
return;
}
#endif
ret = wc_MlDsaKey_Init(&ml_dsa, NULL, WOLFBOOT_DEVID_PUBKEY);
if (ret != 0) {
wolfBoot_printf("error: wc_MlDsaKey_Init returned %d\n", ret);
}
else {
key_inited = 1;
/* Set the ML-DSA security level. */
ret = wc_MlDsaKey_SetParams(&ml_dsa, ML_DSA_LEVEL);
if (ret != 0) {
wolfBoot_printf("error: wc_MlDsaKey_SetParams(%d)" \
" returned %d\n", ML_DSA_LEVEL, ret);
}
}
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
(defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) && \
defined(WOLFBOOT_CERT_CHAIN_VERIFY))
/* Use the public key ID directly with wolfHSM (no local keystore) */
(void)key_slot;
if (ret == 0) {
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
/* If using certificate chain verification and we have a verified leaf
* key ID */
if (g_leafKeyIdValid) {
/* Use the leaf key ID from certificate verification */
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
ret = wh_Client_MlDsaSetKeyId(&ml_dsa, g_certLeafKeyId);
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
ret = wh_Server_MlDsaKeyCacheExport(&hsmServerCtx, g_certLeafKeyId,
&ml_dsa);
#endif
wolfBoot_printf(
"Using leaf cert public key (ID: %08x) for ML-DSA "
"verification\n",
(unsigned int)g_certLeafKeyId);
}
else {
/* Default behavior: use the pre-configured public key ID */
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
ret = wh_Client_MlDsaSetKeyId(&ml_dsa, hsmKeyIdPubKey);
#endif
}
#else
ret = wh_Client_MlDsaSetKeyId(&ml_dsa, hsmKeyIdPubKey);
#endif
if (ret != 0) {
wolfBoot_printf("error: ML-DSA set key ID returned %d\n", ret);
}
}
#else
/* Make sure pub key matches parameters and import it */
if (ret == 0) {
ret = wc_MlDsaKey_GetPubLen(&ml_dsa, &pub_len);
if (ret != 0 || pub_len <= 0) {
wolfBoot_printf("error: wc_MlDsaKey_GetPubLen returned %d\n", ret);
ret = -1;
}
else if (pub_len > KEYSTORE_PUBKEY_SIZE) {
wolfBoot_printf("error: ML-DSA pub key mismatch: got %d bytes " \
"max %d\n", pub_len, KEYSTORE_PUBKEY_SIZE);
ret = -1;
}
}
if (ret == 0) {
ret = wc_MlDsaKey_ImportPubRaw(&ml_dsa, pubkey, pub_len);
if (ret != 0) {
wolfBoot_printf("error: wc_MlDsaKey_ImportPubRaw returned: %d\n",
ret);
}
}
#endif /* WOLFBOOT_ENABLE_WOLFHSM_CLIENT || (SERVER && CERT_CHAIN) */
/* Make sure sig len matches parameters. */
if (ret == 0) {
ret = wc_MlDsaKey_GetSigLen(&ml_dsa, &sig_len);
if (ret != 0 || sig_len <= 0) {
wolfBoot_printf("error: wc_MlDsaKey_GetSigLen returned %d\n", ret);
ret = -1;
}
else if (sig_len != ML_DSA_IMAGE_SIGNATURE_SIZE) {
wolfBoot_printf("error: ML-DSA sig len mismatch: got %d bytes " \
"expected %d\n", sig_len, ML_DSA_IMAGE_SIGNATURE_SIZE);
ret = -1;
}
}
if (ret == 0) {
wolfBoot_printf("info: using ML-DSA security level: %d\n",
ML_DSA_LEVEL);
/* Finally verify signature. */
ret = wc_MlDsaKey_VerifyCtx(&ml_dsa, sig, ML_DSA_IMAGE_SIGNATURE_SIZE,
NULL, 0,
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE,
&verify_res);
#ifdef WOLFBOOT_ARMORED
if (ret == 0) {
uint32_t v = (uint32_t)verify_res;
uint32_t v_inv = ~v;
if ((v == 1U) && (v_inv == 0xFFFFFFFEU) &&
(v == (uint32_t)verify_res) &&
(v_inv == ~(uint32_t)verify_res)) {
wolfBoot_printf("info: wc_MlDsaKey_Verify returned OK\n");
wolfBoot_image_confirm_signature_ok(img);
}
else {
wolfBoot_printf("error: wc_MlDsaKey_Verify returned: ret=%d, "
"res=%d\n", ret, verify_res);
}
}
else {
wolfBoot_printf("error: wc_MlDsaKey_Verify returned: ret=%d, "
"res=%d\n", ret, verify_res);
}
#else
if (ret == 0 && verify_res == 1) {
wolfBoot_printf("info: wc_MlDsaKey_Verify returned OK\n");
wolfBoot_image_confirm_signature_ok(img);
}
else {
wolfBoot_printf("error: wc_MlDsaKey_Verify returned: ret=%d, "
"res=%d\n", ret, verify_res);
}
#endif
}
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \
(defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER))
if (g_leafKeyIdValid) {
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
(void)wh_Client_KeyEvict(&hsmClientCtx, g_certLeafKeyId);
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
(void)wh_Server_KeystoreEvictKey(&hsmServerCtx, g_certLeafKeyId);
#endif
g_leafKeyIdValid = 0;
}
#endif /* WOLFBOOT_CERT_CHAIN_VERIFY && WOLFHSM */
if (key_inited) {
wc_MlDsaKey_Free(&ml_dsa);
}
}
#endif /* WOLFBOOT_SIGN_ML_DSA */
#endif /* WOLFBOOT_TPM && WOLFBOOT_TPM_VERIFY */
/**
* @brief Get the specified header type from the external flash image.
*
* @param img The image to retrieve the header from.
* @param type The type of header to retrieve.
* @param ptr A pointer to the header data.
* @return The size of the header if found, otherwise 0.
*/
static uint16_t get_header_ext(struct wolfBoot_image *img, uint16_t type,
uint8_t **ptr);
/**
* @brief This function searches for the TLV entry in the header and provides
* a pointer to the corresponding data.
*
* @param img The image to retrieve the data from.
* @param type The type of header to retrieve.
* @param ptr A pointer to store the position of the header.
* @return The size of the data if found, otherwise 0.
*/
#define get_header wolfBoot_get_header /* internal reference to function */
uint16_t wolfBoot_get_header(struct wolfBoot_image *img, uint16_t type,
uint8_t **ptr)
{
if (PART_IS_EXT(img))
return get_header_ext(img, type, ptr);
else
return wolfBoot_find_header(img->hdr + IMAGE_HEADER_OFFSET, type, ptr);
}
#ifdef EXT_FLASH
static uint8_t ext_hash_block[WOLFBOOT_SHA_BLOCK_SIZE] XALIGNED(4);
#endif
/**
* @brief Get a block of data to be hashed.
*
* @param img The image to retrieve the data from.
* @param offset The offset to start reading the data from.
* @return A pointer to the data block.
*/
static uint8_t *get_sha_block(struct wolfBoot_image *img, uint32_t offset)
{
uint8_t *p;
#ifdef EXT_FLASH
uint32_t read_sz;
#endif
if (offset >= img->fw_size)
return NULL;
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
/* Read only the bytes that remain in the image: the block
* window must not extend past fw_size. */
read_sz = WOLFBOOT_SHA_BLOCK_SIZE;
if (read_sz > img->fw_size - offset)
read_sz = img->fw_size - offset;
ext_flash_check_read((uintptr_t)(img->fw_base) + offset,
ext_hash_block, read_sz);
return ext_hash_block;
}
#endif
p = (uint8_t *)(img->fw_base + offset);
#if defined(MPFS_DDR_INIT)
/* PolarFire SoC DDR build: route in-DDR image-body reads through the
* non-cached DDR SEG window (0xC0000000 base) so cache fills don't evict
* L2 Scratch lines (where wolfBoot's own code/stack live). PDMA already
* L2-flushed the cached writes at disk-load, so the non-cached side reads
* the correct DDR contents. 0x8xxxxxxx -> 0xCxxxxxxx. Applied at this
* single point so every image-body hasher (SHA256/384/3-384) behaves
* identically; inert for the L2-Scratch QSPI M-mode build (fw_base is not
* in the 0x8xxxxxxx window). */
if (((uintptr_t)p & 0xF0000000UL) == 0x80000000UL) {
p = (uint8_t *)((uintptr_t)p | 0x40000000UL);
}
#endif
return p;
}
#ifdef EXT_FLASH
#ifdef UNIT_TEST
static uint8_t hdr_cpy[IMAGE_HEADER_SIZE] XALIGNED(4);
static int hdr_cpy_done = 0;
#else
/* use from libwolfboot.c */
extern uint8_t hdr_cpy[IMAGE_HEADER_SIZE] XALIGNED(4);
extern int hdr_cpy_done;
#endif
/**
* @brief Get a copy of the image header.
*
* @param img The image to retrieve the header from.
* @return A pointer to the copied header data.
*/
static uint8_t *fetch_hdr_cpy(struct wolfBoot_image *img)
{
if (!hdr_cpy_done) {
memset(hdr_cpy, 0, sizeof(hdr_cpy));
if (ext_flash_check_read((uintptr_t)img->hdr, hdr_cpy,
IMAGE_HEADER_SIZE) == IMAGE_HEADER_SIZE)
hdr_cpy_done = 1;
}
return hdr_cpy;
}
/**
* @brief Invalidate the cached external image header.
*
* fetch_hdr_cpy() loads the header of the first image it sees and serves
* it to every later get_header() call. Call this before opening a
* different image so TLV lookups do not read the stale header.
*/
void wolfBoot_invalidate_hdr_cache(void)
{
hdr_cpy_done = 0;
}
static uint16_t get_header_ext(struct wolfBoot_image *img, uint16_t type,
uint8_t **ptr)
{
return wolfBoot_find_header(fetch_hdr_cpy(img) + IMAGE_HEADER_OFFSET, type,
ptr);
}
#else
# define fetch_hdr_cpy(i) ((uint8_t *)0)
static uint16_t get_header_ext(struct wolfBoot_image *img, uint16_t type,
uint8_t **ptr)
{
(void)img; (void)type; (void)ptr;
return 0;
}
#endif
static uint8_t *get_img_hdr(struct wolfBoot_image *img)
{
if (PART_IS_EXT(img))
return fetch_hdr_cpy(img);
else
return (uint8_t *)(img->hdr);
}
#if defined(WOLFBOOT_HASH_SHA256)
#include <wolfssl/wolfcrypt/sha256.h>
/* Initialize and hash the header part */
static int header_sha256(wc_Sha256 *sha256_ctx, struct wolfBoot_image *img)
{
uint8_t *stored_sha, *end_sha;
uint16_t stored_sha_len;
uint8_t* p;
if (!img)
return -1;
p = get_img_hdr(img);
stored_sha_len = get_header(img, HDR_SHA256, &stored_sha);
if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE)
return -1;
end_sha = stored_sha - (2 * WOLFBOOT_HDR_U16_SZ); /* Subtract 2 Type + 2 Len */
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
if (end_sha <= p) {
return -1;
}
#endif
(void)wc_InitSha256_ex(sha256_ctx, NULL, WOLFBOOT_DEVID_HASH);
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
wc_Sha256Update(sha256_ctx, p, (word32)(end_sha - p));
#else
{
int blksz;
while (p < end_sha) {
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if (end_sha - p < blksz)
blksz = end_sha - p;
wc_Sha256Update(sha256_ctx, p, blksz);
p += blksz;
}
}
#endif
return 0;
}
/**
* @brief Calculate the SHA256 hash of the image.
*
* @param img The image to calculate the hash for.
* @param hash A pointer to store the resulting SHA256 hash.
* @return 0 on success, -1 on failure.
*/
static int image_sha256(struct wolfBoot_image *img, uint8_t *hash)
{
wc_Sha256 sha256_ctx;
if (header_sha256(&sha256_ctx, img) != 0)
return -1;
#if defined(WOLFBOOT_ARCH_C2000)
/* C28x (CHAR_BIT==16): the firmware is stored as native, executable 16-bit
* program words, but the host signed an octet stream in which each program
* word was serialized low-octet-then-high-octet. Reproduce that ordering
* so the on-target digest matches the host's. img->fw_size is the octet
* count (2 octets per program word); each buf[] cell holds one octet, and
* the wide-byte wc_Sha256Update consumes one octet per cell. */
{
const uint16_t *w = (const uint16_t *)img->fw_base;
uint32_t position = 0;
uint8_t buf[64]; /* even; each cell holds one octet */
int n;
uint16_t val;
if (img->fw_base == NULL) {
wc_Sha256Free(&sha256_ctx);
return -1;
}
while (position < img->fw_size) {
n = 0;
while ((n <= (int)sizeof(buf) - 2) && (position < img->fw_size)) {
val = *w++;
buf[n++] = (uint8_t)(val & 0xFF); /* low octet */
position++;
if (position < img->fw_size) {
buf[n++] = (uint8_t)((val >> 8) & 0xFF); /* high octet */
position++;
}
}
wc_Sha256Update(&sha256_ctx, buf, n);
wolfBoot_watchdog_feed();
}
}
#elif defined(WOLFBOOT_IMG_HASH_ONESHOT)
if (img->fw_base == NULL) {
wc_Sha256Free(&sha256_ctx);
return -1;
}
wc_Sha256Update(&sha256_ctx, img->fw_base, img->fw_size);
#else
{
uint32_t position = 0;
uint8_t* p;
int blksz;
do {
p = get_sha_block(img, position);
if (p == NULL)
break;
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if (position + blksz > img->fw_size)
blksz = img->fw_size - position;
wc_Sha256Update(&sha256_ctx, p, blksz);
position += blksz;
wolfBoot_watchdog_feed();
} while (position < img->fw_size);
}
#endif
wc_Sha256Final(&sha256_ctx, hash);
wc_Sha256Free(&sha256_ctx);
return 0;
}
#if !defined(WOLFBOOT_NO_SIGN) && !defined(WOLFBOOT_NO_KEYSTORE)
/**
* @brief Calculate the SHA256 hash of the key.
*
* @param key_slot The key slot ID to calculate the hash for.
* @param hash A pointer to store the resulting SHA256 hash.
*/
static void key_sha256(uint8_t key_slot, uint8_t *hash)
{
uint8_t *pubkey = keystore_get_buffer(key_slot);
int pubkey_sz = keystore_get_size(key_slot);
wc_Sha256 sha256_ctx;
memset(hash, 0, SHA256_DIGEST_SIZE);
if (!pubkey || (pubkey_sz < 0))
return;
(void)wc_InitSha256_ex(&sha256_ctx, NULL, WOLFBOOT_DEVID_HASH);
wc_Sha256Update(&sha256_ctx, pubkey, (word32)pubkey_sz);
wc_Sha256Final(&sha256_ctx, hash);
wc_Sha256Free(&sha256_ctx);
}
#endif /* WOLFBOOT_NO_SIGN */
#endif /* SHA2-256 */
#if defined(WOLFBOOT_HASH_SHA384)
#include <wolfssl/wolfcrypt/sha512.h>
/* Initialize and hash the header part */
static int header_sha384(wc_Sha384 *sha384_ctx, struct wolfBoot_image *img)
{
uint16_t stored_sha_len;
uint8_t *stored_sha, *end_sha;
uint8_t* p;
if (!img)
return -1;
p = get_img_hdr(img);
stored_sha_len = get_header(img, HDR_SHA384, &stored_sha);
if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE)
return -1;
end_sha = stored_sha - (2 * WOLFBOOT_HDR_U16_SZ); /* Subtract 2 Type + 2 Len */
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
if (end_sha <= p) {
return -1;
}
#endif
(void)wc_InitSha384_ex(sha384_ctx, NULL, WOLFBOOT_DEVID_HASH);
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
wc_Sha384Update(sha384_ctx, p, (word32)(end_sha - p));
#else
{
int blksz;
while (p < end_sha) {
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if (end_sha - p < blksz)
blksz = end_sha - p;
wc_Sha384Update(sha384_ctx, p, blksz);
p += blksz;
}
}
#endif
return 0;
}
/**
* @brief Calculate SHA-384 hash of the image.
*
* This function calculates the SHA-384 hash of the given image.
*
* @param img The pointer to the wolfBoot_image structure representing the image.
* @param hash The buffer to store the SHA-384 hash (48 bytes).
* @return 0 on success, -1 on error.
*/
static int image_sha384(struct wolfBoot_image *img, uint8_t *hash)
{
wc_Sha384 sha384_ctx;
if (header_sha384(&sha384_ctx, img) != 0) {
return -1;
}
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
if (img->fw_base == NULL) {
wc_Sha384Free(&sha384_ctx);
return -1;
}
wc_Sha384Update(&sha384_ctx, img->fw_base, img->fw_size);
#else
{
uint32_t position = 0;
uint8_t* p;
int blksz;
do {
p = get_sha_block(img, position);
if (p == NULL)
break;
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if (position + blksz > img->fw_size)
blksz = img->fw_size - position;
/* p is already routed to the non-cached DDR alias by
* get_sha_block() under MPFS_DDR_INIT (see above). */
wc_Sha384Update(&sha384_ctx, p, blksz);
position += blksz;
wolfBoot_watchdog_feed();
} while (position < img->fw_size);
}
#endif
wc_Sha384Final(&sha384_ctx, hash);
wc_Sha384Free(&sha384_ctx);
return 0;
}
#if !defined(WOLFBOOT_NO_SIGN) && !defined(WOLFBOOT_NO_KEYSTORE)
/**
* @brief Calculate SHA-384 hash of a public key in the keystore.
*
* This function calculates the SHA-384 hash of the public key stored in
* the keystore at the specified key slot.
*
* @param key_slot The key slot ID where the public key is stored in the
* keystore.
* @param hash The buffer to store the SHA-384 hash (48 bytes).
* @return None.
*/
static void key_sha384(uint8_t key_slot, uint8_t *hash)
{
uint8_t *pubkey = keystore_get_buffer(key_slot);
int pubkey_sz = keystore_get_size(key_slot);
wc_Sha384 sha384_ctx;
memset(hash, 0, SHA384_DIGEST_SIZE);
if (!pubkey || (pubkey_sz < 0))
return;
(void)wc_InitSha384_ex(&sha384_ctx, NULL, WOLFBOOT_DEVID_HASH);
wc_Sha384Update(&sha384_ctx, pubkey, (word32)pubkey_sz);
wc_Sha384Final(&sha384_ctx, hash);
wc_Sha384Free(&sha384_ctx);
}
#endif /* WOLFBOOT_NO_SIGN */
#endif /* WOLFBOOT_HASH_SHA384 */
#if defined(WOLFBOOT_HASH_SHA3_384)
#include <wolfssl/wolfcrypt/sha3.h>
/* Initialize and hash the header part */
static int header_sha3_384(wc_Sha3 *sha3_ctx, struct wolfBoot_image *img)
{
uint16_t stored_sha_len;
uint8_t *stored_sha, *end_sha;
uint8_t* p;
if (!img)
return -1;
p = get_img_hdr(img);
stored_sha_len = get_header(img, HDR_SHA3_384, &stored_sha);
if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE)
return -1;
end_sha = stored_sha - (2 * WOLFBOOT_HDR_U16_SZ); /* Subtract 2 Type + 2 Len */
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
if (end_sha <= p) {
return -1;
}
#endif
(void)wc_InitSha3_384(sha3_ctx, NULL, WOLFBOOT_DEVID_HASH);
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
wc_Sha3_384_Update(sha3_ctx, p, (word32)(end_sha - p));
#else
{
int blksz;
while (p < end_sha) {
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if (end_sha - p < blksz)
blksz = end_sha - p;
wc_Sha3_384_Update(sha3_ctx, p, blksz);
p += blksz;
}
}
#endif
return 0;
}
/**
* @brief Calculate SHA3-384 hash of the image.
*
* This function calculates the SHA3-384 hash of the given image.
*
* @param img The pointer to the wolfBoot_image structure representing the image.
* @param hash The buffer to store the SHA3-384 hash (48 bytes).
* @return 0 on success, -1 on error.
*/
static int image_sha3_384(struct wolfBoot_image *img, uint8_t *hash)
{
wc_Sha3 sha3_ctx;
if (header_sha3_384(&sha3_ctx, img) != 0)
return -1;
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
if (img->fw_base == NULL) {
wc_Sha3_384_Free(&sha3_ctx);
return -1;
}
wc_Sha3_384_Update(&sha3_ctx, img->fw_base, img->fw_size);
#else
{
uint8_t* p;
int blksz;
uint32_t position = 0;
do {
p = get_sha_block(img, position);
if (p == NULL)
break;
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if (position + blksz > img->fw_size)
blksz = img->fw_size - position;
wc_Sha3_384_Update(&sha3_ctx, p, blksz);
position += blksz;
wolfBoot_watchdog_feed();
} while (position < img->fw_size);
}
#endif
wc_Sha3_384_Final(&sha3_ctx, hash);
wc_Sha3_384_Free(&sha3_ctx);
return 0;
}
#if !defined(WOLFBOOT_NO_SIGN) && !defined(WOLFBOOT_NO_KEYSTORE)
/**
* @brief Calculate SHA3-384 hash of a public key in the keystore.
*
* This function calculates the SHA3-384 hash of the public key stored
* in the keystore at the specified key slot.
*
* @param key_slot The key slot ID where the public key is stored in the
* keystore.
* @param hash The buffer to store the SHA3-384 hash (48 bytes).
* @return None.
*/
static void key_sha3_384(uint8_t key_slot, uint8_t *hash)
{
uint8_t *pubkey = keystore_get_buffer(key_slot);
int pubkey_sz = keystore_get_size(key_slot);
wc_Sha3 sha3_ctx;
memset(hash, 0, WC_SHA3_384_DIGEST_SIZE);
if (!pubkey || (pubkey_sz < 0))
return;
(void)wc_InitSha3_384(&sha3_ctx, NULL, WOLFBOOT_DEVID_HASH);
wc_Sha3_384_Update(&sha3_ctx, pubkey, (word32)pubkey_sz);
wc_Sha3_384_Final(&sha3_ctx, hash);
wc_Sha3_384_Free(&sha3_ctx);
}
#endif /* WOLFBOOT_NO_SIGN */
#endif /* SHA3-384 */
/**
* @brief Convert a 32-bit integer from little-endian to native byte order.
*
* This function converts a 32-bit integer from little-endian byte order to
* the native byte order of the machine.
*
* @param val The 32-bit integer value in little-endian byte order.
* @return The 32-bit integer value in native byte order.
*/
static inline uint32_t im2n(uint32_t val)
{
#ifdef BIG_ENDIAN_ORDER
val = (((val & 0x000000FF) << 24) |
((val & 0x0000FF00) << 8) |
((val & 0x00FF0000) >> 8) |
((val & 0xFF000000) >> 24));
#endif
return val;
}
/**
* @brief Get the size of the image from the image header.
*
* This function retrieves the size of the image from the image header.
*
* @param image The pointer to the image header.
* @return The size of the image in bytes.
*/
uint32_t wolfBoot_image_size(uint8_t *image)
{
return im2n(WOLFBOOT_HDR_GET_U32(image + WOLFBOOT_HDR_U32_SZ));
}
/**
* @brief Open an image using the provided image address.
*
* This function opens an image using the provided image address and initializes
* the wolfBoot_image structure.
* Note that this function initializes the members of the wolfBoot_image structure
* but does not initialize the structure itself. It is expected that the wolfBoot_image
* struct is memset to 0 before being passed in, with img->hdr optionally set.
*
* @param img The pointer to the wolfBoot_image structure to be initialized.
* @param image The pointer to the image address.
* @return 0 on success, -1 on error.
*/
int wolfBoot_open_image_address(struct wolfBoot_image *img, uint8_t *image)
{
/* Read the magic an octet at a time: a uint8_t* cannot be cast to
* uint32_t* where CHAR_BIT != 8 (C28x). */
uint32_t magic = WOLFBOOT_HDR_GET_U32(image);
#ifdef WOLFBOOT_FIXED_PARTITIONS
/* The UPDATE slot may be larger than BOOT (monolithic self-update) */
uint32_t part_size = (img->part == PART_UPDATE) ?
WOLFBOOT_PARTITION_UPDATE_SIZE : WOLFBOOT_PARTITION_SIZE;
#endif
if (magic != WOLFBOOT_MAGIC) {
wolfBoot_printf("Partition %d header magic 0x%08x invalid at %p\n",
img->part, (unsigned int)magic, img->hdr);
return -1;
}
img->fw_size = wolfBoot_image_size(image);
#ifdef WOLFBOOT_FIXED_PARTITIONS
if (img->fw_size > (part_size - IMAGE_HEADER_SIZE)) {
wolfBoot_printf("Image size %u > max %u\n",
(unsigned int)img->fw_size,
(unsigned int)(part_size - IMAGE_HEADER_SIZE));
img->fw_size = part_size - IMAGE_HEADER_SIZE;
return -1;
}
if (!img->hdr_ok) {
img->hdr = image;
}
img->trailer = img->hdr + part_size;
#else
#ifdef WOLFBOOT_RAMBOOT_MAX_SIZE
if (img->fw_size > WOLFBOOT_RAMBOOT_MAX_SIZE) {
wolfBoot_printf("Image size %u > max %u\n",
(unsigned int)img->fw_size,
(unsigned int)WOLFBOOT_RAMBOOT_MAX_SIZE);
return -1;
}
#endif
if (img->hdr == NULL) {
img->hdr = image;
}
#endif
img->hdr_ok = 1;
wolfBoot_image_set_fw_base(img, img->hdr + IMAGE_HEADER_SIZE);
#ifdef EXT_FLASH
img->hdr_cache = image;
#endif
wolfBoot_printf("%s partition: %p (sz %d, ver 0x%x, type 0x%x)\n",
(img->part == PART_BOOT) ? "Boot" : "Update",
img->hdr, (unsigned int)img->fw_size,
wolfBoot_get_blob_version(image),
wolfBoot_get_blob_type(image));
return 0;
}
#if defined(MMU) || defined(WOLFBOOT_FDT)
/**
* @brief Get the size of the Device Tree Blob (DTB).
*
* This function retrieves the size of the Device Tree Blob (DTB) from
* the given DTB address.
*
* Fully validates the blob (header layout plus a structural walk) before
* reporting its size. Every bound is checked against `capacity`, the
* bytes actually readable at dts_addr, not against the size the blob
* claims for itself.
*
* @param dts_addr Device Tree Blob (DTB) address.
* @param capacity Bytes available at dts_addr.
* @return DTB size in bytes, or a negative FDT_ERR_*.
*/
int wolfBoot_get_dts_size(void *dts_addr, uint32_t capacity)
{
fdt_ctx ctx;
int ret = fdt_open(&ctx, dts_addr, capacity);
if (ret == 0) {
ret = (int)fdt_size(&ctx);
}
return ret;
}
/* Hash a raw buffer with the configured image hash (explicit per-algorithm API,
* since the generic update_hash macro's SHA3 mapping is wrong). 0 on success. */
static int wolfBoot_hash_buffer(const void *buf, uint32_t len, uint8_t *out)
{
const uint8_t *p = (const uint8_t *)buf;
int ret;
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256 ctx;
ret = wc_InitSha256_ex(&ctx, NULL, WOLFBOOT_DEVID_HASH);
if (ret == 0) {
while (len > 0) {
uint32_t sz = (len < WOLFBOOT_SHA_BLOCK_SIZE) ?
len : (uint32_t)WOLFBOOT_SHA_BLOCK_SIZE;
ret = wc_Sha256Update(&ctx, p, sz);
if (ret != 0)
break;
p += sz;
len -= sz;
}
if (ret == 0)
ret = wc_Sha256Final(&ctx, out);
wc_Sha256Free(&ctx);
}
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384 ctx;
ret = wc_InitSha384_ex(&ctx, NULL, WOLFBOOT_DEVID_HASH);
if (ret == 0) {
while (len > 0) {
uint32_t sz = (len < WOLFBOOT_SHA_BLOCK_SIZE) ?
len : (uint32_t)WOLFBOOT_SHA_BLOCK_SIZE;
ret = wc_Sha384Update(&ctx, p, sz);
if (ret != 0)
break;
p += sz;
len -= sz;
}
if (ret == 0)
ret = wc_Sha384Final(&ctx, out);
wc_Sha384Free(&ctx);
}
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3 ctx;
ret = wc_InitSha3_384(&ctx, NULL, WOLFBOOT_DEVID_HASH);
if (ret == 0) {
while (len > 0) {
uint32_t sz = (len < WOLFBOOT_SHA_BLOCK_SIZE) ?
len : (uint32_t)WOLFBOOT_SHA_BLOCK_SIZE;
ret = wc_Sha3_384_Update(&ctx, p, sz);
if (ret != 0)
break;
p += sz;
len -= sz;
}
if (ret == 0)
ret = wc_Sha3_384_Final(&ctx, out);
wc_Sha3_384_Free(&ctx);
}
#else
(void)p;
ret = -1;
#endif
return (ret == 0) ? 0 : -1;
}
/* Verify a raw DTB against a firmware-bound digest (from the image's
* HDR_DEVICE_TREE_DIGEST TLV, captured by the caller since the load may reuse
* the image struct). Returns 0 on match, -1 on mismatch/bad args/hash error. */
int wolfBoot_verify_dts_digest(const uint8_t *expected_digest,
const void *dts_addr, uint32_t dts_size)
{
uint8_t calc[WOLFBOOT_SHA_DIGEST_SIZE];
if (expected_digest == NULL || dts_addr == NULL || dts_size == 0)
return -1;
if (wolfBoot_hash_buffer(dts_addr, dts_size, calc) != 0)
return -1;
if (wolfBoot_hardened_CT_compare(expected_digest, calc,
WOLFBOOT_SHA_DIGEST_SIZE) != 0) {
return -1;
}
return 0;
}
#endif /* MMU || WOLFBOOT_FDT */
#ifdef WOLFBOOT_FIXED_PARTITIONS
/**
* @brief Open an image in a specified partition.
*
* This function opens an image in the specified partition and initializes
* the wolfBoot_image structure.
*
* @param img The pointer to the wolfBoot_image structure to be initialized.
* @param part The partition ID (PART_BOOT, PART_UPDATE, PART_SWAP, etc.).
* @return 0 on success, -1 on error.
*/
int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part)
{
int ret;
uint8_t *image;
if (!img)
return -1;
#ifdef EXT_FLASH
hdr_cpy_done = 0; /* reset hdr "open" flag */
#endif
memset(img, 0, sizeof(struct wolfBoot_image));
img->part = part;
if (part == PART_SWAP) {
img->hdr = (void*)WOLFBOOT_PARTITION_SWAP_ADDRESS;
img->hdr_ok = 1;
wolfBoot_image_set_fw_base(img, img->hdr);
img->fw_size = WOLFBOOT_SECTOR_SIZE;
return 0;
}
#ifdef MMU
if (part == PART_DTS_BOOT || part == PART_DTS_UPDATE) {
uint32_t dts_sz = 0;
img->hdr = (part == PART_DTS_BOOT) ?
(void*)WOLFBOOT_DTS_BOOT_ADDRESS :
(void*)WOLFBOOT_DTS_UPDATE_ADDRESS;
wolfBoot_printf("%s partition: %p\n",
(part == PART_DTS_BOOT) ? "DTB boot" : "DTB update", img->hdr);
if (PART_IS_EXT(img))
image = fetch_hdr_cpy(img);
else
image = (uint8_t*)img->hdr;
/* Only the header is readable here: `image` may be
* fetch_hdr_cpy()'s IMAGE_HEADER_SIZE copy. The blob is validated
* in full when it is loaded. Copy into an aligned local first,
* because a memory-mapped partition base is not guaranteed to be
* 4-byte aligned and fdt_peek_size() requires that. */
{
uint8_t hdr[FDT_HEADER_SIZE] XALIGNED(4);
memcpy(hdr, image, sizeof(hdr));
if (fdt_peek_size(hdr, (uint32_t)sizeof(hdr), &dts_sz) != 0)
return -1;
}
ret = (int)dts_sz;
img->hdr_ok = 1;
wolfBoot_image_set_fw_base(img, img->hdr);
img->fw_size = (uint32_t)ret;
return 0;
}
#endif
if (part == PART_BOOT) {
img->hdr = (void*)WOLFBOOT_PARTITION_BOOT_ADDRESS;
}
else if (part == PART_UPDATE) {
img->hdr = (void*)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
}
else {
return -1;
}
/* fetch header address
* (or copy from external device to a local buffer via fetch_hdr_cpy)
*/
if (PART_IS_EXT(img))
image = fetch_hdr_cpy(img);
else
image = (uint8_t *)img->hdr;
img->hdr_ok = 1;
ret = wolfBoot_open_image_address(img, image);
if (ret != 0)
img->hdr_ok = 0;
return ret;
}
#ifdef EXT_FLASH
int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part,
uint8_t* addr)
{
uint8_t* image;
int ret;
if (img == NULL)
return -1;
memset(img, 0, sizeof(struct wolfBoot_image));
img->part = part;
img->hdr = addr;
img->hdr_ok = 1;
hdr_cpy_done = 0; /* reset hdr "open" flag */
image = fetch_hdr_cpy(img);
ret = wolfBoot_open_image_address(img, image);
if (ret != 0)
img->hdr_ok = 0;
return ret;
}
#endif /* EXT_FLASH */
#endif /* WOLFBOOT_FIXED_PARTITIONS */
#ifdef WOLFBOOT_SELF_HEADER
/**
* @brief Open wolfBoot's own image for verification.
*
* This function initializes a wolfBoot_image structure to represent wolfBoot
* itself, using the persisted self-header and the bootloader's flash location.
* The resulting image can be passed to wolfBoot_verify_integrity() and
* wolfBoot_verify_authenticity() to verify the bootloader.
*
* @param img Pointer to a wolfBoot_image structure to be initialized.
*
* @return 0 on success, -1 on failure (NULL pointer or invalid self-header).
*/
int wolfBoot_open_self(struct wolfBoot_image* img)
{
uint8_t* hdr;
int ret;
if (img == NULL) {
return -1;
}
hdr = wolfBoot_get_self_header();
if (hdr == NULL) {
return -1;
}
ret = wolfBoot_open_self_address(img, hdr, (uint8_t*)ARCH_FLASH_OFFSET);
if (ret == 0) {
/* PART_SELF may be marked external for header storage, but wolfBoot
* firmware bytes are always in internal flash at ARCH_FLASH_OFFSET. */
img->not_ext = 1;
}
return ret;
}
/*
* Directly accesses flash, suitable for non-internal-wolfBoot usage
*/
int wolfBoot_open_self_address(struct wolfBoot_image* img, uint8_t* hdr,
uint8_t* image)
{
uint32_t magic;
XMEMSET(img, 0, sizeof(struct wolfBoot_image));
magic = WOLFBOOT_HDR_GET_U32(hdr);
if (magic != WOLFBOOT_MAGIC) {
return -1;
}
img->hdr = hdr;
img->fw_size = wolfBoot_image_size(hdr);
#ifdef WOLFBOOT_FIXED_PARTITIONS
#ifdef WOLFBOOT_SELF_UPDATE_MONOLITHIC
/* A monolithic self image spans the bootloader region and the BOOT
* partition minus its trailer sector (header persisted separately,
* not part of the span) */
{
uint32_t max_span = (uint32_t)(WOLFBOOT_PARTITION_BOOT_ADDRESS -
ARCH_FLASH_OFFSET) + WOLFBOOT_PARTITION_SIZE -
WOLFBOOT_SECTOR_SIZE;
if (img->fw_size > max_span) {
img->fw_size = max_span;
return -1;
}
}
#else
if (img->fw_size > (WOLFBOOT_PARTITION_SIZE - IMAGE_HEADER_SIZE)) {
img->fw_size = WOLFBOOT_PARTITION_SIZE - IMAGE_HEADER_SIZE;
return -1;
}
#endif
#endif
wolfBoot_image_set_fw_base(img, image);
img->part = PART_SELF;
img->hdr_ok = 1;
return 0;
}
#endif
/**
* @brief Verify the integrity of the image using the stored SHA hash.
*
* This function verifies the integrity of the image by calculating its SHA hash
* and comparing it with the stored hash.
*
* @param img The pointer to the wolfBoot_image structure representing the image.
* @return 0 on success, -1 on error.
*/
int wolfBoot_verify_integrity(struct wolfBoot_image *img)
{
uint8_t *stored_sha;
uint16_t stored_sha_len;
/* Reset any cached integrity state up-front, so that a stale sha_ok (and
* its complement/canary) left over from a previous verification of a
* re-used image cannot survive a failed comparison and produce a
* false-positive SHA_OK() result below. */
img->sha_hash = NULL;
wolfBoot_image_clear_sha_ok(img);
stored_sha_len = get_header(img, WOLFBOOT_SHA_HDR, &stored_sha);
if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE)
return -1;
if (image_hash(img, digest) != 0)
return -1;
/* Redundant, fault-hardened digest comparison. On a match this records the
* verified digest and sets sha_ok (plus its complement/canary under
* ARMORED) through an unskippable callback; otherwise the flags stay
* cleared and SHA_OK() below fails the check. */
VERIFY_INTEGRITY_FN(img, digest, stored_sha);
if (!SHA_OK(img))
return -1;
return 0;
}
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
#include "elf.h"
#ifdef ARCH_SIM
#define BASE_OFF ARCH_FLASH_OFFSET
#else
#define BASE_OFF 0
#endif
/* Maximum size of ELF header for any architecture */
typedef union {
elf32_header elf32;
elf64_header elf64;
} elfHeaderMaxBuf;
/*
* Copies an arbitrary amount of data between two flash memory locations
* (internal or external) using an intermediate RAM buffer.
*/
static int copy_flash_buffered(uintptr_t src_addr, uintptr_t dst_addr,
size_t total_size, int is_src_ext,
int is_dst_ext)
{
size_t bytes_copied = 0;
#ifndef BUFFER_DECLARED
#define BUFFER_DECLARED
static uint8_t buffer[FLASHBUFFER_SIZE] XALIGNED(4);
#endif
#ifdef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE
/* Mass erase destination flash in one go before writing */
#ifdef EXT_FLASH
if (is_dst_ext) {
ext_flash_unlock();
ext_flash_erase(dst_addr, total_size);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
hal_flash_erase(dst_addr, total_size);
hal_flash_lock();
}
#endif /* WOLFBOOT_FLASH_MULTI_SECTOR_ERASE */
/* Loop until all requested bytes are copied */
while (bytes_copied < total_size) {
/* Determine the size of the next chunk to copy */
size_t remaining_bytes = total_size - bytes_copied;
size_t chunk_size = (remaining_bytes > FLASHBUFFER_SIZE)
? FLASHBUFFER_SIZE
: remaining_bytes;
/* Read a chunk from the source flash into the RAM buffer */
#ifdef EXT_FLASH
if (is_src_ext) {
ext_flash_unlock();
ext_flash_read(src_addr + bytes_copied, buffer, chunk_size);
ext_flash_lock();
}
else
#endif
{
memcpy(buffer, (const void*)(src_addr + bytes_copied), chunk_size);
}
/* Write the chunk from the RAM buffer to the destination flash */
#ifdef EXT_FLASH
if (is_dst_ext) {
ext_flash_unlock();
#ifndef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE
ext_flash_erase(dst_addr + bytes_copied, chunk_size);
#endif
ext_flash_write(dst_addr + bytes_copied, buffer, chunk_size);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
#ifndef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE
hal_flash_erase(dst_addr + bytes_copied, chunk_size);
#endif
hal_flash_write(dst_addr + bytes_copied, buffer, chunk_size);
hal_flash_lock();
}
/* Update the count of bytes successfully copied */
bytes_copied += chunk_size;
}
/* All bytes copied successfully */
return 0;
}
/*
* Reads data from a given wolfBoot partition's firmware image, properly
* handling internal/external flash.
*/
static int read_flash_fwimage(struct wolfBoot_image* img, uint32_t offset,
void* buffer, uint32_t size)
{
if (img == NULL || buffer == NULL) {
return -1;
}
/* Prevent reading past the end of the image */
if ((uint64_t)offset + size > img->fw_size) {
wolfBoot_printf(
"ERROR: read_flash_fwimage attempt to read past fw_size! "
"Offset %lu, Size %u, TotalSize %lu\n",
(unsigned long)offset, size, (unsigned long)img->fw_size);
return -1;
}
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
if (ext_flash_check_read((uintptr_t)img->fw_base + offset, buffer,
size) < 0) {
wolfBoot_printf(
"ERROR: ext_flash_check_read failed at offset %lu, size %u\n",
(unsigned long)offset, size);
return -1;
}
}
else
#endif
{
/* Internal flash: Direct memory access */
memcpy(buffer, (uint8_t*)img->fw_base + offset, size);
}
return 0;
}
/*
* Reads data from a raw flash address (no offset) into a RAM buffer,
* properly handling internal/external flash.
*/
static int read_flash_addr(void* src, void* buffer, uint32_t size, int src_ext)
{
if (src == NULL || buffer == NULL) {
return -1;
}
#ifdef EXT_FLASH
if (src_ext) {
if (ext_flash_check_read((uintptr_t)src, buffer, size) < 0) {
wolfBoot_printf(
"ERROR: ext_flash_check_read failed at address %p, size %u\n",
src, size);
return -1;
}
}
else
#endif
{
/* Internal flash: Direct memory access */
memcpy(buffer, src, size);
}
return 0;
}
/*
* Hashes a chunk of the firmware image one SHA block at a time, properly
* handling internal/external flash
*/
static int update_hash_flash_fwimg(wolfBoot_hash_t* ctx,
struct wolfBoot_image* img, uint32_t offset,
uint32_t size)
{
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
if (img->fw_base == NULL) {
return -1;
}
if ((uint64_t)offset + size > img->fw_size) {
return -1;
}
update_hash(ctx, img->fw_base + offset, size);
return 0;
#else
uint32_t current_offset = offset;
uint32_t remaining_size = size;
uint8_t read_buf[WOLFBOOT_SHA_BLOCK_SIZE] XALIGNED_STACK(4); /* Use local buffer */
while (remaining_size > 0) {
uint32_t read_size = (remaining_size > WOLFBOOT_SHA_BLOCK_SIZE)
? WOLFBOOT_SHA_BLOCK_SIZE
: remaining_size;
if (read_flash_fwimage(img, current_offset, read_buf, read_size) != 0) {
wolfBoot_printf("ERROR: Failed to read image data for hashing. "
"Offset: %lu, Size: %u\n",
(unsigned long)current_offset, read_size);
return -1;
}
update_hash(ctx, read_buf, read_size);
remaining_size -= read_size;
current_offset += read_size;
}
return 0;
#endif
}
/*
* Hashes a chunk of flash memory at a given absolute address, reading one
* SHA block at a time, properly handling internal/external flash
*/
static int update_hash_flash_addr(wolfBoot_hash_t* ctx, uintptr_t addr,
uint32_t size, int src_ext)
{
#ifdef WOLFBOOT_IMG_HASH_ONESHOT
(void)src_ext;
update_hash(ctx, (uint8_t*)addr, size);
return 0;
#else
uint8_t buffer[WOLFBOOT_SHA_BLOCK_SIZE] XALIGNED_STACK(4);
uint32_t remaining_size = size;
uintptr_t current_addr = addr;
while (remaining_size > 0) {
uint32_t read_size = (remaining_size > WOLFBOOT_SHA_BLOCK_SIZE)
? WOLFBOOT_SHA_BLOCK_SIZE
: remaining_size;
if (read_flash_addr((void*)current_addr, buffer, read_size, src_ext) !=
0) {
wolfBoot_printf(
"ERROR: Failed to read data from address %p, size %u\n",
(void*)current_addr, read_size);
return -1;
}
update_hash(ctx, buffer, read_size);
remaining_size -= read_size;
current_addr += read_size;
}
return 0;
#endif
}
int wolfBoot_check_flash_image_elf(uint8_t part, unsigned long* entry_out)
{
/* Open the partition containing the image */
int is_elf32;
struct wolfBoot_image boot;
uint8_t * elf_h;
size_t elf_hdr_sz = 0;
uint32_t len;
uint16_t entry_count = 0;
size_t entry_off = 0;
size_t ph_size = 0;
size_t current_ph_offset = 0;
int64_t final_offset = -1;
uint8_t calc_digest[WOLFBOOT_SHA_DIGEST_SIZE] XALIGNED_STACK(4);
uint8_t* exp_digest;
int32_t stored_sha_len;
int i;
int32_t entry_out_set = 0;
uint8_t elfHdrBuf[sizeof(elfHeaderMaxBuf)];
uint8_t ph_buf[sizeof(elf64_program_header)]; /* Buffer for current PH */
uint8_t ph_next_buf[sizeof(elf64_program_header)]; /* Buffer for next PH */
wolfBoot_hash_t ctx;
if (wolfBoot_open_image(&boot, part) < 0) {
return -1;
}
/* Initialize hash, feed the manifest header to it */
if (header_hash(&ctx, &boot) < 0) {
return -1;
}
stored_sha_len = get_header(&boot, HDR_HASH, &exp_digest);
if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) {
return -1;
}
/* Get the elf header from the image into a local buffer. We may overread
* the buffer depending on architecture */
memset(elfHdrBuf, 0, sizeof(elfHdrBuf));
read_flash_fwimage(&boot, 0, elfHdrBuf, sizeof(elfHeaderMaxBuf));
elf_h = elfHdrBuf;
if (elf_open(elf_h, &is_elf32) < 0) {
return -1;
}
/* Set up common variables based on ELF type */
if (is_elf32) {
elf32_header* eh = (elf32_header*)elf_h;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
ph_size = sizeof(elf32_program_header);
if (!entry_out_set) {
*entry_out = eh->entry;
entry_out_set = 1;
}
wolfBoot_printf("ELF: [CHECK] 32-bit, entry=0x%08X, "
"ph_offset=0x%08X, ph_count=%u\n",
(unsigned int)eh->entry, (unsigned int)entry_off, entry_count);
}
else { /* 64-bit ELF */
elf64_header* eh = (elf64_header*)elf_h;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
ph_size = sizeof(elf64_program_header);
if (!entry_out_set) {
*entry_out = eh->entry;
entry_out_set = 1;
}
wolfBoot_printf("ELF: [CHECK] 64-bit, entry=0x%08lx, "
"ph_offset=0x%08lx, ph_count=%d\n",
(unsigned long)eh->entry, (unsigned long)entry_off, entry_count);
}
elf_hdr_sz = (size_t)elf_hdr_pht_combined_size(elf_h);
wolfBoot_printf("ELF: [CHECK] Header size: %zu bytes\n", elf_hdr_sz);
/* Hash the elf header and program header in the image, assuming the PHT
* immediately follows the ELF header */
update_hash_flash_fwimg(&ctx, &boot, 0, elf_hdr_sz);
current_ph_offset = entry_off;
/* Calculate padding between ELF+PHT header and first segment */
if (entry_count > 0) {
uint64_t first_offset;
read_flash_fwimage(&boot, current_ph_offset, ph_buf, ph_size);
if (is_elf32) {
first_offset = ((elf32_program_header*)ph_buf)->offset;
}
else {
first_offset = ((elf64_program_header*)ph_buf)->offset;
}
if (first_offset > elf_hdr_sz) {
len = first_offset - elf_hdr_sz;
wolfBoot_printf(
"ELF: [CHECK] Adding %d bytes padding before first segment\n",
(int32_t)len);
update_hash_flash_fwimg(&ctx, &boot, elf_hdr_sz, len); /* Hash actual file content */
}
}
/* Walk the program header table and hash each loadable segment. */
for (i = 0; i < entry_count; i++) {
uint64_t paddr;
uint64_t filesz;
uint64_t offset;
uint32_t type;
uint64_t next_offset = 0; /* Initialize */
/* read the current program header into a local buffer */
read_flash_fwimage(&boot, current_ph_offset, ph_buf, ph_size);
/* Extract common fields based on ELF type */
if (is_elf32) {
elf32_program_header* ph = (elf32_program_header*)ph_buf;
paddr = ph->paddr;
offset = ph->offset;
filesz = ph->file_size;
type = ph->type;
}
else { /* 64-bit */
elf64_program_header* ph = (elf64_program_header*)ph_buf;
paddr = ph->paddr;
offset = ph->offset;
filesz = ph->file_size;
type = ph->type;
}
/* Handle loadable segments */
if (type == ELF_PT_LOAD) {
uint64_t seg_start;
uintptr_t load_addr;
/* Validate the segment before hashing: the flash-address hash
* reader consumes a uint32_t length, the file layout must stay
* inside the manifest image, and the paddr range must fit the
* destination (uintptr_t) address width so the load_addr cast
* below cannot wrap. Reject instead of continuing. */
if (filesz > UINT32_MAX) {
wolfBoot_printf("ELF: [CHECK] ERROR: segment file_size "
"%lu does not fit a 32-bit length\n",
(unsigned long)filesz);
return -1;
}
if (offset > (uint64_t)boot.fw_size ||
filesz > (uint64_t)boot.fw_size - offset) {
wolfBoot_printf("ELF: [CHECK] ERROR: segment offset %lu + "
"size %lu exceeds image size %u\n",
(unsigned long)offset,
(unsigned long)filesz, boot.fw_size);
return -1;
}
seg_start = paddr + (uint64_t)BASE_OFF;
if (seg_start < paddr ||
seg_start > (uint64_t)UINTPTR_MAX - filesz) {
wolfBoot_printf("ELF: [CHECK] ERROR: segment paddr range "
"overflows\n");
return -1;
}
load_addr = (uintptr_t)seg_start;
/* Feed the loadable parts to the hash function */
wolfBoot_printf("ELF: [CHECK] Hashing loadable segment: "
"paddr = 0x%08lx, loadaddr = 0x%08lx, "
"offset = 0x%08lx, size = %lu\n",
(unsigned long)paddr, (unsigned long)load_addr,
(unsigned long)offset, (unsigned long)filesz);
update_hash_flash_addr(&ctx, load_addr, (uint32_t)filesz,
PART_IS_EXT(&boot));
}
else {
wolfBoot_printf("ELF: [CHECK] ERROR: non-loadable segment\n");
return -1;
}
/* Add padding until next program header, if any. */
if (i < entry_count - 1) {
read_flash_fwimage(&boot, current_ph_offset + ph_size, ph_next_buf,
ph_size);
if (is_elf32) {
next_offset = ((elf32_program_header*)ph_next_buf)->offset;
}
else {
next_offset = ((elf64_program_header*)ph_next_buf)->offset;
}
if (next_offset > (offset + filesz)) {
uint32_t padding = next_offset - (offset + filesz);
wolfBoot_printf("ELF: [CHECK] Adding padding: %u bytes (from "
"0x%08lx to 0x%08lx)\n",
padding, (unsigned long)(offset + filesz),
(unsigned long)next_offset);
update_hash_flash_fwimg(&ctx, &boot, offset + filesz, padding); /* Hash actual file content */
}
}
final_offset =
offset + filesz; /* Track end offset of last processed segment */
current_ph_offset += ph_size;
} /* End of program header loop */
if (final_offset < 0 && entry_count > 0) {
/* Should have processed at least one segment if entry_count > 0 */
wolfBoot_printf("ELF: [CHECK] Error determining final offset\n");
return -1;
}
else if (final_offset < 0 && entry_count == 0) {
/* No program headers, hash only ELF header + PHT */
final_offset = elf_hdr_sz;
}
/* Check if final offset is valid */
if (final_offset > (int64_t)boot.fw_size) {
wolfBoot_printf("ELF: [CHECK] Final offset (%d) exceeds image size (%d)\n",
(int32_t)final_offset, (int32_t)boot.fw_size);
return -1;
}
/* Hash any trailing data after the last segment/header */
len = boot.fw_size - final_offset;
if (len > 0) {
wolfBoot_printf("ELF: [CHECK] Hashing %u bytes of trailing data from "
"offset 0x%llX\n",
len, (unsigned long long)final_offset);
update_hash_flash_fwimg(&ctx, &boot, final_offset, len);
}
/* Finalize SHA calculation */
final_hash(&ctx, calc_digest);
if (wolfBoot_hardened_CT_compare(exp_digest, calc_digest,
WOLFBOOT_SHA_DIGEST_SIZE) != 0) {
wolfBoot_printf("ELF: [CHECK] SHA verification FAILED\n");
return -2;
}
wolfBoot_printf("ELF: [CHECK] Verification successful\n");
return 0;
}
int wolfBoot_load_flash_image_elf(int part, unsigned long* entry_out, int ext_flash)
{
const unsigned char* image;
int is_elf32;
uint16_t entry_count;
size_t entry_off;
size_t ph_size;
int i;
const void* eh;
struct wolfBoot_image boot;
uint8_t elfHdrBuf[sizeof(elfHeaderMaxBuf)];
if (wolfBoot_open_image(&boot, part) < 0) {
return -1;
}
image = boot.fw_base;
/* Get the elf header from the image into a local buffer. We may overread
* the buffer depending on architecture */
memset(elfHdrBuf, 0, sizeof(elfHdrBuf));
if (read_flash_fwimage(&boot, 0, elfHdrBuf,
sizeof(elfHeaderMaxBuf)) != 0) {
wolfBoot_printf("ELF: [STORE] ERROR: could not read ELF header\n");
return -1;
}
if (elf_open(elfHdrBuf, &is_elf32) != 0) {
return -1;
}
/* Set up header pointers based on ELF type */
if (is_elf32) {
eh = (const elf32_header*)elfHdrBuf;
entry_count = ((const elf32_header*)eh)->ph_entry_count;
entry_off = ((const elf32_header*)eh)->ph_offset;
*entry_out = (unsigned long)((const elf32_header*)eh)->entry;
wolfBoot_printf("ELF: [STORE] 32-bit, entry=0x%08lx, "
"ph_offset=0x%08lx, ph_count=%d\n",
(unsigned long)((const elf32_header*)eh)->entry,
(unsigned long)entry_off, entry_count);
}
else {
eh = (const elf64_header*)elfHdrBuf;
entry_count = ((const elf64_header*)eh)->ph_entry_count;
entry_off = ((const elf64_header*)eh)->ph_offset;
*entry_out = (unsigned long)((const elf64_header*)eh)->entry;
wolfBoot_printf("ELF: [STORE] 64-bit, entry=0x%08lx, "
"ph_offset=0x%08lx, ph_count=%d\n",
(unsigned long)((const elf64_header*)eh)->entry,
(unsigned long)entry_off, entry_count);
}
/* Walk the program header table and store each loadable segment */
for (i = 0; i < entry_count; ++i) {
uint64_t paddr, filesz, offset;
int is_loadable;
uintptr_t load_addr;
uint64_t seg_start;
/* Read the current program header into a local buffer */
if (is_elf32) {
elf32_program_header p32;
if (read_flash_fwimage(&boot, entry_off, &p32,
sizeof(p32)) != 0) {
wolfBoot_printf("ELF: [STORE] ERROR: could not read "
"program header\n");
return -1;
}
is_loadable = (p32.type == ELF_PT_LOAD);
paddr = p32.paddr;
offset = p32.offset;
filesz = p32.file_size;
ph_size = sizeof(p32);
}
else {
elf64_program_header p64;
if (read_flash_fwimage(&boot, entry_off, &p64,
sizeof(p64)) != 0) {
wolfBoot_printf("ELF: [STORE] ERROR: could not read "
"program header\n");
return -1;
}
is_loadable = (p64.type == ELF_PT_LOAD);
paddr = p64.paddr;
offset = p64.offset;
filesz = p64.file_size;
ph_size = sizeof(p64);
}
/* Skip non-loadable segments */
if (!is_loadable) {
wolfBoot_printf("ELF: [STORE] ERROR: non-loadable segment\n");
return -1;
}
/* Validate the segment before writing: the source must stay
* inside the manifest image and the paddr range must fit the
* destination (uintptr_t) width so the load_addr cast below
* cannot wrap. The scatter destination is the exec region, which
* sits outside the boot partition that stores the signed ELF, so
* it is not bounded here: the program-header paddr values are
* covered by the image signature verified before this restore
* path. Reject instead of writing. */
if (filesz > UINT32_MAX) {
wolfBoot_printf("ELF: [STORE] ERROR: segment file_size "
"%lu does not fit a 32-bit length\n",
(unsigned long)filesz);
return -1;
}
if (offset > (uint64_t)boot.fw_size ||
filesz > (uint64_t)boot.fw_size - offset) {
wolfBoot_printf("ELF: [STORE] ERROR: segment offset %lu + "
"size %lu exceeds image size %u\n",
(unsigned long)offset,
(unsigned long)filesz, boot.fw_size);
return -1;
}
seg_start = paddr + (uint64_t)BASE_OFF;
if (seg_start < paddr ||
seg_start > (uint64_t)UINTPTR_MAX - filesz) {
wolfBoot_printf("ELF: [STORE] ERROR: segment paddr range "
"overflows\n");
return -1;
}
load_addr = (uintptr_t)seg_start;
wolfBoot_printf("ELF: [STORE] Writing loadable segment: "
"loadaddr=0x%08lx, offset=0x%08lx, size=%lu\n",
(unsigned long)load_addr, (unsigned long)offset,
(unsigned long)filesz);
if (copy_flash_buffered((uintptr_t)(image + offset), load_addr,
filesz, ext_flash, ext_flash) != 0) {
wolfBoot_printf("ELF: [STORE] ERROR: could not write "
"loadable segment\n");
return -1;
}
entry_off += ph_size;
}
wolfBoot_printf("ELF: [STORE] Image loading complete\n");
return 0;
}
#undef BASE_OFF
#endif
#ifdef WOLFBOOT_NO_SIGN
/**
* @brief Verify the authenticity of the image using a digital signature.
*
* This function verifies the authenticity of the image by verifying its digital
* signature.
*
* @param img The pointer to the wolfBoot_image structure representing the image.
* @return 0 on success, -1 on error, -2 if the signature verification fails.
*/
int wolfBoot_verify_authenticity(struct wolfBoot_image *img)
{
wolfBoot_image_confirm_signature_ok(img);
return 0;
}
#else
int wolfBoot_verify_authenticity(struct wolfBoot_image *img)
{
uint8_t *stored_signature;
uint16_t stored_signature_size;
uint8_t *pubkey_hint;
uint16_t pubkey_hint_size;
uint8_t *image_type_buf;
uint16_t image_type;
uint16_t image_type_size;
uint32_t key_mask = 0U;
uint32_t image_part = 1U;
int key_slot;
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \
(defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER))
uint8_t* cert_chain;
uint16_t cert_chain_size;
int32_t cert_verify_result;
int hsm_ret;
/* Reset certificate chain usage for this verification */
g_leafKeyIdValid = 0;
#endif
stored_signature_size = get_header(img, HDR_SIGNATURE, &stored_signature);
pubkey_hint_size = get_header(img, HDR_PUBKEY, &pubkey_hint);
if (pubkey_hint_size == WOLFBOOT_SHA_DIGEST_SIZE) {
#if defined(WOLFBOOT_RENESAS_SCEPROTECT) || \
defined(WOLFBOOT_RENESAS_TSIP) || \
defined(WOLFBOOT_RENESAS_RSIP)
/* SCE wrapped key is installed at
* RENESAS_SCE_INSTALLEDKEY_ADDR
* TSIP encrypted key is installed at
* RENESAS_TSIP_INSTALLEDKEY_ADDR
*/
extern int hal_renesas_init(void);
int rc = hal_renesas_init();
if (rc != 0) {
wolfBoot_printf("hal_renesas_init failed! %d\n", rc);
return rc;
}
key_slot = 0;
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
/* Don't care about the key slot, we are using a fixed wolfHSM keyId */
key_slot = 0;
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) && \
defined(WOLFBOOT_CERT_CHAIN_VERIFY)
/* Don't care about the key slot, we are using the public key from the
* leaf cert */
key_slot = 0;
#else
key_slot = keyslot_id_by_sha(pubkey_hint);
if (key_slot < 0) {
return -1; /* Key was not found */
}
#ifdef WOLFBOOT_TPM_KEYSTORE
if (wolfBoot_check_rot(key_slot, pubkey_hint) != 0) {
return -1; /* TPM root of trust failed! */
}
#endif
#endif
}
else {
return -1; /* Invalid hash size for public key hint */
}
image_type_size = get_header(img, HDR_IMG_TYPE, &image_type_buf);
if (image_type_size != WOLFBOOT_HDR_U16_SZ)
return -1;
image_type = (uint16_t)(image_type_buf[0] + (image_type_buf[1] << 8));
if ((image_type & HDR_IMG_TYPE_AUTH_MASK) != HDR_IMG_TYPE_AUTH)
return -1;
if ((img->sha_hash == NULL) || (img->sha_ok != 1U)) {
if (wolfBoot_verify_integrity(img) != 0)
return -1;
}
/* Integrity must hold before authenticity: assert it in a fault-hardened
* way so that skipping the re-check above (or its result) cannot let an
* image with an unverified digest reach signature verification. */
SHA_SANITY_CHECK(img);
image_part = image_type & HDR_IMG_TYPE_PART_MASK;
#ifdef WOLFBOOT_NO_KEYSTORE
/* No local keystore is linked: there is no per-key partition permission
* mask to consult. Key authorization and usage are enforced by the HSM
* (cert-chain root-of-trust plus per-key usage flags), so the wolfBoot
* keystore mask check does not apply here. */
(void)key_slot;
(void)key_mask;
(void)image_part;
#else
key_mask = keystore_get_mask(key_slot);
/* Check if the key permission mask matches the current partition id */
if (((1U << image_part) & key_mask) != (1U << image_part)) {
return -1; /* Key not allowed to verify this partition id */
}
CONFIRM_MASK_VALID(image_part, key_mask);
#endif
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \
(defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER))
/* Check for certificate chain in the image header */
cert_chain_size = get_header(img, HDR_CERT_CHAIN, &cert_chain);
if (cert_chain_size > 0) {
wolfBoot_printf("Found certificate chain (%d bytes)\n",
cert_chain_size);
/* Verify certificate chain using wolfHSM's verification API. Use DMA if
* available in the wolfHSM configuration */
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
#if defined(WOLFHSM_CFG_DMA)
wolfBoot_printf(
"verifying cert chain and caching leaf pubkey (using DMA)\n");
hsm_ret = wh_Client_CertVerifyMultiRootDmaAndCacheLeafPubKey(
&hsmClientCtx, cert_chain, cert_chain_size,
hsmNvmIdCertRootCAList, hsmNvmIdCertRootCACount,
WH_NVM_FLAGS_USAGE_VERIFY, &g_certLeafKeyId, &cert_verify_result);
#else
wolfBoot_printf("verifying cert chain and caching leaf pubkey\n");
hsm_ret = wh_Client_CertVerifyMultiRootAndCacheLeafPubKey(
&hsmClientCtx, cert_chain, cert_chain_size,
hsmNvmIdCertRootCAList, hsmNvmIdCertRootCACount,
WH_NVM_FLAGS_USAGE_VERIFY, &g_certLeafKeyId, &cert_verify_result);
#endif
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
wolfBoot_printf("verifying cert chain and caching leaf pubkey\n");
hsm_ret = wh_Server_CertVerifyMultiRoot(
&hsmServerCtx, cert_chain, cert_chain_size,
hsmNvmIdCertRootCAList, hsmNvmIdCertRootCACount,
WH_CERT_FLAGS_CACHE_LEAF_PUBKEY, WH_NVM_FLAGS_USAGE_VERIFY,
&g_certLeafKeyId);
if (hsm_ret == WH_ERROR_OK) {
cert_verify_result = 0;
}
wolfBoot_printf("wh_Server_CertVerifyMultiRoot returned %d\n", hsm_ret);
#endif
/* Error or verification failure results in standard auth check failure
* path */
if (hsm_ret != 0 || cert_verify_result != 0) {
wolfBoot_printf("Certificate chain verification failed: "
"hsm_ret=%d, verify_result=%d\n",
hsm_ret, cert_verify_result);
return -1;
}
wolfBoot_printf("Certificate chain verified, using leaf key ID: %08x\n",
(unsigned int)g_certLeafKeyId);
/* Set flag to use the leaf certificate's public key for signature
* verification later */
g_leafKeyIdValid = 1;
}
#endif
if (stored_signature_size == 0 || stored_signature == NULL) {
return -1;
}
#if defined(WOLFBOOT_SIGN_ED25519)
if (stored_signature_size != ED25519_IMAGE_SIGNATURE_SIZE)
return -1;
#elif defined(WOLFBOOT_SIGN_ED448)
if (stored_signature_size != ED448_IMAGE_SIGNATURE_SIZE)
return -1;
#elif defined (WOLFBOOT_SIGN_RSA2048) || \
defined (WOLFBOOT_SIGN_RSA3072) || \
defined (WOLFBOOT_SIGN_RSA4096) || \
defined (WOLFBOOT_SIGN_RSA2048ENC) || \
defined (WOLFBOOT_SIGN_RSA3072ENC) || \
defined (WOLFBOOT_SIGN_RSA4096ENC) || \
defined (WOLFBOOT_SIGN_RSAPSS2048) || \
defined (WOLFBOOT_SIGN_RSAPSS3072) || \
defined (WOLFBOOT_SIGN_RSAPSS4096)
if (stored_signature_size != RSA_IMAGE_SIGNATURE_SIZE)
return -1;
#elif defined (WOLFBOOT_SIGN_ECC256) || \
defined (WOLFBOOT_SIGN_ECC384) || \
defined (WOLFBOOT_SIGN_ECC521)
if (stored_signature_size != ECC_IMAGE_SIGNATURE_SIZE)
return -1;
#elif defined(WOLFBOOT_SIGN_LMS)
if (stored_signature_size != LMS_IMAGE_SIGNATURE_SIZE)
return -1;
#elif defined(WOLFBOOT_SIGN_XMSS)
if (stored_signature_size != XMSS_IMAGE_SIGNATURE_SIZE)
return -1;
#elif defined(WOLFBOOT_SIGN_ML_DSA)
if (stored_signature_size != ML_DSA_IMAGE_SIGNATURE_SIZE)
return -1;
#else
return -1;
#endif
/* wolfBoot_verify_signature_ecc() does not return the result directly.
* A call to wolfBoot_image_confirm_signature_ok() is required in order to
* confirm that the signature verification is OK.
*
* only a call to wolfBoot_image_confirm_signature_ok() sets
* img->signature_ok to 1.
*
*/
wolfBoot_verify_signature_primary(key_slot, img, stored_signature);
#ifdef WOLFBOOT_ARMORED
#define SIG_OK(imgp) (((imgp)->signature_ok == 1) && \
((imgp)->not_signature_ok == ~(uint32_t)1))
#else
#define SIG_OK(imgp) ((imgp)->signature_ok == 1)
#endif
#ifdef SIGN_HYBRID
if (SIG_OK(img)) {
uint8_t *stored_secondary_signature;
uint16_t stored_secondary_signature_size;
uint16_t expected_secondary_signature_size = 0;
/* Invalidate the signature_ok flag */
wolfBoot_image_clear_signature_ok(img);
/* Load the pubkey hint for the secondary key */
pubkey_hint_size = get_header(img, HDR_SECONDARY_PUBKEY, &pubkey_hint);
if (pubkey_hint_size == WOLFBOOT_SHA_DIGEST_SIZE) {
key_slot = keyslot_id_by_sha(pubkey_hint);
if (key_slot < 0) {
return -1; /* Key was not found */
}
key_mask = keystore_get_mask(key_slot);
if (((1U << image_part) & key_mask) != (1U << image_part)) {
return -1; /* Key not allowed to verify this partition id */
}
CONFIRM_MASK_VALID(image_part, key_mask);
stored_secondary_signature_size = get_header(img,
HDR_SECONDARY_SIGNATURE, &stored_secondary_signature);
if (stored_secondary_signature_size == 0 ||
stored_secondary_signature == NULL) {
return -1;
}
#if defined(WOLFBOOT_SIGN_SECONDARY_ED25519)
expected_secondary_signature_size = ED25519_IMAGE_SIGNATURE_SIZE;
#elif defined(WOLFBOOT_SIGN_SECONDARY_ED448)
expected_secondary_signature_size = ED448_IMAGE_SIGNATURE_SIZE;
#elif defined (WOLFBOOT_SIGN_SECONDARY_RSA2048) || \
defined (WOLFBOOT_SIGN_SECONDARY_RSA3072) || \
defined (WOLFBOOT_SIGN_SECONDARY_RSA4096) || \
defined (WOLFBOOT_SIGN_SECONDARY_RSA2048ENC) || \
defined (WOLFBOOT_SIGN_SECONDARY_RSA3072ENC) || \
defined (WOLFBOOT_SIGN_SECONDARY_RSA4096ENC) || \
defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \
defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \
defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS4096)
expected_secondary_signature_size = RSA_IMAGE_SIGNATURE_SIZE;
#elif defined (WOLFBOOT_SIGN_SECONDARY_ECC256) || \
defined (WOLFBOOT_SIGN_SECONDARY_ECC384) || \
defined (WOLFBOOT_SIGN_SECONDARY_ECC521)
expected_secondary_signature_size = ECC_IMAGE_SIGNATURE_SIZE;
#elif defined(WOLFBOOT_SIGN_SECONDARY_LMS)
expected_secondary_signature_size = LMS_IMAGE_SIGNATURE_SIZE;
#elif defined(WOLFBOOT_SIGN_SECONDARY_XMSS)
expected_secondary_signature_size = XMSS_IMAGE_SIGNATURE_SIZE;
#elif defined(WOLFBOOT_SIGN_SECONDARY_ML_DSA)
expected_secondary_signature_size = ML_DSA_IMAGE_SIGNATURE_SIZE;
#endif
if (expected_secondary_signature_size == 0 ||
stored_secondary_signature_size !=
expected_secondary_signature_size) {
return -1;
}
wolfBoot_printf("Verification of hybrid signature\n");
wolfBoot_verify_signature_secondary(key_slot, img,
stored_secondary_signature);
wolfBoot_printf("Done.\n");
}
}
#endif
if (SIG_OK(img)) {
return 0;
}
return -2;
#undef SIG_OK
}
#endif
/**
* @brief Peek at the content of the image at a specific offset.
*
* This function allows peeking at the content of the image at a specific offset
* without modifying the image.
*
* @param img The pointer to the wolfBoot_image structure representing the image.
* @param offset The offset within the image to peek at.
* @param sz Optional pointer to store the size of the peeked data.
* @return The pointer to the peeked data, or NULL if the offset is out of bounds.
*/
uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset,
uint32_t* sz)
{
uint8_t* p = get_sha_block(img, offset);
if (sz) {
if (p == NULL) {
*sz = 0;
}
else {
*sz = WOLFBOOT_SHA_BLOCK_SIZE;
if (*sz > img->fw_size - offset) {
*sz = img->fw_size - offset;
}
}
}
return p;
}
#if !defined(WOLFBOOT_NO_SIGN) && !defined(WOLFBOOT_RENESAS_SCEPROTECT) && \
!defined(WOLFBOOT_NO_KEYSTORE)
/* Compare fixed-size key hints without early exit to avoid leaking hash prefix
* matches through lookup timing. */
static int keyslot_CT_hint_matches(const uint8_t *expected,
const uint8_t *actual)
{
volatile uint8_t diff = 0;
uint32_t i;
for (i = 0; i < WOLFBOOT_SHA_DIGEST_SIZE; i++) {
diff |= expected[i] ^ actual[i];
}
return diff == 0;
}
/**
* @brief Get the key slot ID by SHA hash.
*
* This function retrieves the key slot ID from the keystore that matches the
* provided SHA hash.
*
* @param hint The SHA hash of the public key to search for.
* @return The key slot ID if found, -1 if the key was not found.
*/
int keyslot_id_by_sha(const uint8_t *hint)
{
int id;
int match_id = -1;
for (id = 0; id < keystore_num_pubkeys(); id++) {
int match;
key_hash(id, digest);
match = keyslot_CT_hint_matches(digest, hint);
if (match && (match_id < 0))
match_id = id;
}
return match_id;
}
#endif /* !WOLFBOOT_NO_SIGN && !WOLFBOOT_RENESAS_SCEPROTECT */