/* image.h * * Functions to help with wolfBoot image header * * * 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 */ #ifndef IMAGE_H #define IMAGE_H #ifdef __cplusplus extern "C" { #endif #include #include "target.h" #include "wolfboot/wolfboot.h" #ifdef EXT_FLASH #include "hal.h" #endif #if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST)) #include "encrypt.h" #endif #ifndef WOLFBOOT_FLAGS_INVERT #define SECT_FLAG_NEW 0x0F #define SECT_FLAG_SWAPPING 0x07 #define SECT_FLAG_BACKUP 0x03 #define SECT_FLAG_UPDATED 0x00 #else #define SECT_FLAG_NEW 0x00 #define SECT_FLAG_SWAPPING 0x08 #define SECT_FLAG_BACKUP 0x0c #define SECT_FLAG_UPDATED 0x0f #endif #ifdef WOLFBOOT_SIGN_ED25519 #define wolfBoot_verify_signature_primary wolfBoot_verify_signature_ed25519 #endif #ifdef WOLFBOOT_SIGN_ED448 #define wolfBoot_verify_signature_primary wolfBoot_verify_signature_ed448 #endif #if defined (WOLFBOOT_SIGN_RSA2048) || \ defined (WOLFBOOT_SIGN_RSA3072) || \ defined (WOLFBOOT_SIGN_RSA4096) || \ defined (WOLFBOOT_SIGN_RSA2048ENC) || \ defined (WOLFBOOT_SIGN_RSA3072ENC) || \ defined (WOLFBOOT_SIGN_RSA4096ENC) #define wolfBoot_verify_signature_primary wolfBoot_verify_signature_rsa #endif #if defined (WOLFBOOT_SIGN_RSAPSS2048) || \ defined (WOLFBOOT_SIGN_RSAPSS3072) || \ defined (WOLFBOOT_SIGN_RSAPSS4096) #define wolfBoot_verify_signature_primary wolfBoot_verify_signature_rsa_pss #endif #if defined (WOLFBOOT_SIGN_ECC256) || \ defined (WOLFBOOT_SIGN_ECC384) || \ defined (WOLFBOOT_SIGN_ECC521) #define wolfBoot_verify_signature_primary wolfBoot_verify_signature_ecc #endif #if defined(WOLFBOOT_SIGN_LMS) #define wolfBoot_verify_signature_primary wolfBoot_verify_signature_lms #endif #if defined(WOLFBOOT_SIGN_XMSS ) #define wolfBoot_verify_signature_primary wolfBoot_verify_signature_xmss #endif #ifdef WOLFBOOT_SIGN_ML_DSA #define wolfBoot_verify_signature_primary wolfBoot_verify_signature_ml_dsa #endif #ifdef WOLFBOOT_SIGN_SECONDARY_ED25519 #define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_ed25519 #endif #ifdef WOLFBOOT_SIGN_SECONDARY_ED448 #define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_ed448 #endif #if 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) #define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_rsa #endif #if defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \ defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \ defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) #define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_rsa_pss #endif #if defined (WOLFBOOT_SIGN_SECONDARY_ECC256) || \ defined (WOLFBOOT_SIGN_SECONDARY_ECC384) || \ defined (WOLFBOOT_SIGN_SECONDARY_ECC521) #define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_ecc #endif #ifdef WOLFBOOT_SIGN_SECONDARY_LMS #define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_lms #endif #ifdef WOLFBOOT_SIGN_SECONDARY_XMSS #define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_xmss #endif #ifdef WOLFBOOT_SIGN_SECONDARY_ML_DSA #define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_ml_dsa #endif /* Thin wrappers: dispatch RSA / RSA-PSS to the unified verify function */ #define wolfBoot_verify_signature_rsa(ks, img, sig) \ wolfBoot_verify_signature_rsa_common(ks, img, sig, 0) #define wolfBoot_verify_signature_rsa_pss(ks, img, sig) \ wolfBoot_verify_signature_rsa_common(ks, img, sig, 1) #if defined(WOLFBOOT_TPM) && defined (WOLFBOOT_TPM_VERIFY) #undef wolfBoot_verify_signature_primary #define wolfBoot_verify_signature_primary wolfBoot_verify_signature_tpm #endif /* Validate sector size is larger than image header size */ #if defined(WOLFBOOT_SECTOR_SIZE) && defined(IMAGE_HEADER_SIZE) && \ (WOLFBOOT_SECTOR_SIZE < IMAGE_HEADER_SIZE) #error WOLFBOOT_SECTOR_SIZE must be larger than IMAGE_HEADER_SIZE #endif #if (defined(WOLFBOOT_ARMORED) && defined(__WOLFBOOT)) #if !defined(ARCH_ARM) || (!defined(__GNUC__) && \ !(defined(__ICCARM__) && defined(__IAR_SYSTEMS_ICC__))) # error WOLFBOOT_ARMORED only available for ARM with IAR or gcc compilers #endif #if defined(__GNUC__) #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) #else #define likely(x) (x) #define unlikely(x) (x) #endif struct wolfBoot_image { uint8_t *hdr; #ifdef EXT_FLASH uint8_t *hdr_cache; #endif uint8_t *trailer; uint8_t *sha_hash; uint8_t *fw_base; uint32_t fw_size; uint32_t part; uint32_t hdr_ok; uint32_t canary_FEED4567; uint32_t signature_ok; uint32_t canary_FEED6789; uint32_t not_signature_ok; uint32_t canary_FEED89AB; uint32_t sha_ok; uint32_t canary_FEEDCAFE; uint32_t not_sha_ok; uintptr_t not_fw_base; /* complement of fw_base, for FI hardening */ uint32_t not_ext; /* image is no longer external */ }; /** * This function sets the flag that indicates the signature is valid for the * wolfBoot_image. * * With ARMORED setup, the flag is redundant, and the information is wrapped in * between canary variables, to mitigate attacks based on memory corruptions. */ static void NOINLINEFUNCTION wolfBoot_image_confirm_signature_ok( struct wolfBoot_image *img) { img->canary_FEED4567 = 0xFEED4567UL; img->signature_ok = 1UL; img->canary_FEED6789 = 0xFEED6789UL; img->not_signature_ok = ~(1UL); img->canary_FEED89AB = 0xFEED89ABUL; } static void NOINLINEFUNCTION wolfBoot_image_clear_signature_ok( struct wolfBoot_image *img) { img->canary_FEED4567 = 0xFEED4567UL; img->signature_ok = 0UL; img->canary_FEED6789 = 0xFEED6789UL; img->not_signature_ok = 1UL; img->canary_FEED89AB = 0xFEED89ABUL; } /** * This function sets the flag that indicates the digest (integrity) check * succeeded for the wolfBoot_image. * * As with the signature flag, the value is redundant and wrapped between * canary variables, so that a single fault cannot forge a valid state. */ static void NOINLINEFUNCTION UNUSEDFUNCTION wolfBoot_image_confirm_sha_ok( struct wolfBoot_image *img) { img->canary_FEED89AB = 0xFEED89ABUL; img->sha_ok = 1UL; img->canary_FEEDCAFE = 0xFEEDCAFEUL; img->not_sha_ok = ~(1UL); } static void NOINLINEFUNCTION wolfBoot_image_clear_sha_ok( struct wolfBoot_image *img) { img->canary_FEED89AB = 0xFEED89ABUL; img->sha_ok = 0UL; img->canary_FEEDCAFE = 0xFEEDCAFEUL; img->not_sha_ok = 1UL; } /** * Sets sha_ok from the value the digest comparison produced rather than from * the control flow that reached here. Sole setter of sha_ok in the ARMORED * integrity path, so skipping the call leaves the flag clear. */ static void NOINLINEFUNCTION wolfBoot_image_seal_sha_ok( struct wolfBoot_image *img, uint32_t witness) { volatile uint32_t z1 = 0U, z2 = 0U, z3 = 0U, ok = 0U; z1 = (witness - 1U) >> 31; /* 1 iff witness == 0 */ z2 = 1U ^ ((witness | (0U - witness)) >> 31); /* 1 iff witness == 0 */ z3 = (witness == 0U) ? 1U : 0U; /* 1 iff witness == 0 */ ok = z1 & z2 & z3; ok &= z1; ok &= z2; ok &= z3; img->canary_FEED89AB = 0xFEED89ABUL; img->sha_ok = ok; img->canary_FEEDCAFE = 0xFEEDCAFEUL; img->not_sha_ok = ~ok; } /** * Records the image entry base together with its complement, so that a single * fault on the pointer that do_boot() jumps through can be detected by * FW_BASE_SANITY_CHECK() before the branch is taken. */ static void NOINLINEFUNCTION wolfBoot_image_set_fw_base( struct wolfBoot_image *img, void *base) { img->fw_base = (uint8_t *)base; img->not_fw_base = ~(uintptr_t)base; } /** * Final sanity check, performed just before do_boot, or before starting an * update that has been verified. * * This procedure detects if any of the previous checks has been skipped. * If any of the required flags does not match the expected value, wolfBoot * panics. */ #define PART_SANITY_CHECK(p) \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading hdr_ok flag, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->hdr_ok):"r2"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-12"); \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading hdr_ok flag, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->sha_ok):"r2"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-12"); \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading ~(sha_ok) flag, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->not_sha_ok):"r2"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-12"); \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading canary value, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->canary_FEEDCAFE):"r2"); \ asm volatile("mov r0, %0" ::"r"(0xFEEDCAFE):"r0"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-12"); \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading signature_ok flag, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->signature_ok):"r2"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-12"); \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0"); \ asm volatile("mov r2, #0"); \ asm volatile("mov r2, #0"); \ asm volatile("mov r2, #0"); \ asm volatile("mov r2, #0"); \ /* Loading ~(signature_ok) flag, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->not_signature_ok):"r2"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-12"); \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading canary value, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->canary_FEED6789):"r2"); \ asm volatile("mov r0, %0" ::"r"(0xFEED6789):"r0"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-12"); \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading canary value, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->canary_FEED4567):"r2"); \ asm volatile("mov r0, %0" ::"r"(0xFEED4567):"r0"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-12"); \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading canary value, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->canary_FEED89AB):"r2"); \ asm volatile("mov r0, %0" ::"r"(0xFEED89AB):"r0"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("cmp r2, r0":::"cc"); \ asm volatile("bne .-12") \ /** * First part of RSA verification. Ensure that the function is called by * double checking its return value contains a valid * len (>= WOLFBOOT_SHA_DIGEST_SIZE). * * Uses GAS local numeric labels (1f/1:) so the macro can be safely expanded * multiple times in the same function (e.g. RSA PKCS#1.5 + RSA-PSS paths). */ #define RSA_VERIFY_FN(ret,fn,...) \ { \ /* Redundant set of r0=0 */ \ asm volatile("mov r0, #0":::"r0"); \ asm volatile("mov r0, #0":::"r0"); \ asm volatile("mov r0, #0":::"r0"); \ /* Call the function */ \ int tmp_ret = fn(__VA_ARGS__); \ ret = -1; \ /* Redundant set of r2=SHA_DIGEST_SIZE */ \ asm volatile("mov r2, %0" ::"r"(WOLFBOOT_SHA_DIGEST_SIZE):"r2"); \ asm volatile("mov r2, %0" ::"r"(WOLFBOOT_SHA_DIGEST_SIZE):"r2"); \ asm volatile("mov r2, %0" ::"r"(WOLFBOOT_SHA_DIGEST_SIZE):"r2"); \ /* Redundant check for fn() return value >= r2 */ \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("blt 1f"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("blt 1f"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("blt 1f"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("blt 1f"); \ /* Return value is set here in case of success */ \ ret = tmp_ret; \ asm volatile("1:"); \ asm volatile("nop"); \ } /** * Second part of RSA verification. * * Compare the digest twice, then confirm via * wolfBoot_image_confirm_signature_ok(); * * Uses GAS local numeric labels (2f/2:) for safe multi-expansion. */ #define RSA_VERIFY_HASH(img,digest) \ { \ volatile int compare_res; \ if (!img || !digest) \ asm volatile("b 2f"); \ /* Redundant set of r0=50*/ \ asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ compare_res = image_CT_compare(digest, img->sha_hash, \ WOLFBOOT_SHA_DIGEST_SIZE); \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 2f":::"cc"); \ asm volatile("cmp r0, #0"); \ asm volatile("cmp r0, #0"); \ asm volatile("cmp r0, #0"); \ asm volatile("bne 2f":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 2f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 2f"); \ /* Repeat comparison call */ \ compare_res = image_CT_compare(digest, img->sha_hash, \ WOLFBOOT_SHA_DIGEST_SIZE); \ compare_res; \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 2f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 2f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 2f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 2f"); \ /* Confirm that the signature is OK */ \ wolfBoot_image_confirm_signature_ok(img); \ asm volatile("2:"); \ asm volatile("nop"); \ } /** * Second part of RSA-PSS verification. * * Call wc_RsaPSS_CheckPadding twice, then confirm via * wolfBoot_image_confirm_signature_ok(); * * Uses GAS local numeric labels (3f/3:) for safe multi-expansion. */ #define RSA_PSS_VERIFY_HASH(img, pss_data, pss_data_sz, hash_type) \ { \ volatile int pss_res; \ if (!img || !pss_data) \ asm volatile("b 3f"); \ /* Redundant set of r0=50*/ \ asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ pss_res = wc_RsaPSS_CheckPadding(img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, \ pss_data, pss_data_sz, hash_type); \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 3f":::"cc"); \ asm volatile("cmp r0, #0"); \ asm volatile("cmp r0, #0"); \ asm volatile("cmp r0, #0"); \ asm volatile("bne 3f":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 3f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 3f"); \ /* Repeat wc_RsaPSS_CheckPadding call */ \ pss_res = wc_RsaPSS_CheckPadding(img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, \ pss_data, pss_data_sz, hash_type); \ pss_res; \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 3f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 3f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 3f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 3f"); \ /* Confirm that the signature is OK */ \ wolfBoot_image_confirm_signature_ok(img); \ asm volatile("3:"); \ asm volatile("nop"); \ } /* Redundant, canary-wrapped test for a confirmed integrity (digest) check. */ #define SHA_OK(imgp) (((imgp)->sha_ok == 1) && \ ((imgp)->not_sha_ok == ~(uint32_t)1)) /** * Digest comparison with no call and no return register, run inline as an * additional gate. Emits a witness in `out`, zero only on a clean full pass. * Branches to label 5 on mismatch, like the checks around it. */ #define CT_COMPARE_INLINE(out, a, b, len) \ asm volatile( \ "mov r0, %1\n" /* left */ \ "mov r1, %2\n" /* right */ \ "mov r2, #0\n" /* index */ \ "mov r2, #0\n" \ "mov r2, #0\n" \ "mov r3, #0\n" /* difference accumulator */ \ "mov r3, #0\n" \ "mov r3, #0\n" \ "mov r6, %3\n" /* iteration budget */ \ "mov r6, %3\n" \ "mov r6, %3\n" \ "7:\n" \ "ldrb r4, [r0, r2]\n" \ "ldrb r5, [r1, r2]\n" \ "eors r4, r4, r5\n" \ "orrs r3, r3, r4\n" \ "adds r2, r2, #1\n" \ "subs r6, r6, #1\n" \ "beq 8f\n" /* budget spent: stop regardless */ \ "cmp r2, %3\n" \ "blo 7b\n" \ "8:\n" \ /* witness = difference | iteration shortfall */ \ "eor r4, r2, %3\n" \ "orr %0, r3, r4\n" \ "orr %0, %0, r3\n" \ /* every byte matched */ \ "cmp r3, #0\n" \ "cmp r3, #0\n" \ "cmp r3, #0\n" \ "bne 5f\n" \ "cmp r3, #0\n" \ "cmp r3, #0\n" \ "cmp r3, #0\n" \ "bne 5f\n" \ /* and the loop really ran over the whole buffer */ \ "cmp r2, %3\n" \ "cmp r2, %3\n" \ "cmp r2, %3\n" \ "bne 5f\n" \ "cmp r2, %3\n" \ "cmp r2, %3\n" \ "cmp r2, %3\n" \ "bne 5f\n" \ : "=&r"(out) \ : "r"(a), "r"(b), "r"((uint32_t)(len)) \ : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "cc", "memory") /** * Digest (integrity) verification. * * Compare the freshly computed digest against the stored one twice through * image_CT_compare(), checking each result redundantly, then a third time * inline. Only then record the digest and raise sha_ok from the witness. * * Uses GAS local numeric labels (5f/5:, 7f/7b, 8f/8:) for safe expansion. */ #define VERIFY_INTEGRITY_FN(img, computed_digest, stored) \ { \ volatile int compare_res; \ /* Pre-set to "mismatch". */ \ volatile uint32_t ct_witness = 0xFFFFFFFFU; \ if (!(img) || !(stored)) \ asm volatile("b 5f"); \ /* Redundant set of r0=50 */ \ asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ compare_res = image_CT_compare((computed_digest), (stored), \ WOLFBOOT_SHA_DIGEST_SIZE); \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 5f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 5f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 5f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 5f"); \ /* Repeat comparison call */ \ compare_res = image_CT_compare((computed_digest), (stored), \ WOLFBOOT_SHA_DIGEST_SIZE); \ compare_res; \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 5f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 5f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 5f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 5f"); \ /* Third comparison, inline */ \ CT_COMPARE_INLINE(ct_witness, (computed_digest), (stored), \ WOLFBOOT_SHA_DIGEST_SIZE); \ (img)->sha_hash = (stored); \ wolfBoot_image_seal_sha_ok((img), ct_witness); \ asm volatile("5:"); \ asm volatile("nop"); \ } /** * Hardened assertion that the integrity (digest) check has actually been * performed and passed. Mirrors the sha portion of PART_SANITY_CHECK and is * used right after the integrity re-check in wolfBoot_verify_authenticity(), * so that skipping the re-check (or its result) cannot let an unverified * image proceed to signature verification. */ #define SHA_SANITY_CHECK(p) \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading sha_ok flag, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->sha_ok):"r2"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne .-12"); \ /* Redundant set of r2=0 */ \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ asm volatile("mov r2, #0":::"r2"); \ /* Loading ~(sha_ok) flag, verifying */ \ asm volatile("mov r2, %0" ::"r"((p)->not_sha_ok):"r2"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne ."); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-4"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-8"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-12") /** * Hardened assertion that the image entry base is consistent with its stored * complement (fw_base == ~not_fw_base), performed immediately before do_boot() * dereferences fw_base. A single fault on either the pointer or the loads * fails the check safely (spins) instead of redirecting the boot jump. */ #define FW_BASE_SANITY_CHECK(p) \ do { \ /* Single asm block so the compiler cannot allocate over r0/r2 between \ * steps: r2 = fw_base, r0 = ~not_fw_base (== expected fw_base), then \ * a redundant self-trapping comparison that spins on any mismatch. */ \ asm volatile( \ "mov r2, %[fwb]\n\t" \ "mov r0, %[nfwb]\n\t" \ "mvn r0, r0\n\t" \ "cmp r2, r0\n\t" \ "cmp r2, r0\n\t" \ "cmp r2, r0\n\t" \ "bne .\n\t" \ "cmp r2, r0\n\t" \ "cmp r2, r0\n\t" \ "cmp r2, r0\n\t" \ "bne .-4\n\t" \ "cmp r2, r0\n\t" \ "cmp r2, r0\n\t" \ "cmp r2, r0\n\t" \ "bne .-8\n\t" \ "cmp r2, r0\n\t" \ "cmp r2, r0\n\t" \ "cmp r2, r0\n\t" \ "bne .-12\n\t" \ : \ : [fwb] "r" ((uintptr_t)(p)->fw_base), \ [nfwb] "r" ((uintptr_t)(p)->not_fw_base) \ : "r0", "r2", "cc"); \ } while (0) /** * ECC / Ed / PQ signature verification. * Those verify functions set an additional value 'p_res' * which is passed as a pointer. * * Ensure that the verification function has been called, and then * set the return value accordingly. * * Double check by reading the value in p_res from memory a few times. * * Uses GAS local numeric labels (4f/4:) for safe multi-expansion. */ #if defined(__GNUC__) #define VERIFY_FN(img,p_res,fn,...) \ /* Redundant set of r0=50*/ \ asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ /* Call the verify function */ \ fn(__VA_ARGS__); \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne 4f"); \ /* Check that res = 1, a few times, reading the value from memory */ \ asm volatile("ldr r2, [%0]" ::"r"(p_res)); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("mvn r3, r2":::"r3"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("ldr r2, [%0]" ::"r"(p_res)); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("mvn r3, r2":::"r3"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("ldr r2, [%0]" ::"r"(p_res)); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("mvn r3, r2":::"r3"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("ldr r2, [%0]" ::"r"(p_res)); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("cmp r2, #1":::"cc"); \ asm volatile("bne 4f"); \ asm volatile("mvn r3, r2":::"r3"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \ asm volatile("bne 4f"); \ /* Confirm that the signature is OK */ \ wolfBoot_image_confirm_signature_ok(img); \ asm volatile("4:"); \ asm volatile("nop") \ #elif defined(__ICCARM__) && defined(__IAR_SYSTEMS_ICC__) #define VERIFY_FN(img, p_res, fn, ...) \ do { \ __asm volatile( \ "mov r0, #50\n" \ "mov r0, #50\n" \ "mov r0, #50\n" \ : /* No output operands */ \ : /* No input operands */ \ : "r0" /* Clobbered registers */ \ ); \ void (*confirm_func)(struct wolfBoot_image *) = \ wolfBoot_image_confirm_signature_ok; \ fn(__VA_ARGS__); \ __asm volatile( \ "cmp r0, #0\n" \ "cmp r0, #0\n" \ "cmp r0, #0\n" \ "beq 30f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "30:\n" \ "ldr r2, [%0]\n" \ "cmp r2, #1\n" \ "cmp r2, #1\n" \ "cmp r2, #1\n" \ "beq 31f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "31:\n" \ "mvn r3, r2\n" \ "cmp r3, #0xFFFFFFFE\n" \ "cmp r3, #0xFFFFFFFE\n" \ "cmp r3, #0xFFFFFFFE\n" \ "beq 32f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "32:\n" \ "ldr r2, [%0]\n" \ "cmp r2, #1\n" \ "cmp r2, #1\n" \ "cmp r2, #1\n" \ "beq 33f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "33:\n" \ "mvn r3, r2\n" \ "cmp r3, #0xFFFFFFFE\n" \ "cmp r3, #0xFFFFFFFE\n" \ "cmp r3, #0xFFFFFFFE\n" \ "beq 34f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "34:\n" \ "ldr r2, [%0]\n" \ "cmp r2, #1\n" \ "cmp r2, #1\n" \ "cmp r2, #1\n" \ "beq 35f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "35:\n" \ "mvn r3, r2\n" \ "cmp r3, #0xFFFFFFFE\n" \ "cmp r3, #0xFFFFFFFE\n" \ "cmp r3, #0xFFFFFFFE\n" \ "beq 36f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "36:\n" \ "ldr r2, [%0]\n" \ "cmp r2, #1\n" \ "cmp r2, #1\n" \ "cmp r2, #1\n" \ "beq 37f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "37:\n" \ "mvn r3, r2\n" \ "cmp r3, #0xFFFFFFFE\n" \ "cmp r3, #0xFFFFFFFE\n" \ "cmp r3, #0xFFFFFFFE\n" \ "beq 38f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "38:\n" \ /* Load 'img' into r0 (first argument to the function) */ \ "mov r0, %1\n" \ /* Load the function pointer into r3 */ \ "mov r3, %2\n" \ "blx r3\n"\ "b 2f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "1:\n" \ "nop\n" \ "2:\n" \ : /* No output operands */ \ : "r"(p_res), "r"(img), "r"(confirm_func) /* Input operands */ \ : "r0", "r2", "r3", "lr" /* Clobbered registers */ \ ); \ } while (0) #endif /** * This macro is only invoked after a successful update version check, prior to * initiating the update installation. * * At this point, wolfBoot thinks that the version check has been successful. * * * The fallback flag (checked with redundancy) causes wolfBoot to skip the * redundant version checks. * * The redundant checks here ensure that the image version is read twice per * each partition, and the two return values are the same. * * * The comparison is also redundant, causing wolfBoot to panic if the update * version is not strictly greater than the current one. * */ #if defined(__GNUC__) #define VERIFY_VERSION_ALLOWED(fb_ok) \ asm volatile( \ "push {r4, r5, r6, r7}\n" \ "mov r0, #0\n" \ "mov r4, #1\n" \ "mov r5, #0\n" \ "mov r6, #2\n" \ "mov r7, #0\n" \ "mov r0, #0\n" \ "mov r4, #1\n" \ "mov r5, #0\n" \ "mov r6, #2\n" \ "mov r7, #0\n" \ "mov r0, %0\n" \ "mov r4, %0\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "beq 20f\n" \ "bne do_check\n" \ "bne do_check\n" \ "bne do_check\n" \ "bne do_check\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "20:\n" \ "cmp r4, #1\n" \ "cmp r4, #1\n" \ "cmp r4, #1\n" \ "beq 21f\n" \ "bne do_check\n" \ "bne do_check\n" \ "bne do_check\n" \ "bne do_check\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "21:\n" \ "cmp r0, r4\n" \ "cmp r0, r4\n" \ "cmp r0, r4\n" \ "beq 22f\n" \ "bne do_check\n" \ "bne do_check\n" \ "bne do_check\n" \ "bne do_check\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "22:\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "beq 23f\n" \ "bne do_check\n" \ "bne do_check\n" \ "bne do_check\n" \ "bne do_check\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "23:\n" \ "b end_check\n" \ "do_check:\n" \ "mov r0, #1\n" \ "mov r0, #1\n" \ "mov r0, #1\n" \ "bl wolfBoot_get_image_version\n" \ "mov r5, r0\n" \ "mov r5, r0\n" \ "mov r5, r0\n" \ "mov r0, #1\n" \ "mov r0, #1\n" \ "mov r0, #1\n" \ "bl wolfBoot_get_image_version\n" \ "mov r7, r0\n" \ "mov r7, r0\n" \ "mov r7, r0\n" \ "cmp r5, r7\n" \ "cmp r5, r7\n" \ "cmp r5, r7\n" \ "beq 24f\n" \ "bne ver_panic\n" \ "bne ver_panic\n" \ "bne ver_panic\n" \ "bne ver_panic\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "24:\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "bl wolfBoot_get_image_version\n" \ "mov r4, r0\n" \ "mov r4, r0\n" \ "mov r4, r0\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "bl wolfBoot_get_image_version\n" \ "mov r6, r0\n" \ "mov r6, r0\n" \ "mov r6, r0\n" \ "cmp r4, r6\n" \ "cmp r4, r6\n" \ "cmp r4, r6\n" \ "beq 25f\n" \ "bne ver_panic\n" \ "bne ver_panic\n" \ "bne ver_panic\n" \ "bne ver_panic\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "25:\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "cmp r4, r5\n" \ "cmp r4, r5\n" \ "cmp r4, r5\n" \ "blo 26f\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "26:\n" \ "cmp r6, r7\n" \ "cmp r6, r7\n" \ "cmp r6, r7\n" \ "blo 27f\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "27:\n" \ "cmp r4, r5\n" \ "cmp r4, r5\n" \ "cmp r4, r5\n" \ "blo 28f\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "28:\n" \ "cmp r6, r7\n" \ "cmp r6, r7\n" \ "cmp r6, r7\n" \ "blo 29f\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "bhs ver_panic\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "29:\n" \ "b end_check\n" \ "ver_panic:\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "end_check:\n" \ "pop {r4, r5, r6, r7}\n" \ : \ : "r"(fb_ok) \ : "r0", "r4", "r5", "r6", "r7", "lr", "cc", "memory" \ ) #elif defined(__ICCARM__) && defined(__IAR_SYSTEMS_ICC__) #define VERIFY_VERSION_ALLOWED(fb_ok) \ do { \ __asm volatile( \ "push {r4, r5, r6, r7}\n" \ "mov r0, #0\n" \ "mov r4, #1\n" \ "mov r5, #0\n" \ "mov r6, #2\n" \ "mov r7, #0\n" \ "mov r0, #0\n" \ "mov r4, #1\n" \ "mov r5, #0\n" \ "mov r6, #2\n" \ "mov r7, #0\n" \ "mov r0, %0\n" \ "mov r4, %0\n" \ "cmp r0, #0\n" \ "cmp r0, #0\n" \ "cmp r0, #0\n" \ "beq 4f\n" \ "bne 6f\n" \ "bne 6f\n" \ "bne 6f\n" \ "bne 6f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "6:\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "beq 4f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "4:\n" \ "cmp r4, #0\n" \ "cmp r4, #0\n" \ "cmp r4, #0\n" \ "beq 5f\n" \ "bne 7f\n" \ "bne 7f\n" \ "bne 7f\n" \ "bne 7f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "7:\n" \ "cmp r4, #1\n" \ "cmp r4, #1\n" \ "cmp r4, #1\n" \ "beq 5f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "5:\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "beq 8f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "8:\n" \ "cmp r4, #1\n" \ "cmp r4, #1\n" \ "cmp r4, #1\n" \ "beq 9f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "9:\n" \ "cmp r0, r4\n" \ "cmp r0, r4\n" \ "cmp r0, r4\n" \ "beq 10f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "10:\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "cmp r0, #1\n" \ "beq 11f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "bne 1f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "11:\n" \ "b 2f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "1:\n" \ "mov r0, #1\n" \ "mov r0, #1\n" \ "mov r0, #1\n" \ "bl wolfBoot_get_image_version\n" \ "mov r5, r0\n" \ "mov r5, r0\n" \ "mov r5, r0\n" \ "mov r0, #1\n" \ "mov r0, #1\n" \ "mov r0, #1\n" \ "bl wolfBoot_get_image_version\n" \ "mov r7, r0\n" \ "mov r7, r0\n" \ "mov r7, r0\n" \ "cmp r5, r7\n" \ "cmp r5, r7\n" \ "cmp r5, r7\n" \ "beq 12f\n" \ "bne 3f\n" \ "bne 3f\n" \ "bne 3f\n" \ "bne 3f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "12:\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "bl wolfBoot_get_image_version\n" \ "mov r4, r0\n" \ "mov r4, r0\n" \ "mov r4, r0\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "bl wolfBoot_get_image_version\n" \ "mov r6, r0\n" \ "mov r6, r0\n" \ "mov r6, r0\n" \ "cmp r4, r6\n" \ "cmp r4, r6\n" \ "cmp r4, r6\n" \ "beq 13f\n" \ "bne 3f\n" \ "bne 3f\n" \ "bne 3f\n" \ "bne 3f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "13:\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "mov r0, #0\n" \ "cmp r4, r5\n" \ "cmp r4, r5\n" \ "cmp r4, r5\n" \ "blo 14f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "14:\n" \ "cmp r6, r7\n" \ "cmp r6, r7\n" \ "cmp r6, r7\n" \ "blo 15f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "15:\n" \ "cmp r4, r5\n" \ "cmp r4, r5\n" \ "cmp r4, r5\n" \ "blo 16f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "16:\n" \ "cmp r6, r7\n" \ "cmp r6, r7\n" \ "cmp r6, r7\n" \ "blo 17f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "bhs 3f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "17:\n" \ "b 2f\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "3:\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "b .\n" \ "2:\n" \ "pop {r4, r5, r6, r7}\n" \ : /* No output operands */ \ : "r"(fb_ok) /* Input operands */ \ : "r0", "r4", "r5", "r6", "r7" /* Clobbered registers */ \ ); \ } while (0) #endif #define CONFIRM_MASK_VALID(id, mask) \ asm volatile("mov r1, %0" :: "r"(id):"r1"); \ /* id &= 0x0F */ \ asm volatile("and.w r1, r1, #15":::"r1"); \ asm volatile("mov r0, %0" :: "r"(mask):"r0"); \ asm volatile("movs r2, #1":::"r2"); \ asm volatile("lsls r2, r1":::"r2","cc"); \ asm volatile("ands r2, r0":::"r2","cc"); \ asm volatile("movs r0, #1":::"cc"); \ asm volatile("lsls r0, r1":::"r0","cc"); \ asm volatile("cmp r0, r2"); \ asm volatile("cmp r0, r2"); \ asm volatile("cmp r0, r2"); \ asm volatile("bne ."); \ asm volatile("mov r0, %0" :: "r"(mask)); \ asm volatile("movs r2, #1":::"r2"); \ asm volatile("lsls r2, r1":::"r2", "cc"); \ asm volatile("ands r2, r0":::"r2", "cc"); \ asm volatile("movs r0, #1":::"r0"); \ asm volatile("lsls r0, r1":::"r0", "cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("bne ."); \ asm volatile("mov r0, %0" :: "r"(mask):"r0"); \ asm volatile("movs r2, #1":::"r2"); \ asm volatile("lsls r2, r1":::"r2", "cc"); \ asm volatile("ands r2, r0":::"r2", "cc"); \ asm volatile("movs r0, #1":::"r0"); \ asm volatile("lsls r0, r1":::"r0", "cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("cmp r0, r2":::"cc"); \ asm volatile("bne ."); \ #else struct wolfBoot_image { uint8_t *hdr; #ifdef EXT_FLASH uint8_t *hdr_cache; #endif uint8_t *trailer; uint8_t *sha_hash; uint8_t *fw_base; uint32_t fw_size; uint8_t part; uint8_t hdr_ok : 1; uint8_t signature_ok : 1; uint8_t sha_ok : 1; uint8_t not_ext : 1; /* image is no longer external */ }; /* do not warn if this is not used */ static void UNUSEDFUNCTION wolfBoot_image_confirm_signature_ok( struct wolfBoot_image *img) { img->signature_ok = 1; } static void UNUSEDFUNCTION wolfBoot_image_clear_signature_ok( struct wolfBoot_image *img) { img->signature_ok = 0; } static void UNUSEDFUNCTION wolfBoot_image_confirm_sha_ok( struct wolfBoot_image *img) { img->sha_ok = 1; } static void UNUSEDFUNCTION wolfBoot_image_clear_sha_ok( struct wolfBoot_image *img) { img->sha_ok = 0; } static void UNUSEDFUNCTION wolfBoot_image_set_fw_base( struct wolfBoot_image *img, void *base) { img->fw_base = (uint8_t *)base; } #define likely(x) (x) #define unlikely(x) (x) #define VERIFY_FN(img,p_res,fn,...) {\ int ret = fn(__VA_ARGS__); \ if ((ret == 0) && (*p_res == 1)) \ wolfBoot_image_confirm_signature_ok(img); \ } #define RSA_VERIFY_FN(ret,fn,...) \ ret = fn(__VA_ARGS__); #define RSA_VERIFY_HASH(img,digest) \ if (image_CT_compare(img->sha_hash, digest, WOLFBOOT_SHA_DIGEST_SIZE) == 0) \ wolfBoot_image_confirm_signature_ok(img); #define RSA_PSS_VERIFY_HASH(img, pss_data, pss_data_sz, hash_type) \ if (wc_RsaPSS_CheckPadding(img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, \ pss_data, pss_data_sz, hash_type) == 0) \ wolfBoot_image_confirm_signature_ok(img); #define SHA_OK(imgp) ((imgp)->sha_ok == 1) #define VERIFY_INTEGRITY_FN(img, computed_digest, stored) \ if (image_CT_compare((computed_digest), (stored), \ WOLFBOOT_SHA_DIGEST_SIZE) == 0) { \ (img)->sha_hash = (stored); \ wolfBoot_image_confirm_sha_ok(img); \ } #define PART_SANITY_CHECK(p) \ if (((p)->hdr_ok != 1) || ((p)->sha_ok != 1) || ((p)->signature_ok != 1)) \ wolfBoot_panic() #define SHA_SANITY_CHECK(p) \ if ((p)->sha_ok != 1) \ wolfBoot_panic() #define FW_BASE_SANITY_CHECK(p) do{} while(0) #define CONFIRM_MASK_VALID(id, mask) \ if ((mask & (1UL << id)) != (1UL << id)) \ wolfBoot_panic() #define VERIFY_VERSION_ALLOWED(fb_ok) do{} while(0) /* okay */ #endif /* Defined in image.c */ int image_CT_compare(const uint8_t *expected, const uint8_t *actual, uint32_t len); int wolfBoot_hardened_CT_compare(const uint8_t *expected, const uint8_t *actual, uint32_t len); #if defined(MMU) || defined(WOLFBOOT_FDT) /* Validate a DTB and return its size. `capacity` is the number of bytes * readable at dts_addr. Callers holding only a header use fdt_peek_size(). */ int wolfBoot_get_dts_size(void *dts_addr, uint32_t capacity); int wolfBoot_verify_dts_digest(const uint8_t *expected_digest, const void *dts_addr, uint32_t dts_size); #endif int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part); #ifdef EXT_FLASH int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part, uint8_t* addr); #endif int wolfBoot_open_image_address(struct wolfBoot_image* img, uint8_t* image); #ifdef WOLFBOOT_SELF_HEADER int wolfBoot_open_self(struct wolfBoot_image *img); int wolfBoot_open_self_address(struct wolfBoot_image *img, uint8_t *hdr, uint8_t *image); #endif int wolfBoot_verify_integrity(struct wolfBoot_image *img); int wolfBoot_verify_authenticity(struct wolfBoot_image *img); int wolfBoot_set_partition_state(uint8_t part, uint8_t newst); int wolfBoot_get_update_sector_flag(uint16_t sector, uint8_t *flag); int wolfBoot_set_update_sector_flag(uint16_t sector, uint8_t newflag); #ifdef WOLFBOOT_ELF_FLASH_SCATTER /* Support for ELF scatter/gather format */ int wolfBoot_load_flash_image_elf(int part, unsigned long* entry_out, int ext_flash); int wolfBoot_check_flash_image_elf(uint8_t part, unsigned long* entry_out); #endif uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset, uint32_t* sz); /* get header type for image */ uint16_t wolfBoot_get_header(struct wolfBoot_image *img, uint16_t type, uint8_t **ptr); /* Find the key slot ID based on the SHA hash of the key. */ int keyslot_id_by_sha(const uint8_t *hint); #ifdef EXT_FLASH # ifdef PART_BOOT_EXT # define BOOT_EXT 1 # else # define BOOT_EXT 0 # endif # ifdef PART_UPDATE_EXT # define UPDATE_EXT 1 # else # define UPDATE_EXT 0 # endif # ifdef PART_SWAP_EXT # define SWAP_EXT 1 # else # define SWAP_EXT 0 # endif # if defined(WOLFBOOT_SELF_HEADER_EXT) # define SELF_HEADER_EXT 1 # else # define SELF_HEADER_EXT 0 # endif # define PARTN_IS_EXT(pn) \ ((pn == PART_BOOT || pn == PART_DTS_BOOT) ? BOOT_EXT: \ ((pn == PART_UPDATE || pn == PART_DTS_UPDATE) ? UPDATE_EXT : \ ((pn == PART_SWAP) ? SWAP_EXT : \ ((pn == PART_SELF) ? SELF_HEADER_EXT : 0)))) # define PART_IS_EXT(x) (!(x)->not_ext) && PARTN_IS_EXT(((x)->part)) #if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST)) #define ext_flash_check_write ext_flash_encrypt_write #define ext_flash_check_read ext_flash_decrypt_read #else #define ext_flash_check_write ext_flash_write #define ext_flash_check_read ext_flash_read #endif static inline int wb_flash_erase(struct wolfBoot_image *img, uint32_t off, uint32_t size) { if (PART_IS_EXT(img)) return ext_flash_erase((uintptr_t)(img->hdr) + off, size); else return hal_flash_erase((uintptr_t)(img->hdr) + off, size); } static inline int wb_flash_write(struct wolfBoot_image *img, uint32_t off, const void *data, uint32_t size) { if (PART_IS_EXT(img)) return ext_flash_check_write((uintptr_t)(img->hdr) + off, data, size); else return hal_flash_write((uintptr_t)(img->hdr) + off, data, size); } static inline int wb_flash_write_verify_word(struct wolfBoot_image *img, uint32_t off, uint32_t word) { int ret; volatile uint32_t copy; if (PART_IS_EXT(img)) { ext_flash_check_read((uintptr_t)(img->hdr) + off, (void *)©, sizeof(uint32_t)); while (copy != word) { ret = ext_flash_check_write((uintptr_t)(img->hdr) + off, (void *)&word, sizeof(uint32_t)); if (ret < 0) return ret; ext_flash_check_read((uintptr_t)(img->hdr) + off, (void *)©, sizeof(uint32_t)); } } else { volatile uint32_t *pcopy = (volatile uint32_t*)(img->hdr + off); while(*pcopy != word) { hal_flash_write((uintptr_t)pcopy, (void *)&word, sizeof(uint32_t)); } } return 0; } #else # define SWAP_EXT (0) # define BOOT_EXT (0) # define UPDATE_EXT (0) # define PART_IS_EXT(x) (0) # define PARTN_IS_EXT(x) (0) # define wb_flash_erase(im, of, siz) \ hal_flash_erase(((uintptr_t)(((im)->hdr)) + of), siz) # define wb_flash_write(im, of, dat, siz) \ hal_flash_write(((uintptr_t)((im)->hdr)) + of, dat, siz) #endif /* EXT_FLASH */ /* -- Image Formats -- */ /* Legacy U-Boot Image */ #ifdef BIG_ENDIAN_ORDER #define UBOOT_IMG_HDR_MAGIC 0x27051956UL #else #define UBOOT_IMG_HDR_MAGIC 0x56190527UL #endif #define UBOOT_IMG_HDR_SZ 64 /* --- Flattened Device Tree Blob */ #if defined(MMU) || defined(WOLFBOOT_FDT) #include "fdt.h" #endif #ifndef EXT_ENCRYPTED #define WOLFBOOT_MAX_SPACE (WOLFBOOT_PARTITION_SIZE - \ (TRAILER_SKIP + sizeof(uint32_t) + \ ((WOLFBOOT_PARTITION_SIZE + 1) / (WOLFBOOT_SECTOR_SIZE * 8)))) #else #define WOLFBOOT_MAX_SPACE (WOLFBOOT_PARTITION_SIZE - ENCRYPT_TMP_SECRET_OFFSET) #endif #ifdef __cplusplus } #endif #endif /* !IMAGE_H */