This document covers how Heads uses the TPM for measured boot, secret sealing, rollback protection, and PCR extension.
See also: architecture.md, boot-process.md, security-model.md.
initrd/bin/tpmr.sh is a shell script wrapper that presents a single interface
over both TPM 1.2 (tpm / trousers) and TPM 2.0 (tpm2-tools). All Heads
scripts call tpmr.sh rather than invoking tpm or tpm2 directly.
| TPM version | Hash algorithm | PCR size |
|---|---|---|
| TPM 1.2 | SHA-1 | 20 bytes |
| TPM 2.0 | SHA-256 | 32 bytes |
| Subcommand | Description |
|---|---|
pcrread |
Read a PCR value |
pcrsize |
Print PCR byte size (20 or 32) |
calcfuturepcr |
Compute a PCR value by replaying the firmware event log |
extend |
Extend a PCR with a hash or file |
seal |
Seal a file to TPM NVRAM with a PCR policy |
unseal |
Unseal from TPM NVRAM |
startsession |
Start an authorization session (TPM2 only) |
counter_read |
Read a monotonic counter |
counter_increment |
Increment a monotonic counter |
counter_create |
Create a new monotonic counter |
destroy |
Destroy an NVRAM index |
reset |
Reset the TPM |
kexec_finalize |
Flush TPM2 sessions, lock platform hierarchy (TPM2 only) |
shutdown |
Orderly shutdown (TPM2 only) |
config/coreboot-*.config defines slot assignments via CONFIG_PCR_* for
optional coreboot measured-boot features. Coreboot supports several modes:
| coreboot mode | PCRs used | Status in Heads |
|---|---|---|
| SRTM (Static Root of Trust for Measurement) | PCR 2 (CONFIG_PCR_SRTM=2) |
Active on TPM boards with measured boot — current coreboot uses CONFIG_TPM_MEASURED_BOOT=y; older forks (KGPE-D16, Librem L1UM) use the legacy CONFIG_TPM_INIT=y |
| Boot mode measurement | PCR 1 (CONFIG_PCR_BOOT_MODE=1) |
Not enabled |
| Hardware ID measurement | PCR 1 (CONFIG_PCR_HWID=1) |
Not enabled |
| Runtime data | PCR 3 (CONFIG_PCR_RUNTIME_DATA=3) |
Not enabled — CONFIG_TPM_MEASURE_MRC_CACHE is off and CONFIG_TPM_MEASURED_BOOT_RUNTIME_DATA is empty, so PCR 3 stays zero |
| Firmware version | PCR 10 (CONFIG_PCR_FW_VER=10) |
Not enabled |
Coreboot's measured boot establishes a Core Root of Trust for Measurement (CRTM). When the CRTM executes only once per power cycle — as it does on all Heads boards — this is a Static CRTM (S-CRTM), creating an SRTM chain.
The bootblock (IBB — Initial Boot Block) is the S-CRTM: the first code executed by the CPU after reset, directly from SPI flash. Its integrity is guaranteed by hardware write-protection of the flash, not by a prior measurement. Measured boot is independent of vboot and does not require vboot to be enabled.
CBFS stages are measured as raw data before decompression; CBFS headers are excluded from measurements.
On the majority of Heads boards, the TPM chip is not initialized until ramstage.
The measurement chain is transitive: each stage measures the next during CBFS file
loading, before executing it. Measurements taken before TPM init are cached in the
preram log (a software-only log kept in CAR/SRAM, then migrated to CBMEM). When
ramstage initializes the TPM via tpm_setup(), the cached preram entries are flushed
to PCR 2 by tspi_measure_cache_to_pcr(). After TPM init, subsequent measurements
extend PCR 2 directly on the TPM hardware in addition to the software log.
The full chain recorded into PCR 2 is:
bootblock → romstage → ramstage → Heads Linux kernel + initrd (payload)
The gap between first measurement (bootblock, preram log) and first TPM hardware extend (ramstage) is covered by hardware write-protection of the SPI flash — the contents of stages measured before TPM init cannot change without physical flash access. The bootblock remains the S-CRTM; the TPM hardware just begins recording later.
After this chain is recorded, the TPM state reflects the complete firmware stack. Any modification to any of these stages produces a different PCR 2 value, causing unseal operations to fail.
Under the active Heads configuration, only PCR 2 is extended by coreboot. PCRs 0, 1, and 3 are read live at seal time and are normally zero.
CONFIG_TPM_MEASURED_BOOT is the config key used in current coreboot versions.
Older coreboot releases (notably 4.11, used by KGPE-D16 and some Librem server
boards) used CONFIG_TPM_INIT=y before this key existed. The absence of
CONFIG_TPM_MEASURED_BOOT in an older-coreboot config does not automatically
mean measured boot is absent — it may use the older naming.
Notable exceptions from the standard SRTM path:
| Board family | coreboot fork/version | TPM situation | Notes |
|---|---|---|---|
| KGPE-D16 server/workstation variants | 4.11 (unmaintained) | CONFIG_TPM_INIT=y (old key); no CONFIG_TPM_MEASURED_BOOT |
Pre-dates the current measured boot config naming |
| ThinkPad T520 | 4.22.01 (unmaintained) | CONFIG_TPM_INIT_RAMSTAGE=y but CONFIG_TPM_MEASURED_BOOT explicitly not set |
TPM initialized but SRTM measurements disabled |
| Librem l1um (original) | purism fork (unmaintained) | CONFIG_TPM_INIT=y (old key); no CONFIG_TPM_MEASURED_BOOT |
Purism fork; pre-dates current measured boot naming |
| Librem Mini, Librem Mini v2, Librem 11 | purism fork (maintained) | CONFIG_NO_TPM=y |
No TPM hardware; falls back to ROM-hash HOTP mode (see below) |
| OptiPlex 7010/9010 TXT (EOL) | xx30 (maintained) | CONFIG_INTEL_TXT=y; CONFIG_TPM_MEASURED_BOOT_INIT_BOOTBLOCK=y |
Intel TXT requires TPM init in bootblock because the SINIT ACM (which launches the DRTM chain) runs before ramstage and needs TPM access for the Measured Launch Environment |
On boards where coreboot SRTM measurements are absent or uncertain, PCR 2
remains at zero from coreboot's perspective. Heads still seals secrets to the
TPM (where a TPM exists), but the PCR 2 component of the seal offers no
firmware tamper detection. Boot integrity on these platforms relies on
write-protection of the flash and GPG-signed /boot.
On boards with no TPM hardware, Heads uses ROM-hash HOTP as the sole attestation mechanism. See security-model.md — HOTP on boards without a TPM for the mechanism and its known limitations.
The software S-CRTM (bootblock measuring itself) has a known limitation: the IBB is self-referential — it asserts its own integrity. To address this, processor vendors provide external RoT mechanisms that validate the IBB via hardware before execution:
- Intel BootGuard — validates the IBB against a signed manifest fused into the CPU/PCH at reset. On the legacy Intel TXT path the IBB is the CBFS file or files referenced by FIT type 7; on CBnT boards coreboot does not use FIT type 7 for the IBB and the digest comes from the signed Boot Policy Manifest
- AMD Hardware Validated Boot (HVB) — equivalent AMD mechanism
These are hardware features of the platform, not coreboot configuration choices. Where a board's CPU supports BootGuard or HVB, that hardware layer validates the IBB below the coreboot SRTM chain. A BootGuard measurement reaches PCR 0 only when the platform is provisioned with a boot profile that includes measurement; see PCR assignments for what is measured and when.
One board — the Dell OptiPlex configured with Intel Trusted Execution Technology
(CONFIG_INTEL_TXT=y, CONFIG_TPM_MEASURED_BOOT_INIT_BOOTBLOCK=y) — initializes
the TPM in the bootblock itself, closing the gap described above: the IBB measures
itself and then each subsequent stage, so measurements begin at the very first
stage. It also enables a Dynamic Root of Trust for Measurement (DRTM) path
via the Intel SINIT ACM, which allows a DRTM chain to be re-established within
a single power cycle with hardware-rooted trust. The PCR 2 SRTM chain is
unchanged; the TXT mechanism adds the DRTM capability on top of it.
| PCR | Extended by | Content |
|---|---|---|
| 0 | unused by Heads | Read live at seal time — normally zero; a BootGuard measurement lands in PCR 0 only when the platform is provisioned with a boot profile that includes measurement |
| 1 | unused by Heads | Read live at seal time — normally zero; coreboot's boot mode and HWID measurement features are not enabled |
| 2 | coreboot SRTM | Boot block, ROM stage, RAM stage, Heads Linux kernel + initrd (payload), plus other loaded CBFS files (bootsplash.jpg, fallback/*) |
| 3 | unused by Heads | Read live at seal time — normally zero; MRC cache runtime measurement is disabled |
| 4 | Heads (usb-init.sh, kexec-insert-key.sh, kexec-select-boot.sh, initrd/etc/functions.sh) |
Boot path: "usb" on USB boot, "generic" on normal boot (after the DUK is unsealed, to block further unsealing), "recovery" on entering the recovery shell. Sealing records the value before any of these extensions |
| 5 | Heads insmod wrapper |
Each loaded kernel module: parameters + binary content (default MODULE_PCR=5) |
| 6 | Heads qubes-measure-luks.sh |
LUKS header dump for each encrypted drive |
| 7 | Heads cbfs-init.sh, uefi-init.sh |
Each CBFS/UEFI file: filename then content (default CONFIG_PCR=7) — covers config.user, GPG keyring, user CBFS files, not UEFI Secure Boot state |
PCRs 0 through 3 are read at seal time and included in sealing policies. PCRs 1 and 3 are zero while their coreboot features stay disabled. PCR 0 is normally zero and holds a measurement only when the platform is provisioned with a BootGuard boot profile that includes measurement; in that case the BootGuard ACM measures the Key Manifest, Boot Policy Manifest, IBB and policy data into PCR 0 at locality 3; a platform provisioned without such a profile leaves PCR 0 unchanged. Any extension of these PCRs changes the value recorded in the policy and breaks the seal.
For the measurement behavior and its history see the heads PR #1172 discussion and the Dasharo PCR measurements knowledge base.
Only PCRs 0 through 7 are extended or sealed against by Heads; other PCRs are not
used (the integrity report may still read and display all PCRs). PCR 16 is not
used: calcfuturepcr replays the cbmem -L event log (replay_pcr rejects
pcr >= 8), so no scratch PCR is needed.
The prerequisites for a PCR 0 measurement (fuses, signed manifests, the Startup ACM, and coreboot's role) are described in ibb-measurement.md.
The DUK is a 128-byte random key generated from /dev/urandom (1024 bits of
entropy). This brute-force space (
| PCR | How obtained | Reason |
|---|---|---|
| 0 | pcrread (current value) |
Platform state at seal time |
| 1 | pcrread (current value) |
Platform state at seal time |
| 2 | pcrread (current value) |
coreboot SRTM measurement |
| 3 | pcrread (current value) |
Platform state at seal time |
| 4 | calcfuturepcr |
Replayed from the event log: PCR 4 before any Heads extension, normally zero |
| 5 | pcrread or calcfuturepcr 5 |
Actual if extra modules loaded; zeroed future value if no extra modules |
| 6 | calcfuturepcr 6 /tmp/luksDump.txt |
Pre-computed LUKS header measurement |
| 7 | pcrread (current value) |
User CBFS files |
PCR 5 is conditional on whether the board loads extra kernel modules
(USB HID, libata, HOTP token). The condition in kexec-seal-key.sh
checks CONFIG_USER_USB_KEYBOARD, CONFIG_USB_KEYBOARD_REQUIRED, the
presence of /lib/modules/libata.ko, and /bin/hotp_verification.
If any is true, the actual post-load PCR 5 value is read from hardware
via tpmr.sh pcrread. If none are true, calcfuturepcr 5 computes
the zeroed (never-extended) future value. This means the seal is valid
only for the expected module set.
PCR 6 is pre-computed: calcfuturepcr 6 /tmp/luksDump.txt replays the
LUKS header extension to compute the expected post-measurement value. If
the LUKS header changes (key slot added/removed), the DUK unseal fails.
| PCR | Included | Reason |
|---|---|---|
| 0 | Yes | Platform state |
| 1 | Yes | Platform state |
| 2 | Yes | coreboot SRTM measurement |
| 3 | Yes | Platform state |
| 4 | Yes | PCR 4 before any Heads extension, normally zero |
| 5 | No | Kernel modules are not firmware integrity attestation |
| 6 | No | LUKS header consistency is not firmware integrity attestation |
| 7 | Yes | User CBFS files |
The narrower policy means a LUKS header change or different kernel module set does not prevent TOTP from unsealing. TOTP/HOTP attests firmware and ROM configuration integrity, not disk state.
tpmr.sh extend -ix <pcr_num> -ic <string> extends a PCR with the hash of a
string. -if <file> extends with the hash of a file.
calcfuturepcr replays the firmware event log (cbmem -L) to compute a PCR
value without reading or extending the TPM. Any file or hash arguments are
folded in after the log entries, which is how PCR 5 and PCR 6 are modeled.
Sealing uses it to record the value a PCR will hold at unseal time, before any
Heads extension.
When a recovery shell is entered, initrd/etc/functions.sh extends PCR 4 with
the string "recovery". This permanently invalidates TOTP and LUKS DUK
unsealing for the rest of the boot session — the TPM will refuse to unseal
secrets that were sealed against the PCR 4 value from before any Heads extension.
Coreboot records each PCR extension into a TPM event log. Three log formats are supported: coreboot-specific, TPM 1.2 spec, and TPM 2.0 spec. The log can be inspected from an OS or recovery shell with:
cbmem -L
This is the authoritative record of what was measured into each PCR during firmware boot. Useful for diagnosing unexpected PCR values or verifying that a new board's SRTM chain matches expectations.
Heads uses a TPM monotonic counter stored in TPM NVRAM to detect rollback
attacks. The counter is incremented every time /boot is re-signed (i.e.
every time kexec-sign-config runs after an OS update).
The rollback counter prevents TPM swap attacks and /boot disk swap attacks:
-
TPM swap: An attacker swaps the TPM with a different one. The new TPM doesn't have the sealed secrets (TOTP/HOTP/DUK) that are bound to the original TPM's NVRAM. Even if the attacker has the original TPM, its PCR values would be different from the current firmware state, so unseal would fail.
-
Disk swap: An attacker swaps the /boot disk with an older one. The old disk has an older counter hash that doesn't match the current TPM counter value.
-
Combined attack: An attacker tries to use an old TPM with an old /boot to bypass security updates or revert to a known-vulnerable state.
The counter is stored in the TPM NVRAM: on TPM 1 the TPM assigns the counter
ID at creation (the -la label is only a caller supplied tag), and on TPM 2
Heads generates the NV index at creation time. Both bind the counter to that TPM.
A SHA-256 hash of the counter value is stored on /boot
(/boot/kexec_rollback.txt). This creates a two-way binding:
- Cannot swap TPM without breaking /boot consistency
- Cannot swap /boot without breaking TPM consistency
At boot, verify_rollback_counter in kexec-select-boot verifies the
counter hash matches. Before presenting TOTP/HOTP prompts, preflight_rollback_counter_before_reseal
validates the counter is readable from TPM, ensuring secrets can actually be
unsealed.
The counter is created via tpmr.sh counter_create during OEM Factory Reset
(initrd/bin/oem-factory-reset.sh), or by check_tpm_counter
(initrd/etc/functions.sh) on the kexec-sign-config / gui-init paths.
On TPM 2, Heads generates the rollback counter's NV index when it creates the
counter (tpm2_counter_create in initrd/bin/tpmr.sh): a 1 prefix plus 3
random bytes, yielding a valid NV handle (0x01 handle type prefix) in
0x01000000 through 0x01FFFFFF.
Only 0x01800000 through 0x01BFFFFF is allocatable by the owner; the
generated range also overlaps the TPM manufacturer and platform manufacturer
ranges (0x01000000 through 0x017FFFFF) and the TCG/global range
(0x01C00000 through 0x01FFFFFF). The TPM does not enforce these allocation
ranges, so an index authorized by the owner but outside the owner range is
normally accepted — but it can collide with an index provisioned by the
manufacturer or by the TCG; if that index already exists, creation fails with
TPM_RC_NV_DEFINED (TPM_RC_NV_SPACE if NV space is exhausted).
References:
- TCG, Registry of Reserved TPM 2.0 Handles and Localities — NV index ranges; "The TPM does not enforce the conventional usage of NV indices".
- TCG, TPM 2.0 Part 3: Commands, §31.3
TPM2_NV_DefineSpace— error codes.
read_tpm_counter in initrd/etc/functions.sh reads the counter from the TPM
and writes the result to /tmp/counter-<index>. The format is
<index>: <hex_value>: TPM 1 writes the decimal counter ID, TPM 2 the
generated hex index.
/boot/kexec_rollback.txt stores the SHA-256 hash of that counter file.
At boot, kexec-select-boot reads the counter, hashes the file, and checks
it against the stored hash. A missing rollback record blocks boot only when
CONFIG_BOOT_REQ_ROLLBACK=y; an existing record mismatch aborts boot unless
CONFIG_IGNORE_ROLLBACK=y. No board config enables the missing record check.
Before presenting TOTP/HOTP recovery prompts, gui-init calls
preflight_rollback_counter_before_reseal to confirm the rollback counter
is consistent. This catches TPM replacements, /boot disk swaps, and counter
corruption before any secrets are resealed.
Failure conditions and their diagnostic messages:
| Condition | Message shown to user |
|---|---|
/boot/kexec_rollback.txt missing on initialized system |
"Boot integrity counter file missing. This means /boot was restored or swapped." |
| Counter index unreadable from TPM | "TPM integrity counter cannot be read. Possible cause: TPM was swapped or reset. This could indicate a TPM swap attack." |
TPM2: counter has ownerwrite but not authwrite |
"TPM counter has invalid security policy." |
TPM2: counter has neither authwrite nor ownerwrite |
"TPM counter is not writable." |
| TPM2: counter attributes empty or unreadable | "TPM counter policy is corrupted." |
The exact diagnostic message from fail_preflight is shown directly in the
error dialog — not a vague paraphrase. This tells the user and any support
context exactly which condition was detected. The action guidance ("Reset TPM
from GUI...") is stripped from the dialog since the menu already offers those
options.
The user is offered four actions: show the integrity report, OEM Factory Reset, Reset the TPM, or continue to the main menu. The dialog loops until the counter passes preflight or the user chooses to continue.
tpmr.sh counter_read must be called with a direct redirect, not piped through
tee. Piping through tee hides tpmr.sh failures because || checks the
exit status of tee (always 0), not tpmr.sh. See
ux-patterns.md for the correct pattern.
TPM1 rollback counters use empty auth (SHA1("")) per the TCG
specification (TPM Main Spec Part 3: TPM_CreateCounter uses owner
auth -pwdo, but TPM_IncrementCounter uses the counter's own
authData — not the owner password).
A regression in PR #2068 changed increment_tpm_counter from
hardcoded -pwdc '' to -pwdc "${tpm_passphrase:-}" (owner
passphrase), while counters continued to be created with -pwdc ''.
This caused every increment to compute SHA1(owner_pass) against a
counter created with SHA1(""), producing persistent TPM_AUTHFAIL.
The repeated auth failures (3 per boot) accumulated the TPM's
dictionary attack (DA) failedTries counter until lockout was reached
(~10 boots = 30 failures). Users reported "hours of waiting" on
affected hardware. On some implementations, the DA state persisted
through tpm forceclear.
The fix (PR #2117):
tpm1_counter_increment: detect explicit-pwdc ''and calltpm counter_incrementdirectly, bypassing_tpm_auth_retry. Non-empty-pwdcor absent flag falls through to the owner-auth retry path for migration of counters created by pre-fix code.check_tpm_counterandincrement_tpm_counter: create and increment counters with-pwdc ''instead of owner passphrase.oem-factory-reset.sh: uses-pwdc ''for counter creation.
TPM2 counters are unaffected — they still require owner auth for
tpm2 nvincrement.
TPM2 sealing uses NVRAM persistent objects with a combined PCR + optional password policy:
- A policy session is started (
tpm2 startauthsession --policy-session). - PCR values are bound to the session (
tpm2 policypcr). - If a password is set,
tpm2 policyauthvalueadds it to the policy. - The secret is stored in a persistent NVRAM handle.
- At unseal time, the same policy session is reconstructed and
tpm2 unsealretrieves the plaintext.
The primary handle file must exist before unsealing. If it is missing (after
a TPM reset), tpm2_unseal exits with a clear warning rather than producing
a confusing low-level error.
The following table maps each configurable aspect of the RoT and PCR policy to the file that controls it. Use this when adding a board, changing a sealing policy, or investigating why a seal/unseal operation fails.
| What you want to understand or change | Where to look | What to look for |
|---|---|---|
| Which coreboot PCRs are active on a board | config/coreboot-<board>.config |
CONFIG_PCR_SRTM, CONFIG_TPM_INIT_RAMSTAGE, CONFIG_TPM_MEASURED_BOOT_INIT_BOOTBLOCK, CONFIG_INTEL_TXT |
| Which coreboot version / fork a board uses | modules/coreboot + boards/<board>/ |
CONFIG_COREBOOT_VERSION in board config selects the coreboot source defined in modules/coreboot |
| LUKS DUK sealing policy (which PCRs) | initrd/bin/kexec-seal-key.sh |
tpmr.sh seal call and surrounding pcrread / calcfuturepcr calls; DEBUG comments explain each PCR |
| TOTP/HOTP sealing policy (which PCRs) | initrd/bin/seal-totp.sh |
tpmr.sh seal call; DEBUG messages explain why PCR 5 and PCR 6 are excluded |
| PCR 4 (boot path) tracking | initrd/bin/usb-init.sh, initrd/bin/kexec-insert-key.sh, initrd/bin/kexec-select-boot.sh, initrd/etc/functions.sh |
tpmr.sh extend calls with "usb", "generic", "recovery" |
| PCR 5 (kernel modules) | initrd/sbin/insmod.sh |
MODULE_PCR variable; default MODULE_PCR=5; each insmod extends PCR 5 |
| PCR 6 (LUKS header) | initrd/bin/qubes-measure-luks.sh |
tpmr.sh extend call against /tmp/luksDump.txt |
| PCR 7 (CBFS / ROM files) | initrd/bin/cbfs-init.sh, initrd/bin/uefi-init.sh |
CONFIG_PCR variable; default CONFIG_PCR=7; each extracted file extends PCR 7 |
| Rollback counter logic | initrd/etc/functions.sh |
check_tpm_counter, read_tpm_counter, increment_tpm_counter |
To verify that a new board's coreboot config matches the expected RoT:
- Check that
CONFIG_TPM_MEASURED_BOOT=yandCONFIG_PCR_SRTM=2are set. For boards using coreboot 4.11 or older forks, the equivalent older key isCONFIG_TPM_INIT=y; confirm whether that version's measured boot is active. - Confirm
CONFIG_TPM_INIT_RAMSTAGE=y(standard) or document why it differs. If the board has no TPM hardware, verifyCONFIG_NO_TPM=yis intentional and note that TPM-based attestation (TOTP, LUKS DUK) will not be available. - Check that
CONFIG_PCR_BOOT_MODE,CONFIG_PCR_HWID,CONFIG_PCR_RUNTIME_DATAare set to their slot numbers but not enabled (no corresponding=yfeature flag). These are slot reservations; enabling them would extend PCRs 1 and 3, breaking all existing seals on that board. - If the board uses Intel TXT, verify
CONFIG_INTEL_TXT=yandCONFIG_TPM_MEASURED_BOOT_INIT_BOOTBLOCK=yare intentional and document the DRTM capability in the board's README.
| Feature | TPM 1.2 | TPM 2.0 |
|---|---|---|
| PCR hash | SHA-1 (20 bytes) | SHA-256 (32 bytes) |
| Sealing | tpm sealfile2 -ix <pcr> <hash> ... (specify PCRs and expected values) |
tpm2 nvdefine + policy session |
| Unsealing | tpm unsealfile (no PCR args — TPM enforces from baked blob) |
tpm2 unseal with policy session |
| Counter | tpm counter_create, counter_read, counter_increment |
tpm2 nvdefine, nvread, nvincrement |
| Auth sessions | Not used | Required for policy-based unseal |
kexec_finalize |
Does nothing | Flushes sessions and locks the platform hierarchy; does not extend PCRs |
startsession |
No-op | Creates encryption session |
Google's CR50 is a microcontroller-based TPM 2.0 implementation used on Chromebooks and ChromeOS-derived devices (e.g. Kano). It implements a subset of the TPM 2.0 command set: 64 library commands of the 100+ defined in the PC Client profile, plus 1 vendor command (65 total).
Identifying a CR50: tpm2 getcap properties-fixed | grep MANUFACTURER
returns 0x43524F53 ("CROS"); vendor strings show xCG fTPM.
Heads invokes 22 unique tpm2 subcommands (all via initrd/bin/tpmr.sh
except two direct calls in initrd/etc/functions.sh). 21 of 22 are
supported by CR50. The single gap is ClearControl:
Heads tpm2 subcommand |
TPM2 command (index) | On CR50? |
|---|---|---|
tpm2 pcrread |
PCR_Read (0x17E) | yes |
tpm2 pcrextend |
PCR_Extend (0x182) | yes |
tpm2 nvread |
NV_Read (0x14E) | yes |
tpm2 nvincrement |
NV_Increment (0x134) | yes |
tpm2 nvdefine |
NV_DefineSpace (0x12A) | yes |
tpm2 nvreadpublic |
NV_ReadPublic (0x169) | yes |
tpm2 flushcontext |
FlushContext (0x165) | yes |
tpm2 readpublic |
ReadPublic (0x173) | yes |
tpm2 startauthsession |
StartAuthSession (0x176) | yes |
tpm2 evictcontrol |
EvictControl (0x120) | yes |
tpm2 policypcr |
PolicyPCR (0x17F) | yes |
tpm2 policyauthvalue |
PolicyAuthValue (0x16B) | yes |
tpm2 create |
Create (0x153) | yes |
tpm2 load |
Load (0x157) | yes |
tpm2 unseal |
Unseal (0x15E) | yes |
tpm2 clearcontrol |
ClearControl (0x127) | no |
tpm2 clear |
Clear (0x126) | yes |
tpm2 changeauth |
HierarchyChangeAuth (0x129) / ObjectChangeAuth (0x150) | yes |
tpm2 createprimary |
CreatePrimary (0x131) | yes |
tpm2 dictionarylockout |
DictionaryAttackParameters (0x13A) | yes |
tpm2 shutdown |
Shutdown (0x145) | yes |
tpm2 sessionconfig |
(tpm2-tools library call, no standalone TPM command) | n/a |
ClearControl (0x127) is the only Heads command missing from CR50.
tpm2_reset() invokes it to clear the disableClear flag before
TPM2_Clear. On CR50 this returns command code not supported (error
0xB0143); Heads logs the failure and proceeds to TPM2_Clear anyway,
since CR50 does not have disableClear set by default.
PolicyAuthValue vs PolicyPassword: Heads initially used PolicyPassword
(TPM2_CC_PolicyPassword, 0x18C) for policy-based authorization, but changed
to tpm2 policyauthvalue (TPM2_CC_PolicyAuthValue, 0x16B) in PR #2055.
PolicyAuthValue is present on CR50. PolicyPassword is not, but this is
unimportant since Heads no longer invokes it.
These commands exist in the TPM 2.0 spec but are not implemented by CR50. None are invoked by Heads, so their absence has no functional impact:
| Command (index) | Category |
|---|---|
| ChangeEPS (0x123), ChangePPS (0x124) | Endorsement/Platform hierarchy management |
| ClockSet (0x128), ClockRateAdjust (0x12D) | TPM clock control |
| SetPrimaryPolicy (0x12C) | Primary object policy |
| CreateLoaded (0x12E) | Create + load in one call |
| NV_GlobalWriteLock (0x12F) | Global NV write lock |
| NV_SetBits (0x135) | NV bitwise OR |
| NV_ChangeAuth (0x13B) | NV index auth change |
| PCR_SetAuthPolicy (0x13E), PCR_SetAuthValue (0x13F) | PCR auth configuration |
| IncrementalSelfTest (0x142) | Partial self-test (SelfTest 0x143 covers full test) |
| HMAC_Start (0x15A), SequenceUpdate (0x15B), SequenceComplete (0x15C) | HMAC sequence operations |
| EncryptDecrypt (0x164), EncryptDecrypt2 (0x166) | Symmetric encrypt/decrypt |
| MakeCredential (0x168) | Credential creation for remote attestation |
| PolicyPassword (0x18C) | Password-based policy auth (Heads used this before PR #2055 switched to PolicyAuthValue 0x16B) |
| PolicyCounterTimer (0x16D), PolicyCpHash (0x16E), PolicyLocality (0x16F), PolicyNameHash (0x170), PolicyTicket (0x172), PolicyNvWritten (0x175), PolicyAuthorize (0x179) | Extended policy authorization |
| ReadClock (0x181) | TPM clock read |
| PCR_Event (0x183) | PCR extend with event log (Heads uses PCR_Extend 0x182 instead) |
| GetCommandAuditDigest (0x185), GetSessionAuditDigest (0x186) | Audit digest retrieval |
| GetTime (0x187) | TPM time attestation |
| EC_Ephemeral (0x18E), ZGen_2Phase (0x18D) | ECC 2-phase operations |
| FieldUpgradeStart (0x18F), FieldUpgradeData (0x190) | Firmware field upgrade |