Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4a3f9f0
F-14097 - Correct boot pointer-validation comment
aidangarske Sep 22, 2026
2ad72b0
F-14098 - Clarify VNET RX token exposure
aidangarske Sep 22, 2026
2183259
F-14096 - Correct secure partition privilege comments
aidangarske Sep 22, 2026
cd95ff8
F-14111 - Make queued mutex acquisition idempotent
aidangarske Sep 22, 2026
f6fbbac
F-14114 - Check active coroutine stack canaries
aidangarske Sep 22, 2026
99af9ab
F-14104 - Panic on invalid wait output pointers
aidangarske Sep 22, 2026
e4ad972
F-14112 - Dispatch runnable lock waiters
aidangarske Sep 22, 2026
6a5e485
F-14118 - Erase shared HSM relay buffers
aidangarske Sep 22, 2026
6c85f87
F-14124 - Erase HSM client response buffers
aidangarske Sep 22, 2026
cd2f367
F-14125 - Erase storage client request copies
aidangarske Sep 22, 2026
da41799
F-14119 - Erase per-guest HSM relay buffers
aidangarske Sep 22, 2026
986ff51
F-14122 - Erase HSM state during relay recovery
aidangarske Sep 22, 2026
6a1f083
F-14123 - Erase HSM tasklet state on fault
aidangarske Sep 22, 2026
c0901f9
F-14095 - Correct HSM fault notification comments
aidangarske Sep 22, 2026
8e4b5ab
F-14109 - Restore residual virtual SysTick period
aidangarske Sep 22, 2026
e25079b
F-14126 - Scrub restart-clear RAM before quarantine
aidangarske Sep 22, 2026
0428c38
F-14127 - Erase non-word-aligned guest RAM tails
aidangarske Sep 22, 2026
61dd392
F-14108 - Release IPC resources on quarantine
aidangarske Sep 22, 2026
b03ac65
F-14101 - Address the Non-secure NVIC banks
aidangarske Sep 22, 2026
af64aae
F-14102 - Support the full Secure IRQ range
aidangarske Sep 22, 2026
ad5ebac
F-14120 - Erase flash programming words
aidangarske Sep 22, 2026
b5e2219
F-14121 - Erase flash read-back buffers
aidangarske Sep 22, 2026
7397831
F-14117 - Pad unaligned firmware update writes
aidangarske Sep 22, 2026
fda8cbd
F-14107 - Preserve FWU state when disarm fails
aidangarske Sep 22, 2026
68447af
F-14099 - Erase vault request and plaintext buffers
aidangarske Sep 22, 2026
2e99843
F-14100 - Erase storage request and plaintext buffers
aidangarske Sep 22, 2026
12cabaf
F-14106 - Preserve sealed removal and reject key deletion
aidangarske Sep 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/API-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -476,9 +476,9 @@ monotonic version. The current service supports one primary component and
commits installation at authenticated reboot. It does not offer a persistent
trial state, so `psa_fwu_accept` returns
`PSA_ERROR_NOT_SUPPORTED`.
Unlike PSA Firmware Update 1.0, the service rejects unaligned block sizes rather
than padding them and returns `PSA_ERROR_INVALID_ARGUMENT` for unknown
component IDs instead of `PSA_ERROR_DOES_NOT_EXIST`.
The service pads an unaligned block size to the backend write alignment. It
returns `PSA_ERROR_INVALID_ARGUMENT` for unknown component IDs instead of
`PSA_ERROR_DOES_NOT_EXIST`.

## Initial Attestation

Expand Down
10 changes: 5 additions & 5 deletions docs/Services.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,16 @@ exported.
## Firmware Update

`SERVICE_FWU` exposes a single-component subset of PSA Firmware Update 1.0.
Unlike the specification, it rejects unaligned block sizes instead of padding
them and returns `PSA_ERROR_INVALID_ARGUMENT` rather than
`PSA_ERROR_DOES_NOT_EXIST` for unknown component IDs. The normal flow is:
It pads an unaligned block size to the backend write alignment and returns
`PSA_ERROR_INVALID_ARGUMENT` rather than `PSA_ERROR_DOES_NOT_EXIST` for an
unknown component ID. The normal flow is:

