Documentation
¶
Overview ¶
Package devtpm implements a pure-Go github.com/go-tpm2/common.Transport over a Linux kernel TPM character device — by default /dev/tpmrm0, the in-kernel TPM2 resource-manager channel.
Device model ¶
The Linux tpm subsystem (drivers/char/tpm) exposes a TPM through two character devices:
- /dev/tpm0 — the raw device: a single-open, unmediated TPM2 command channel with no handle virtualization.
- /dev/tpmrm0 — the resource-manager device (DefaultDevice): each open file descriptor is an independent, multiplexed command channel; the kernel resource manager virtualizes the TPM's scarce transient-object and session slots and flushes a client's transient context when the descriptor is closed.
Both devices share the same framing, and it is the framing this package depends on: a single write() delivers exactly one complete TPM 2.0 command to the TPM, and the matching read() returns exactly one complete response (the driver buffers the response and returns it whole). Send therefore performs one Write of the full command and one Read of the full response — no length prefix, no chunking, no partial-write retry.
Because that contract is just "raw TPM2 bytes, one command per write, one response per read", New also accepts any io.ReadWriteCloser that honors it: most usefully a unix-domain socket to swtpm's --server data channel, which speaks the identical raw TPM2 protocol. The validate harness uses exactly that to exercise this transport against a real swtpm on a host that has no /dev/tpmrm0.
Security and usage (weft attestation) ¶
This is the node-side host-TPM transport for weft remote attestation. On a real Linux node, the node agent opens the host's hardware (or firmware/vTPM) TPM via devtpm.Open(devtpm.DefaultDevice), layers the go-tpm2/tpm2 command API on top, and drives go-tpm2/attest's Node side: reading PCRs, loading/creating the attestation key, and producing the quote the verifier checks.
Operational notes:
- Prefer /dev/tpmrm0. The resource manager prevents a misbehaving or crashing client from leaking transient handles and wedging the shared TPM, and it isolates concurrent users of the same TPM.
- Access to /dev/tpmrm0 is privileged (typically root or the "tss" group). Any process that can open it can ask the TPM to sign with keys it can use and can read every PCR; treat the descriptor as a sensitive capability and keep it inside the node agent.
- This transport carries opaque bytes only; it performs no policy, authorization, or measurement itself. The attestation semantics (which PCRs, which key, freshness/nonce) live in go-tpm2/attest.
Conventions ¶
Pure Go, CGO_ENABLED=0, no architecture-specific assembly, BSD-3-Clause on every file, 100% statement coverage (GOWORK=off go test -cover), and GOWORK=off. The package consumes github.com/go-tpm2/common's Transport contract and HeaderSize/Error helpers and nothing else.
Index ¶
Constants ¶
const ( // ErrShortWrite is returned when the device accepts fewer bytes than // the full command. The Linux tpm character device treats each // write() as one whole command and never performs a partial write of // a valid command, so a short write means the command was not // delivered intact and must not be paired with a read. ErrShortWrite = common.Error("devtpm: device accepted a short write of the command") // ErrShortResponse is returned when a read() returns fewer bytes than // a TPM 2.0 response header (common.HeaderSize). Such a buffer cannot // contain a parseable response. ErrShortResponse = common.Error("devtpm: response shorter than a TPM 2.0 header") )
Error sentinels for the devtpm transport, typed as common.Error so callers may compare with ==.
const DefaultDevice = "/dev/tpmrm0"
DefaultDevice is the Linux kernel TPM *resource-manager* character device. Each open file descriptor on /dev/tpmrm0 is an independent TPM2 command channel multiplexed by the in-kernel resource manager (the "tpm2-space" / kernel-space RM): it virtualizes transient-object and session handle slots so concurrent users do not exhaust the TPM's scarce volatile memory, and it flushes a client's transient context when that client closes the device.
The raw, non-resource-managed device is /dev/tpm0: it speaks the same one-write-one-command / one-read-one-response framing but with no handle virtualization and a single-open exclusivity constraint, so a caller that leaks a transient handle can wedge the whole TPM. Prefer /dev/tpmrm0 unless you specifically need raw, unmediated access.
Linux exposes both through drivers/char/tpm: tpm_dev_common.c implements the file_operations such that a write() delivers exactly one complete command to the TPM and the matching read() returns exactly one complete response (the driver buffers the response and hands it back whole), which is the framing this transport relies on.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Transport ¶
type Transport struct {
// contains filtered or unexported fields
}
Transport is a github.com/go-tpm2/common.Transport over a Linux TPM character device (typically /dev/tpmrm0, the kernel resource-manager channel). It carries the raw TPM 2.0 command/response byte stream: one Write delivers one complete command and one Read returns the whole response, matching the drivers/char/tpm file-operations contract.
The wrapped io.ReadWriteCloser is normally the *os.File returned by Open, but New accepts any io.ReadWriteCloser that honors the same one-write-one-command / one-read-one-response framing — for example a unix-domain socket to swtpm's --server data channel, which speaks the identical raw TPM2 protocol the character device carries.
func New ¶
func New(rwc io.ReadWriteCloser) *Transport
New wraps an existing io.ReadWriteCloser as a Transport. It exists for tests and for non-file transports that nonetheless honor the one-write-one-command / one-read-one-response framing — such as a unix socket to swtpm's raw-TPM2 data channel — letting them ride the same code path as the real character device.
func Open ¶
Open opens the TPM character device at path for reading and writing and wraps it in a Transport. Pass DefaultDevice for the resource-manager channel (/dev/tpmrm0).
It opens with os.O_RDWR and no O_CREAT: the device node must already exist (the kernel tpm driver creates it), so a nonexistent path is an error rather than a freshly created regular file.
func (*Transport) Close ¶
Close closes the underlying device. For /dev/tpmrm0 this releases the resource-manager session, flushing any transient objects and sessions the kernel RM created on this client's behalf.
func (*Transport) Send ¶
Send transmits one fully-marshaled TPM 2.0 command buffer and returns the full response buffer (header + parameters). It satisfies common.Transport.
The Linux tpm character device frames each command as a single write() and each response as a single read() (drivers/char/tpm, tpm_dev_common.c): Send therefore writes the whole command in one Write and reads the whole response in one Read.
- Write the entire command in one Write. The driver does not perform partial writes of a valid command, so a short count means the command was not delivered intact; that is reported as ErrShortWrite rather than silently retried, since a re-issued tail would be misframed as a new command.
- Read the response in one Read into a maxResponse buffer. The driver returns the entire buffered response in a single read().
- Validate that at least a TPM 2.0 header was returned (common.HeaderSize); otherwise return ErrShortResponse.
Write and read errors from the underlying device are returned unwrapped so callers can inspect the concrete os/syscall error.
