tpm2

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

README

go-tpm2/tpm2

go-tpm2/tpm2

CI Go Reference Coverage License

Transport-agnostic, pure-Go TPM 2.0 command API. v0.5.0.

It sits one layer above github.com/go-tpm2/common: common owns the big-endian wire codec and the Transport contract, and this package turns that codec into typed TPM 2.0 commands — from Startup/GetRandom/PCR ops up through full measured-boot attestation: Quote/VerifyQuote, GetCapability decoders, NV storage, EK (Endorsement Key) creation, PolicyPCR seal/unseal, and MakeCredential/ActivateCredential.

Sibling repos: common (interfaces + codec), and the transports it runs over — crb (CRB MMIO) and tis (TIS/FIFO MMIO) — plus validate, which proves every flow below against a real swtpm.

Install

go get github.com/go-tpm2/tpm2

Usage

A TPM wraps any common.Transport (e.g. a *crb.CRB or *tis.TIS):

tpm := tpm2.New(transport) // transport implements common.Transport

if err := tpm.Startup(uint16(common.SUClear)); err != nil { /* ... */ }

rnd, err := tpm.GetRandom(20)

sel := []tpm2.PCRSelection{{Hash: uint16(common.AlgSHA256), PCRs: []int{0, 7}}}
_, digests, err := tpm.PCRRead(sel)
err = tpm.PCRExtend(0, uint16(common.AlgSHA256), eventDigest)
Attestation (Quote + off-TPM verify)
ak, akPub, _ := tpm.CreatePrimary()                  // ECDSA-P256 AK
quoted, sig, _ := tpm.Quote(ak, nonce, sel)          // TPM2_Quote
info, err := tpm2.VerifyQuote(akPub, quoted, sig, expectedPCRs) // off-TPM
Seal a secret to PCR state (measured-boot payoff)
parent, _ := tpm.CreateStoragePrimary()
priv, pub, _, _ := tpm.SealToPCR(parent, secret, sel, pcrValues)
// …later, unseals ONLY if the PCRs still hold pcrValues:
got, err := tpm.UnsealWithPCR(parent, priv, pub, sel, nonceCaller)
Credential activation (attestation identity)
ek, ekPub, _ := tpm.CreateEK()                        // EK Credential Profile
res, _ := tpm2.MakeCredential(ekPub, akName, secret, rand.Reader) // off-TPM
recovered, err := tpm.ActivateCredential(ak, ek, session,
    res.CredentialBlob, res.Secret)                   // TPM2_ActivateCredential

Other groups: NV (NVDefineSpace/NVWrite/NVRead/NVReadPublic/ NVUndefineSpace) and typed GetCapability decoders (GetPCRBanks, GetTPMProperties, GetManufacturer, GetAlgorithms, GetHandles).

Each method marshals its parameters with common.BuildCommand (the right TPM_ST tag + TPM_CC), calls Transport.Send, parses with common.ParseResponse, checks the response code, and unmarshals the response parameters. A non-success response code surfaces as a typed *TPMError carrying both the raw rc and the CC it came from.

Commands — core set

The table below is the foundation layer; the attestation/seal/NV/EK/credential commands listed under Usage build on it. Each cites TCG "TPM 2.0 Part 3: Commands" (structure shapes from "Part 2: Structures").

Method TPM2_… CC Tag
Startup(su uint16) error Startup 0x144 NoSessions
Shutdown(su uint16) error Shutdown 0x145 NoSessions
SelfTest(full bool) error SelfTest 0x143 NoSessions
GetRandom(n uint16) ([]byte, error) GetRandom 0x17B NoSessions
GetCapability(cap, prop, count uint32) (more bool, data []byte, err error) GetCapability 0x17A NoSessions
PCRRead(sel []PCRSelection) (updateCounter uint32, digests [][]byte, err error) PCR_Read 0x17E NoSessions
PCRExtend(pcr int, hash uint16, digest []byte) error PCR_Extend 0x182 Sessions

GetCapability returns moreData plus the raw TPMS_CAPABILITY_DATA blob (everything after the one-byte moreData flag). The typed decoders (GetPCRBanks, GetTPMProperties, GetManufacturer, GetAlgorithms, GetHandles) parse that union for the common capability classes.

PCRSelection is { Hash uint16; PCRs []int }; helpers convert PCR indices to and from the octet-indexed pcrSelect bitmap (PCR n = bit n mod 8 of octet n div 8, least-significant bit first). PCR[0..23] fit in the three-octet selection this stack emits.

Authorization area — PCRExtend

PCR_Extend is the one command in this set that carries an authorization area, so its command body has three regions (TCG "TPM 2.0 Part 1: Architecture", Command Authorization Area Structure):

handle area : pcrHandle (u32)                       — the PCR being extended
auth   area : authorizationSize (u32) || TPMS_AUTH_COMMAND
param  area : TPML_DIGEST_VALUES { count, {hashAlg, digest} }

It authorizes with a cleartext password session over the empty authValue — the standard way to extend an unowned platform PCR. The TPMS_AUTH_COMMAND is:

sessionHandle     = TPM_RS_PW (0x40000009)
nonce             = empty TPM2B          -> 0x0000
sessionAttributes = continueSession      -> 0x01
hmac              = empty TPM2B          -> 0x0000

which is 9 bytes on the wire (4 + 2 + 1 + 2), so authorizationSize is 0x00000009.

INFERRED field: the PCR handle. PCR[n] is encoded as the bare index n because TPM_HT_PCR (the PCR handle type) is 0x00, so PCR handles occupy 0x00000000..0x0000001F (TCG "TPM 2.0 Part 2: Structures", TPM_HT (Handle Types)). This is the one offset transcribed from the handle-type table rather than spelled out verbatim in the PCR_Extend command clause.

TPM_RS_PW (0x40000009) is defined in this package (it is not in common).

Conventions

  • Pure Go, CGO_ENABLED=0, no assembly.
  • BSD-3-Clause on every file.
  • 100% statement coverage (GOWORK=off go test -cover ./...).
  • GOWORK=off — this module is not part of any workspace.
  • Big-endian: every multi-byte field is MSB-first (TPM 2.0 wire format).
  • Every command/flow validated against real swtpm 0.10.1 by validate.

Specifications

  • TCG TPM 2.0 Library, Parts 1–4 (Architecture, Structures, Commands, Support Routines).
  • TCG PC Client Platform TPM Profile (PTP).
  • TCG EK Credential Profile (the EK ECC-P256 L-2 template).

License

BSD-3-Clause. See LICENSE.

Documentation

Overview

Package tpm2 is the transport-agnostic, pure-Go TPM 2.0 command API. It sits one layer above github.com/go-tpm2/common: common owns the big-endian wire codec and the Transport contract, and this package turns that codec into typed TPM 2.0 commands.

A TPM value wraps a common.Transport. Each command method marshals its parameters (with common.BuildCommand and the appropriate TPM_ST tag and TPM_CC code), hands the buffer to Transport.Send, parses the reply with common.ParseResponse, checks the response code, and unmarshals the response parameters. A non-success response code is surfaced as a typed *TPMError carrying both the raw rc and the command code it came from.

All multi-byte fields are encoded big-endian, as the TPM 2.0 wire format mandates (TCG "TPM 2.0 Part 1: Architecture", "Data Marshaling").

This covers the measured-boot milestones: the starter set (Startup, Shutdown, SelfTest, GetRandom, GetCapability, PCR_Read, PCR_Extend); the attestation flow (CreatePrimary, Quote, VerifyQuote); the PCR-sealing payoff (CreateStoragePrimary, Create, Load, StartAuthSession, PolicyPCR, PolicyGetDigest, Unseal, plus the offline PolicyPCRDigest computation and the SealToPCR/UnsealWithPCR helpers — see seal.go); NV storage (NVDefineSpace, NVWrite, NVRead, NVReadPublic, NVUndefineSpace — see nv.go); typed GetCapability decoders (GetPCRBanks, GetTPMProperties, GetManufacturer, GetAlgorithms, GetHandles — see capability.go); the Endorsement Key (CreateEK, the EK Credential Profile ECC-P256 L-2 template — see ek.go); and Credential Protection / credential activation — the identity step of remote attestation that binds an Attestation Key to a TPM's Endorsement Key. That last flow is the off-TPM verifier side MakeCredential (ECDH + KDFe + KDFa + AES-CFB + HMAC, see makecred.go and the KDFa/KDFe helpers in kdf.go), the object Name it commits to (ObjectName, see name.go), and the attesting side TPM2_ActivateCredential with its dual-session authorization plus TPM2_PolicySecret to satisfy the EK policy, tied together by ActivateAKWithEK (see activate.go). Each method cites the relevant clause of TCG "TPM 2.0 Part 3: Commands" (with structure shapes from "Part 2: Structures", the policy digest / authorization rules from "Part 1: Architecture", and the EK template from the TCG "EK Credential Profile for TPM Family 2.0").

Conventions: pure Go, CGO_ENABLED=0, no architecture-specific assembly, BSD-3-Clause on every file, 100% statement coverage, and GOWORK=off (the module is not part of any workspace).

Index

Constants

View Source
const (
	// AlgRSA is TPM_ALG_RSA (an object/asymmetric algorithm). Unused by the
	// ECC AK flow but listed for completeness of the registry transcription.
	AlgRSA uint16 = 0x0001
	// AlgECDSA is TPM_ALG_ECDSA, the ECC signing scheme used by the AK.
	AlgECDSA uint16 = 0x0018
	// AlgECC is TPM_ALG_ECC, the object type of an ECC key.
	AlgECC uint16 = 0x0023
)

Algorithm identifiers used by the attestation flow that are not already in common's starter set. Values are TPM_ALG_ID per TCG "TPM 2.0 Part 2: Structures", clause "TPM_ALG_ID", as registered in the TCG "Algorithm Registry" (the "Part 4" algorithm IDs). They are encoded big-endian as UINT16 on the wire.

View Source
const (
	// PTFixed is PT_FIXED, the group base; it is also the property value to
	// start a fixed-property enumeration from.
	PTFixed uint32 = 0x00000100
	// PTManufacturer is TPM_PT_MANUFACTURER (PT_FIXED + 5): the four-character
	// vendor identifier (e.g. "IBM" for swtpm) packed big-endian into a UINT32.
	PTManufacturer uint32 = PTFixed + 5
)

TPM_PT property tags within TPM_CAP_TPM_PROPERTIES. TCG "TPM 2.0 Part 2: Structures", clause "TPM_PT". PTFixed (0x100) is the base of the fixed (manufacturer/firmware) property group.

View Source
const (
	// NVOwnerWrite (bit 1): owner authorization may write the index.
	NVOwnerWrite uint32 = 1 << 1
	// NVAuthWrite (bit 2): the index's authValue may authorize a write.
	NVAuthWrite uint32 = 1 << 2
	// NVOwnerRead (bit 17): owner authorization may read the index.
	NVOwnerRead uint32 = 1 << 17
	// NVAuthRead (bit 18): the index's authValue may authorize a read.
	NVAuthRead uint32 = 1 << 18
)

TPMA_NV attribute bits. TPMA_NV is a UINT32 bit field (encoded big-endian). TCG "TPM 2.0 Part 2: Structures", clause "TPMA_NV". The four bits below are the read/write controls an owner-authorized ordinary index sets: the index may be written/read either by presenting owner authorization (OWNERWRITE / OWNERREAD) or by presenting the index's own authValue (AUTHWRITE / AUTHREAD).

View Source
const (
	// ErrBadMagic is returned when a TPMS_ATTEST does not begin with
	// TPM_GENERATED_VALUE (0xFF544347).
	ErrBadMagic = common.Error("tpm2: attest magic is not TPM_GENERATED_VALUE")
	// ErrNotQuote is returned when a TPMS_ATTEST is not a TPM_ST_ATTEST_QUOTE.
	ErrNotQuote = common.Error("tpm2: attest type is not TPM_ST_ATTEST_QUOTE")
	// ErrSigInvalid is returned when the ECDSA signature over the attest does
	// not verify against the AK public key.
	ErrSigInvalid = common.Error("tpm2: ECDSA signature does not verify")
	// ErrPCRDigestMismatch is returned when the quoted pcrDigest does not
	// equal the SHA-256 of the concatenated expected PCR values.
	ErrPCRDigestMismatch = common.Error("tpm2: quoted pcrDigest != recomputed PCR digest")
)

Attestation/verification error sentinels (typed and constant for ==).

View Source
const (
	// ECCNistP256 is TPM_ECC_NIST_P256, the NIST P-256 (secp256r1) curve.
	ECCNistP256 uint16 = 0x0003
)

TPM_ECC_CURVE identifiers. TCG "TPM 2.0 Part 2: Structures", clause "TPM_ECC_CURVE"; the registered curve values are in the TCG "Algorithm Registry". Encoded big-endian as UINT16.

View Source
const ErrEKPointNotOnCurve = common.Error("tpm2: EK public point is not on the P-256 curve")

ErrEKPointNotOnCurve is returned by MakeCredential when the supplied EK public point does not lie on NIST P-256 (a malformed or wrong EK public).

View Source
const ErrPropertyNotFound = common.Error("tpm2: requested TPM property not found")

ErrPropertyNotFound is returned when a requested TPM_PT property is absent from the TPM's reply.

View Source
const ErrSRKPointNotOnCurve = common.Error("tpm2: SRK public point is not on the P-256 curve")

ErrSRKPointNotOnCurve is returned by WrapToPCR when the supplied storage-key public point does not lie on NIST P-256 (a malformed or wrong SRK public).

View Source
const ErrUnexpectedCapability = common.Error("tpm2: unexpected capability in response")

ErrUnexpectedCapability is returned when the TPM echoes a capability selector in TPMS_CAPABILITY_DATA other than the one requested.

View Source
const ErrUnexpectedSigAlg = common.Error("tpm2: unexpected signature algorithm (want ECDSA)")

ErrUnexpectedSigAlg is returned when a TPMT_SIGNATURE carries a signature algorithm other than the ECDSA this package decodes.

View Source
const ErrUnsupportedNameAlg = common.Error("tpm2: unsupported nameAlg for ObjectName (want SHA-256)")

ErrUnsupportedNameAlg is returned by ObjectName when the public area's nameAlg is not one this package can compute a Name for (only SHA-256 is supported, matching the AK/EK templates).

View Source
const RHEndorsement uint32 = 0x4000000B

RHEndorsement is TPM_RH_ENDORSEMENT, the endorsement hierarchy permanent handle under which the EK primary is created. TCG "TPM 2.0 Part 2: Structures", clause "TPM_RH (Permanent Handles)".

View Source
const RHOwner uint32 = 0x40000001

RHOwner is TPM_RH_OWNER (the storage/owner hierarchy permanent handle), the authHandle under which these NV operations are authorized. It mirrors common.RHOwner as a bare uint32 for this package's marshaling helpers. TCG "TPM 2.0 Part 2: Structures", clause "TPM_RH (Permanent Handles)".

View Source
const TPM_RS_PW uint32 = 0x40000009

TPM_RS_PW is the password-authorization session handle: the well-known handle that selects a cleartext-password authorization in a command's session area, in lieu of a real HMAC/policy session. TCG "TPM 2.0 Part 2: Structures", clause "TPM_RS (Reserved Handles)"; used by the password authorization described in "TPM 2.0 Part 1: Architecture", "Password Authorizations".

Variables

This section is empty.

Functions

func KDFa added in v0.5.0

func KDFa(key []byte, label string, contextU, contextV []byte, bits int) []byte

KDFa implements the TPM 2.0 SP800-108 counter-mode HMAC KDF (KDFa) with SHA-256 as the hash. It derives `bits` bits of key material from `key`, bound to a textual `label` and a (contextU || contextV) context.

Per TCG "TPM 2.0 Part 1: Architecture", "Key Derivation Functions" (KDFa()):