```text
READY -> WRITING -> CANDIDATE -> STAGED -> authenticated reboot
```

Writes are copied, bounded, aligned to the target flash granularity, and staged
in the wolfBoot update partition. Finish validates the complete wolfBoot image
Writes are copied, bounded, offset-aligned, padded with erased-flash bytes when
needed, and staged in the wolfBoot update partition. Finish validates the image
header and binds the candidate version. Install checks the candidate again
against the persistent version floor loaded when the Firmware Update partition
started, then writes the wolfBoot update trigger. A reboot request is allowed
Expand Down
4 changes: 2 additions & 2 deletions docs/TF-M-Compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ algorithm or feature is enabled in every build.
| Internal Trusted Storage | 1.0 | Core set/get/get-info/remove subset with the `WRITE_ONCE` lifecycle deviation below | `include/psa/internal_trusted_storage.h` and `src/services/wolfhsm/wt_hsm_vault.c` |
| Protected Storage | 1.0 | Core set/get/get-info/remove subset; optional create/set-extended absent and the `WRITE_ONCE` lifecycle deviation below applies | `include/psa/protected_storage.h` and `src/services/storage_service.c` |
| Initial Attestation | 1.0 API subset with a nonconformant RFC 9783-derived token | Token and exact-size operations are supported, but the advertised TF-M profile has the claim-semantic deviations below | `lib/wolfPSA/wolfpsa/psa/initial_attestation.h` and `src/services/initial_attestation.c` |
| Firmware Update | 1.0 subset | Single-component staging and authenticated reboot supported, with the alignment and status deviations below | `include/psa/update.h` and `src/services/fwu_service.c` |
| Firmware Update | 1.0 subset | Single-component staging and authenticated reboot supported, with the status deviation below | `include/psa/update.h` and `src/services/fwu_service.c` |
| RoT lifecycle query | FF-M 1.0 | Secure Partition only; there is no Non-secure adapter or veneer | `include/psa/lifecycle.h` and `src/arch/armv8m/spm_sp_api.c` |
| Secure Partition signals and IRQ APIs | FF-M 1.0 plus one wolfTrust-specific beta-extension backport | The 1.0 signal APIs and `psa_eoi` are supported; only `psa_irq_enable()` is backported from the FF-M 1.1 Extension Beta, Issue 0, while `psa_irq_status_t`, `psa_irq_is_enabled`, `psa_irq_disable`, and `psa_irq_restore` are absent | `include/psa/service.h` and the Armv8-M SVC implementation |
| Guest identity | FF-M convention | Non-secure guest `N` is client `-(N + 1)` | `src/arch/armv8m/ffm_nsc.c` |
Expand All @@ -47,7 +47,7 @@ algorithm or feature is enabled in every build.
| The attestation token advertises `tag:psacertified.org,2023:psa#tfm` but does not implement that profile's claim semantics. | Known token-profile deviation | The boot seed is deterministic across equivalent boots; software-component measurement type and description values are reversed; signer ID hashes the literal name `wolfBoot` rather than identifying the signing key; and implementation ID hashes a software label rather than identifying the immutable PSA RoT hardware assembly. A distinct derived profile identifier is required until these claims conform to [RFC 9783](https://www.rfc-editor.org/rfc/rfc9783.html). |
| Firmware Update has no persistent trial-accept flow. | Scoped | Installation commits only after wolfBoot authenticates the swapped image at reboot; `psa_fwu_accept()` returns `PSA_ERROR_NOT_SUPPORTED`. |
| The firmware-update detached manifest is a 32-bit version word. | Scoped integration | Passing `NULL, 0` instead binds the version from the staged wolfBoot header. Other manifest encodings require an adapter. |
| Firmware Update rejects unaligned block sizes and reports unknown component IDs as `PSA_ERROR_INVALID_ARGUMENT`. | Known API deviations | PSA Firmware Update 1.0 pads unaligned final block sizes and specifies `PSA_ERROR_DOES_NOT_EXIST` for unknown component IDs. |
| Firmware Update reports unknown component IDs as `PSA_ERROR_INVALID_ARGUMENT`. | Known API deviation | PSA Firmware Update 1.0 specifies `PSA_ERROR_DOES_NOT_EXIST` for unknown component IDs. Unaligned block sizes are padded to the backend write alignment. |
| Secure memory uses no dynamic allocation. | Stronger resource policy | Fixed pools and buffers can reject excess work rather than expanding at runtime. |
| Manifests use wolfTrust JSON and generated C. | Integration difference | Existing TF-M manifests are not consumed directly. Security resources and services must be represented in the wolfTrust schema. |
| Secure Partition entry functions are bound at build time instead of being selected by each manifest's `entry_point` field. | Integration difference | The numeric field validates an executable window, but adding a service also requires a compiled entry wrapper and an explicit start call in `wt_ffm_boot_start_sched()`. |
Expand Down
28 changes: 12 additions & 16 deletions include/wolftrust/services/hsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,9 @@ struct wt_co *wt_hsm_guest_tasklet(wt_guest_id_t guest_id);
* dispatcher to map a faulted tasklet back to its NS client. */
wt_guest_id_t wt_hsm_guest_for_tasklet(const struct wt_co *tasklet);

