Skip to content

Commit 47e2ee4

Browse files
committed
fwTPM: append-only NV journal for write-once flash ports and swtpm UART symlink support
1 parent 5a32263 commit 47e2ee4

10 files changed

Lines changed: 790 additions & 73 deletions

File tree

CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ set(WOLFTPM_FWTPM_FUZZ "no" CACHE STRING
3939
"Enable fwTPM fuzz target (requires libFuzzer, default: disabled)")
4040
set_property(CACHE WOLFTPM_FWTPM_FUZZ
4141
PROPERTY STRINGS "yes;no")
42+
set(WOLFTPM_FWTPM_NV_APPEND_ONLY "no" CACHE STRING
43+
"Enable append-only NV journal for write-once flash fwTPM ports (default: disabled)")
44+
set_property(CACHE WOLFTPM_FWTPM_NV_APPEND_ONLY
45+
PROPERTY STRINGS "yes;no")
4246

4347
# fwtpm-only forces fwtpm on
4448
if(WOLFTPM_FWTPM_ONLY)
@@ -523,6 +527,9 @@ if(WOLFTPM_FWTPM)
523527
if(WOLFTPM_FWTPM_TIS)
524528
target_compile_definitions(fwtpm_server PRIVATE "WOLFTPM_FWTPM_TIS")
525529
endif()
530+
if(WOLFTPM_FWTPM_NV_APPEND_ONLY)
531+
target_compile_definitions(fwtpm_server PRIVATE "WOLFTPM_FWTPM_NV_APPEND_ONLY")
532+
endif()
526533
target_link_libraries(fwtpm_server PRIVATE wolftpm_wolfssl_dep)
527534
target_include_directories(fwtpm_server PRIVATE
528535
${CMAKE_CURRENT_SOURCE_DIR}
@@ -546,6 +553,9 @@ if(WOLFTPM_FWTPM)
546553
"WOLFTPM_FWTPM"
547554
"FWTPM_NV_FILE=\"fwtpm_test_nv.bin\""
548555
)
556+
if(WOLFTPM_FWTPM_NV_APPEND_ONLY)
557+
target_compile_definitions(fwtpm_unit_test PRIVATE "WOLFTPM_FWTPM_NV_APPEND_ONLY")
558+
endif()
549559
target_link_libraries(fwtpm_unit_test PRIVATE wolftpm_wolfssl_dep)
550560
target_include_directories(fwtpm_unit_test PRIVATE
551561
${CMAKE_CURRENT_SOURCE_DIR}
@@ -562,6 +572,9 @@ if(WOLFTPM_FWTPM)
562572
${FWTPM_CORE_SOURCES}
563573
)
564574
target_compile_definitions(fwtpm_fuzz PRIVATE "WOLFTPM_FWTPM")
575+
if(WOLFTPM_FWTPM_NV_APPEND_ONLY)
576+
target_compile_definitions(fwtpm_fuzz PRIVATE "WOLFTPM_FWTPM_NV_APPEND_ONLY")
577+
endif()
565578
target_link_libraries(fwtpm_fuzz PRIVATE wolftpm_wolfssl_dep)
566579
target_include_directories(fwtpm_fuzz PRIVATE
567580
${CMAKE_CURRENT_SOURCE_DIR}

configure.ac

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,17 @@ then
415415
fi
416416
fi
417417

418+
# Append-only NV journal for write-once flash fwTPM ports (opt-in)
419+
AC_ARG_ENABLE([fwtpm-nv-appendonly],
420+
[AS_HELP_STRING([--enable-fwtpm-nv-appendonly],[Enable the append-only NV journal mode for write-once flash fwTPM ports (default: disabled)])],
421+
[ ENABLED_FWTPM_NV_APPENDONLY=$enableval ],
422+
[ ENABLED_FWTPM_NV_APPENDONLY=no ]
423+
)
424+
if test "x$ENABLED_FWTPM_NV_APPENDONLY" = "xyes"
425+
then
426+
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_FWTPM_NV_APPEND_ONLY"
427+
fi
428+
418429
# Fuzz target
419430
AC_ARG_ENABLE([fuzz],
420431
[AS_HELP_STRING([--enable-fuzz],[Enable fuzz targets (default: disabled)])],

docs/FWTPM.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ fwTPM can replace a hardware TPM for:
4343
|------|------|
4444
| `fwtpm_command.c` | TPM 2.0 command processor and dispatch table (~9500 lines) |
4545
| `fwtpm_io.c` | Transport layer -- SWTPM TCP socket protocol (default) |
46-
| `fwtpm_nv.c` | NV storage -- file-based (default), HAL-abstracted for embedded |
46+
| `fwtpm_nv.c` | NV storage -- file-based (default); HAL-abstracted, with a built-in append-only mode for write-once flash |
4747
| `fwtpm_tis.c` | TIS register state machine (transport-agnostic) |
4848
| `fwtpm_tis_shm.c` | POSIX shared memory + semaphore TIS transport |
4949
| `fwtpm_main.c` | Server entry point, CLI argument parsing |
@@ -120,6 +120,7 @@ make
120120
| `--enable-fwtpm` | Build `fwtpm_server` binary (alongside client library) |
121121
| `--enable-fwtpm-only` | Build only `fwtpm_server` (no client library, examples, or tests) |
122122
| `--enable-swtpm` | Use SWTPM TCP socket transport (ports 2321/2322) |
123+
| `--enable-fwtpm-nv-appendonly` | Append-only NV journal for write-once flash ports (off by default) |
123124
| `--enable-debug` | Enable debug logging |
124125

125126
| Compile Define | Set By |
@@ -129,6 +130,7 @@ make
129130
| `WOLFTPM_FWTPM_HAL` | `--enable-fwtpm` without `--enable-swtpm` |
130131
| `WOLFTPM_FWTPM_TIS` | `--enable-fwtpm` without `--enable-swtpm` |
131132
| `WOLFTPM_ADV_IO` | Set with `WOLFTPM_FWTPM_HAL` |
133+
| `WOLFTPM_FWTPM_NV_APPEND_ONLY` | `--enable-fwtpm-nv-appendonly` (CMake `WOLFTPM_FWTPM_NV_APPEND_ONLY=yes`) |
132134

133135

134136
## Usage
@@ -190,6 +192,51 @@ NV indices) in `fwtpm_nv.bin` (configurable via `FWTPM_NV_FILE`). On first
190192
start, seeds are randomly generated and saved. Subsequent starts reload
191193
existing state.
192194

195+
#### NV Storage HAL (embedded ports)
196+
197+
NV access is abstracted behind `FWTPM_NV_HAL` (`read`/`write`/`erase`/`ctx`/
198+
`maxSize`/`get_integrity_key`). The default backend is the file above; an
199+
embedded port supplies its own HAL and registers it before `FWTPM_Init()`.
200+
201+
The journal is log-structured. On a byte-addressable backend (the file
202+
default) it writes TLV entries at byte-granular offsets, rewrites the header in
203+
place, and rewrites a trailing integrity MAC after every append. Internal flash
204+
and NOR are write-once and program-granularity aligned, so they cannot service
205+
in-place rewrites. Build with `--enable-fwtpm-nv-appendonly`
206+
(`-DWOLFTPM_FWTPM_NV_APPEND_ONLY`) and set `appendOnly` and `writeAlign` on the
207+
HAL; the journal then runs in append-only mode: the header is written only at
208+
compaction, `writePos` is derived by scanning on load, and each commit is
209+
sealed with an appended MAC-checkpoint entry padded up to `writeAlign`. The
210+
existing `read`/`write`/`erase` HAL is the integration point - no separate
211+
adapter.
212+
213+
```c
214+
/* Native flash HAL. In append-only mode the journal only ever calls write()
215+
* with writeAlign-aligned, forward, into-erased bytes, so write() is a simple
216+
* flash program; erase() erases the region (sector loop); read() reads raw. */
217+
FWTPM_NV_HAL hal;
218+
XMEMSET(&hal, 0, sizeof(hal));
219+
hal.read = myRead; hal.write = myProgram; hal.erase = myErase;
220+
hal.ctx = myCtx; hal.maxSize = NV_SIZE;
221+
hal.appendOnly = 1;
222+
hal.writeAlign = PROG_SIZE; /* flash word size, e.g. 16 (STM32H5) */
223+
hal.get_integrity_key = myDeviceSecret;/* recommended on flash */
224+
FWTPM_NV_SetHAL(&ctx, &hal); /* before FWTPM_Init() */
225+
```
226+
227+
In append-only mode the journal buffers a pending program granule internally and
228+
flushes full, aligned granules through `write()`, so a programmed cell is never
229+
rewritten and a whole sector is erased only on compaction. The header sector is
230+
therefore not erased on every append, and a torn final commit (e.g. on power
231+
loss) is ignored on the next load while all previously committed state survives.
232+
A `get_integrity_key` callback is strongly recommended on flash so the MAC
233+
checkpoints authenticate the journal and reject a torn or tampered tail.
234+
`writeAlign <= 1` selects no buffering, so byte-writable NV (EEPROM/FRAM) works
235+
with a plain `write()`. Compaction still erases the whole region before
236+
rewriting, so a power loss during compaction itself remains a vulnerable window
237+
(a future two-region ping-pong layout would close it). See
238+
`src/fwtpm/ports/README.md` for the full porting guide.
239+
193240
194241
## Supported TPM 2.0 Commands
195242

0 commit comments

Comments
 (0)