mirror of https://github.com/wolfSSL/wolfTPM.git
fwTPM: add finer per-command-group gating macros
parent
55d1185498
commit
20dae36517
|
|
@ -149,6 +149,64 @@ jobs:
|
|||
build_only: true
|
||||
extra_cflags: -DFWTPM_NO_PARAM_ENC
|
||||
|
||||
# Build-only: finer per-command-group FWTPM_NO_* macros
|
||||
- name: fwtpm-no-key-migration
|
||||
os: ubuntu-latest
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen
|
||||
build_only: true
|
||||
extra_cflags: -DFWTPM_NO_KEY_MIGRATION
|
||||
|
||||
- name: fwtpm-no-ecdh
|
||||
os: ubuntu-latest
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen
|
||||
build_only: true
|
||||
extra_cflags: -DFWTPM_NO_ECDH
|
||||
|
||||
- name: fwtpm-no-hash-cmds
|
||||
os: ubuntu-latest
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen
|
||||
build_only: true
|
||||
extra_cflags: -DFWTPM_NO_HASH_CMDS
|
||||
|
||||
- name: fwtpm-no-context
|
||||
os: ubuntu-latest
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen
|
||||
build_only: true
|
||||
extra_cflags: -DFWTPM_NO_CONTEXT
|
||||
|
||||
- name: fwtpm-no-sym-encrypt
|
||||
os: ubuntu-latest
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen
|
||||
build_only: true
|
||||
extra_cflags: -DFWTPM_NO_SYM_ENCRYPT
|
||||
|
||||
- name: fwtpm-no-clock
|
||||
os: ubuntu-latest
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen
|
||||
build_only: true
|
||||
extra_cflags: -DFWTPM_NO_CLOCK
|
||||
|
||||
# Build-only: fewer PCRs than the 24 standard ones. The locality
|
||||
# tables are sized to a fixed 24 while IMPLEMENTATION_PCR sets the
|
||||
# implemented count, so this leg proves the two boundaries compile
|
||||
# apart. PLATFORM_PCR must drop with it or PCR_SELECT_MIN exceeds
|
||||
# PCR_SELECT_MAX. Runtime coverage is in fwtpm-gated-runtime.
|
||||
- name: fwtpm-reduced-pcr
|
||||
os: ubuntu-latest
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen
|
||||
build_only: true
|
||||
extra_cflags: -DIMPLEMENTATION_PCR=8 -DPLATFORM_PCR=8
|
||||
|
||||
# Note: the all-gates combinations are built AND run by the
|
||||
# fwtpm-gated-runtime job below, so they are not duplicated here.
|
||||
|
||||
# Build-only: cross-algorithm + feature macro combinations
|
||||
- name: fwtpm-no-rsa-no-policy
|
||||
os: ubuntu-latest
|
||||
|
|
@ -300,6 +358,89 @@ jobs:
|
|||
tests/*.log
|
||||
retention-days: 5
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Command-group gates: runtime behavior, not just compilation.
|
||||
#
|
||||
# `make check` cannot be used here - it also runs fwtpm_check.sh, which
|
||||
# drives the examples and tpm2-tools against a server that no longer
|
||||
# implements the gated commands. This job builds each gated configuration
|
||||
# and runs only tests/fwtpm_unit.test, whose test_fwtpm_command_gates /
|
||||
# test_fwtpm_total_commands cases assert that a gated command is rejected
|
||||
# with TPM_RC_COMMAND_CODE, is absent from TPM_CAP_COMMANDS, and is not
|
||||
# counted in TPM_PT_TOTAL_COMMANDS.
|
||||
# ----------------------------------------------------------------
|
||||
fwtpm-gated-runtime:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Every command-group gate together on an ECC-only build - the
|
||||
# minimal configuration documented in docs/FWTPM.md.
|
||||
- name: all-gates-ecc-only
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen --disable-rsa
|
||||
extra_cflags: >-
|
||||
-DFWTPM_NO_POLICY
|
||||
-DFWTPM_NO_ATTESTATION
|
||||
-DFWTPM_NO_CREDENTIAL
|
||||
-DFWTPM_NO_DA
|
||||
-DFWTPM_NO_PARAM_ENC
|
||||
-DFWTPM_NO_KEY_MIGRATION
|
||||
-DFWTPM_NO_ECDH
|
||||
-DFWTPM_NO_HASH_CMDS
|
||||
-DFWTPM_NO_CONTEXT
|
||||
-DFWTPM_NO_SYM_ENCRYPT
|
||||
-DFWTPM_NO_CLOCK
|
||||
|
||||
# Same gates with ML-DSA: SequenceUpdate must survive (the ML-DSA
|
||||
# verify sequences stream through it) while SequenceComplete must
|
||||
# not, and the ML-DSA sign/verify sequence tests must still pass.
|
||||
- name: all-gates-mldsa
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm --enable-v185 --enable-mldsa
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen --enable-experimental --enable-dilithium --enable-mlkem
|
||||
extra_cflags: >-
|
||||
-DFWTPM_NO_POLICY
|
||||
-DFWTPM_NO_ATTESTATION
|
||||
-DFWTPM_NO_CREDENTIAL
|
||||
-DFWTPM_NO_DA
|
||||
-DFWTPM_NO_PARAM_ENC
|
||||
-DFWTPM_NO_KEY_MIGRATION
|
||||
-DFWTPM_NO_ECDH
|
||||
-DFWTPM_NO_HASH_CMDS
|
||||
-DFWTPM_NO_CONTEXT
|
||||
-DFWTPM_NO_SYM_ENCRYPT
|
||||
-DFWTPM_NO_CLOCK
|
||||
|
||||
# Fewer PCRs than the 24 standard ones: exercises the highest
|
||||
# implemented PCR and the first unavailable one
|
||||
# (test_fwtpm_pcr_bounds).
|
||||
- name: reduced-pcr
|
||||
wolftpm_config: --enable-fwtpm --enable-swtpm
|
||||
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen
|
||||
extra_cflags: -DIMPLEMENTATION_PCR=8 -DPLATFORM_PCR=8
|
||||
|
||||
steps:
|
||||
- name: Checkout wolfTPM
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup wolfSSL
|
||||
uses: ./.github/actions/setup-wolfssl
|
||||
with:
|
||||
configure-flags: ${{ matrix.wolfssl_config }}
|
||||
cflags: -DWC_RSA_NO_PADDING ${{ matrix.extra_cflags }}
|
||||
|
||||
- name: Build wolfTPM (${{ matrix.name }})
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure ${{ matrix.wolftpm_config }} \
|
||||
CFLAGS="${{ matrix.extra_cflags }}"
|
||||
make -j"$(nproc)" tests/fwtpm_unit.test
|
||||
|
||||
- name: Run fwTPM handler tests
|
||||
run: ./tests/fwtpm_unit.test
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Dictionary Attack TPM_RC_RETRY (daUsed) end-to-end.
|
||||
# Builds the server with -DFWTPM_DA_USED_RETRY so it returns TPM_RC_RETRY on
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ Features:
|
|||
* TIS register-level transport over shared memory or SPI/I2C for bare-metal integration
|
||||
* HAL abstractions for IO transport and NV storage portability
|
||||
* File-based or custom NV storage via HAL callbacks
|
||||
* Compile-time algorithm and feature selection (e.g., `NO_RSA`, `FWTPM_NO_NV`)
|
||||
* Compile-time algorithm and per-command-group feature selection (e.g., `NO_RSA`, `FWTPM_NO_NV`, and independent per-command-group gates you pick and choose to shrink the fTPM footprint)
|
||||
* `WOLFTPM_SMALL_STACK` support for constrained environments
|
||||
|
||||
See [docs/FWTPM.md](docs/FWTPM.md) for build instructions, configuration, and API reference.
|
||||
|
|
|
|||
|
|
@ -664,22 +664,43 @@ to reduce code size on constrained targets.
|
|||
| `FWTPM_NO_POLICY` | not defined | `PolicyGetDigest`, `PolicyRestart`, `PolicyPCR`, `PolicyPassword`, `PolicyAuthValue`, `PolicyCommandCode`, `PolicyOR`, `PolicySecret`, `PolicyAuthorize`, `PolicyNV` |
|
||||
| `FWTPM_NO_CREDENTIAL` | not defined | `MakeCredential`, `ActivateCredential` |
|
||||
| `FWTPM_NO_DA` | not defined | `DictionaryAttackParameters`, `DictionaryAttackLockReset`, and all lockout accounting |
|
||||
| `FWTPM_NO_PARAM_ENC` | not defined | command/response parameter (XOR/AES-CFB) encryption support in sessions |
|
||||
| `FWTPM_NO_KEY_MIGRATION` | not defined | `Import`, `Duplicate`, `Rewrap` |
|
||||
| `FWTPM_NO_ECDH` | not defined | `ECDH_KeyGen`, `ECDH_ZGen`, `EC_Ephemeral`, `ZGen_2Phase`, `ECC_Parameters` (ECDSA sign/verify retained), plus the `ecEphemeral*` commit state in `FWTPM_CTX` |
|
||||
| `FWTPM_NO_HASH_CMDS` | not defined | `Hash`, `HMAC`, `HMAC_Start`, `HashSequenceStart`, `SequenceUpdate`, `SequenceComplete`, `EventSequenceComplete`, and the `FWTPM_CTX` hash-sequence slots |
|
||||
| `FWTPM_NO_CONTEXT` | not defined | `ContextSave`, `ContextLoad` (`FlushContext` retained), plus the per-boot context protection key and saved-context replay list in `FWTPM_CTX` |
|
||||
| `FWTPM_NO_SYM_ENCRYPT` | not defined | `EncryptDecrypt`, `EncryptDecrypt2` |
|
||||
| `FWTPM_NO_CLOCK` | not defined | `ReadClock`, `ClockSet`, `ClockRateAdjust` |
|
||||
|
||||
Removing a command group also removes it from the `TPM2_GetCapability(TPM_CAP_COMMANDS)` advertisement and the `TPM_PT_TOTAL_COMMANDS` count, since both are derived from the dispatch table. Note: when `WOLFTPM_MLDSA` is built, `SequenceUpdate` alone is retained under `FWTPM_NO_HASH_CMDS`, because ML-DSA verify sequences stream their message through it. `SequenceComplete` is not shared -- ML-DSA sequences finalize through `TPM2_SignSequenceComplete` / `TPM2_VerifySequenceComplete` -- so it is gated out with the rest of the hash commands rather than advertised as a command that can never succeed.
|
||||
|
||||
The `FWTPM_DA_USED_RETRY` macro (off by default) does not remove commands; it
|
||||
makes the server return `TPM_RC_RETRY` on the first DA-protected auth use after
|
||||
startup, emulating a real TPM persisting `daUsed`. See
|
||||
[Dictionary Attack (DA) Protection](#dictionary-attack-da-protection).
|
||||
|
||||
**Minimal build example** (measured boot only):
|
||||
**Minimal build example.** There is no umbrella macro - select the command
|
||||
groups to drop explicitly, so each is a deliberate choice. For example, to build
|
||||
a small ECC-only signing + NV fTPM (this set drops attestation; keep
|
||||
`Sign`/`VerifySignature`, PCR, and NV):
|
||||
|
||||
```sh
|
||||
./configure --enable-fwtpm --enable-swtpm \
|
||||
CFLAGS="-DNO_RSA -DFWTPM_NO_NV -DFWTPM_NO_ATTESTATION \
|
||||
-DFWTPM_NO_POLICY -DFWTPM_NO_CREDENTIAL"
|
||||
CFLAGS="-DNO_RSA \
|
||||
-DFWTPM_NO_POLICY -DFWTPM_NO_ATTESTATION -DFWTPM_NO_CREDENTIAL \
|
||||
-DFWTPM_NO_DA -DFWTPM_NO_PARAM_ENC -DFWTPM_NO_KEY_MIGRATION \
|
||||
-DFWTPM_NO_ECDH -DFWTPM_NO_HASH_CMDS -DFWTPM_NO_CONTEXT \
|
||||
-DFWTPM_NO_SYM_ENCRYPT -DFWTPM_NO_CLOCK"
|
||||
```
|
||||
|
||||
This retains only: `Startup`, `Shutdown`, `SelfTest`, `GetRandom`, `GetCapability`,
|
||||
`PCR_Read`, `PCR_Extend`, `PCR_Reset`, `Hash`, ECC keygen/sign, and session support.
|
||||
That set retains a core fTPM: `Startup`, `Shutdown`, `SelfTest`, `GetRandom`,
|
||||
`GetCapability`, the `PCR_*` commands, `Create`/`CreatePrimary`/`Load`/
|
||||
`ReadPublic`/`FlushContext`, `Sign`/`VerifySignature`, the `NV_*` commands, and
|
||||
session support (`StartAuthSession`/`Unseal`). Add `-DFWTPM_NO_NV` to also drop
|
||||
NV, or drop any `-DFWTPM_NO_*` above to keep that group. This ECC-only build is
|
||||
small enough to run as a soft-core fTPM on a constrained FPGA (see the MicroBlaze
|
||||
V example in the `wolftpm-examples` repo, which fits an ECC-only fTPM into
|
||||
~192 KB of on-chip memory).
|
||||
|
||||
**Dependencies:**
|
||||
- `FWTPM_NO_NV` also removes `NV_Certify` (even if `FWTPM_NO_ATTESTATION` is not set)
|
||||
|
|
|
|||
|
|
@ -96,8 +96,25 @@ All tests below run in GitHub Actions CI. Run manually before PR submission.
|
|||
|------|---------------|-------|-------|
|
||||
| fwtpm-socket | `--enable-fwtpm --enable-swtpm --enable-debug` | | Primary test |
|
||||
| fwtpm-tis | `--enable-fwtpm --enable-debug` | | TIS/SHM transport |
|
||||
| fwtpm-asan | `--enable-fwtpm --enable-swtpm --enable-debug` | `-fsanitize=address` | Memory errors |
|
||||
| fwtpm-ubsan | `--enable-fwtpm --enable-swtpm --enable-debug` | `-fsanitize=undefined` | UB detection |
|
||||
| fwtpm-v185 | `--enable-fwtpm --enable-v185` | | PQC: wrapper + handler unit tests |
|
||||
| fwtpm-macos-socket | `--enable-fwtpm --enable-swtpm --enable-debug` | | macOS runner |
|
||||
|
||||
ASan / UBSan / LeakSan coverage lives in `sanitizer.yml`, not this workflow.
|
||||
|
||||
### Runtime Tests, gated builds (`fwtpm-gated-runtime` job)
|
||||
|
||||
These configurations remove commands, so `make check` (which drives the
|
||||
examples and tpm2-tools) does not apply. The job builds and runs only
|
||||
`tests/fwtpm_unit.test`, whose `test_fwtpm_command_gates` /
|
||||
`test_fwtpm_total_commands` / `test_fwtpm_pcr_bounds` cases assert that a gated
|
||||
command is rejected with `TPM_RC_COMMAND_CODE`, is absent from
|
||||
`TPM_CAP_COMMANDS`, and is not counted in `TPM_PT_TOTAL_COMMANDS`.
|
||||
|
||||
| Name | wolfTPM Config | wolfSSL Config | Extra CFLAGS |
|
||||
|------|---------------|---------------|-------------|
|
||||
| all-gates-ecc-only | `--enable-fwtpm --enable-swtpm` | `--disable-rsa` | the eleven command-group `-DFWTPM_NO_*` gates together (NV retained) |
|
||||
| all-gates-mldsa | `--enable-fwtpm --enable-swtpm --enable-v185 --enable-mldsa` | `--enable-dilithium --enable-mlkem` | the same eleven gates; proves SequenceUpdate survives for ML-DSA while SequenceComplete does not |
|
||||
| reduced-pcr | `--enable-fwtpm --enable-swtpm` | | `-DIMPLEMENTATION_PCR=8 -DPLATFORM_PCR=8` |
|
||||
|
||||
### Build-Only Tests
|
||||
|
||||
|
|
@ -105,6 +122,9 @@ All tests below run in GitHub Actions CI. Run manually before PR submission.
|
|||
|------|---------------|---------------|-------------|
|
||||
| fwtpm-no-rsa | `--enable-fwtpm --enable-swtpm` | `--disable-rsa` | |
|
||||
| fwtpm-no-ecc | `--enable-fwtpm --enable-swtpm` | `--disable-ecc` | |
|
||||
| fwtpm-no-sha384 | `--enable-fwtpm --enable-swtpm` | `--disable-sha384` | |
|
||||
| fwtpm-no-sha1 | `--enable-fwtpm --enable-swtpm` | `--disable-sha` | `-DNO_SHA` |
|
||||
| fwtpm-v185-build-only | `--enable-fwtpm --enable-v185` | | `-DDEBUG_WOLFTPM` |
|
||||
| fwtpm-only | `--enable-fwtpm-only --enable-swtpm` | | No client library |
|
||||
| fwtpm-minimal | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_ATTESTATION -DFWTPM_NO_NV -DFWTPM_NO_POLICY -DFWTPM_NO_CREDENTIAL -DFWTPM_NO_DA -DFWTPM_NO_PARAM_ENC` |
|
||||
| fwtpm-no-policy | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_POLICY` |
|
||||
|
|
@ -113,6 +133,13 @@ All tests below run in GitHub Actions CI. Run manually before PR submission.
|
|||
| fwtpm-no-credential | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_CREDENTIAL` |
|
||||
| fwtpm-no-da | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_DA` |
|
||||
| fwtpm-no-param-enc | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_PARAM_ENC` |
|
||||
| fwtpm-no-key-migration | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_KEY_MIGRATION` |
|
||||
| fwtpm-no-ecdh | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_ECDH` |
|
||||
| fwtpm-no-hash-cmds | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_HASH_CMDS` |
|
||||
| fwtpm-no-context | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_CONTEXT` |
|
||||
| fwtpm-no-sym-encrypt | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_SYM_ENCRYPT` |
|
||||
| fwtpm-no-clock | `--enable-fwtpm --enable-swtpm` | | `-DFWTPM_NO_CLOCK` |
|
||||
| fwtpm-reduced-pcr | `--enable-fwtpm --enable-swtpm` | | `-DIMPLEMENTATION_PCR=8 -DPLATFORM_PCR=8` |
|
||||
| fwtpm-no-rsa-no-policy | `--enable-fwtpm --enable-swtpm` | `--disable-rsa` | `-DFWTPM_NO_POLICY` |
|
||||
| fwtpm-no-ecc-no-nv | `--enable-fwtpm --enable-swtpm` | `--disable-ecc` | `-DFWTPM_NO_NV` |
|
||||
| fwtpm-small-stack | `--enable-fwtpm --enable-swtpm` | | `-DWOLFTPM_SMALL_STACK` |
|
||||
|
|
@ -140,7 +167,9 @@ control), spec version targeting, and HAL abstraction details.
|
|||
Key options: `--enable-fwtpm`, `--enable-fwtpm-only`, `--enable-swtpm`,
|
||||
`--enable-fwtpm-small-ctx`, `--enable-fuzz`. Feature disable macros:
|
||||
`FWTPM_NO_ATTESTATION`, `FWTPM_NO_NV`, `FWTPM_NO_POLICY`, `FWTPM_NO_CREDENTIAL`,
|
||||
`FWTPM_NO_DA`, `FWTPM_NO_PARAM_ENC`. Feature enable macros:
|
||||
`FWTPM_NO_DA`, `FWTPM_NO_PARAM_ENC`, `FWTPM_NO_KEY_MIGRATION`, `FWTPM_NO_ECDH`,
|
||||
`FWTPM_NO_HASH_CMDS`, `FWTPM_NO_CONTEXT`, `FWTPM_NO_SYM_ENCRYPT`,
|
||||
`FWTPM_NO_CLOCK`. Feature enable macros:
|
||||
`WOLFTPM_FWTPM_TCG_TEST` (optional `Vendor_TCG_Test` echo command, off by default).
|
||||
|
||||
Command codes with reserved bits set (only the 16-bit index plus the `CC_VEND` V bit are valid), or codes not in the dispatch table, are rejected with `TPM_RC_COMMAND_CODE`.
|
||||
|
|
@ -186,16 +215,20 @@ CI exercises 7 build-only configure permutations + the two e2e modes on
|
|||
|
||||
The fwTPM implements 105 of 113 commands from the v1.38 baseline (93% coverage).
|
||||
|
||||
**Always enabled (47 commands):**
|
||||
**Core set -- never gated (36 commands):**
|
||||
Startup, Shutdown, SelfTest, IncrementalSelfTest, GetTestResult, GetRandom,
|
||||
StirRandom, GetCapability, TestParms, PCR\_Read, PCR\_Extend, PCR\_Reset,
|
||||
PCR\_Event, PCR\_Allocate, PCR\_SetAuthPolicy, PCR\_SetAuthValue,
|
||||
ReadClock, ClockSet, ClockRateAdjust, CreatePrimary, FlushContext,
|
||||
ContextSave, ContextLoad, ReadPublic, Clear, ClearControl, ChangeEPS, ChangePPS, HierarchyControl,
|
||||
HierarchyChangeAuth, SetPrimaryPolicy, EvictControl, Create, ObjectChangeAuth,
|
||||
Load, Sign, VerifySignature, Hash, HMAC, HMAC\_Start, HashSequenceStart,
|
||||
SequenceUpdate, SequenceComplete, EventSequenceComplete, StartAuthSession,
|
||||
Unseal, LoadExternal, Import, Duplicate, Rewrap, CreateLoaded
|
||||
CreatePrimary, FlushContext, ReadPublic, Clear, ClearControl, ChangeEPS,
|
||||
ChangePPS, HierarchyControl, HierarchyChangeAuth, SetPrimaryPolicy,
|
||||
EvictControl, Create, ObjectChangeAuth, Load, Sign, VerifySignature,
|
||||
StartAuthSession, Unseal, LoadExternal, CreateLoaded
|
||||
|
||||
These are present in every build. A default build (no `FWTPM_NO_*` macro set)
|
||||
additionally enables every group listed under **Conditional on feature macros**
|
||||
below; setting a gate macro removes that group's commands from the dispatch
|
||||
table, from `TPM2_GetCapability(TPM_CAP_COMMANDS)`, and from the
|
||||
`TPM_PT_TOTAL_COMMANDS` count.
|
||||
|
||||
**Optional vendor command (off by default, `WOLFTPM_FWTPM_TCG_TEST`):**
|
||||
Vendor\_TCG\_Test
|
||||
|
|
@ -221,6 +254,33 @@ EncryptDecrypt, EncryptDecrypt2
|
|||
- `FWTPM_NO_PARAM_ENC`: Disables parameter encryption/decryption for command and
|
||||
response parameters. Sessions still work for HMAC auth, but encrypted transport
|
||||
is disabled. Reduces code size by removing AES-CFB and XOR param encryption.
|
||||
- `FWTPM_NO_KEY_MIGRATION`: Import, Duplicate, Rewrap (3 commands). Shared key
|
||||
helpers (used by Create/Load) are retained.
|
||||
- `FWTPM_NO_ECDH`: ECDH\_KeyGen, ECDH\_ZGen, EC\_Ephemeral, ZGen\_2Phase,
|
||||
ECC\_Parameters (5 commands). ECDSA sign/verify are retained. Also drops the
|
||||
`ecEphemeral*` commit state from `FWTPM_CTX`.
|
||||
- `FWTPM_NO_HASH_CMDS`: Hash, HMAC, HMAC\_Start, HashSequenceStart,
|
||||
SequenceUpdate, SequenceComplete, EventSequenceComplete (7 commands). When
|
||||
`WOLFTPM_MLDSA` is built, only SequenceUpdate is retained -- the MLDSA
|
||||
verify sequences stream their message through it. SequenceComplete is not
|
||||
shared: MLDSA sequences finalize through SignSequenceComplete /
|
||||
VerifySequenceComplete, so advertising it in a gated build would expose a
|
||||
command that can never succeed. Also drops the per-instance hash-sequence
|
||||
slots (`hashSeq[FWTPM_MAX_HASH_SEQ]`) from `FWTPM_CTX`.
|
||||
- `FWTPM_NO_CONTEXT`: ContextSave, ContextLoad (2 commands). FlushContext is
|
||||
retained. Also drops the per-boot context protection key and the saved-context
|
||||
replay list from `FWTPM_CTX`.
|
||||
- `FWTPM_NO_SYM_ENCRYPT`: EncryptDecrypt, EncryptDecrypt2 (2 commands). Nests
|
||||
inside `NO_AES`; AES itself is retained for session parameter encryption,
|
||||
AES-GCM, and (unless `FWTPM_NO_CONTEXT` is also set) context protection.
|
||||
- `FWTPM_NO_CLOCK`: ReadClock, ClockSet, ClockRateAdjust (3 commands). GetTime is
|
||||
under `FWTPM_NO_ATTESTATION`, not this flag.
|
||||
These gates are independent and there is intentionally no umbrella macro: pick
|
||||
exactly the groups your fTPM does not need. Applying all of them plus the earlier
|
||||
`FWTPM_NO_POLICY/ATTESTATION/CREDENTIAL/DA/PARAM_ENC` (keeping NV, or adding
|
||||
`FWTPM_NO_NV` to drop it) leaves a core fTPM (Startup/GetCapability/GetRandom/
|
||||
PCR/Create/Load/Sign/VerifySignature/NV/sessions) - see the MicroBlaze V example
|
||||
in wolftpm-examples for a worked selection.
|
||||
|
||||
### Missing Commands -- TODO
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#endif
|
||||
|
||||
#include <wolftpm/fwtpm/fwtpm.h>
|
||||
#include <wolftpm/fwtpm/fwtpm_command.h>
|
||||
#include <wolftpm/fwtpm/fwtpm_nv.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -90,6 +91,7 @@ int FWTPM_Init(FWTPM_CTX* ctx)
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
/* Generate per-boot context protection key (volatile only) for
|
||||
* ContextSave/ContextLoad HMAC + AES-CFB session blob protection. */
|
||||
if (rc == 0) {
|
||||
|
|
@ -99,6 +101,7 @@ int FWTPM_Init(FWTPM_CTX* ctx)
|
|||
ctx->ctxProtectKeyValid = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialize NV storage - loads existing state or creates fresh seeds */
|
||||
#ifndef FWTPM_NO_NV
|
||||
|
|
@ -148,6 +151,11 @@ int FWTPM_Cleanup(FWTPM_CTX* ctx)
|
|||
rc = TPM_RC_SUCCESS;
|
||||
#endif
|
||||
|
||||
/* Release transient objects, sessions, and hash/sign sequence slots first.
|
||||
* ForceZero alone would drop the live wc_HashAlg / Hmac contexts they own,
|
||||
* leaking their heap allocations under WOLFTPM_SMALL_STACK. */
|
||||
FWTPM_ResetCommandClient(ctx);
|
||||
|
||||
wc_FreeRng(&ctx->rng);
|
||||
wolfCrypt_Cleanup();
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,9 @@ static UINT64 FwDaNowMs(FWTPM_CTX* ctx);
|
|||
#ifdef WOLFTPM_MLDSA
|
||||
static FWTPM_SignSeq* FwFindSignSeq(FWTPM_CTX* ctx, TPM_HANDLE handle);
|
||||
#endif
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
static FWTPM_HashSeq* FwFindHashSeq(FWTPM_CTX* ctx, TPM_HANDLE handle);
|
||||
#endif
|
||||
|
||||
/* Command table accessors (fwCmdTable is defined near end of file) */
|
||||
static int FwGetCmdCount(void);
|
||||
|
|
@ -478,23 +480,31 @@ static void FwLookupEntityAuth(FWTPM_CTX* ctx, TPM_HANDLE handle,
|
|||
*authValSz = objEnt->authValue.size;
|
||||
}
|
||||
else {
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
FWTPM_HashSeq* seqEnt = FwFindHashSeq(ctx, handle);
|
||||
#endif
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
FWTPM_SignSeq* signEnt = NULL;
|
||||
#endif
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
if (seqEnt != NULL) {
|
||||
*authVal = seqEnt->authValue.buffer;
|
||||
*authValSz = seqEnt->authValue.size;
|
||||
}
|
||||
#endif
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
else {
|
||||
/* v1.85 sign/verify sequences also carry their own
|
||||
* authValue. Without this lookup the password/HMAC
|
||||
* verifier resolves these handles to authSz=0, which
|
||||
* effectively bypasses the per-sequence auth set at
|
||||
* SignSequenceStart / VerifySequenceStart. */
|
||||
FWTPM_SignSeq* signEnt = FwFindSignSeq(ctx, handle);
|
||||
if (signEnt != NULL) {
|
||||
*authVal = signEnt->authValue.buffer;
|
||||
*authValSz = signEnt->authValue.size;
|
||||
}
|
||||
/* v1.85 sign/verify sequences also carry their own authValue.
|
||||
* Without this lookup the password/HMAC verifier resolves these
|
||||
* handles to authSz=0, which effectively bypasses the
|
||||
* per-sequence auth set at SignSequenceStart /
|
||||
* VerifySequenceStart. */
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
if (seqEnt == NULL)
|
||||
#endif
|
||||
signEnt = FwFindSignSeq(ctx, handle);
|
||||
if (signEnt != NULL) {
|
||||
*authVal = signEnt->authValue.buffer;
|
||||
*authValSz = signEnt->authValue.size;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -710,7 +720,9 @@ static int FwComputeSessionHmac(FWTPM_Session* sess,
|
|||
/* Forward declarations for helpers used by Startup */
|
||||
static void FwFlushAllObjects(FWTPM_CTX* ctx);
|
||||
static void FwFlushAllSessions(FWTPM_CTX* ctx);
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
static void FwFreeHashSeq(FWTPM_HashSeq* seq);
|
||||
#endif
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
static void FwFreeSignSeq(FWTPM_SignSeq* seq);
|
||||
#endif
|
||||
|
|
@ -777,11 +789,13 @@ static TPM_RC FwCmd_Startup(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize,
|
|||
* primary cache, and reset PCRs */
|
||||
FwFlushAllObjects(ctx);
|
||||
FwFlushAllSessions(ctx);
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
for (i = 0; i < FWTPM_MAX_HASH_SEQ; i++) {
|
||||
if (ctx->hashSeq[i].used) {
|
||||
FwFreeHashSeq(&ctx->hashSeq[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
for (i = 0; i < FWTPM_MAX_SIGN_SEQ; i++) {
|
||||
if (ctx->signSeq[i].used) {
|
||||
|
|
@ -799,9 +813,11 @@ static TPM_RC FwCmd_Startup(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize,
|
|||
}
|
||||
}
|
||||
ctx->globalNvWriteLock = 0;
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
/* Saved contexts are invalidated by TPM Reset */
|
||||
ctx->contextLiveCount = 0;
|
||||
#ifdef HAVE_ECC
|
||||
#endif
|
||||
#if defined(HAVE_ECC) && !defined(FWTPM_NO_ECDH)
|
||||
ctx->ecEphemeralCounter = 0;
|
||||
ctx->ecEphemeralKeySz = 0;
|
||||
#endif
|
||||
|
|
@ -2145,7 +2161,13 @@ static TPM_RC FwCmd_PCR_Read(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize,
|
|||
* dump (loc2 reset of PCR 20-22 hardware-confirmed over SPI).
|
||||
* reset: 0-15 never; 16,23 loc0-3; 17-19 loc4; 20-22 loc2-4.
|
||||
* extend: 0-16,23 any; 17,18 loc2-4; 19 loc2-3; 20 loc1-3; 21,22 loc2. */
|
||||
static const byte fwPcrResetLocality[IMPLEMENTATION_PCR] = {
|
||||
/* The TCG PCR locality attributes are defined for the 24 standard PCRs. Size the
|
||||
* tables to that fixed count (not IMPLEMENTATION_PCR) so a build implementing
|
||||
* fewer PCRs (IMPLEMENTATION_PCR < 24, e.g. a minimal soft-core fTPM) neither
|
||||
* drops initializers nor warns; FwPcrLocalityAllowed() only indexes valid PCRs. */
|
||||
#define FW_PCR_LOCALITY_TABLE 24
|
||||
|
||||
static const byte fwPcrResetLocality[FW_PCR_LOCALITY_TABLE] = {
|
||||
/* 0- 7 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 8-15 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 16 */ 0x0F, /* loc0-3 */
|
||||
|
|
@ -2158,7 +2180,7 @@ static const byte fwPcrResetLocality[IMPLEMENTATION_PCR] = {
|
|||
/* 23 */ 0x0F /* loc0-3 */
|
||||
};
|
||||
|
||||
static const byte fwPcrExtendLocality[IMPLEMENTATION_PCR] = {
|
||||
static const byte fwPcrExtendLocality[FW_PCR_LOCALITY_TABLE] = {
|
||||
/* 0- 7 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, /* any */
|
||||
/* 8-15 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, /* any */
|
||||
/* 16 */ 0x1F, /* any */
|
||||
|
|
@ -2177,7 +2199,8 @@ static int FwPcrLocalityAllowed(int pcrIndex, int locality, int isReset)
|
|||
{
|
||||
byte mask;
|
||||
|
||||
if (pcrIndex < 0 || pcrIndex >= IMPLEMENTATION_PCR) {
|
||||
if (pcrIndex < 0 || pcrIndex >= IMPLEMENTATION_PCR ||
|
||||
pcrIndex >= FW_PCR_LOCALITY_TABLE) {
|
||||
return 0;
|
||||
}
|
||||
if (locality < 0 || locality > WOLFTPM_LOCALITY_MAX) {
|
||||
|
|
@ -2705,6 +2728,7 @@ static TPM_RC FwCmd_PCR_SetAuthValue(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
return rc;
|
||||
}
|
||||
|
||||
#ifndef FWTPM_NO_CLOCK
|
||||
/* --- TPM2_ReadClock (CC 0x0181) --- */
|
||||
static TPM_RC FwCmd_ReadClock(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize,
|
||||
TPM2_Packet* rsp, UINT16 cmdTag)
|
||||
|
|
@ -2824,6 +2848,7 @@ static TPM_RC FwCmd_ClockRateAdjust(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_CLOCK */
|
||||
|
||||
/* --- Object management helpers --- */
|
||||
|
||||
|
|
@ -2945,19 +2970,25 @@ static void FwFlushAllSessions(FWTPM_CTX* ctx)
|
|||
|
||||
void FWTPM_ResetCommandClient(FWTPM_CTX* ctx)
|
||||
{
|
||||
#if !defined(FWTPM_NO_HASH_CMDS) || defined(WOLFTPM_MLDSA)
|
||||
int i;
|
||||
#endif
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
FwFlushAllObjects(ctx);
|
||||
FwFlushAllSessions(ctx);
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
/* Saved-context replay set belongs to the prior client */
|
||||
ctx->contextLiveCount = 0;
|
||||
#endif
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
for (i = 0; i < FWTPM_MAX_HASH_SEQ; i++) {
|
||||
if (ctx->hashSeq[i].used) {
|
||||
FwFreeHashSeq(&ctx->hashSeq[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
for (i = 0; i < FWTPM_MAX_SIGN_SEQ; i++) {
|
||||
if (ctx->signSeq[i].used) {
|
||||
|
|
@ -3523,6 +3554,7 @@ static TPM_RC FwCmd_FlushContext(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
* stored externally (e.g. a .ctx file) and later reloaded with ContextLoad.
|
||||
* We use an opaque blob that stores the handle number; the object remains
|
||||
* in its slot so ContextLoad can find it for the lifetime of the server. */
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
#define FWTPM_CTX_MAGIC 0x4657544Du /* 'FWTM' */
|
||||
#define FWTPM_CTX_VER 1u
|
||||
static TPM_RC FwCmd_ContextSave(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
||||
|
|
@ -3861,6 +3893,7 @@ static TPM_RC FwCmd_ContextLoad(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* --- TPM2_ReadPublic (CC 0x0173) --- */
|
||||
static TPM_RC FwCmd_ReadPublic(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
||||
|
|
@ -5482,6 +5515,7 @@ static TPM_RC FwCmd_LoadExternal(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
}
|
||||
|
||||
|
||||
#ifndef FWTPM_NO_KEY_MIGRATION
|
||||
/* --- TPM2_Import (CC 0x156) ---
|
||||
* Import an externally created key (outer-wrapped) under a parent key.
|
||||
* Response: [paramSz] | outPrivate */
|
||||
|
|
@ -6598,6 +6632,7 @@ static TPM_RC FwCmd_Rewrap(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
FWTPM_FREE_BUF(encSeedBuf);
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_KEY_MIGRATION */
|
||||
|
||||
/* --- TPM2_CreateLoaded (CC 0x0191) ---
|
||||
* Like Create but also loads the key into a transient slot.
|
||||
|
|
@ -7536,6 +7571,7 @@ static TPM_RC FwCmd_RSA_Decrypt(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
/* Hash, HMAC, HashSequence, ECDH */
|
||||
/* ================================================================== */
|
||||
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
/* --- TPM2_Hash (CC 0x017D) --- */
|
||||
static TPM_RC FwCmd_Hash(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
||||
int cmdSize, TPM2_Packet* rsp, UINT16 cmdTag)
|
||||
|
|
@ -7964,7 +8000,14 @@ static TPM_RC FwCmd_HashSequenceStart(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
|
||||
/* SequenceUpdate is shared with the MLDSA sign/verify sequences - keep it
|
||||
* whenever MLDSA is built, even if the hash commands are gated.
|
||||
* SequenceComplete is NOT shared: MLDSA sequences finalize through
|
||||
* SignSequenceComplete / VerifySequenceComplete, so it stays under
|
||||
* FWTPM_NO_HASH_CMDS alone. */
|
||||
#if !defined(FWTPM_NO_HASH_CMDS) || defined(WOLFTPM_MLDSA)
|
||||
/* --- TPM2_SequenceUpdate (CC 0x015C) --- */
|
||||
static TPM_RC FwCmd_SequenceUpdate(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
||||
int cmdSize, TPM2_Packet* rsp, UINT16 cmdTag)
|
||||
|
|
@ -7973,7 +8016,9 @@ static TPM_RC FwCmd_SequenceUpdate(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
UINT32 seqHandle;
|
||||
UINT16 dataSize = 0;
|
||||
FWTPM_DECLARE_BUF(dataBuf, FWTPM_MAX_DATA_BUF);
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
FWTPM_HashSeq* seq;
|
||||
#endif
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
FWTPM_SignSeq* signSeq = NULL;
|
||||
#endif
|
||||
|
|
@ -7987,20 +8032,26 @@ static TPM_RC FwCmd_SequenceUpdate(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
if (rc == 0) {
|
||||
TPM2_Packet_ParseU32(cmd, &seqHandle);
|
||||
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
seq = FwFindHashSeq(ctx, seqHandle);
|
||||
#endif
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
if (seq == NULL) {
|
||||
/* Not a hash sequence — check sign/verify sequence slots. */
|
||||
/* Not a hash sequence - check sign/verify sequence slots. Per Part 3
|
||||
* Sec.20.6.1, TPM_RC_ONE_SHOT_SIGNATURE is a Sign SequenceComplete-time
|
||||
* RC ("sequenceHandle references a non-empty sequence"), not an
|
||||
* Update-time RC. We accept the Update bytes here (accumulator below
|
||||
* holds them) and let SignSequenceComplete fail with the spec-mandated
|
||||
* RC if the key is one-shot and any bytes accumulated. */
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
if (seq == NULL)
|
||||
#endif
|
||||
signSeq = FwFindSignSeq(ctx, seqHandle);
|
||||
if (signSeq == NULL) {
|
||||
rc = TPM_RC_HANDLE;
|
||||
}
|
||||
/* Per Part 3 Sec.20.6.1, TPM_RC_ONE_SHOT_SIGNATURE is a Sign
|
||||
* SequenceComplete-time RC ("sequenceHandle references a
|
||||
* non-empty sequence"), not an Update-time RC. We accept the
|
||||
* Update bytes here (accumulator below holds them) and let
|
||||
* SignSequenceComplete fail with the spec-mandated RC if the
|
||||
* key is one-shot and any bytes accumulated. */
|
||||
if (signSeq == NULL
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
&& seq == NULL
|
||||
#endif
|
||||
) {
|
||||
rc = TPM_RC_HANDLE;
|
||||
}
|
||||
#else
|
||||
if (seq == NULL) {
|
||||
|
|
@ -8083,8 +8134,11 @@ static TPM_RC FwCmd_SequenceUpdate(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
signSeq->msgBufSz += dataSize;
|
||||
}
|
||||
}
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
else
|
||||
#endif
|
||||
#endif
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
if (seq->isHmac) {
|
||||
rc = wc_HmacUpdate(&seq->ctx.hmac, dataBuf, dataSize);
|
||||
if (rc != 0) {
|
||||
|
|
@ -8098,6 +8152,7 @@ static TPM_RC FwCmd_SequenceUpdate(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
|
|
@ -8108,7 +8163,9 @@ static TPM_RC FwCmd_SequenceUpdate(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
FWTPM_FREE_BUF(dataBuf);
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_HASH_CMDS || WOLFTPM_MLDSA */
|
||||
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
/* --- TPM2_SequenceComplete (CC 0x013E) --- */
|
||||
static TPM_RC FwCmd_SequenceComplete(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
||||
int cmdSize, TPM2_Packet* rsp, UINT16 cmdTag)
|
||||
|
|
@ -8124,9 +8181,6 @@ static TPM_RC FwCmd_SequenceComplete(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
TPMI_ALG_HASH hashAlg = TPM_ALG_NULL;
|
||||
int paramSzPos, paramStart;
|
||||
int trc;
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
FWTPM_SignSeq* misRoutedSign = NULL;
|
||||
#endif
|
||||
|
||||
FWTPM_ALLOC_BUF(dataBuf, FWTPM_MAX_DATA_BUF);
|
||||
|
||||
|
|
@ -8139,12 +8193,11 @@ static TPM_RC FwCmd_SequenceComplete(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
|
||||
seq = FwFindHashSeq(ctx, seqHandle);
|
||||
if (seq == NULL) {
|
||||
/* A sign/verify sequence handle mis-routed here is rejected but
|
||||
* NOT freed: this path runs before the auth area is parsed, so
|
||||
* freeing would let an unauthenticated caller destroy any sequence
|
||||
* by guessing its handle. FlushContext releases those slots. */
|
||||
rc = TPM_RC_HANDLE;
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
/* Free a sign/verify slot mis-routed here so it doesn't leak. */
|
||||
misRoutedSign = FwFindSignSeq(ctx, seqHandle);
|
||||
if (misRoutedSign != NULL) FwFreeSignSeq(misRoutedSign);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
hashAlg = seq->hashAlg;
|
||||
|
|
@ -8384,8 +8437,10 @@ static TPM_RC FwCmd_EventSequenceComplete(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
FWTPM_FREE_BUF(dataBuf);
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
#ifndef FWTPM_NO_ECDH
|
||||
/* --- TPM2_ECDH_KeyGen (CC 0x0163) --- */
|
||||
static TPM_RC FwCmd_ECDH_KeyGen(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
||||
int cmdSize, TPM2_Packet* rsp, UINT16 cmdTag)
|
||||
|
|
@ -8680,6 +8735,7 @@ static TPM_RC FwCmd_ECDH_ZGen(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
FWTPM_FREE_VAR(inPoint);
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_ECDH */
|
||||
#endif /* HAVE_ECC */
|
||||
|
||||
/* --- TPM2_StartAuthSession (CC 0x0176) --- */
|
||||
|
|
@ -12392,6 +12448,7 @@ static TPM_RC FwCmd_DictionaryAttackParameters(FWTPM_CTX* ctx,
|
|||
#endif /* !FWTPM_NO_DA */
|
||||
|
||||
#ifndef NO_AES
|
||||
#ifndef FWTPM_NO_SYM_ENCRYPT
|
||||
/* --- TPM2_EncryptDecrypt (CC 0x0164) and EncryptDecrypt2 (CC 0x0187) ---
|
||||
* Symmetric encrypt/decrypt using a loaded SYMCIPHER key.
|
||||
* EncryptDecrypt: keyHandle, decrypt, mode, ivIn, inData
|
||||
|
|
@ -12684,6 +12741,7 @@ static TPM_RC FwCmd_EncryptDecrypt2(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
{
|
||||
return FwEncryptDecryptCore(ctx, cmd, cmdSize, rsp, cmdTag, 1);
|
||||
}
|
||||
#endif /* !FWTPM_NO_SYM_ENCRYPT */
|
||||
#endif /* !NO_AES */
|
||||
|
||||
/* ================================================================== */
|
||||
|
|
@ -13818,6 +13876,7 @@ static TPM_RC FwCmd_ActivateCredential(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
/* ECC Parameters */
|
||||
/* ================================================================== */
|
||||
|
||||
#ifndef FWTPM_NO_ECDH
|
||||
/* Convert hex string to binary. Returns byte count, or -1 on error. */
|
||||
static int FwHexToBin(const char* hex, byte* out, int outSz)
|
||||
{
|
||||
|
|
@ -14251,6 +14310,7 @@ static TPM_RC FwCmd_ZGen_2Phase(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
FWTPM_FREE_VAR(peerPub);
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_ECDH */
|
||||
#endif /* HAVE_ECC */
|
||||
|
||||
#ifdef WOLFTPM_FWTPM_TCG_TEST
|
||||
|
|
@ -16140,14 +16200,18 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = {
|
|||
{ TPM_CC_PCR_Allocate, FwCmd_PCR_Allocate, 1, 1, 0, 0 },
|
||||
{ TPM_CC_PCR_SetAuthPolicy, FwCmd_PCR_SetAuthPolicy, 1, 1, 0, FW_CMD_FLAG_ENC },
|
||||
{ TPM_CC_PCR_SetAuthValue, FwCmd_PCR_SetAuthValue, 1, 1, 0, FW_CMD_FLAG_ENC },
|
||||
#ifndef FWTPM_NO_CLOCK
|
||||
{ TPM_CC_ReadClock, FwCmd_ReadClock, 0, 0, 0, 0 },
|
||||
{ TPM_CC_ClockSet, FwCmd_ClockSet, 1, 1, 0, 0 },
|
||||
{ TPM_CC_ClockRateAdjust, FwCmd_ClockRateAdjust, 1, 1, 0, 0 },
|
||||
#endif /* !FWTPM_NO_CLOCK */
|
||||
/* --- Key management (always enabled, algorithm checks inside) --- */
|
||||
{ TPM_CC_CreatePrimary, FwCmd_CreatePrimary, 1, 1, 1, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_FlushContext, FwCmd_FlushContext, 1, 0, 0, 0 },
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
{ TPM_CC_ContextSave, FwCmd_ContextSave, 1, 0, 0, 0 },
|
||||
{ TPM_CC_ContextLoad, FwCmd_ContextLoad, 0, 0, 1, 0 },
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
{ TPM_CC_ReadPublic, FwCmd_ReadPublic, 1, 0, 0, FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_Clear, FwCmd_Clear, 1, 1, 0, 0 },
|
||||
{ TPM_CC_ClearControl, FwCmd_ClearControl, 1, 1, 0, 0 },
|
||||
|
|
@ -16167,19 +16231,32 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = {
|
|||
{ TPM_CC_RSA_Decrypt, FwCmd_RSA_Decrypt, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
#endif
|
||||
/* --- Hash/HMAC --- */
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
{ TPM_CC_Hash, FwCmd_Hash, 0, 0, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_HMAC, FwCmd_HMAC, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_HMAC_Start, FwCmd_HMAC_Start, 1, 1, 1, FW_CMD_FLAG_ENC },
|
||||
{ TPM_CC_HashSequenceStart, FwCmd_HashSequenceStart, 0, 0, 1, FW_CMD_FLAG_ENC },
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
/* SequenceUpdate is shared with the MLDSA sign/verify sequences - keep it
|
||||
* whenever MLDSA is built, even if the hash commands are gated. */
|
||||
#if !defined(FWTPM_NO_HASH_CMDS) || defined(WOLFTPM_MLDSA)
|
||||
{ TPM_CC_SequenceUpdate, FwCmd_SequenceUpdate, 1, 1, 0, FW_CMD_FLAG_ENC },
|
||||
#endif
|
||||
/* SequenceComplete only finalizes hash/HMAC sequences; MLDSA sequences use
|
||||
* SignSequenceComplete / VerifySequenceComplete, so it is never advertised
|
||||
* once the hash commands (and their sequence producers) are gated out. */
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
{ TPM_CC_SequenceComplete, FwCmd_SequenceComplete, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_EventSequenceComplete, FwCmd_EventSequenceComplete, 2, 2, 0, FW_CMD_FLAG_ENC },
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
/* --- ECC --- */
|
||||
#ifdef HAVE_ECC
|
||||
#ifndef FWTPM_NO_ECDH
|
||||
{ TPM_CC_ECDH_KeyGen, FwCmd_ECDH_KeyGen, 1, 0, 0, FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_ECDH_ZGen, FwCmd_ECDH_ZGen, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_EC_Ephemeral, FwCmd_EC_Ephemeral, 0, 0, 0, FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_ZGen_2Phase, FwCmd_ZGen_2Phase, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
#endif /* !FWTPM_NO_ECDH */
|
||||
#endif
|
||||
/* --- Sessions --- */
|
||||
{ TPM_CC_StartAuthSession, FwCmd_StartAuthSession, 2, 0, 1, 0 },
|
||||
|
|
@ -16214,14 +16291,18 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = {
|
|||
#endif /* !FWTPM_NO_POLICY */
|
||||
/* --- Key import/export --- */
|
||||
{ TPM_CC_LoadExternal, FwCmd_LoadExternal, 0, 0, 1, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
#ifndef FWTPM_NO_KEY_MIGRATION
|
||||
{ TPM_CC_Import, FwCmd_Import, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_Duplicate, FwCmd_Duplicate, 2, 1, 0, FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_Rewrap, FwCmd_Rewrap, 2, 1, 0, 0 },
|
||||
#endif /* !FWTPM_NO_KEY_MIGRATION */
|
||||
{ TPM_CC_CreateLoaded, FwCmd_CreateLoaded, 1, 1, 1, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
/* --- Symmetric --- */
|
||||
#ifndef NO_AES
|
||||
#ifndef FWTPM_NO_SYM_ENCRYPT
|
||||
{ TPM_CC_EncryptDecrypt, FwCmd_EncryptDecrypt, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
{ TPM_CC_EncryptDecrypt2, FwCmd_EncryptDecrypt2, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
|
||||
#endif /* !FWTPM_NO_SYM_ENCRYPT */
|
||||
#endif
|
||||
/* --- NV RAM --- */
|
||||
#ifndef FWTPM_NO_NV
|
||||
|
|
@ -16241,7 +16322,9 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = {
|
|||
#endif /* !FWTPM_NO_NV */
|
||||
/* --- ECC Parameters --- */
|
||||
#ifdef HAVE_ECC
|
||||
#ifndef FWTPM_NO_ECDH
|
||||
{ TPM_CC_ECC_Parameters, FwCmd_ECC_Parameters, 0, 0, 0, 0 },
|
||||
#endif /* !FWTPM_NO_ECDH */
|
||||
#endif
|
||||
/* --- Attestation --- */
|
||||
#ifndef FWTPM_NO_ATTESTATION
|
||||
|
|
|
|||
|
|
@ -2296,6 +2296,7 @@ int FwUnwrapPrivate(FWTPM_Object* parent,
|
|||
/* Context blob wrap/unwrap (ContextSave/ContextLoad) */
|
||||
/* ================================================================== */
|
||||
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
/* Fold a 64-bit value into an HMAC as big-endian, used to bind the context
|
||||
* sequence counter into the blob MAC for replay protection. */
|
||||
static int FwHmacUpdateU64(Hmac* hmac, UINT64 v)
|
||||
|
|
@ -2472,6 +2473,7 @@ int FwUnwrapContextBlob(FWTPM_CTX* ctx, UINT64 seq, byte ctxType,
|
|||
FWTPM_FREE_VAR(hmac);
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* ================================================================== */
|
||||
/* Seed encrypt/decrypt */
|
||||
|
|
|
|||
|
|
@ -743,6 +743,7 @@ static void test_fwtpm_getcap_commands_tpma(void)
|
|||
AssertIntEQ(TpmaCcRHandle(tpma), 0); /* no response handle */
|
||||
AssertIntEQ(tpma & (UINT32)CC_VEND, 0);
|
||||
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
/* HashSequenceStart returns a sequence handle: expect rHandle == 1. */
|
||||
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
|
||||
PutU32BE(gCmd + cmdSz, TPM_CAP_COMMANDS); cmdSz += 4;
|
||||
|
|
@ -758,6 +759,7 @@ static void test_fwtpm_getcap_commands_tpma(void)
|
|||
tpma = GetU32BE(gRsp + TPM2_HEADER_SIZE + 9);
|
||||
AssertIntEQ(TpmaCcToCmdCode(tpma), (UINT32)TPM_CC_HashSequenceStart);
|
||||
AssertIntEQ(TpmaCcRHandle(tpma), 1); /* returns a handle */
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
|
||||
/* StartAuthSession returns a session handle: expect rHandle == 1. */
|
||||
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
|
||||
|
|
@ -970,26 +972,287 @@ static void test_fwtpm_getcap_paging(void)
|
|||
fwtpm_pass("GetCapability paging convergence:", 0);
|
||||
}
|
||||
|
||||
/* ================================================================== */
|
||||
/* Command-group gates (FWTPM_NO_* macros) */
|
||||
/* ================================================================== */
|
||||
|
||||
/* Whether this build advertises each gated command group. Kept as 0/1 macros
|
||||
* so one table drives both the default build and every gated CI leg. */
|
||||
#ifdef FWTPM_NO_KEY_MIGRATION
|
||||
#define FW_GATED_KEY_MIGRATION 0
|
||||
#else
|
||||
#define FW_GATED_KEY_MIGRATION 1
|
||||
#endif
|
||||
#if defined(HAVE_ECC) && !defined(FWTPM_NO_ECDH)
|
||||
#define FW_GATED_ECDH 1
|
||||
#else
|
||||
#define FW_GATED_ECDH 0
|
||||
#endif
|
||||
#ifdef FWTPM_NO_HASH_CMDS
|
||||
#define FW_GATED_HASH_CMDS 0
|
||||
#else
|
||||
#define FW_GATED_HASH_CMDS 1
|
||||
#endif
|
||||
#ifdef FWTPM_NO_CONTEXT
|
||||
#define FW_GATED_CONTEXT 0
|
||||
#else
|
||||
#define FW_GATED_CONTEXT 1
|
||||
#endif
|
||||
#if !defined(NO_AES) && !defined(FWTPM_NO_SYM_ENCRYPT)
|
||||
#define FW_GATED_SYM_ENCRYPT 1
|
||||
#else
|
||||
#define FW_GATED_SYM_ENCRYPT 0
|
||||
#endif
|
||||
#ifdef FWTPM_NO_CLOCK
|
||||
#define FW_GATED_CLOCK 0
|
||||
#else
|
||||
#define FW_GATED_CLOCK 1
|
||||
#endif
|
||||
#ifdef FWTPM_NO_POLICY
|
||||
#define FW_GATED_POLICY 0
|
||||
#else
|
||||
#define FW_GATED_POLICY 1
|
||||
#endif
|
||||
#ifdef FWTPM_NO_NV
|
||||
#define FW_GATED_NV 0
|
||||
#else
|
||||
#define FW_GATED_NV 1
|
||||
#endif
|
||||
#ifdef FWTPM_NO_ATTESTATION
|
||||
#define FW_GATED_ATTESTATION 0
|
||||
#else
|
||||
#define FW_GATED_ATTESTATION 1
|
||||
#endif
|
||||
#ifdef FWTPM_NO_CREDENTIAL
|
||||
#define FW_GATED_CREDENTIAL 0
|
||||
#else
|
||||
#define FW_GATED_CREDENTIAL 1
|
||||
#endif
|
||||
#ifdef FWTPM_NO_DA
|
||||
#define FW_GATED_DA 0
|
||||
#else
|
||||
#define FW_GATED_DA 1
|
||||
#endif
|
||||
|
||||
/* SequenceUpdate is the one sequence command shared with the ML-DSA verify
|
||||
* sequences, so it survives FWTPM_NO_HASH_CMDS when ML-DSA is built.
|
||||
* SequenceComplete is NOT shared - ML-DSA finalizes through
|
||||
* SignSequenceComplete / VerifySequenceComplete - so it must disappear with
|
||||
* the rest of the hash commands. */
|
||||
#if !defined(FWTPM_NO_HASH_CMDS) || defined(WOLFTPM_MLDSA)
|
||||
#define FW_GATED_SEQ_UPDATE 1
|
||||
#else
|
||||
#define FW_GATED_SEQ_UPDATE 0
|
||||
#endif
|
||||
|
||||
typedef struct FwGateCase {
|
||||
UINT32 cc;
|
||||
int present; /* 1 = must be advertised and dispatchable */
|
||||
const char* name;
|
||||
} FwGateCase;
|
||||
|
||||
static const FwGateCase fwGateCases[] = {
|
||||
{ TPM_CC_Import, FW_GATED_KEY_MIGRATION, "Import" },
|
||||
{ TPM_CC_Duplicate, FW_GATED_KEY_MIGRATION, "Duplicate" },
|
||||
{ TPM_CC_Rewrap, FW_GATED_KEY_MIGRATION, "Rewrap" },
|
||||
{ TPM_CC_ECC_Parameters, FW_GATED_ECDH, "ECC_Parameters" },
|
||||
{ TPM_CC_EC_Ephemeral, FW_GATED_ECDH, "EC_Ephemeral" },
|
||||
{ TPM_CC_Hash, FW_GATED_HASH_CMDS, "Hash" },
|
||||
{ TPM_CC_HashSequenceStart, FW_GATED_HASH_CMDS, "HashSequenceStart" },
|
||||
{ TPM_CC_SequenceComplete, FW_GATED_HASH_CMDS, "SequenceComplete" },
|
||||
{ TPM_CC_EventSequenceComplete, FW_GATED_HASH_CMDS,
|
||||
"EventSequenceComplete" },
|
||||
{ TPM_CC_SequenceUpdate, FW_GATED_SEQ_UPDATE, "SequenceUpdate" },
|
||||
{ TPM_CC_ContextSave, FW_GATED_CONTEXT, "ContextSave" },
|
||||
{ TPM_CC_ContextLoad, FW_GATED_CONTEXT, "ContextLoad" },
|
||||
{ TPM_CC_EncryptDecrypt, FW_GATED_SYM_ENCRYPT, "EncryptDecrypt" },
|
||||
{ TPM_CC_EncryptDecrypt2, FW_GATED_SYM_ENCRYPT, "EncryptDecrypt2" },
|
||||
{ TPM_CC_ReadClock, FW_GATED_CLOCK, "ReadClock" },
|
||||
{ TPM_CC_ClockSet, FW_GATED_CLOCK, "ClockSet" },
|
||||
{ TPM_CC_ClockRateAdjust, FW_GATED_CLOCK, "ClockRateAdjust" },
|
||||
{ TPM_CC_PolicyPCR, FW_GATED_POLICY, "PolicyPCR" },
|
||||
{ TPM_CC_NV_ReadPublic, FW_GATED_NV, "NV_ReadPublic" },
|
||||
{ TPM_CC_Quote, FW_GATED_ATTESTATION, "Quote" },
|
||||
{ TPM_CC_MakeCredential, FW_GATED_CREDENTIAL, "MakeCredential" },
|
||||
{ TPM_CC_DictionaryAttackLockReset, FW_GATED_DA,
|
||||
"DictionaryAttackLockReset" },
|
||||
/* Never gated - guards against a table/#ifdef slip removing a core
|
||||
* command that every build must keep. */
|
||||
{ TPM_CC_GetRandom, 1, "GetRandom" },
|
||||
{ TPM_CC_PCR_Read, 1, "PCR_Read" },
|
||||
{ TPM_CC_FlushContext, 1, "FlushContext" },
|
||||
};
|
||||
|
||||
/* Enumerate TPM_CAP_COMMANDS in full and return the number of entries, writing
|
||||
* each advertised command code into ccOut (up to ccMax). Asserts the list was
|
||||
* not truncated so callers can treat the result as the complete set. */
|
||||
static UINT32 GetAdvertisedCommands(FWTPM_CTX* ctx, UINT32* ccOut, UINT32 ccMax)
|
||||
{
|
||||
int rc, rspSize, cmdSz;
|
||||
UINT32 count, i;
|
||||
|
||||
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
|
||||
PutU32BE(gCmd + cmdSz, TPM_CAP_COMMANDS); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 0); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 512); cmdSz += 4;
|
||||
PutU32BE(gCmd + 2, (UINT32)cmdSz);
|
||||
|
||||
rspSize = 0;
|
||||
rc = FWTPM_ProcessCommand(ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
/* moreData must be NO: the whole list has to fit for the count to be
|
||||
* comparable against TPM_PT_TOTAL_COMMANDS. */
|
||||
AssertIntEQ(gRsp[TPM2_HEADER_SIZE], 0);
|
||||
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 1), (int)TPM_CAP_COMMANDS);
|
||||
|
||||
count = GetU32BE(gRsp + TPM2_HEADER_SIZE + 5);
|
||||
AssertTrue(count <= ccMax);
|
||||
for (i = 0; i < count; i++) {
|
||||
ccOut[i] = TpmaCcToCmdCode(
|
||||
GetU32BE(gRsp + TPM2_HEADER_SIZE + 9 + (int)(i * 4)));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int CommandAdvertised(const UINT32* ccList, UINT32 count, UINT32 cc)
|
||||
{
|
||||
UINT32 i;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (ccList[i] == cc) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Every FWTPM_NO_* command group must disappear from both the dispatcher and
|
||||
* the TPM_CAP_COMMANDS advertisement together. A command that is advertised
|
||||
* but always fails (or dispatchable but unadvertised) is a gating bug. */
|
||||
static void test_fwtpm_command_gates(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
UINT32 advertised[512];
|
||||
UINT32 count;
|
||||
int rc, rspSize, cmdSz;
|
||||
unsigned int i;
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
|
||||
count = GetAdvertisedCommands(&ctx, advertised,
|
||||
(UINT32)(sizeof(advertised) / sizeof(advertised[0])));
|
||||
AssertIntGT(count, 0);
|
||||
|
||||
for (i = 0; i < sizeof(fwGateCases) / sizeof(fwGateCases[0]); i++) {
|
||||
const FwGateCase* g = &fwGateCases[i];
|
||||
|
||||
/* (a) Capability advertisement matches the build. */
|
||||
Assert(CommandAdvertised(advertised, count, g->cc) == g->present,
|
||||
("%s advertised == %d", g->name, g->present),
|
||||
("%s advertised == %d", g->name,
|
||||
CommandAdvertised(advertised, count, g->cc)));
|
||||
|
||||
/* (b) Dispatch agrees: a gated command is rejected with
|
||||
* TPM_RC_COMMAND_CODE; a live one fails for some other reason
|
||||
* (missing handles/params), never as an unknown command. */
|
||||
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, g->cc);
|
||||
PutU32BE(gCmd + 2, (UINT32)cmdSz);
|
||||
rspSize = 0;
|
||||
rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
||||
if (g->present) {
|
||||
AssertIntNE(GetRspRC(gRsp), TPM_RC_COMMAND_CODE);
|
||||
}
|
||||
else {
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_COMMAND_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("Command-group gates (dispatch + CAP_COMMANDS):", 0);
|
||||
}
|
||||
|
||||
/* TPM_PT_TOTAL_COMMANDS is derived from the same dispatch table as
|
||||
* TPM_CAP_COMMANDS, so gating a group must move both together. */
|
||||
static void test_fwtpm_total_commands(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
UINT32 advertised[512];
|
||||
UINT32 count, props, i, total = 0;
|
||||
int rc, rspSize, cmdSz, found = 0;
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
|
||||
count = GetAdvertisedCommands(&ctx, advertised,
|
||||
(UINT32)(sizeof(advertised) / sizeof(advertised[0])));
|
||||
|
||||
/* GetCapability(TPM_CAP_TPM_PROPERTIES, first=TPM_PT_TOTAL_COMMANDS) */
|
||||
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
|
||||
PutU32BE(gCmd + cmdSz, TPM_CAP_TPM_PROPERTIES); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_PT_TOTAL_COMMANDS); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 1); cmdSz += 4;
|
||||
PutU32BE(gCmd + 2, (UINT32)cmdSz);
|
||||
|
||||
rspSize = 0;
|
||||
rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 1),
|
||||
(int)TPM_CAP_TPM_PROPERTIES);
|
||||
|
||||
/* TPMS_TAGGED_PROPERTY list: count, then (property, value) pairs. */
|
||||
props = GetU32BE(gRsp + TPM2_HEADER_SIZE + 5);
|
||||
AssertIntGT(props, 0);
|
||||
for (i = 0; i < props; i++) {
|
||||
int off = TPM2_HEADER_SIZE + 9 + (int)(i * 8);
|
||||
if (GetU32BE(gRsp + off) == (UINT32)TPM_PT_TOTAL_COMMANDS) {
|
||||
total = GetU32BE(gRsp + off + 4);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
AssertIntEQ(found, 1);
|
||||
AssertIntEQ(total, count);
|
||||
|
||||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("TPM_PT_TOTAL_COMMANDS matches CAP_COMMANDS:", 0);
|
||||
}
|
||||
|
||||
/* ================================================================== */
|
||||
/* 6. PCR Operations */
|
||||
/* ================================================================== */
|
||||
|
||||
/* Build PCR_Read command for SHA-256 bank */
|
||||
/* A PCR that is both readable and extendable from locality 0 in any build:
|
||||
* PCR 16 (the debug PCR) when all 24 standard PCRs are implemented, otherwise
|
||||
* the highest implemented PCR (0-15 also accept any locality for extend). */
|
||||
#if IMPLEMENTATION_PCR >= 24
|
||||
#define FW_TEST_PCR 16
|
||||
#else
|
||||
#define FW_TEST_PCR (IMPLEMENTATION_PCR - 1)
|
||||
#endif
|
||||
|
||||
/* Build PCR_Read command for SHA-256 bank. The selection bitmap is sized from
|
||||
* PCR_SELECT_MAX rather than a fixed 3 so the command stays well-formed in a
|
||||
* build that implements fewer than 24 PCRs. */
|
||||
static int BuildPcrReadCmd(byte* buf, UINT32 pcrIndex)
|
||||
{
|
||||
int pos;
|
||||
int pos, i;
|
||||
pos = BuildCmdHeader(buf, TPM_ST_NO_SESSIONS, 0, TPM_CC_PCR_Read);
|
||||
/* TPML_PCR_SELECTION: count=1 */
|
||||
PutU32BE(buf + pos, 1); pos += 4;
|
||||
/* TPMS_PCR_SELECTION: hash=SHA256, sizeofSelect=3, pcrSelect[3] */
|
||||
/* TPMS_PCR_SELECTION: hash=SHA256, sizeofSelect, pcrSelect[] */
|
||||
PutU16BE(buf + pos, TPM_ALG_SHA256); pos += 2;
|
||||
buf[pos++] = 3; /* sizeofSelect */
|
||||
buf[pos++] = (byte)PCR_SELECT_MAX; /* sizeofSelect */
|
||||
/* Set bit for pcrIndex in the bitmap */
|
||||
buf[pos] = 0; buf[pos+1] = 0; buf[pos+2] = 0;
|
||||
if (pcrIndex < 24) {
|
||||
for (i = 0; i < PCR_SELECT_MAX; i++) {
|
||||
buf[pos + i] = 0;
|
||||
}
|
||||
if (pcrIndex < (UINT32)IMPLEMENTATION_PCR) {
|
||||
buf[pos + (pcrIndex / 8)] = (byte)(1 << (pcrIndex % 8));
|
||||
}
|
||||
pos += 3;
|
||||
pos += PCR_SELECT_MAX;
|
||||
PutU32BE(buf + 2, (UINT32)pos);
|
||||
return pos;
|
||||
}
|
||||
|
|
@ -1027,7 +1290,7 @@ static void test_fwtpm_pcr_extend_and_read(void)
|
|||
AssertIntEQ(rc, 0);
|
||||
|
||||
/* Read PCR 16 (resettable) before extend */
|
||||
cmdSz = BuildPcrReadCmd(gCmd, 16);
|
||||
cmdSz = BuildPcrReadCmd(gCmd, FW_TEST_PCR);
|
||||
rspSize = 0;
|
||||
rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
||||
|
|
@ -1050,8 +1313,8 @@ static void test_fwtpm_pcr_extend_and_read(void)
|
|||
PutU16BE(gCmd + cmdSz, TPM_ST_SESSIONS); cmdSz += 2;
|
||||
PutU32BE(gCmd + cmdSz, 0); cmdSz += 4; /* size placeholder */
|
||||
PutU32BE(gCmd + cmdSz, TPM_CC_PCR_Extend); cmdSz += 4;
|
||||
/* pcrHandle = PCR 16 */
|
||||
PutU32BE(gCmd + cmdSz, 16); cmdSz += 4;
|
||||
/* pcrHandle */
|
||||
PutU32BE(gCmd + cmdSz, FW_TEST_PCR); cmdSz += 4;
|
||||
/* Auth area: size(4) + sessionHandle(4) + nonce(2) + attrs(1) + hmac(2) */
|
||||
PutU32BE(gCmd + cmdSz, 9); cmdSz += 4; /* authAreaSize */
|
||||
PutU32BE(gCmd + cmdSz, TPM_RS_PW); cmdSz += 4; /* password session */
|
||||
|
|
@ -1072,7 +1335,7 @@ static void test_fwtpm_pcr_extend_and_read(void)
|
|||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
/* Read PCR 16 again - should be different from before */
|
||||
cmdSz = BuildPcrReadCmd(gCmd, 16);
|
||||
cmdSz = BuildPcrReadCmd(gCmd, FW_TEST_PCR);
|
||||
rspSize = 0;
|
||||
rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
||||
|
|
@ -1114,7 +1377,7 @@ static void test_fwtpm_pw_session_continue_set(void)
|
|||
PutU16BE(gCmd + cmdSz, TPM_ST_SESSIONS); cmdSz += 2;
|
||||
PutU32BE(gCmd + cmdSz, 0); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_CC_PCR_Extend); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 16); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, FW_TEST_PCR); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 9); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_RS_PW); cmdSz += 4;
|
||||
PutU16BE(gCmd + cmdSz, 0); cmdSz += 2;
|
||||
|
|
@ -1141,6 +1404,7 @@ static void test_fwtpm_pw_session_continue_set(void)
|
|||
}
|
||||
|
||||
/* PCR_Event into DRTM PCR 17 must require locality 4 (Part 1 Sec.11.4.6). */
|
||||
#if IMPLEMENTATION_PCR >= 24
|
||||
static void test_fwtpm_pcr_event_drtm_locality_enforced(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -1177,6 +1441,7 @@ static void test_fwtpm_pcr_event_drtm_locality_enforced(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tPCR_Event DRTM locality enforced:\tPassed\n");
|
||||
}
|
||||
#endif /* IMPLEMENTATION_PCR >= 24 */
|
||||
|
||||
/* Per TPM 2.0 Part 3 Sec.22.3, PCR_Extend takes Auth Role USER on the
|
||||
* PCR handle. When PCR_SetAuthValue has installed a non-empty
|
||||
|
|
@ -1199,7 +1464,7 @@ static void test_fwtpm_pcr_extend_empty_pw_rejected_after_setauth(void)
|
|||
PutU16BE(gCmd + cmdSz, TPM_ST_SESSIONS); cmdSz += 2;
|
||||
PutU32BE(gCmd + cmdSz, 0); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_CC_PCR_SetAuthValue); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 16); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, FW_TEST_PCR); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 9); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_RS_PW); cmdSz += 4;
|
||||
PutU16BE(gCmd + cmdSz, 0); cmdSz += 2;
|
||||
|
|
@ -1219,7 +1484,7 @@ static void test_fwtpm_pcr_extend_empty_pw_rejected_after_setauth(void)
|
|||
PutU16BE(gCmd + cmdSz, TPM_ST_SESSIONS); cmdSz += 2;
|
||||
PutU32BE(gCmd + cmdSz, 0); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_CC_PCR_Extend); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 16); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, FW_TEST_PCR); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 9); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_RS_PW); cmdSz += 4;
|
||||
PutU16BE(gCmd + cmdSz, 0); cmdSz += 2;
|
||||
|
|
@ -1240,7 +1505,7 @@ static void test_fwtpm_pcr_extend_empty_pw_rejected_after_setauth(void)
|
|||
PutU16BE(gCmd + cmdSz, TPM_ST_SESSIONS); cmdSz += 2;
|
||||
PutU32BE(gCmd + cmdSz, 0); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_CC_PCR_SetAuthValue); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 16); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, FW_TEST_PCR); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 9 + pcrAuthSz); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_RS_PW); cmdSz += 4;
|
||||
PutU16BE(gCmd + cmdSz, 0); cmdSz += 2;
|
||||
|
|
@ -1262,6 +1527,7 @@ static void test_fwtpm_pcr_extend_empty_pw_rejected_after_setauth(void)
|
|||
/* 7. ReadClock */
|
||||
/* ================================================================== */
|
||||
|
||||
#ifndef FWTPM_NO_CLOCK
|
||||
static void test_fwtpm_readclock(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -1284,6 +1550,7 @@ static void test_fwtpm_readclock(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("ReadClock:", 0);
|
||||
}
|
||||
#endif /* !FWTPM_NO_CLOCK */
|
||||
|
||||
/* ================================================================== */
|
||||
/* 8. CreatePrimary (RSA and ECC) */
|
||||
|
|
@ -4332,6 +4599,7 @@ static void test_fwtpm_sign_x509sign_returns_attributes(void)
|
|||
/* End-to-end positive: TPM2_Hash produces a real HASHCHECK ticket that
|
||||
* SignDigest must accept on a restricted key. Confirms ticket-validation
|
||||
* actually verifies the HMAC (not just rejects everything). */
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
static void test_fwtpm_signdigest_restricted_valid_ticket_succeeds(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -4406,6 +4674,7 @@ static void test_fwtpm_signdigest_restricted_valid_ticket_succeeds(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("SignDigest restricted+valid ticket (success):", 1);
|
||||
}
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
|
||||
/* F-4: VerifyDigestSignature rejects sigHashAlg != key's hashAlg with
|
||||
* TPM_RC_SCHEME per Part 3 Sec.20.4.1. Key is Hash-ML-DSA-65/SHA-256; wire
|
||||
|
|
@ -5986,6 +6255,7 @@ test_fwtpm_signseqcomplete_hash_mldsa_genvalue_via_update_returns_value(void)
|
|||
* the slot allocated lets a buggy or hostile client exhaust
|
||||
* FWTPM_MAX_SIGN_SEQ slots by repeatedly issuing Start + wrong-key
|
||||
* Complete, denying service to legitimate Sign sequences (CWE-772). */
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
static void test_fwtpm_signseqcomplete_wrong_key_frees_slot(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -6071,6 +6341,7 @@ static void test_fwtpm_signseqcomplete_wrong_key_frees_slot(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("SignSeqComplete wrong key frees slot:", 1);
|
||||
}
|
||||
#endif /* WOLFTPM_MLDSA */
|
||||
|
||||
#ifdef WOLFTPM_V185
|
||||
/* Extended CreatePrimary builder that overrides the default MLDSA/MLKEM
|
||||
|
|
@ -6447,6 +6718,7 @@ static void test_fwtpm_mlkem1024_maxbuf(void)
|
|||
* FWTPM_CTX holds FWTPM_MAX_SIGN_SEQ (4) slots for sign+verify sequences.
|
||||
* Starting more than that must return TPM_RC_OBJECT_MEMORY from
|
||||
* FwAllocSignSeq per Part 3 Sec.17.5. */
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
static void test_fwtpm_signseq_slot_exhaustion(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -6491,6 +6763,7 @@ static void test_fwtpm_signseq_slot_exhaustion(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("SignSeq slot exhaustion:", 1);
|
||||
}
|
||||
#endif /* WOLFTPM_MLDSA */
|
||||
|
||||
/* ---- Long-message accumulation boundary for Pure-MLDSA verify seq ----
|
||||
* msgBuf is FWTPM_MAX_DATA_BUF (1024) bytes. Accumulating across
|
||||
|
|
@ -6899,6 +7172,7 @@ static void test_fwtpm_testparms_mldsa_supported_returns_success(void)
|
|||
/* 9. Hash Sequence */
|
||||
/* ================================================================== */
|
||||
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
static void test_fwtpm_hash(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -6942,6 +7216,7 @@ static void test_fwtpm_hash(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("Hash(SHA256, \"abc\"):", 0);
|
||||
}
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
|
||||
/* ================================================================== */
|
||||
/* 10. NULL pointer checks */
|
||||
|
|
@ -8103,6 +8378,7 @@ static void test_fwtpm_sign_ecdaa_scheme(void)
|
|||
/* ECDH key-agreement commands must reject a key without TPMA_OBJECT_decrypt
|
||||
* per Part 3 Sec.14.3.3/14.7/21.3. A sign-only AIK would otherwise act as a
|
||||
* CDH oracle over its private scalar. */
|
||||
#ifndef FWTPM_NO_ECDH
|
||||
static void test_fwtpm_ecdh_keygen_signkey_returns_attributes(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -8124,7 +8400,9 @@ static void test_fwtpm_ecdh_keygen_signkey_returns_attributes(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tECDH_KeyGen(sign key) rejected:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_ECDH */
|
||||
|
||||
#ifndef FWTPM_NO_ECDH
|
||||
static void test_fwtpm_ecdh_zgen_signkey_returns_attributes(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -8154,7 +8432,9 @@ static void test_fwtpm_ecdh_zgen_signkey_returns_attributes(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tECDH_ZGen(sign key) rejected:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_ECDH */
|
||||
|
||||
#ifndef FWTPM_NO_ECDH
|
||||
static void test_fwtpm_zgen_2phase_signkey_returns_attributes(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -8187,6 +8467,7 @@ static void test_fwtpm_zgen_2phase_signkey_returns_attributes(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tZGen_2Phase(sign key) rejected:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_ECDH */
|
||||
|
||||
/* Quote requires a restricted signing key per Part 3 Sec.18.4. Build the
|
||||
* command once and run it against keys that violate each requirement. */
|
||||
|
|
@ -8518,6 +8799,7 @@ static void test_fwtpm_test_parms(void)
|
|||
memset(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
|
||||
#ifndef NO_RSA
|
||||
/* TestParms: RSA-2048. TPMS_RSA_PARMS is symmetric, scheme, keyBits,
|
||||
* exponent - in that order. */
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms);
|
||||
|
|
@ -8552,6 +8834,21 @@ static void test_fwtpm_test_parms(void)
|
|||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SCHEME);
|
||||
#else
|
||||
/* An ECC-only build drops TPM_ALG_RSA from the TPMI_ALG_PUBLIC selectors
|
||||
* TestParms accepts, so it must report the type as unsupported rather than
|
||||
* silently validating parameters it cannot honor. */
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms);
|
||||
PutU16BE(gCmd + pos, TPM_ALG_RSA); pos += 2;
|
||||
PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* symmetric */
|
||||
PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* scheme */
|
||||
PutU16BE(gCmd + pos, 2048); pos += 2; /* keyBits */
|
||||
PutU32BE(gCmd + pos, 0); pos += 4; /* exponent */
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_TYPE);
|
||||
#endif /* !NO_RSA */
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
/* An unsupported ECC curve must be rejected. */
|
||||
|
|
@ -8651,7 +8948,11 @@ static void test_fwtpm_test_parms(void)
|
|||
#endif /* !NO_AES */
|
||||
|
||||
FWTPM_Cleanup(&ctx);
|
||||
#ifndef NO_RSA
|
||||
fwtpm_pass("TestParms(RSA-2048):", 0);
|
||||
#else
|
||||
fwtpm_pass("TestParms(ECC-only):", 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void test_fwtpm_incremental_selftest(void)
|
||||
|
|
@ -8775,6 +9076,51 @@ static TPM_RC SendPcrExtendLoc(FWTPM_CTX* ctx, int pcrIndex, int locality)
|
|||
return GetRspRC(gRsp);
|
||||
}
|
||||
|
||||
/* The PCR locality tables are sized to the 24 standard PCRs while the
|
||||
* implemented PCR count is IMPLEMENTATION_PCR, so a build with fewer PCRs has
|
||||
* two distinct boundaries. Exercise the highest implemented PCR (must work)
|
||||
* and the first unavailable one (must be rejected, never silently aliased). */
|
||||
static void test_fwtpm_pcr_bounds(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
int rspSize, cmdSz;
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
|
||||
/* TPM_PT_PCR_COUNT must report the implemented count. */
|
||||
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
|
||||
PutU32BE(gCmd + cmdSz, TPM_CAP_TPM_PROPERTIES); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, TPM_PT_PCR_COUNT); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, 1); cmdSz += 4;
|
||||
PutU32BE(gCmd + 2, (UINT32)cmdSz);
|
||||
rspSize = 0;
|
||||
AssertIntEQ(FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0),
|
||||
TPM_RC_SUCCESS);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 9), TPM_PT_PCR_COUNT);
|
||||
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 13), IMPLEMENTATION_PCR);
|
||||
|
||||
/* Highest implemented PCR: readable, and extendable from locality 0
|
||||
* (PCR 0-16 and 23 accept any locality; a reduced-PCR build tops out
|
||||
* inside that range). */
|
||||
cmdSz = BuildPcrReadCmd(gCmd, (UINT32)(IMPLEMENTATION_PCR - 1));
|
||||
rspSize = 0;
|
||||
AssertIntEQ(FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0),
|
||||
TPM_RC_SUCCESS);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
AssertIntEQ(SendPcrExtendLoc(&ctx, IMPLEMENTATION_PCR - 1, 0),
|
||||
TPM_RC_SUCCESS);
|
||||
|
||||
/* First unavailable PCR: rejected, not wrapped onto a valid index. */
|
||||
AssertIntEQ(SendPcrExtendLoc(&ctx, IMPLEMENTATION_PCR, 0), TPM_RC_VALUE);
|
||||
AssertIntEQ(SendPcrResetLoc(&ctx, IMPLEMENTATION_PCR, 0), TPM_RC_VALUE);
|
||||
|
||||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("PCR bounds (highest implemented / first absent):", 0);
|
||||
}
|
||||
|
||||
#if IMPLEMENTATION_PCR >= 24
|
||||
static void test_fwtpm_pcr_reset(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -8788,6 +9134,7 @@ static void test_fwtpm_pcr_reset(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("PCR_Reset(16):", 0);
|
||||
}
|
||||
#endif /* IMPLEMENTATION_PCR >= 24 */
|
||||
|
||||
/* Per the TCG PC Client TPM Profile per-PCR reset locality map:
|
||||
* PCR 16, 23 reset from localities 0-3 (not 4);
|
||||
|
|
@ -8795,6 +9142,7 @@ static void test_fwtpm_pcr_reset(void)
|
|||
* PCR 20-22 reset from localities 2-4;
|
||||
* PCR 0-15 are never user-resettable.
|
||||
* Verify both the reject (wrong locality) and allow (correct locality) paths. */
|
||||
#if IMPLEMENTATION_PCR >= 24
|
||||
static void test_fwtpm_pcr_reset_locality_enforced(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -8827,9 +9175,11 @@ static void test_fwtpm_pcr_reset_locality_enforced(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("PCR_Reset locality map enforced:", 0);
|
||||
}
|
||||
#endif /* IMPLEMENTATION_PCR >= 24 */
|
||||
|
||||
/* Per-PCR extend locality map: 0-16,23 any; 17,18 loc2-4; 19 loc2-3;
|
||||
* 20 loc1-3; 21,22 loc2. Verify reject and allow paths. */
|
||||
#if IMPLEMENTATION_PCR >= 24
|
||||
static void test_fwtpm_pcr_extend_locality_enforced(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -8861,7 +9211,9 @@ static void test_fwtpm_pcr_extend_locality_enforced(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("PCR_Extend locality map enforced:", 0);
|
||||
}
|
||||
#endif /* IMPLEMENTATION_PCR >= 24 */
|
||||
|
||||
#if IMPLEMENTATION_PCR >= 24
|
||||
/* TPM_CAP_PCR_PROPERTIES must report a well-formed TPML_TAGGED_PCR_PROPERTY
|
||||
* whose RESET_Lx / EXTEND_Lx / DRTM_RESET bitmaps match the enforcement table. */
|
||||
static int PcrSelHas(const byte* sel, int selSz, int pcr)
|
||||
|
|
@ -8959,7 +9311,9 @@ static void test_fwtpm_pcr_properties_capability(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("PCR_PROPERTIES capability map:", 0);
|
||||
}
|
||||
#endif /* IMPLEMENTATION_PCR >= 24 */
|
||||
|
||||
#if IMPLEMENTATION_PCR >= 24
|
||||
static void test_fwtpm_pcr_event(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -8984,6 +9338,7 @@ static void test_fwtpm_pcr_event(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("PCR_Event(16):", 0);
|
||||
}
|
||||
#endif /* IMPLEMENTATION_PCR >= 24 */
|
||||
|
||||
static void test_fwtpm_hierarchy_change_auth(void)
|
||||
{
|
||||
|
|
@ -9159,6 +9514,7 @@ static void test_fwtpm_sessions_trial_in_auth_slot_rejected(void)
|
|||
|
||||
/* A command with two @auth handles that supplies only one auth entry must be
|
||||
* rejected with TPM_RC_AUTH_MISSING. */
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
static void test_fwtpm_sessions_short_authcount_rejected(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -9182,6 +9538,7 @@ static void test_fwtpm_sessions_short_authcount_rejected(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("SESSIONS two-auth one-entry (AUTH_MISSING):", 0);
|
||||
}
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
|
||||
/* A SESSIONS command that ends before its authorizationSize field is malformed
|
||||
* and must be reported as TPM_RC_COMMAND_SIZE, not an auth error. */
|
||||
|
|
@ -10148,6 +10505,7 @@ static void test_fwtpm_loadexternal_symcipher_bad_keysize_rejected(void)
|
|||
/* Rewrap must re-encrypt under a storage parent. A TPM_RH_NULL newParent
|
||||
* would serialize the unwrapped TPMT_SENSITIVE in the clear, so it must be
|
||||
* rejected per Part 3 Sec.23.4.2. */
|
||||
#ifndef FWTPM_NO_KEY_MIGRATION
|
||||
static void test_fwtpm_rewrap_null_newparent_rejected(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10176,11 +10534,13 @@ static void test_fwtpm_rewrap_null_newparent_rejected(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tRewrap(NULL newParent) rejected:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_KEY_MIGRATION */
|
||||
|
||||
/* ================================================================== */
|
||||
/* Group D: Hash/HMAC Sequences */
|
||||
/* ================================================================== */
|
||||
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
static void test_fwtpm_hash_sequence(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10231,8 +10591,9 @@ static void test_fwtpm_hash_sequence(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("HashSequence (Start/Upd/Comp):", 0);
|
||||
}
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
#if defined(HAVE_ECC) && !defined(FWTPM_NO_ECDH)
|
||||
static void test_fwtpm_ecc_parameters(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10254,6 +10615,7 @@ static void test_fwtpm_ecc_parameters(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
static void test_fwtpm_context_save(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10281,9 +10643,11 @@ static void test_fwtpm_context_save(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("ContextSave:", 0);
|
||||
}
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* A saved context must load at most once; replaying the same blob is
|
||||
* rejected to prevent resurrecting a satisfied policy session. */
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
static void test_fwtpm_context_load_replay_rejected(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10356,9 +10720,11 @@ static void test_fwtpm_context_load_replay_rejected(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tContextLoad object reload + session replay:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* A hand-forged object context blob (unauthenticated plaintext format) must be
|
||||
* rejected on load with TPM_RC_INTEGRITY. */
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
static void test_fwtpm_contextload_forged_object_blob_rejected(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10398,10 +10764,12 @@ static void test_fwtpm_contextload_forged_object_blob_rejected(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tContextLoad forged object blob rejected:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* A ContextLoad whose blobSz claims more bytes than the command carries must
|
||||
* be rejected before any blob is parsed, so a short command cannot leave the
|
||||
* wrapped-blob buffer partly uninitialized. */
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
static void test_fwtpm_contextload_short_blob_rejected(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10438,9 +10806,11 @@ static void test_fwtpm_contextload_short_blob_rejected(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tContextLoad short blob rejected:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* A context saved by the TPM must load back successfully and yield a usable
|
||||
* handle. Guards that the object blob wrapping is self-consistent. */
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
static void test_fwtpm_context_object_roundtrip(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10485,9 +10855,11 @@ static void test_fwtpm_context_object_roundtrip(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tContextLoad object roundtrip:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* The context type is bound into the blob MAC, so a blob wrapped for one
|
||||
* domain must not verify when unwrapped as the other. */
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
static void test_fwtpm_context_blob_domain_separation(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10515,9 +10887,11 @@ static void test_fwtpm_context_blob_domain_separation(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tContext blob domain separation:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* When the command client changes, transient objects must be flushed so a
|
||||
* replacement client cannot enumerate and use the previous client's handles. */
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
static void test_fwtpm_reset_command_client_flushes_transient(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10551,6 +10925,7 @@ static void test_fwtpm_reset_command_client_flushes_transient(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tResetCommandClient flushes transient:\tPassed\n");
|
||||
}
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
static void test_fwtpm_evict_control(void)
|
||||
{
|
||||
|
|
@ -10746,6 +11121,7 @@ static void test_fwtpm_evict_control_persistent_object_rejected(void)
|
|||
fwtpm_pass("EvictControl persistent object reject (HANDLE):", 0);
|
||||
}
|
||||
|
||||
#ifndef FWTPM_NO_CLOCK
|
||||
static void test_fwtpm_clock_set(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -10784,6 +11160,7 @@ static void test_fwtpm_clock_set(void)
|
|||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("ClockSet/ClockRateAdjust:", 0);
|
||||
}
|
||||
#endif /* !FWTPM_NO_CLOCK */
|
||||
|
||||
/* ================================================================== */
|
||||
/* HAL registration tests */
|
||||
|
|
@ -11404,22 +11781,33 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
test_fwtpm_getcap_properties();
|
||||
test_fwtpm_getcap_pcrs();
|
||||
test_fwtpm_getcap_paging();
|
||||
test_fwtpm_total_commands();
|
||||
|
||||
/* Command-group gates (FWTPM_NO_* macros) */
|
||||
test_fwtpm_command_gates();
|
||||
|
||||
/* PCR operations */
|
||||
test_fwtpm_pcr_read();
|
||||
test_fwtpm_pcr_bounds();
|
||||
test_fwtpm_pcr_extend_and_read();
|
||||
test_fwtpm_pw_session_continue_set();
|
||||
#if IMPLEMENTATION_PCR >= 24
|
||||
test_fwtpm_pcr_event_drtm_locality_enforced();
|
||||
#endif /* IMPLEMENTATION_PCR >= 24 */
|
||||
test_fwtpm_pcr_extend_empty_pw_rejected_after_setauth();
|
||||
#if IMPLEMENTATION_PCR >= 24
|
||||
test_fwtpm_pcr_reset();
|
||||
test_fwtpm_pcr_reset_locality_enforced();
|
||||
test_fwtpm_pcr_extend_locality_enforced();
|
||||
test_fwtpm_pcr_properties_capability();
|
||||
test_fwtpm_pcr_event();
|
||||
#endif /* IMPLEMENTATION_PCR >= 24 */
|
||||
|
||||
/* Clock */
|
||||
#ifndef FWTPM_NO_CLOCK
|
||||
test_fwtpm_readclock();
|
||||
test_fwtpm_clock_set();
|
||||
#endif /* !FWTPM_NO_CLOCK */
|
||||
|
||||
/* HAL registration (clock + NV). The NV HAL test uses a mock
|
||||
* backend and must not leave the default file in a half-written
|
||||
|
|
@ -11484,7 +11872,9 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
test_fwtpm_sequenceupdate_neg();
|
||||
test_fwtpm_signdigest_restricted_null_ticket_returns_ticket();
|
||||
test_fwtpm_signdigest_x509sign_returns_attributes();
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
test_fwtpm_signdigest_restricted_valid_ticket_succeeds();
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
test_fwtpm_verifydigest_sig_hashalg_mismatch_returns_scheme();
|
||||
test_fwtpm_create_primary_mldsa_extmu_returns_ext_mu();
|
||||
test_fwtpm_testparms_mldsa_extmu_returns_ext_mu();
|
||||
|
|
@ -11510,9 +11900,13 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
test_fwtpm_verifyseqcomplete_hash_mldsa_ticket_tag_digest();
|
||||
test_fwtpm_verifyseqcomplete_hash_mldsa_ticket_binds_message();
|
||||
test_fwtpm_signseqcomplete_hash_mldsa_genvalue_via_update_returns_value();
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
test_fwtpm_signseqcomplete_wrong_key_frees_slot();
|
||||
#endif /* WOLFTPM_MLDSA */
|
||||
test_fwtpm_pqc_nv_persistence();
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
test_fwtpm_signseq_slot_exhaustion();
|
||||
#endif /* WOLFTPM_MLDSA */
|
||||
test_fwtpm_signseq_longmsg_boundary();
|
||||
test_fwtpm_mldsa87_maxbuf();
|
||||
test_fwtpm_response_buffer_capacity();
|
||||
|
|
@ -11521,11 +11915,14 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
#endif
|
||||
test_fwtpm_read_public();
|
||||
test_fwtpm_loadexternal_symcipher_bad_keysize_rejected();
|
||||
#ifndef FWTPM_NO_KEY_MIGRATION
|
||||
test_fwtpm_rewrap_null_newparent_rejected();
|
||||
#endif /* !FWTPM_NO_KEY_MIGRATION */
|
||||
test_fwtpm_evict_control();
|
||||
test_fwtpm_evict_control_cross_hierarchy_rejected();
|
||||
test_fwtpm_evict_control_bad_persistent_handle_rejected();
|
||||
test_fwtpm_evict_control_persistent_object_rejected();
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
test_fwtpm_context_save();
|
||||
test_fwtpm_context_load_replay_rejected();
|
||||
test_fwtpm_contextload_forged_object_blob_rejected();
|
||||
|
|
@ -11533,11 +11930,14 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
test_fwtpm_context_object_roundtrip();
|
||||
test_fwtpm_context_blob_domain_separation();
|
||||
test_fwtpm_reset_command_client_flushes_transient();
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* Crypto */
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
test_fwtpm_hash();
|
||||
test_fwtpm_hash_sequence();
|
||||
#ifdef HAVE_ECC
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
#if defined(HAVE_ECC) && !defined(FWTPM_NO_ECDH)
|
||||
test_fwtpm_ecc_parameters();
|
||||
#endif
|
||||
|
||||
|
|
@ -11610,9 +12010,11 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
test_fwtpm_quote_ecdaa_scheme();
|
||||
test_fwtpm_sign_ecdaa_scheme();
|
||||
test_fwtpm_certify_creation_ecdaa_scheme();
|
||||
#ifndef FWTPM_NO_ECDH
|
||||
test_fwtpm_ecdh_keygen_signkey_returns_attributes();
|
||||
test_fwtpm_ecdh_zgen_signkey_returns_attributes();
|
||||
test_fwtpm_zgen_2phase_signkey_returns_attributes();
|
||||
#endif /* !FWTPM_NO_ECDH */
|
||||
test_fwtpm_quote_decrypt_key_returns_key();
|
||||
test_fwtpm_quote_unrestricted_sign_returns_attributes();
|
||||
test_fwtpm_sign_scheme_downgrade_rejected();
|
||||
|
|
@ -11634,7 +12036,9 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
test_fwtpm_clear_no_sessions_returns_auth_missing();
|
||||
test_fwtpm_sessions_empty_autharea_rejected();
|
||||
test_fwtpm_sessions_trial_in_auth_slot_rejected();
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
test_fwtpm_sessions_short_authcount_rejected();
|
||||
#endif /* !FWTPM_NO_HASH_CMDS */
|
||||
test_fwtpm_sessions_missing_authsize_command_size();
|
||||
test_fwtpm_clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -440,7 +440,10 @@ static void test_TPM2_PCRSel(void)
|
|||
{
|
||||
int rc = 0;
|
||||
TPML_PCR_SELECTION pcr;
|
||||
byte pcrArray[PCR_SELECT_MAX];
|
||||
/* This array holds PCR indexes, not a select bitmap, so it is sized by the
|
||||
* number of indexes the test uses. PCR_SELECT_MAX is a byte count and is
|
||||
* only 1 in a reduced-PCR build (IMPLEMENTATION_PCR <= 8). */
|
||||
byte pcrArray[3];
|
||||
word32 pcrArraySz;
|
||||
|
||||
XMEMSET(&pcr, 0, sizeof(pcr));
|
||||
|
|
|
|||
|
|
@ -110,6 +110,40 @@
|
|||
#define FWTPM_MAX_RANDOM_BYTES 48
|
||||
#endif
|
||||
|
||||
/* Command feature-group toggles - opt-in macros that compile out fwTPM command
|
||||
* groups a minimal build does not need. All default OFF (the full command set is
|
||||
* built). Removing a group drops its handlers AND its TPM2_GetCapability
|
||||
* advertisement (the command list is derived from the dispatch table).
|
||||
* FWTPM_NO_POLICY - policy session commands (TPM2_Policy*)
|
||||
* FWTPM_NO_ATTESTATION - Quote/Certify/CertifyCreation/GetTime/NV_Certify
|
||||
* FWTPM_NO_CREDENTIAL - MakeCredential/ActivateCredential
|
||||
* FWTPM_NO_DA - dictionary-attack lockout protection (see below)
|
||||
* FWTPM_NO_PARAM_ENC - command/response parameter encryption
|
||||
* FWTPM_NO_NV - all NV_* commands
|
||||
* FWTPM_NO_KEY_MIGRATION - Import/Duplicate/Rewrap
|
||||
* FWTPM_NO_ECDH - ECDH_KeyGen/ECDH_ZGen/EC_Ephemeral/ZGen_2Phase/
|
||||
* ECC_Parameters (ECDSA sign/verify are retained)
|
||||
* FWTPM_NO_HASH_CMDS - Hash, HMAC, and the hash/HMAC sequence commands
|
||||
* FWTPM_NO_CONTEXT - ContextSave/ContextLoad (FlushContext retained)
|
||||
* FWTPM_NO_SYM_ENCRYPT - EncryptDecrypt/EncryptDecrypt2
|
||||
* FWTPM_NO_CLOCK - ReadClock/ClockSet/ClockRateAdjust
|
||||
*
|
||||
* These flags are independent; select exactly the command groups your fTPM does
|
||||
* not need. There is intentionally no single "minimal" umbrella macro - dropping
|
||||
* a command group removes real TPM functionality, so each choice must be made
|
||||
* deliberately. For a worked example that picks a set for a constrained target,
|
||||
* see the MicroBlaze V build in the wolftpm-examples repository.
|
||||
*
|
||||
* Gates that own per-instance state also drop it from FWTPM_CTX below, so the
|
||||
* RAM saving is real and not just code size:
|
||||
* FWTPM_NO_HASH_CMDS - hashSeq[FWTPM_MAX_HASH_SEQ]. The FWTPM_MAX_HASH_SEQ
|
||||
* macro itself stays defined: MLDSA sequence handles are
|
||||
* numbered above the hash-sequence range, so removing it
|
||||
* would renumber them between gated and ungated builds.
|
||||
* FWTPM_NO_CONTEXT - ctxProtectKey/ctxProtectKeyValid, contextSeqCounter,
|
||||
* contextLive[]/contextLiveCount
|
||||
* FWTPM_NO_ECDH - ecEphemeralCounter/Key/KeySz/Curve */
|
||||
|
||||
/* Dictionary Attack (DA) feature toggles:
|
||||
* FWTPM_NO_DA - compile out all DA lockout protection.
|
||||
* FWTPM_DA_USED_RETRY - emulate real-TPM behavior where the first use of a
|
||||
|
|
@ -486,7 +520,10 @@ typedef struct FWTPM_HashSeq {
|
|||
#endif
|
||||
} FWTPM_HashSeq;
|
||||
|
||||
#ifdef WOLFTPM_V185
|
||||
/* Guarded on WOLFTPM_MLDSA, not WOLFTPM_V185: a lean WOLFTPM_PQC build defines
|
||||
* WOLFTPM_MLDSA on its own, and every consumer of these slots tests
|
||||
* WOLFTPM_MLDSA. */
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
/* ML-DSA sign/verify sequence slot (v1.85 Part 3 Sec.17.5, Sec.17.6). Pure ML-DSA
|
||||
* is one-shot — the message arrives via the `buffer` parameter of
|
||||
* TPM2_SignSequenceComplete and TPM2_SequenceUpdate is rejected with
|
||||
|
|
@ -531,7 +568,7 @@ typedef struct FWTPM_SignSeq {
|
|||
#ifndef FWTPM_MAX_SIGN_SEQ
|
||||
#define FWTPM_MAX_SIGN_SEQ 4
|
||||
#endif
|
||||
#endif /* WOLFTPM_V185 */
|
||||
#endif /* WOLFTPM_MLDSA */
|
||||
|
||||
/* Auth session slot */
|
||||
typedef struct FWTPM_Session {
|
||||
|
|
@ -708,8 +745,10 @@ typedef struct FWTPM_CTX {
|
|||
FWTPM_NvIndex nvIndices[FWTPM_MAX_NV_INDICES];
|
||||
|
||||
/* Hash sequence slots */
|
||||
#ifndef FWTPM_NO_HASH_CMDS
|
||||
FWTPM_HashSeq hashSeq[FWTPM_MAX_HASH_SEQ];
|
||||
#ifdef WOLFTPM_V185
|
||||
#endif
|
||||
#ifdef WOLFTPM_MLDSA
|
||||
FWTPM_SignSeq signSeq[FWTPM_MAX_SIGN_SEQ];
|
||||
#endif
|
||||
|
||||
|
|
@ -738,11 +777,13 @@ typedef struct FWTPM_CTX {
|
|||
TPM2B_DIGEST lockoutPolicy;
|
||||
TPMI_ALG_HASH lockoutPolicyAlg;
|
||||
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
/* Per-boot context protection key (volatile only, never persisted).
|
||||
* Used by ContextSave/ContextLoad for HMAC + AES-CFB protection of
|
||||
* session context blobs per TPM 2.0 Part 1 Sec.30. */
|
||||
byte ctxProtectKey[AES_256_KEY_SIZE];
|
||||
int ctxProtectKeyValid;
|
||||
#endif
|
||||
|
||||
/* TIS transport state (when not using sockets) */
|
||||
#ifdef WOLFTPM_FWTPM_TIS
|
||||
|
|
@ -769,18 +810,21 @@ typedef struct FWTPM_CTX {
|
|||
word32 nvGranuleFill; /* bytes buffered (0..writeAlign) */
|
||||
#endif
|
||||
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
/* ContextSave sequence counter (monotonic, reset on init) */
|
||||
UINT64 contextSeqCounter;
|
||||
/* Live (saved-but-not-yet-loaded) context sequences. A context loads at
|
||||
* most once and saved contexts may load in any order. */
|
||||
UINT64 contextLive[FWTPM_MAX_OBJECTS + FWTPM_MAX_SESSIONS];
|
||||
int contextLiveCount;
|
||||
#endif
|
||||
|
||||
/* Set once TPM2_SelfTest has completed successfully */
|
||||
int selfTestRun;
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
/* EC_Ephemeral commit counter and key storage (volatile) */
|
||||
#if defined(HAVE_ECC) && !defined(FWTPM_NO_ECDH)
|
||||
/* EC_Ephemeral commit counter and key storage (volatile). Only the ECDH
|
||||
* command group (EC_Ephemeral / ZGen_2Phase) uses this state. */
|
||||
UINT16 ecEphemeralCounter;
|
||||
byte ecEphemeralKey[FWTPM_MAX_PRIVKEY_DER];
|
||||
int ecEphemeralKeySz;
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ int FwUnwrapPrivate(FWTPM_Object* parent,
|
|||
|
||||
/* --- Context blob wrap/unwrap (ContextSave/Load) --- */
|
||||
|
||||
#ifndef FWTPM_NO_CONTEXT
|
||||
/* Domain-separation tag bound into the blob MAC so an object blob cannot be
|
||||
* verified through the session path or vice versa. */
|
||||
#define FWTPM_CTX_TYPE_SESSION 0x01
|
||||
|
|
@ -266,6 +267,7 @@ int FwWrapContextBlob(FWTPM_CTX* ctx, UINT64 seq, byte ctxType,
|
|||
int FwUnwrapContextBlob(FWTPM_CTX* ctx, UINT64 seq, byte ctxType,
|
||||
const byte* in, int inSz,
|
||||
byte* out, int outBufSz, int* outSz);
|
||||
#endif /* !FWTPM_NO_CONTEXT */
|
||||
|
||||
/* --- Seed encrypt/decrypt --- */
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue