The PKCS#11 non-secure-callable veneers deep-copy every NS attribute
value and every mechanism parameter into secure-world heap. On key
import (C_CreateObject/C_UnwrapKey/C_CopyObject/C_SetAttributeValue
carrying CKA_VALUE or the RSA private components) and on password-based
derivation (CKM_PKCS5_PBKD2 pPassword) those bounce buffers hold
plaintext secrets, but nsc_tmpl_free() and nsc_mech_free() released them
with a bare XFREE(), leaving the material in the freed secure heap block
until something else happens to overwrite it.
Scrub each block with wc_ForceZero() before releasing it. The template
values use the prepare-time snapshot length, since wolfPKCS11 rewrites
work[].ulValueLen on the C_GetAttributeValue path; nsc_alloc() now
records the length of each mechanism allocation for the same reason.
Adds unit-pkcs11-nsc-zeroize, which drives C_CreateObject_nsc_call and
C_DeriveKey_nsc_call over a secure-heap stand-in that is never cleared,
and fails if the imported key or the PBKDF2 password survives the free.
main() called make_header()/make_hybrid_header() and discarded their
return value. Both are wrappers around make_header_ex(), which returns
-1 on every "goto failure" path (image file not openable, header malloc
failure, firmware version out of range, certificate chain errors,
signing and output write errors). Since ret is initialized to 0 and is
only reassigned by the optional base_diff() delta step, a signing run
that produced no output image still terminated with status 0, so
Makefile recipes and CI treated the failure as success and moved on
with a missing or stale *_v<ver>_signed.bin. This was also asymmetric
with the key loading path just above, which exits on failure.
Capture the return value of both header helpers, skip the delta step
when header generation failed, and let main() return it.
Add tools/unit-tests/unit-sign-header-failure, covering the exit status
of both the plain and the hybrid signing path when the input image
cannot be opened.
main() checked load_key() for the primary key but not for the hybrid
secondary key, and load_key() left *pubkey/*pubkey_sz untouched (or
dangling, after the ED25519/ED448 free(*pubkey)) on its failure paths.
With a missing or undecodable secondary key file the sign tool therefore
either silently emitted a manifest with no secondary public key hashed,
dereferenced a freed pubkey buffer and double-freed it, or crashed on the
uninitialized pubkey_sz2 stack value.
Clear *pubkey/*pubkey_sz on every load_key() failure path, initialize
pubkey_sz2, and exit(1) when the secondary key fails to load.
Add tools/unit-tests/unit-sign-hybrid-keyload, covering the missing-file
and decode-failure contracts of load_key() plus the end-to-end exit
status of the sign tool.
wolfBoot_tpm2_read_cert() is a cmse_nonsecure_entry veneer. It dereferenced
the caller-supplied 'certSz' to bound-check 'cert' with
cmse_check_address_range(), then passed the same non-secure pointer to
wolfTPM2_NVReadCert(), which re-reads '*len' as the destination capacity
before copying the NV data (lib/wolfTPM/src/tpm2_wrap.c:7221). The length was
therefore fetched twice from non-secure memory with no snapshot in between,
so a racing non-secure agent could present a small capacity to pass the CMSE
check and enlarge it before wolfTPM's own check, making the secure world write
the certificate past the validated range and into adjacent Secure SRAM.
Single-fetch the capacity into a secure local before validating, hand wolfTPM
the local, and copy the result back, matching ns_outlen_begin() in
src/pkcs11_callable.c and the rsp_capacity handling in src/wolfhsm_callable.c
and src/fwtpm_callable.c.
Add unit-tpm-nsc-cert, which drives the veneer through a CMSE stub that models
Secure SRAM immediately after the validated non-secure buffer and a wolfTPM
stub that enlarges the non-secure length word in the race window. The
out-of-bounds write test fails before this fix and passes after it.
arm_tee_psa_call() only ran cmse_check_address_range() on descriptors
whose .len was non-zero, so a non-secure caller could pass an outvec of
{Secure address, 0} and skip attribution checking entirely. Several
dispatch handlers write a fixed-size object through out_vec[0].base
without consulting out_vec[0].len (ARM_TEE_PS_GET_SUPPORT,
ARM_TEE_CRYPTO_OPEN_KEY/IMPORT_KEY/GENERATE_KEY and
GET_KEY_ATTRIBUTES), which turned that into an arbitrary write into
Secure memory from the non-secure world.
Check every non-NULL .base with at least one byte regardless of the
declared length, and require the handlers that write a fixed-size object
to be given a large enough output descriptor.
Adds unit tests covering the zero-length Secure outvec and the
PS_GET_SUPPORT length check; the CMSE stub is now a test-provided
function so it can model a Secure region.
- wolfBoot_fit_memcpy: return int so a failed PDMA copy propagates instead
of being swallowed. The weak default (memcpy) returns 0; the MPFS250 PDMA
override returns -1 if any chunk's mpfs_pdma_memcpy() fails. Callers now
fail closed: fit_load_image_inner returns NULL (kernel load then panics
via update_disk.c), the update_disk DTS copy panics, and hal_dts_fixup
returns an error on a failed L2->DDR copy-back.
- options.mk: gate -DSTACK_SIZE_PER_HART behind RISC-V arch (RISCV/RISCV64).
The macro is only consumed by the RISC-V startup asm and the mpfs250-m.ld
sed token, so it is no longer emitted for PPC/ARM/other targets. The
unconditional default (?= 0) is kept because the linker sed always needs
a value to substitute.
Allows extracting a public key from a DER file and adding it in the same
format as the keystore:
- `X||Y` for ECC
- Raw for Ed25519/Ed448
- Public key DER for RSA