KDFa(hashAlg, key, label, contextU, contextV, bits):
  bytes = ceil(bits / 8)
  for i = 1, 2, ... until `bytes` produced:
    K_i = HMAC(key, BE32(i)            ||  // the SP800-108 counter
                    label || 0x00      ||  // Label, NULL-terminated
                    contextU           ||  // PartyUInfo
                    contextV           ||  // PartyVInfo
                    BE32(bits))            // the requested bit length
  result = (K_1 || K_2 || ...) truncated to `bytes`

When `bits` is not a whole number of bytes the leftmost `bits` bits are kept and the excess low-order bits of the last produced byte are masked to zero (TCG "Part 1": "the resulting string of bits is the least number of bytes ... the bits not used are zeroed"). For the 128- and 256-bit uses in Credential Protection `bits` is byte-aligned, but the masking is implemented for completeness/correctness.

func KDFe added in v0.5.0

func KDFe(z []byte, label string, partyU, partyV []byte, bits int) []byte

KDFe implements the TPM 2.0 SP800-56A concatenation KDF (KDFe) with SHA-256 as the hash. It derives `bits` bits of shared key material from the ECDH shared secret `z` (the x-coordinate of the shared point), bound to a textual `label` and the two parties' contributions partyU and partyV.

Per TCG "TPM 2.0 Part 1: Architecture", "Key Derivation Functions" (KDFe()):

KDFe(hashAlg, Z, label, partyUInfo, partyVInfo, bits):
  bytes = ceil(bits / 8)
  for counter = 1, 2, ... until `bytes` produced:
    K_i = H(BE32(counter)        ||  // SP800-56A counter, starts at 1
            Z                    ||  // the ECDH shared secret (point x)
            label || 0x00        ||  // the NULL-terminated label
            partyUInfo           ||  // initiator contribution
            partyVInfo)              // responder contribution
  result = (K_1 || K_2 || ...) truncated to `bytes`

For Credential Protection the seed is a single SHA-256 block (256 bits), so exactly one iteration runs; the loop and masking handle the general case.

func ObjectName added in v0.5.0

func ObjectName(publicArea []byte) ([]byte, error)

ObjectName computes the Name of an object from its public area (the TPMT_PUBLIC bytes, i.e. the CONTENTS of the TPM2B_PUBLIC, without the 2-byte size prefix). For an object whose nameAlg is a hash algorithm, the Name is:

Name = nameAlg (UINT16, big-endian) || H_nameAlg( TPMT_PUBLIC )

where the hash covers the entire marshaled TPMT_PUBLIC. The nameAlg is the second UINT16 of the public area (after the 2-byte `type`). This package supports the SHA-256 nameAlg used by the AK/EK templates; a public area with any other nameAlg is rejected. TCG "Part 1", "Names" (the Name of a loaded object); "Part 2", "TPMT_PUBLIC" (field order) and "TPM2B_NAME".

The returned Name is the bare (algorithm-id || digest) value — NOT wrapped in a TPM2B. Callers that need a TPM2B_NAME wrap it with common.MarshalTPM2B.

func PolicyPCRDigest added in v0.3.0

func PolicyPCRDigest(sel []PCRSelection, pcrValues [][]byte) []byte

PolicyPCRDigest computes, offline, the policyDigest that a TPM2_PolicyPCR over a SHA-256 policy session would accumulate for the given selection and PCR values. It is the value placed in the sealed object's authPolicy so the TPM will release the secret only to a session whose policyDigest matches.

Per TCG "TPM 2.0 Part 1: Architecture", clause "TPM2_PolicyPCR" / "Policy Digest" (the policy update rule), with SHA-256 as the session's authHash:

policyDigestStart = 0x00 * 32                  (an all-zero digest)
pcrDigest         = SHA256( value_0 || value_1 || ... )  over the
                    selected PCR values in selection order
policyDigest      = SHA256( policyDigestStart
                            || TPM_CC_PolicyPCR (0x0000017F, big-endian)
                            || marshal(TPML_PCR_SELECTION pcrs)
                            || pcrDigest )

The TPML_PCR_SELECTION marshaled into the hash is the SAME selection sent to TPM2_PolicyPCR. sel and pcrValues must be in corresponding order (the ascending order PCRRead returns). TCG "TPM 2.0 Part 1", "Policy PCR"; "Part 3", "TPM2_PolicyPCR".

Types

type AKPublic added in v0.2.0

type AKPublic struct {
	// X is the big-endian x coordinate of the public point.
	X []byte
	// Y is the big-endian y coordinate of the public point.
	Y []byte
}

AKPublic is the parsed public part of an ECC Attestation Key: the affine coordinates of the public point on NIST P-256, big-endian. X and Y are the raw TPM2B contents of the TPMS_ECC_POINT in the key's TPMT_PUBLIC.unique field. TCG "TPM 2.0 Part 2: Structures", clauses "TPMS_ECC_POINT" and "TPMT_PUBLIC".

type AlgProperty added in v0.4.0

type AlgProperty struct {
	// Alg is the TPM_ALG_ID.
	Alg uint16
	// Attributes is the TPMA_ALGORITHM bit field (asymmetric, symmetric,
	// hash, object, signing, encrypting, method).
	Attributes uint32
}

AlgProperty is one entry of a TPML_ALG_PROPERTY: an algorithm id and its TPMA_ALGORITHM attribute bits. TCG "TPM 2.0 Part 2: Structures", clause "TPMS_ALG_PROPERTY".

type AttestInfo added in v0.2.0

type AttestInfo struct {
	// ExtraData echoes the caller's qualifyingData nonce (the TPM2B_DATA
	// extraData field), so a verifier can bind the quote to its challenge.
	ExtraData []byte
	// Quote is the TPMS_QUOTE_INFO union member.
	Quote QuoteInfo
}

AttestInfo is the parsed, attestation-relevant subset of a TPMS_ATTEST of type TPM_ST_ATTEST_QUOTE. TCG "TPM 2.0 Part 2: Structures", clause "TPMS_ATTEST".

func ParseAttest added in v0.2.0

func ParseAttest(data []byte) (AttestInfo, error)

ParseAttest decodes a TPMS_ATTEST of type TPM_ST_ATTEST_QUOTE from the raw TPM2B_ATTEST data (i.e. the bytes that were signed, NOT including the TPM2B size prefix). The fixed prefix is:

magic            : UINT32  (must be 0xFF544347)
type             : TPM_ST  (UINT16; must be 0x8018 ATTEST_QUOTE)
qualifiedSigner  : TPM2B_NAME
extraData        : TPM2B_DATA
clockInfo        : TPMS_CLOCK_INFO = clock:u64, resetCount:u32,
                                     restartCount:u32, safe:u8  (17 bytes)
firmwareVersion  : UINT64
attested         : TPMS_QUOTE_INFO = TPML_PCR_SELECTION pcrSelect,
                                      TPM2B_DIGEST pcrDigest

TCG "TPM 2.0 Part 2: Structures", clauses "TPMS_ATTEST", "TPMS_CLOCK_INFO", "TPMS_QUOTE_INFO".

func VerifyQuote added in v0.2.0

func VerifyQuote(akPub AKPublic, quoted []byte, sig ECDSASignature, expectedPCRs [][]byte) (AttestInfo, error)

VerifyQuote performs the full attestation check for an ECDSA-P256 AK:

  1. parse the TPMS_ATTEST from quoted (the TPM2B_ATTEST.data) and confirm it is a genuine TPM_GENERATED quote;
  2. verify the ECDSA signature (sig.R, sig.S) over SHA-256(quoted) using the AK public point (akPub.X, akPub.Y) on NIST P-256;
  3. recompute the PCR digest as SHA-256(expectedPCRs[0] || expectedPCRs[1] || ...) and confirm it equals the pcrDigest the TPM committed to.

expectedPCRs must be the selected PCR values, in the TPM's ascending selection order, exactly as PCRRead returns them. On success it returns the parsed AttestInfo (so callers can also check extraData against their nonce).