/* Signal a terminal Secure-side fault for guest_id: drops the NVM lock
* if the dying tasklet was holding it, writes a WH_ERROR_ABORTED
* fatal-response into the guest's transport, and clears the ready bit
* so subsequent NSC veneers reject HSM calls from this guest. Safe to
* call from handler mode. Returns WH_ERROR_OK on success. */
/* Signal a terminal Secure-side fault for guest_id: drop held locks, invoke
* the optional fault-notification hook, erase retained tasklet state, and
* clear the ready bit. Safe from handler mode. */
int wt_hsm_signal_fault(wt_guest_id_t guest_id);

/* Drop every secure-side wolfHSM lock held by a faulted coroutine. Used by the
Expand All @@ -134,9 +132,8 @@ struct wt_mutex *wt_hsm_nvm_lock_mutex(void);
* state unusable. Fails closed — a guest whose re-init fails stays down. */
int wt_hsm_relay_reinit_servers(void);

/* Terminal-fault NS-client notifier. wt_hsm_signal_fault calls the installed
* callback; the arch transport installs its concrete notifier at boot. The
* default is a no-op so engine-less/host builds link. */
/* Terminal-fault NS-client notifier. The default is a no-op, and no current
* port installs a replacement, so this path does not notify NS clients. */
typedef int (*wt_hsm_fault_notify_fn)(wt_guest_id_t guest_id);
void wt_hsm_set_fault_notify(wt_hsm_fault_notify_fn fn);

Expand Down Expand Up @@ -167,9 +164,9 @@ int wt_hsm_vault_init(struct whNvmContext_t* nvm);
struct wt_vault_backend;
extern const struct wt_vault_backend wt_hsm_vault_backend;

/* Vault sealer (WT-FFM-0048): AES-GCM confidentiality + rollback binding for
* WT_VAULT_FLAG_SEALED objects, running entirely inside the privileged vault
* domain — the device-unique key never reaches any Secure Partition. seal
/* Vault sealer (WT-FFM-0048): AES-GCM confidentiality and rollback binding
* for WT_VAULT_FLAG_SEALED objects. Operations run in the confined vault SP,
* with the device-unique key kept in the shared keystore trust band. seal
* writes pt_len + WT_VAULT_SEAL_TAG_LEN bytes ([ciphertext][tag]); unseal
* takes ct_len >= tag length and writes ct_len - tag plaintext bytes. The
* monotonic rollback counter is the GCM nonce, so a replayed (rolled-back)
Expand Down Expand Up @@ -200,7 +197,7 @@ void wt_hsm_vault_set_sealer(const wt_vault_sealer_t* sealer);
int wt_hsm_seal_init(struct whNvmContext_t* nvm);
extern const wt_vault_sealer_t wt_hsm_sealer;

/* Shared vault directory helpers (wt_hsm_vault.c) for privileged backends:
/* Shared vault directory helpers (wt_hsm_vault.c) for confined backends:
* label-addressed lookup over the vault NVM id window, and the label
* make/flags codec. whNvmMetadata is an untagged typedef, so wh_common.h
* must be included for the real type. */
Expand All @@ -211,10 +208,9 @@ void wt_hsm_vault_make_label(uint8_t* label, int32_t owner, int32_t sub,
uint64_t uid, uint32_t flags);
uint32_t wt_hsm_vault_flags_of(const uint8_t* label);

/* Vault-domain RNG (WT-FFM-0054): entropy for SERVICE_VAULT's RANDOM face,
* produced by a wolfCrypt DRBG owned by the privileged vault domain. Installed
* via wt_vault_service_set_rng at boot. Only linked into builds that carry
* wolfCrypt. */
/* Vault RNG (WT-FFM-0054): entropy for SERVICE_VAULT's RANDOM face, produced
* by a wolfCrypt DRBG in the shared keystore trust band. Installed via
* wt_vault_service_set_rng at boot. Only linked into wolfCrypt builds. */
psa_status_t wt_hsm_vault_random(uint8_t* out, size_t len);

#endif /* WOLFTRUST_SERVICES_HSM_H */
2 changes: 1 addition & 1 deletion include/wolftrust/services/storage_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
* (partition, client id, uid) via the delegated sub_owner (WT-FFM-0044).
* The PS instance additionally ORs WT_VAULT_FLAG_SEALED into every request:
* AES-GCM under the device-unique wolfHSM key plus rollback binding, applied
* entirely inside the privileged vault domain (WT-FFM-0048). */
* inside the confined vault partition (WT-FFM-0048). */

/* psa_call request types (client face). Ops 1-4 are the shared ITS/PS core;
* 5-7 exist only on the PS face. */
Expand Down
14 changes: 7 additions & 7 deletions include/wolftrust/services/vault_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@

/* Internal flag a storage frontend ORs in (never a PSA create flag): the
* object is AES-GCM sealed under the device-unique wolfHSM key with the
* monotonic rollback counter as nonce (WT-FFM-0048). Sealing runs entirely
* inside the privileged vault domain. */
* monotonic rollback counter as nonce (WT-FFM-0048). Sealing runs inside the
* confined vault partition. */
#define WT_VAULT_FLAG_SEALED 0x10000U

/* Label marker for key objects (WT-FFM-0046). Never accepted from a storage
Expand Down Expand Up @@ -129,9 +129,9 @@ typedef struct wt_vault_backend {
psa_status_t (*remove)(int32_t owner, int32_t sub, uint64_t uid);
} wt_vault_backend_t;

/* Key-operation vtable (WT-FFM-0046): every operation executes INSIDE the
* privileged vault domain against material that never leaves it. There is
* deliberately no private-export entry point. sign/verify operate on a
/* Key-operation vtable (WT-FFM-0046): every operation executes inside the
* confined vault partition against material in its keystore trust band. There
* is deliberately no private-export entry point. sign/verify operate on a
* caller-supplied digest; encrypt frames its output [nonce][ct][tag] and
* decrypt consumes the same framing. */
typedef struct wt_vault_key_backend {
Expand All @@ -156,8 +156,8 @@ typedef struct wt_vault_key_backend {
uint8_t* out, size_t cap, size_t* out_len);
} wt_vault_key_backend_t;

/* Vault-domain randomness (WT-FFM-0054): fill out[0..len) from an RNG owned
* by the privileged vault domain, never a frontend partition. This is entropy
/* Vault randomness (WT-FFM-0054): fill out[0..len) from an RNG owned by the
* confined vault partition, never a frontend partition. This is entropy
* plumbing, kept separate from the key backend so retiring the key backend
* does not disturb the RANDOM face. */
typedef psa_status_t (*wt_vault_rng_fn)(uint8_t* out, size_t len);
Expand Down
4 changes: 2 additions & 2 deletions include/wolftrust/services/vnet_relay.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
* (WT-FFM-0056). A non-secure guest reaches its switch port only through
* psa_connect/psa_call; the operation rides the FF-M call type and the SPM
* stamps the caller identity, which selects the port. Frame bytes cross as
* copied FF-M vectors, so pool slot/generation tokens never leave the
* secure side and cannot be forged or replayed by a guest. */
* copied FF-M vectors. RX_FETCH returns the slot/generation metadata only
* after the relay has consumed and released the corresponding token. */

