AxlTpm — TPM 2.0 presence + capability

TPM 2.0 access via EFI_TCG2_PROTOCOL: presence + capability readout, the Endorsement Key public part, and PCR-bound seal/unseal.

Header: <axl/axl-tpm.h>. The TCG2 protocol is a singleton, so there is nothing to enumerate. The read-only surface is a presence check plus one typed capability struct; beyond that the module drives raw TPM2 commands over SubmitCommand for the Endorsement Key and for sealing a secret to the platform’s measured-boot state (see below). Measurement (PCR extend) and the event log remain out of scope — sealing reads PCRs into a policy, it does not extend them.

The protocol is located lazily and cached (like the CPU-arch / MP-services helpers). axl_tpm_present() reports whether the TCG2 protocol is published; axl_tpm_get_capability() calls GetCapability and projects EFI_TCG2_BOOT_SERVICE_CAPABILITY into AxlTpmCapability.

if (axl_tpm_present()) {
    AxlTpmCapability cap;
    if (axl_tpm_get_capability(&cap) == AXL_OK && cap.present) {
        axl_printf("TPM mfr=0x%08x banks=%u active=0x%x\n",
                   cap.manufacturer_id, cap.number_of_pcr_banks,
                   cap.active_pcr_banks);
    }
}

Two presence concepts: axl_tpm_present() is “the TCG2 protocol is published” (a stack is available to query); AxlTpmCapability.present is the firmware’s TPMPresentFlag (“a chip is installed and responding”). When the protocol is absent axl_tpm_get_capability returns AXL_ERR and the consumer reports the TPM as not present (the QEMU-default golden, {"tpm":{"present":false}}).

active_pcr_banks is a hash-algorithm bitmask (EFI_TCG2_BOOT_HASH_ALG_*, consumer decodes names); number_of_pcr_banks is a count. Both are valid only when the capability structure version is >= 1.1.

Endorsement Key + PCR-bound seal/unseal

Beyond the read-only capability surface, the module also drives raw TPM2 commands over EFI_TCG2_PROTOCOL.SubmitCommand:

  • axl_tpm_read_ek_pub derives the Endorsement Key public part (a stable per-device identity) via TPM2_CreatePrimary in the endorsement hierarchy.

  • axl_tpm_seal / axl_tpm_unseal seal a small secret (e.g. a TLS private key) to the chosen SHA-256 PCRs and recover it only when the live PCRs reproduce the seal-time measured state. The chain is CreatePrimary (a deterministic ECC SRK) -> PCR_Read (the PolicyPCR digest is computed in software) -> Create (a keyedhash sealed object whose authPolicy is that digest); unseal runs Load -> StartAuthSession -> PolicyPCR -> Unseal. The blob axl_tpm_seal returns is opaque ciphertext the caller persists; axl_tpm_unseal returns AXL_DENIED if the measured state changed. Uses empty owner/parent authorization (the common firmware default); cross-boot unseal also needs a stable owner primary seed (no TPM2_Clear between).

The raw-command paths are validated against swtpm in QEMU (test/integration/test-tpm-qemu.sh, test-tpm-seal-qemu.sh); the absent path is covered on both arches by the unit suite.

API Reference

TPM 2.0 presence and capability readout.

Reads the platform’s TPM 2.0 capability via the firmware’s TCG2 protocol (EFI_TCG2_PROTOCOL.GetCapability). Unlike the other platform readers there is nothing to enumerate — the TCG2 protocol is a singleton — so this is a presence check plus one typed capability struct.

if (axl_tpm_present()) {
    AxlTpmCapability cap;
    if (axl_tpm_get_capability(&cap) == AXL_OK) {
        // ... report manufacturer, banks, sizes ...
    }
}

Scope is the boot-service capability fields a diagnostic/inventory view reports. Measurement, the event log, and PCR extension are out of scope.

Defines

AXL_TPM_SEAL_MAX_SECRET

Largest secret axl_tpm_seal accepts (TPM sealed-data limit).

Enums

enum AxlTpmEkAlg

EK public-key algorithm.

Values:

enumerator AXL_TPM_EK_RSA2048

RSA-2048 EK (TCG template L-1). The returned bytes are the modulus.

enumerator AXL_TPM_EK_ECC_P256

ECC NIST P-256 EK (TCG template L-2). The returned bytes are the uncompressed point X||Y (64 bytes).

Functions

bool axl_tpm_present(void)

Report whether the firmware publishes the TCG2 protocol.

A cheap presence gate: true means a TPM 2.0 software stack is available to query (call axl_tpm_get_capability for the details). It does not by itself guarantee a physical TPM is responding — that is AxlTpmCapability.present (TPMPresentFlag). Result is cached after the first call.

Returns:

true if the TCG2 protocol is published, false otherwise.

int axl_tpm_get_capability(AxlTpmCapability *out)

Read the TPM 2.0 boot-service capability.

Parameters:
  • out – [out] populated on success

Returns:

AXL_OK on success, AXL_ERR if the TCG2 protocol is not published, the GetCapability call fails, or out is NULL. A present-but-wedged TPM (protocol published, GetCapability fails) reports the same AXL_ERR as an absent protocol — the consumer reports the TPM as not present in both cases (the axl_tpm_present() == false case).

bool axl_tpm_ek_available(void)

Whether a TPM 2.0 with a readable Endorsement Key is present.

A consumer can branch on this to fall back to a weaker machine id (e.g. the SMBIOS UUID) when no TPM is available. Returns false immediately when the TCG2 protocol is absent; otherwise it confirms the EK can actually be derived. The result is cached after the first call.