All cryptography is pure-Go (crypto/ecdsa + crypto/elliptic P-256), so the helper is usable from a tamago guest with CGO disabled.

type ECCPublic added in v0.6.0

type ECCPublic struct {
	// X is the big-endian x coordinate of the storage key's public point.
	X []byte
	// Y is the big-endian y coordinate of the storage key's public point.
	Y []byte
}

ECCPublic is an ECC P-256 public point (big-endian x, y) — the storage key's public the node exposes to the control plane. It is the same shape as EKPublic/AKPublic; a distinct name documents its role as the duplication PARENT public. TCG "TPM 2.0 Part 2: Structures", "TPMS_ECC_POINT".

type ECDSASignature added in v0.2.0

type ECDSASignature struct {
	// R is the big-endian r component.
	R []byte
	// S is the big-endian s component.
	S []byte
}

ECDSASignature is a parsed TPMS_SIGNATURE_ECDSA: the (r, s) pair as big-endian byte strings. TCG "TPM 2.0 Part 2: Structures", clause "TPMS_SIGNATURE_ECDSA".

type EKPublic added in v0.4.0

type EKPublic struct {
	// X is the big-endian x coordinate of the public point.
	X []byte
	// Y is the big-endian y coordinate of the public point.
	Y []byte
}

EKPublic is the parsed public point of the ECC Endorsement Key: the affine (x, y) coordinates on NIST P-256, big-endian, from the TPMS_ECC_POINT in the key's TPMT_PUBLIC.unique. TCG "TPM 2.0 Part 2: Structures", clauses "TPMS_ECC_POINT" and "TPMT_PUBLIC".

type MakeCredentialResult added in v0.5.0

type MakeCredentialResult struct {
	// CredentialBlob is the TPM2B_ID_OBJECT { integrityHMAC || encIdentity }
	// (the size-prefixed id-object), ready to pass as ActivateCredential's
	// credentialBlob parameter.
	CredentialBlob []byte
	// Secret is the TPM2B_ENCRYPTED_SECRET carrying the ephemeral public point
	// (TPMS_ECC_POINT), ready to pass as ActivateCredential's secret parameter.
	Secret []byte
}

MakeCredentialResult is the output of MakeCredential: the two blobs a verifier hands to the attesting platform for TPM2_ActivateCredential.

func MakeCredential added in v0.5.0

func MakeCredential(ekPublic EKPublic, akName, credential []byte, rng io.Reader) (MakeCredentialResult, error)

MakeCredential performs the off-TPM verifier side of Credential Protection for an ECC P-256 Endorsement Key. ekPublic is the EK's public point, akName is the loaded AK's Name (from ObjectName), and credential is the secret challenge to bind (a TPM2B_DIGEST payload, at most 32 bytes for the SHA-256 scheme). It returns the credentialBlob and secret to feed to TPM2_ActivateCredential.

The ephemeral key is drawn from crypto/rand; rng may override the source for deterministic testing (pass nil for crypto/rand).

type NVPublic added in v0.4.0

type NVPublic struct {
	// Index is the NV index handle (nvIndex), a handle in the 0x01xxxxxx
	// (TPM_HT_NV_INDEX) range.
	Index uint32
	// NameAlg is the index's name algorithm (a TPM_ALG_ID).
	NameAlg uint16
	// Attributes is the TPMA_NV attribute bit field.
	Attributes uint32
	// AuthPolicy is the index's authorization policy digest (empty for an
	// index with no policy).
	AuthPolicy []byte
	// DataSize is the size in bytes of the index's data area.
	DataSize uint16
}

NVPublic is the parsed public area of an NV index: the typed form of the TPMS_NV_PUBLIC wire structure. TCG "TPM 2.0 Part 2: Structures", clause "TPMS_NV_PUBLIC".

type PCRSelection

type PCRSelection struct {
	// Hash is the bank's hash algorithm (a TPM_ALG_ID value, e.g.
	// common.AlgSHA256).
	Hash uint16
	// PCRs is the list of PCR indices selected in this bank.
	PCRs []int
}

PCRSelection names a set of PCRs within one PCR bank, identified by its hash algorithm. It is the typed form of the TPMS_PCR_SELECTION wire structure { hash, sizeofSelect, pcrSelect[] }. TCG "TPM 2.0 Part 2: Structures", clause "TPMS_PCR_SELECTION".

type QuoteInfo added in v0.2.0

type QuoteInfo struct {
	// PCRSelect is the TPML_PCR_SELECTION the TPM committed to.
	PCRSelect []PCRSelection
	// PCRDigest is the TPM2B_DIGEST: H(concatenation of selected PCR values).
	PCRDigest []byte
}

QuoteInfo is the parsed TPMS_QUOTE_INFO carried in a quote attestation: the set of PCRs that were quoted and the digest the TPM computed over their values. TCG "TPM 2.0 Part 2: Structures", clause "TPMS_QUOTE_INFO".

type TPM

type TPM struct {
	// contains filtered or unexported fields
}

TPM is the command-layer handle. It wraps a common.Transport and issues fully-marshaled TPM 2.0 commands through it. It holds no state of its own; concurrency and framing are the transport's concern.

func New

func New(t common.Transport) *TPM

New returns a TPM that issues commands over t.

func (*TPM) ActivateAKWithEK added in v0.5.0

func (tpm *TPM) ActivateAKWithEK(ak, ek uint32, akPublic, ekPublic, credential, nonceCaller []byte) ([]byte, error)

ActivateAKWithEK ties the whole credential-activation flow together. It computes MakeCredential OFF the TPM from ekPublic and the AK's Name (derived from akPublic), opens a policy session and satisfies the EK policy with PolicySecret(TPM_RH_ENDORSEMENT), then runs TPM2_ActivateCredential with the AK as activateHandle and the EK as keyHandle. It returns the recovered credential, which a caller asserts equals the original challenge — proving the AK and EK are on the same TPM.

ak and ek are the loaded transient handles; akPublic and ekPublic are the corresponding TPMT_PUBLIC bytes (the contents of TPM2B_PUBLIC, used to derive the AK Name and the EK point). credential is the challenge to bind. nonceCaller is the 32-byte caller nonce for the policy session.

func (*TPM) ActivateCredential added in v0.5.0

func (tpm *TPM) ActivateCredential(activateHandle, keyHandle, policySession uint32, credentialBlob, secret []byte) ([]byte, error)

ActivateCredential runs TPM2_ActivateCredential. activateHandle is the AK (authorized with the empty RS_PW password, since the AK has userWithAuth); keyHandle is the EK (authorized with a POLICY SESSION that already satisfies the EK policy via PolicySecret). credentialBlob is the TPM2B_ID_OBJECT and secret the TPM2B_ENCRYPTED_SECRET produced by MakeCredential — each passed as its complete, already-TPM2B-wrapped wire form (i.e. the MakeCredentialResult fields verbatim). The TPM unwraps them and returns certInfo: the recovered credential (a TPM2B_DIGEST), which equals the original iff the AK Name and EK match.

Body (TagSessions) — note TWO handles and TWO sessions:

handle area: activateHandle (u32) = AK || keyHandle (u32) = EK
auth   area: authorizationSize (u32) ||
             TPMS_AUTH_COMMAND #1 (AK:  RS_PW, empty) ||
             TPMS_AUTH_COMMAND #2 (EK:  policy session)
param  area: TPM2B_ID_OBJECT credentialBlob || TPM2B_ENCRYPTED_SECRET secret