/* The WT_VNET_OP_* operation codes and SID live in vnet_abi.h, shared with
* the non-secure client transport.
Expand Down
23 changes: 9 additions & 14 deletions include/wolftrust/spm_sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,18 @@ typedef void (*wt_spm_sp_entry_fn)(void* arg);
int wt_spm_sched_add(wt_ffm_runtime_t* runtime, int32_t partition_id,
wt_spm_sp_entry_fn entry, void* arg);

/* Start the SERVICE_HSM relay partition (WT-FFM-0054) as a scheduled
* PRIVILEGED coroutine: its loop hands each mediated wolfHSM packet to the
* monitor's inline server pump, which reads secure state and may block on
* the shared NVM mutex — the same privilege rationale as the vault. */
/* Start SERVICE_HSM (WT-FFM-0054) as a confined, unprivileged scheduled SP.
* Keystore NVM, flash, and entropy operations cross the privileged SVC gate. */
int wt_spm_hsm_start(wt_ffm_runtime_t* runtime, int32_t partition_id);

/* Start the SERVICE_ATTEST partition as a scheduled PRIVILEGED coroutine: its
* dispatch loop runs on its own stack and reaches the secure attestation
* server state, so it runs privileged like the HSM relay for now. Defined only
/* Start SERVICE_ATTEST as a confined, unprivileged scheduled SP. Keystore NVM,
* flash, and entropy operations cross the privileged SVC gate. Defined only
* in attestation-enabled builds. */
int wt_spm_attest_start(wt_ffm_runtime_t* runtime, int32_t partition_id);

/* Start the vault partition (WT-FFM-0047) as a scheduled PRIVILEGED coroutine:
* same slot machinery and SVC gate, but wt_co_set_domain is never called, so
* the loop may reach the wolfHSM NVM state and block on its mutex. Clients
* still cross the gate; the manifest's dependencies[] authorizes them. */
/* Start SERVICE_VAULT (WT-FFM-0047) as a confined, unprivileged scheduled SP.
* Its thread domain is installed with wt_co_set_domain; NVM and flash access
* cross the privileged SVC gate. */
int wt_spm_vault_start(wt_ffm_runtime_t* runtime, int32_t partition_id);

/* Start the ITS partition as a normal UNPRIVILEGED scheduled SP whose service
Expand All @@ -64,9 +60,8 @@ int wt_spm_its_start(wt_ffm_runtime_t* runtime, int32_t partition_id);
/* Schedule the PS partition: the storage loop with sealing forced on. */
int wt_spm_ps_start(wt_ffm_runtime_t* runtime, int32_t partition_id);

/* Start the Firmware Update partition (WT-FWU-0001) as a scheduled PRIVILEGED
* coroutine: it programs the wolfBoot update partition flash to stage a
* candidate, so it runs privileged like the vault. */
/* Start SERVICE_FWU (WT-FWU-0001) as a confined, unprivileged scheduled SP.
* Update-partition flash operations cross the privileged SVC gate. */
int wt_spm_fwu_start(wt_ffm_runtime_t* runtime, int32_t partition_id);

int wt_spm_vnet_start(wt_ffm_runtime_t* runtime, int32_t partition_id);
Expand Down
36 changes: 36 additions & 0 deletions include/wolftrust/zeroize.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* zeroize.h
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfTrust.
*
* wolfTrust 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.
*
* wolfTrust 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, see <https://www.gnu.org/licenses/>.
*/

#ifndef WOLFTRUST_ZEROIZE_H
#define WOLFTRUST_ZEROIZE_H

#include <stddef.h>

static inline void wt_forceZero(void* memory, size_t size)
{
volatile unsigned char* bytes = (volatile unsigned char*)memory;

while (size > 0U) {
*bytes++ = 0U;
size--;
}
}

#endif /* WOLFTRUST_ZEROIZE_H */
Loading
Loading