Returns:

true if axl_tpm_read_ek_pub() will succeed.

int axl_tpm_read_ek_pub(uint8_t *buf, size_t buf_size, size_t *out_len, AxlTpmEkAlg *out_alg)

Read the TPM 2.0 Endorsement Key public part.

Derives the EK with TPM2_CreatePrimary in the endorsement hierarchy using the standard TCG EK template — ECC P-256 first, falling back to RSA-2048 — and returns the public key’s canonical bytes: for ECC the uncompressed point X||Y (64 bytes for P-256); for RSA the modulus (256 bytes for RSA-2048). Bytes are in their natural big-endian order, so they are deterministic for a given TPM across boots — hash them for a stable machine id. Derivation is transient (the primary handle is flushed); nothing is persisted in the TPM.

Output-buffer protocol: call with buf == NULL to query the required size (written to *out_len). Otherwise buf_size is the capacity; on success *out_len is the byte count and *out_alg the key type. If buf_size is too small, returns AXL_ERR with *out_len set to the required size and buf untouched.

Parameters:
  • buf – [out] EK public bytes, or NULL to size-query

  • buf_size – capacity of buf in bytes

  • out_len – [out] bytes written / required size

  • out_alg – [out] EK algorithm (may be NULL)

Returns:

AXL_OK on success; AXL_ERR if no TPM / no TCG2 protocol, the EK could not be derived, the buffer is too small, or out_len is NULL.

int axl_tpm_seal(const uint8_t *secret, size_t secret_len, const uint32_t *pcrs, size_t pcr_count, uint8_t **out_blob, size_t *out_blob_len)

Seal a secret under a PCR policy.

Binds secret to the current values of the PCRs listed in pcrs (SHA-256 bank, indices 0..23) and returns an opaque sealed blob in out_blob that the caller persists and later passes to axl_tpm_unseal. The blob carries everything unseal needs (the sealed object and the PCR selection); the secret is never in it in the clear.

out_blob is allocated with axl_malloc — free it with axl_free.

Parameters:
  • secret – secret bytes to seal

  • secret_len – secret length (1..AXL_TPM_SEAL_MAX_SECRET)

  • pcrs – PCR indices to bind to (each 0..23)

  • pcr_count – number of PCRs in pcrs

  • out_blob – [out] sealed blob (free with axl_free)

  • out_blob_len – [out] sealed blob length

Returns:

AXL_OK on success; AXL_INVALID if secret / out_blob / out_blob_len is NULL, secret_len is 0 or exceeds AXL_TPM_SEAL_MAX_SECRET, or pcr_count is 0 or names a PCR > 23; AXL_ERR if no TPM (!axl_tpm_present()) or a TPM command fails.

int axl_tpm_unseal(const uint8_t *blob, size_t blob_len, uint8_t **out_secret, size_t *out_secret_len)

Unseal a blob produced by axl_tpm_seal.

Recovers the secret only if the current PCR values satisfy the policy baked into the blob at seal time. The recovered secret is returned in out_secret, allocated with axl_malloc — free it with axl_free.

Parameters:
  • blob – sealed blob from axl_tpm_seal

  • blob_len – blob length

  • out_secret – [out] recovered secret (free with axl_free)

  • out_secret_len – [out] recovered secret length

Returns:

AXL_OK on success; AXL_INVALID if an argument is NULL or the blob is malformed; AXL_DENIED if the current PCRs do not satisfy the seal-time policy (the firmware/measured state changed); AXL_ERR if no TPM or a TPM command fails.

struct AxlTpmCapability
#include <axl-tpm.h>

TPM 2.0 boot-service capability.

Typed projection of the firmware’s EFI_TCG2_BOOT_SERVICE_CAPABILITY. present is the firmware’s own TPMPresentFlag — a TPM chip is installed and responding — which is distinct from axl_tpm_present() reporting that the TCG2 protocol is published.

Note the two PCR fields are different kinds of value: number_of_pcr_banks is a count, active_pcr_banks is a hash-algorithm bitmask (not a count). Both are meaningful only when the structure version is >= 1.1; on older firmware they read 0 — and since a present TPM always has at least one bank, number_of_pcr_banks == 0 on a present TPM means the firmware predates struct ver 1.1, not a bankless TPM.

The event-log format flags (SupportedEventLogs) are omitted — the event log is measurement-domain (out of scope). The two hash-algorithm bitmasks below are the supported/active pair an inventory view reports.

Public Members

bool present

TPMPresentFlag: a TPM is installed and responding.

uint8_t structure_version_major

capability structure version major

uint8_t structure_version_minor

capability structure version minor

uint8_t protocol_version_major

TCG2 protocol version major.

uint8_t protocol_version_minor

TCG2 protocol version minor.

uint32_t manufacturer_id

TPM manufacturer ID (TCG vendor ID, 4 packed ASCII bytes)

uint32_t max_command_size

max supported command buffer size in bytes

uint32_t max_response_size

max supported response buffer size in bytes

uint32_t number_of_pcr_banks

COUNT of PCR banks the TPM supports (struct ver >= 1.1)

uint32_t supported_hash_algorithms

hash algorithms the TCG2 stack supports, EFI_TCG2_BOOT_HASH_ALG_* BITMASK (HashAlgorithmBitmap; superset of active_pcr_banks; consumer decodes names)

uint32_t active_pcr_banks

active PCR-bank hash-algorithm BITMASK, EFI_TCG2_BOOT_HASH_ALG_* (struct ver >= 1.1; consumer decodes names)