The two sessions are in handle order: the AK's password session first (activateHandle is handle #1), the EK's policy session second (keyHandle is handle #2). TCG "Part 3", "TPM2_ActivateCredential".

Response (TagSessions): parameterSize (u32) || TPM2B_DIGEST certInfo.

func (*TPM) Create added in v0.3.0

func (tpm *TPM) Create(parent uint32, secret, policyDigest []byte) (priv, pub []byte, err error)

Create runs TPM2_Create under the parent (empty-auth password session), creating a sealed keyedhash object that holds `secret` and is bound to `policyDigest`. It returns the object's TPM2B_PRIVATE and TPM2B_PUBLIC, which Load later turns back into a transient handle.

Body (TagSessions):

handle area: parentHandle (u32)
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: TPM2B_SENSITIVE_CREATE inSensitive (userAuth empty, the
             secret as data) || TPM2B_PUBLIC inPublic (keyedhash, sealed
             object, authPolicy=policyDigest) || TPM2B_DATA outsideInfo
             (empty) || TPML_PCR_SELECTION creationPCR (count 0)

Wire: TagSessions, CC 0x00000153. TCG "Part 3", "TPM2_Create".

Response (TagSessions): parameterSize (u32) || TPM2B_PRIVATE outPrivate || TPM2B_PUBLIC outPublic || (creation data/hash/ticket, not parsed).

func (*TPM) CreateEK added in v0.4.0

func (tpm *TPM) CreateEK() (ekHandle uint32, pub EKPublic, err error)

CreateEK runs TPM2_CreatePrimary under TPM_RH_ENDORSEMENT (empty-auth password session) with the EK Credential Profile L-2 ECC P-256 template, creating the Endorsement Key. It returns the transient EK handle and the EK's public point.

Body (TagSessions):

handle area: primaryHandle (u32) = TPM_RH_ENDORSEMENT
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: TPM2B_SENSITIVE_CREATE (empty) || TPM2B_PUBLIC (EK template) ||
             TPM2B_DATA outsideInfo (empty) || TPML_PCR_SELECTION (count 0)

Wire: TagSessions, CC 0x00000131. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_CreatePrimary"; "EK Credential Profile", "Template L-2".

Response: objectHandle (u32) || parameterSize (u32) || TPM2B_PUBLIC outPublic (from which the EK point is extracted) || trailing creation fields.

func (*TPM) CreatePrimary added in v0.2.0

func (tpm *TPM) CreatePrimary() (akHandle uint32, pub AKPublic, err error)

CreatePrimary runs TPM2_CreatePrimary under TPM_RH_OWNER with empty password authorization, creating a primary ECC P-256 restricted signing Attestation Key. It returns the transient object handle the TPM assigned to the new key and the key's public point.

This command carries a session (authorization) area, so the body has three regions (TCG "TPM 2.0 Part 1: Architecture", "Command Authorization Area Structure"):

handle area: primaryHandle (u32) = TPM_RH_OWNER
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: TPM2B_SENSITIVE_CREATE || TPM2B_PUBLIC ||
             TPM2B_DATA outsideInfo || TPML_PCR_SELECTION creationPCR

The TPM2B_SENSITIVE_CREATE wraps a TPMS_SENSITIVE_CREATE with empty userAuth and empty data (the TPM originates the key material), so its inner bytes are two empty TPM2B (0x0000 0x0000), size-prefixed to 0x0004.

The TPM2B_PUBLIC wraps a TPMT_PUBLIC describing the ECC restricted signing key (see marshalECCAKPublic). outsideInfo is an empty TPM2B_DATA, and creationPCR is an empty TPML_PCR_SELECTION (count 0).

Wire: TagSessions, CC 0x00000131. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_CreatePrimary".

Response (TCG "Part 3", "TPM2_CreatePrimary" response):

handle area: objectHandle (u32)
(no auth area parsed here: it is present but trailing)
param  area: parameterSize (u32, present because TagSessions) ||
             TPM2B_PUBLIC outPublic || TPM2B_CREATION_DATA creationData ||
             TPM2B_DIGEST creationHash || TPMT_TK_CREATION creationTicket ||
             TPM2B_NAME name

We parse outPublic to extract the AK's public point and consume (but do not further decode) the remaining creation fields.

func (*TPM) CreatePrimaryPublic added in v0.5.0

func (tpm *TPM) CreatePrimaryPublic() (akHandle uint32, outPublic []byte, err error)

CreatePrimaryPublic is CreatePrimary but also returns the AK's TPMT_PUBLIC bytes (the CONTENTS of the response TPM2B_PUBLIC, without the 2-byte size prefix). Those bytes are exactly what the TPM loaded, so ObjectName over them yields the AK's Name — the value MakeCredential must commit to. The credential-activation flow needs the Name, which cannot be reconstructed reliably from the template alone, so it is taken from the TPM's own outPublic here. TCG "Part 3", "TPM2_CreatePrimary" (outPublic); "Part 1", "Names".

func (*TPM) CreateStoragePrimary added in v0.3.0

func (tpm *TPM) CreateStoragePrimary() (handle uint32, err error)

CreateStoragePrimary runs TPM2_CreatePrimary under TPM_RH_OWNER with empty password authorization, creating a primary ECC P-256 RESTRICTED DECRYPT (storage) key — the standard SRK-shaped parent under which a sealed object is created and loaded. It returns the transient parent handle.

The TPMT_PUBLIC differs from the AK's in two ways that define a storage key (TCG "TPM 2.0 Part 2: Structures", "TPMT_PUBLIC", "TPMS_ECC_PARMS"):

  • objectAttributes = restricted|decrypt (storageObjectAttributes), not restricted|sign;
  • the TPMS_ECC_PARMS.symmetric is a real TPMT_SYM_DEF_OBJECT (AES-128-CFB), because a parent MUST carry a symmetric algorithm to protect (encrypt) the sensitive areas of its children. A signing key uses symmetric=NULL; a storage key cannot. (TCG "Part 1", "Protected Storage".)

scheme=NULL, curve=P256, kdf=NULL as for the AK.

Wire: TagSessions, CC 0x00000131. TCG "Part 3", "TPM2_CreatePrimary".

func (*TPM) CreateStoragePrimaryPub added in v0.6.0

func (tpm *TPM) CreateStoragePrimaryPub() (handle uint32, pub ECCPublic, err error)

CreateStoragePrimaryPub is CreateStoragePrimary that ALSO returns the storage key's public point (x, y). The node exposes that point to a control plane so it can WrapToPCR a secret to this exact key offline (the duplication ECDH is against this public). It is the same TPM2_CreatePrimary command; only the response parse extends to the TPM2B_PUBLIC outPublic's TPMS_ECC_POINT.

Wire: TagSessions, CC 0x00000131. Response: objectHandle (u32) || parameterSize (u32) || TPM2B_PUBLIC outPublic || (creation tail, ignored). TCG "TPM 2.0 Part 3: Commands", "TPM2_CreatePrimary"; "Part 2", "TPMS_ECC_POINT".

func (*TPM) GetAlgorithms added in v0.4.0

func (tpm *TPM) GetAlgorithms(firstAlg, count uint32) ([]AlgProperty, error)

GetAlgorithms runs TPM2_GetCapability(TPM_CAP_ALGS) starting at firstAlg and decodes the TPML_ALG_PROPERTY: a count followed by that many TPMS_ALG_PROPERTY { alg:u16, algProperties:TPMA_ALGORITHM(u32) }. TCG "TPM 2.0 Part 2: Structures", clauses "TPM_CAP_ALGS" / "TPML_ALG_PROPERTY".

func (*TPM) GetCapability

func (tpm *TPM) GetCapability(cap, prop, count uint32) (more bool, data []byte, err error)

GetCapability runs TPM2_GetCapability. cap is a TPM_CAP selector, prop is the first property to return, and count caps how many values to return. It reports moreData (whether the TPM has further values beyond this reply) and the raw TPMS_CAPABILITY_DATA blob.

The TPMS_CAPABILITY_DATA union is large and capability-specific; decoding it fully is a deliberate follow-up. The documented boundary here is: the returned data is everything after the one-byte moreData flag, i.e. the complete TPMS_CAPABILITY_DATA { capability:u32, data:union } as the TPM sent it, ready to be parsed by a capability-aware caller.

Wire request: TagNoSessions, CC 0x0000017A, parameters TPM_CAP capability (u32), UINT32 property, UINT32 propertyCount. Wire response: TPMI_YES_NO moreData (u8) followed by TPMS_CAPABILITY_DATA. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_GetCapability".

func (*TPM) GetHandles added in v0.4.0

func (tpm *TPM) GetHandles(firstHandle, count uint32) ([]uint32, error)

GetHandles runs TPM2_GetCapability(TPM_CAP_HANDLES) starting at firstHandle and decodes the TPML_HANDLE: a count followed by that many TPM_HANDLE (u32). The starting handle selects the handle range (e.g. 0x81000000 for persistent objects, 0x80000000 for transient). TCG "TPM 2.0 Part 2: Structures", clauses "TPM_CAP_HANDLES" / "TPML_HANDLE".

func (*TPM) GetManufacturer added in v0.4.0

func (tpm *TPM) GetManufacturer() (uint32, error)

GetManufacturer is a convenience over GetTPMProperties: it reads TPM_PT_MANUFACTURER and returns its raw UINT32 value (the four packed ASCII vendor characters, big-endian). TCG "TPM 2.0 Part 2: Structures", clause "TPM_PT".

func (*TPM) GetPCRBanks added in v0.4.0

func (tpm *TPM) GetPCRBanks() ([]PCRSelection, error)

GetPCRBanks runs TPM2_GetCapability(TPM_CAP_PCRS) and decodes the resulting TPML_PCR_SELECTION: the TPM's allocated PCR banks (one TPMS_PCR_SELECTION per bank, the selection bitmap marking which PCRs the bank implements). TCG "TPM 2.0 Part 2: Structures", clauses "TPM_CAP_PCRS" / "TPML_PCR_SELECTION".

func (*TPM) GetRandom

func (tpm *TPM) GetRandom(n uint16) ([]byte, error)

GetRandom runs TPM2_GetRandom and returns up to n bytes from the TPM's random number generator. The TPM may return fewer than n bytes.

Wire request: TagNoSessions, CC 0x0000017B, parameter UINT16 bytesRequested. Wire response: TPM2B_DIGEST randomBytes (a u16 size prefix followed by the bytes). TCG "TPM 2.0 Part 3: Commands", clause "TPM2_GetRandom".

func (*TPM) GetTPMProperties added in v0.4.0

func (tpm *TPM) GetTPMProperties(firstProp, count uint32) ([]TaggedProperty, error)

GetTPMProperties runs TPM2_GetCapability(TPM_CAP_TPM_PROPERTIES) starting at firstProp and decodes the TPML_TAGGED_TPM_PROPERTY: a count followed by that many TPMS_TAGGED_PROPERTY { property:u32, value:u32 }. TCG "TPM 2.0 Part 2: Structures", clauses "TPM_CAP_TPM_PROPERTIES" / "TPML_TAGGED_TPM_PROPERTY".

func (*TPM) Import added in v0.6.0

func (tpm *TPM) Import(parent uint32, objectPublic, duplicate, inSymSeed []byte) (outPrivate []byte, err error)

Import runs TPM2_Import under parent (empty-auth RS_PW password session), importing a duplicated object into the parent's hierarchy. It returns the object's outPrivate (TPM2B_PRIVATE), which Load turns into a transient handle.

objectPublic is the bare TPMT_PUBLIC (as produced by WrapToPCR); Import wraps it in a TPM2B_PUBLIC. duplicate and inSymSeed are WrapToPCR's outputs, each already a complete TPM2B (TPM2B_PRIVATE and TPM2B_ENCRYPTED_SECRET respectively) and emitted on the wire verbatim.

Body (TagSessions):

handle area: parentHandle (u32)
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: TPM2B_DATA encryptionKey (empty) ||
             TPM2B_PUBLIC objectPublic ||
             TPM2B_PRIVATE duplicate ||
             TPM2B_ENCRYPTED_SECRET inSymSeed ||
             TPMT_SYM_DEF_OBJECT symmetricAlg = TPM_ALG_NULL (no inner wrap)

encryptionKey is empty AND symmetricAlg is TPM_ALG_NULL because WrapToPCR produces an OUTER-WRAP-ONLY duplication: there is no INNER symmetric wrap of the sensitive area. The Import symmetricAlg argument describes the optional INNER wrapper on `duplicate`, NOT the parent's child-protection cipher; with no inner wrap it MUST be NULL and encryptionKey MUST be empty (per TCG and the go-tpm reference: "encryptionKey and sym non-nil iff an inner wrapper is used"). Passing the parent's AES-128-CFB here makes the TPM expect a non-empty encryptionKey of the AES key size and reject the empty one with TPM_RC_SIZE on parameter 1 (rc 0x1D5) — the bug a real swtpm caught. Wire: TagSessions, CC 0x00000156. TCG "TPM 2.0 Part 3: Commands", "TPM2_Import"; "Part 2", "TPMT_SYM_DEF_OBJECT".

Response (TagSessions): parameterSize (u32) || TPM2B_PRIVATE outPrivate.

func (*TPM) ImportAndUnseal added in v0.6.0

func (tpm *TPM) ImportAndUnseal(parent uint32, objectPublic, duplicate, inSymSeed []byte, sel []PCRSelection, nonceCaller []byte) ([]byte, error)

ImportAndUnseal is the high-level node-side recover path: it TPM2_Imports the duplicated object under parent, TPM2_Loads the resulting (outPrivate, objectPublic) into a transient handle, opens a fresh PCR policy session, runs PolicyPCR(sel) against the node's CURRENT PCR state, and Unseals. It returns the recovered secret on success, or the TPM's error — a policy failure (TPM_RC_POLICY_FAIL) if the live PCRs no longer match the values the control plane wrapped to.

objectPublic, duplicate, inSymSeed are exactly WrapToPCR's outputs; sel is the same PCR selection used at wrap time. nonceCaller is the 32-byte caller nonce for the policy session. This ties TPM2_Import -> TPM2_Load -> TPM2_PolicyPCR -> TPM2_Unseal into one call.

func (*TPM) Load added in v0.3.0

func (tpm *TPM) Load(parent uint32, priv, pub []byte) (handle uint32, err error)

Load runs TPM2_Load under the parent (empty-auth password session), turning a sealed object's (priv, pub) pair back into a transient object handle usable by Unseal.

Body (TagSessions):

handle area: parentHandle (u32)
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: TPM2B_PRIVATE inPrivate || TPM2B_PUBLIC inPublic

Wire: TagSessions, CC 0x00000157. TCG "Part 3", "TPM2_Load".

Response (TagSessions): objectHandle (u32) || parameterSize (u32) || TPM2B_NAME name (not parsed).

func (*TPM) NVDefineSpace added in v0.4.0

func (tpm *TPM) NVDefineSpace(pub NVPublic) error

NVDefineSpace runs TPM2_NV_DefineSpace under TPM_RH_OWNER (empty-auth password session), defining an ordinary NV index. The new index gets an empty authValue (the TPM2B_AUTH inSensitive is empty) and the public area built from the supplied NVPublic.

Body (TagSessions):

handle area: authHandle (u32) = TPM_RH_OWNER
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: TPM2B_AUTH auth (empty) || TPM2B_NV_PUBLIC publicInfo

Wire: TagSessions, CC 0x0000012A. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_NV_DefineSpace".

func (*TPM) NVRead added in v0.4.0

func (tpm *TPM) NVRead(nvIndex uint32, size, offset uint16) ([]byte, error)

NVRead runs TPM2_NV_Read under TPM_RH_OWNER (empty-auth password session), reading size bytes from the index at nvIndex starting at offset. The OWNER authorization is accepted because the index carries OWNERREAD.

Body (TagSessions):

handle area: authHandle (u32) = TPM_RH_OWNER || nvIndex (u32)
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: UINT16 size || UINT16 offset

Wire: TagSessions, CC 0x0000014E. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_NV_Read".

Response (TagSessions): parameterSize (u32) || TPM2B_MAX_NV_BUFFER data.

func (*TPM) NVReadPublic added in v0.4.0

func (tpm *TPM) NVReadPublic(nvIndex uint32) (NVPublic, []byte, error)

NVReadPublic runs TPM2_NV_ReadPublic, reading back the public area and the computed Name of the index at nvIndex. It carries no authorization (the public area is world-readable).

Body (TagNoSessions):

handle area: nvIndex (u32)
(no parameters)

Wire: TagNoSessions, CC 0x00000169. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_NV_ReadPublic".

Response (TagNoSessions): TPM2B_NV_PUBLIC nvPublic || TPM2B_NAME nvName.

func (*TPM) NVUndefineSpace added in v0.4.0

func (tpm *TPM) NVUndefineSpace(nvIndex uint32) error

NVUndefineSpace runs TPM2_NV_UndefineSpace under TPM_RH_OWNER (empty-auth password session), deleting the index at nvIndex.

Body (TagSessions):

handle area: authHandle (u32) = TPM_RH_OWNER || nvIndex (u32)
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
(no parameters)

Wire: TagSessions, CC 0x00000122. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_NV_UndefineSpace".

func (*TPM) NVWrite added in v0.4.0

func (tpm *TPM) NVWrite(nvIndex uint32, data []byte, offset uint16) error

NVWrite runs TPM2_NV_Write under TPM_RH_OWNER (empty-auth password session), writing data into the index at nvIndex starting at offset. The authHandle is the OWNER hierarchy, which authorizes the write because the index carries OWNERWRITE.

Body (TagSessions):

handle area: authHandle (u32) = TPM_RH_OWNER || nvIndex (u32)
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: TPM2B_MAX_NV_BUFFER data || UINT16 offset

Wire: TagSessions, CC 0x00000137. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_NV_Write".

func (*TPM) PCRExtend

func (tpm *TPM) PCRExtend(pcr int, hash uint16, digest []byte) error

PCRExtend runs TPM2_PCR_Extend, folding digest into PCR pcr in the bank named by hash (a TPM_ALG_ID). It authorizes the operation with a cleartext password session over the empty authValue — the standard way to extend an unowned platform PCR.

This is the one command in the starter set that carries an authorization area, so the body is laid out in three regions (TCG "TPM 2.0 Part 1: Architecture", "Command Authorization Area Structure"):

handle area: pcrHandle (u32)                          — the PCR being extended
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND
param  area: TPML_DIGEST_VALUES                        — { count, {hashAlg, digest} }

The TPMS_AUTH_COMMAND is the password session:

sessionHandle      = TPM_RS_PW (0x40000009)
nonce              = empty TPM2B               -> 0x0000
sessionAttributes  = continueSession (0x01)
hmac               = empty TPM2B               -> 0x0000

which is 9 bytes on the wire (4 sessionHandle + 2 empty-nonce size + 1 attributes + 2 empty-hmac size), so authorizationSize is 0x00000009. The 0x00 high byte of TPM_HT_PCR (the PCR handle type) is INFERRED from "Part 2: Structures", "TPM_HT (Handle Types)": PCR handles occupy 0x00000000..0x0000001F, so PCR[n] marshals as the bare index n.

Wire: TagSessions, CC 0x00000182. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_PCR_Extend"; TPML_DIGEST_VALUES / TPMT_HA per "Part 2: Structures".

func (*TPM) PCRRead

func (tpm *TPM) PCRRead(sel []PCRSelection) (updateCounter uint32, digests [][]byte, err error)

PCRRead runs TPM2_PCR_Read for the banks/PCRs named in sel. It returns the PCR update counter, and the digests in the order the TPM returns them (which follows the selection's ascending PCR order, possibly split across multiple replies when more PCRs are selected than fit in one response — this starter reads exactly one reply).

Wire request: TagNoSessions, CC 0x0000017E, parameter TPML_PCR_SELECTION pcrSelectionIn. Wire response: UINT32 pcrUpdateCounter, TPML_PCR_SELECTION pcrSelectionOut (echoed; skipped here), TPML_DIGEST pcrValues (a u32 count followed by that many TPM2B_DIGEST). TCG "TPM 2.0 Part 3: Commands", clause "TPM2_PCR_Read".

func (*TPM) PolicyGetDigest added in v0.3.0

func (tpm *TPM) PolicyGetDigest(session uint32) ([]byte, error)

PolicyGetDigest runs TPM2_PolicyGetDigest, reading back the current policyDigest accumulated in the session. It is a debugging aid: comparing it to PolicyPCRDigest confirms the offline construction matches what the TPM computed.

Body (TagNoSessions): handle area policySession (u32). Response: TPM2B_DIGEST policyDigest.

Wire: TagNoSessions, CC 0x00000189. TCG "Part 3", "TPM2_PolicyGetDigest".

func (*TPM) PolicyPCR added in v0.3.0

func (tpm *TPM) PolicyPCR(session uint32, sel []PCRSelection) error

PolicyPCR runs TPM2_PolicyPCR in the given policy session, extending the session's policyDigest by the selected PCRs. pcrDigest is passed as an EMPTY TPM2B so the TPM uses the CURRENT PCR values to compute the digest it folds in — which means the session's resulting policyDigest equals PolicyPCRDigest(sel, currentPCRs). Unseal then succeeds iff that equals the sealed object's authPolicy.

Body (TagNoSessions — PolicyPCR's session handle is in the HANDLE area, not an auth area):

handle area: policySession (u32)
param  area: TPM2B_DIGEST pcrDigest (empty) || TPML_PCR_SELECTION pcrs

Wire: TagNoSessions, CC 0x0000017F. TCG "Part 3", "TPM2_PolicyPCR".

func (*TPM) PolicySecret added in v0.5.0

func (tpm *TPM) PolicySecret(authHandle, policySession uint32) error

PolicySecret runs TPM2_PolicySecret in policySession, asserting knowledge of authHandle's authValue. For the EK policy, authHandle is TPM_RH_ENDORSEMENT authorized with the empty endorsement password (RS_PW), and the call extends the session's policyDigest by the PolicySecret assertion. With empty nonceTPM, cpHashA, policyRef and expiration=0, the resulting policyDigest equals the well-known EK authPolicy (= policyDigest of TPM2_PolicySecret(TPM_RH_ENDORSEMENT)).

Body (TagSessions):

handle area: authHandle (u32) = TPM_RH_ENDORSEMENT
             policySession (u32)              — second handle, NO auth area
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: TPM2B_NONCE nonceTPM (empty) || TPM2B_DIGEST cpHashA (empty) ||
             TPM2B_NONCE policyRef (empty) || INT32 expiration (0)

Only authHandle requires authorization, so the auth area carries exactly ONE TPMS_AUTH_COMMAND (for authHandle); policySession sits in the handle area without an auth entry. TCG "Part 3", "TPM2_PolicySecret".

Response (TagSessions): parameterSize (u32) || TPM2B_TIMEOUT timeout || TPMT_TK_AUTH policyTicket. Neither is needed when expiration is 0, so they are not parsed.

func (*TPM) Quote added in v0.2.0

func (tpm *TPM) Quote(keyHandle uint32, qualifyingData []byte, sel []PCRSelection) (quoted []byte, sig ECDSASignature, err error)

Quote runs TPM2_Quote: the AK signs a TPMS_ATTEST that commits to the current values of the selected PCRs, with qualifyingData (a caller nonce) folded into the attest's extraData. It returns the raw TPM2B_ATTEST data (the bytes that were signed) and the parsed ECDSA signature.

Body (TagSessions):

handle area: keyHandle (u32) = the AK handle from CreatePrimary
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND (RS_PW, empty)
param  area: TPM2B_DATA qualifyingData || TPMT_SIG_SCHEME inScheme ||
             TPML_PCR_SELECTION pcrSelect

inScheme is passed as TPM_ALG_NULL (0x0010) to use the key's own scheme (ECDSA+SHA256); a NULL scheme has no details, so it marshals as the bare 2-byte algorithm id.

Wire: TagSessions, CC 0x00000158. TCG "TPM 2.0 Part 3: Commands", clause "TPM2_Quote".

Response:

param area: parameterSize (u32, present for TagSessions) ||
            TPM2B_ATTEST quoted || TPMT_SIGNATURE signature

func (*TPM) SealToPCR added in v0.3.0

func (tpm *TPM) SealToPCR(parent uint32, secret []byte, sel []PCRSelection, pcrValues [][]byte) (priv, pub, policyDigest []byte, err error)

SealToPCR is the high-level seal helper: it computes the PCR-bound policyDigest from the given selection and PCR values, then runs TPM2_Create under parent to produce a sealed object holding secret. It returns the object's (priv, pub) blobs and the policyDigest it sealed to (so a caller can record/compare it). The PCR values must be the ones the object should later unseal against (typically the result of PCRRead just before sealing).

func (*TPM) SelfTest

func (tpm *TPM) SelfTest(full bool) error

SelfTest runs TPM2_SelfTest. When full is true (TPMI_YES_NO == YES) the TPM tests every implemented function; when false it tests only those not yet tested since the last reset.

Wire: TagNoSessions, CC 0x00000143, parameter TPMI_YES_NO fullTest (u8; YES = 1, NO = 0). TCG "TPM 2.0 Part 3: Commands", clause "TPM2_SelfTest"; TPMI_YES_NO per "Part 2: Structures".

func (*TPM) Shutdown

func (tpm *TPM) Shutdown(su uint16) error

Shutdown runs TPM2_Shutdown, preparing the TPM for power loss. su is a TPM_SU: common.SUClear or common.SUState.

Wire: TagNoSessions, CC 0x00000145, parameter TPM_SU shutdownType (u16). TCG "TPM 2.0 Part 3: Commands", clause "TPM2_Shutdown".

func (*TPM) StartAuthSession added in v0.3.0

func (tpm *TPM) StartAuthSession(nonceCaller []byte) (sessionHandle uint32, nonceTPM []byte, err error)

StartAuthSession runs TPM2_StartAuthSession to open a POLICY session (TPM_SE_POLICY) with SHA-256 as its authHash, unsalted and unbound (tpmKey = bind = TPM_RH_NULL), no parameter encryption (symmetric = TPM_ALG_NULL). nonceCaller is the caller's initial nonce (32 random bytes for SHA-256). It returns the session handle and the TPM's nonceTPM.

Body (TagNoSessions — StartAuthSession itself carries no auth area):

handle area: tpmKey (u32) = TPM_RH_NULL, bind (u32) = TPM_RH_NULL
param  area: TPM2B_NONCE nonceCaller || TPM2B_ENCRYPTED_SECRET
             encryptedSalt (empty) || TPM_SE sessionType (u8) ||
             TPMT_SYM_DEF symmetric (algorithm = TPM_ALG_NULL) ||
             TPMI_ALG_HASH authHash (u16)

A NULL TPMT_SYM_DEF is just the 2-byte algorithm id (no keyBits/mode).

Wire: TagNoSessions, CC 0x00000176. TCG "Part 3", "TPM2_StartAuthSession"; "Part 2", "TPMT_SYM_DEF", "TPM_SE".

Response: sessionHandle (u32) || TPM2B_NONCE nonceTPM.

func (*TPM) Startup

func (tpm *TPM) Startup(su uint16) error

Startup runs TPM2_Startup, which the platform firmware issues exactly once after _TPM_Init to bring the TPM out of reset. su is a TPM_SU: common.SUClear for Startup(CLEAR) or common.SUState for Startup(STATE).

Wire: TagNoSessions, CC 0x00000144, parameter TPM_SU startupType (u16). TCG "TPM 2.0 Part 3: Commands", clause "TPM2_Startup".

func (*TPM) Unseal added in v0.3.0

func (tpm *TPM) Unseal(item, session uint32) ([]byte, error)

Unseal runs TPM2_Unseal on the loaded sealed object, authorizing it with the POLICY SESSION (which must already have been driven through PolicyPCR so its policyDigest matches the object's authPolicy). It returns the secret the object holds.

Body (TagSessions):

handle area: itemHandle (u32) = the loaded sealed object
auth   area: authorizationSize (u32) || TPMS_AUTH_COMMAND carrying the
             POLICY SESSION handle (marshalPolicyAuth)
(no parameters)

Wire: TagSessions, CC 0x0000015E. TCG "Part 3", "TPM2_Unseal".

Response (TagSessions): parameterSize (u32) || TPM2B_SENSITIVE_DATA outData (the secret).

func (*TPM) UnsealWithPCR added in v0.3.0

func (tpm *TPM) UnsealWithPCR(parent uint32, priv, pub []byte, sel []PCRSelection, nonceCaller []byte) ([]byte, error)

UnsealWithPCR is the high-level unseal helper: it loads the sealed object under parent, opens a fresh PCR policy session, runs PolicyPCR(sel) against the CURRENT PCR state, and unseals. It returns the secret on success, or the TPM's error (a policy failure if the current PCRs no longer match the sealed-to state). nonceCaller is the 32-byte caller nonce for the session.

type TPMError

type TPMError struct {
	// CC is the command code the failing command was issued under.
	CC uint32
	// RC is the raw response code returned by the TPM (non-zero).
	RC uint32
}

TPMError is the typed error returned when a command completes with a non-success response code. It carries the raw TPM_RC reported by the device and the TPM_CC the command was issued under, so callers can distinguish, for example, a TPM_RC_VALUE from PCR_Extend from the same rc raised by some other command.

func (*TPMError) Error

func (e *TPMError) Error() string

Error implements the error interface.

type TaggedProperty added in v0.4.0

type TaggedProperty struct {
	// Property is the TPM_PT tag.
	Property uint32
	// Value is the property's UINT32 value.
	Value uint32
}

TaggedProperty is one entry of a TPML_TAGGED_TPM_PROPERTY: a property tag and its UINT32 value. TCG "TPM 2.0 Part 2: Structures", clause "TPMS_TAGGED_PROPERTY".

type WrapToPCRResult added in v0.6.0

type WrapToPCRResult struct {
	// ObjectPublic is the marshaled TPMT_PUBLIC (the CONTENTS of TPM2B_PUBLIC,
	// without the 2-byte size prefix) of the sealed keyedhash object. It is
	// passed to Import (which wraps it in a TPM2B_PUBLIC) and later to Load.
	ObjectPublic []byte
	// Duplicate is the TPM2B_PRIVATE-shaped duplication blob:
	// TPM2B( TPM2B(outerHMAC) || encSensitive ).
	Duplicate []byte
	// InSymSeed is the TPM2B_ENCRYPTED_SECRET carrying the ephemeral public
	// point (TPMS_ECC_POINT) the node's TPM uses to recover the outer-wrap
	// seed by ECDH against its storage key.
	InSymSeed []byte
}

WrapToPCRResult is the output of WrapToPCR: the three blobs the control plane ships to the node for TPM2_Import.

func WrapToPCR added in v0.6.0

func WrapToPCR(srkPub ECCPublic, secret []byte, sel []PCRSelection, pcrValues [][]byte, rng io.Reader) (WrapToPCRResult, error)

WrapToPCR performs the OFFLINE, no-TPM control-plane side of object duplication: it builds a KEYEDHASH sealed-data object holding secret and bound to authPolicy = PolicyPCRDigest(sel, pcrValues), then OUTER-WRAPS its sensitive area for the node's ECC P-256 storage key srkPub. The returned (ObjectPublic, Duplicate, InSymSeed) are exactly the inputs to TPM2_Import.

sel/pcrValues are the PCR selection and the values the object must unseal against (the node's boot state); they are folded into the object's authPolicy via the same PolicyPCRDigest construction the seal/unseal path uses, so a node can Import + Load the object but only Unseal it when its live PCRs reproduce pcrValues.

The ephemeral key is drawn from crypto/rand; rng may override the source for deterministic testing (pass nil for crypto/rand).

TCG "TPM 2.0 Part 1: Architecture", "Duplication" / "Protected Storage" (the outer wrap: KDFe seed "DUPLICATE", KDFa "STORAGE"/"INTEGRITY", the HMAC over encSensitive||Name); "Part 2", "TPMT_SENSITIVE", "TPMT_PUBLIC".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL