mirror of https://github.com/wolfSSL/wolfBoot.git
2961 lines
97 KiB
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 */
|