Host-side TPM command drivers — the client side of a TPM. These crates build TPM command byte streams, hand them to a TPM, and parse the responses. They are not a TPM implementation (that's the guest, e.g. libtpms compiled to WASM) and contain no host or persistence knowledge — the same driver runs natively on Linux or inside UEFI firmware.
citadel (Linux, std) ──EngineTransport (wasmtime JIT)──┐
├─▶ tpm2-driver ──process(cmd)──▶ TPM (libtpms guest)
uefi-wasmtime (UEFI, no_std) ─ComponentTransport (Pulley)─┘
The host seam is one trait:
pub trait TpmTransport {
fn process(&mut self, command: &[u8]) -> Result<Vec<u8>>;
}Implement it for your host (a wasmtime component call, a hardware TIS, a socket to swtpm, …) and every driver operation works over it.
| crate | what |
|---|---|
tpm-driver-common |
the TpmTransport seam, TpmError, send_command (transparent TPM_RC_RETRY/TPM_RC_TESTING retry), response_rc, hash helpers (sha256/sha384/sha1), and shared value types (Algorithm, PcrValue) |
tpm2-driver |
the full TPM 2.0 protocol — cmd (every command builder) + ops (high-level sequences: create-key, sign, sign-with-policy, verify, seal/unseal, PCR, NV counters, PolicyAuthorize) plus the direct-TPM2_Quote attestation builders |
tpm12-driver |
a TPM 1.2 scaffold — real 1.2 wire structure (TPM_TAG_RQU_COMMAND + TPM_ORD_*) for the measured-boot subset (startup / self-test / random / extend / PCR-read); the key/quote/seal surface is stubbed Unimplemented |
TPM 1.2 and 2.0 are entirely different wire protocols (1.2 uses TPM_ORD_*
ordinals and SHA-1 PCRs; 2.0 uses TPM_CC_* command codes). They share almost
no bytes — so the shared abstraction lives at the TpmTransport seam and (in
consumers) a TpmBackend-style trait, not in a merged byte protocol. The split
mirrors the tpm-wit interface package.
All crates are no_std + alloc. Enable the std feature for
impl std::error::Error for TpmError (so std hosts get ?-into-anyhow):
tpm2-driver = { path = "...", features = ["std"] } # std host (citadel)
tpm2-driver = { path = "..." } # no_std host (UEFI)cargo build # no_std libraries
cargo test # std test harness (builders, retry, parsers)
cargo build --target thumbv7em-none-eabi \
-p tpm-driver-common -p tpm2-driver -p tpm12-driver # prove genuine no_stdCI (.github/workflows/ci.yml) runs fmt + clippy + tests and the bare-target
build.
use tpm2_driver::{cmd, ops, send_command, TpmTransport};
fn quote(t: &mut impl TpmTransport, pcr: u32, nonce: &[u8]) -> tpm2_driver::Result<Vec<u8>> {
ops::startup(t, cmd::TPM_SU_CLEAR)?;
ops::pcr_extend(t, "sha256", pcr, &[0x42; 32])?;
let cp = send_command(t, &cmd::build_create_primary_signing())?; // restricted ECC P-256 AK
let ak = u32::from_be_bytes([cp[10], cp[11], cp[12], cp[13]]);
send_command(t, &cmd::build_quote(ak, pcr, nonce))
}The 2.0 protocol is the byte-for-byte extraction of Citadel's vtpm-backend,
plus the direct-TPM2_Quote builders proven in the uefi-wasmtime firmware
attestation work. cmd::quote_attestation_digest preserves Citadel's own
sign-based-quote folding function verbatim so its quote/verify_quote pair
stays self-consistent across the extraction.