Skip to content

tpmkey: add support for keys sealed to a local TPM - #166

Merged
andrew-d merged 1 commit into
tailscale:mainfrom
andrew-d:andrew/tpm
Jul 22, 2026
Merged

tpmkey: add support for keys sealed to a local TPM#166
andrew-d merged 1 commit into
tailscale:mainfrom
andrew-d:andrew/tpm

Conversation

@andrew-d

Copy link
Copy Markdown
Member

Add a new tpmkey package that seals the key-encryption key to a TPM 2.0 device, as an alternative to a cloud KMS.

TPMs cannot perform AEAD operations directly, so instead a random 32-byte key is generated and sealed to the TPM, with the sealed blob stored in a file on disk. At startup the blob is unsealed through the TPM, and the key is used with a software AEAD.

The sealed blob can only be used by the TPM that created it, so a copy of the database and key file together cannot be decrypted elsewhere. This also works with virtual TPMs such as those QEMU (and thus Proxmox) provide.

@andrew-d
andrew-d requested a review from creachadair July 19, 2026 04:18
Comment thread docs/server.md Outdated
@andrew-d

Copy link
Copy Markdown
Member Author

I've tested this against the simulated TPM, but it's a bit late here and I haven't had the chance to test against a "real" (i.e. "Proxmox-provided") TPM yet. Will do that tomorrow.

Comment thread cmd/setec/setec.go Outdated
Comment thread docs/README.md Outdated
Comment thread docs/server.md Outdated
Comment thread docs/server.md Outdated
Comment thread docs/server.md Outdated
Comment thread docs/server.md Outdated
Comment thread tpmkey/tpmkey.go
return nil, fmt.Errorf("loading sealed key from %q into TPM: %w", path, err)
}
defer func() {
flushCmd := tpm2.FlushContext{FlushHandle: loadRsp.ObjectHandle}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A question for my understanding: I see that createSRK returns a callback to flush the transaction, as do we here, but the Unseal step below does not. Should that one also do this? (I don't know if the flush is per transaction or per session, or what)

Comment thread tpmkey/tpmkey_test.go Outdated
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause

package tpmkey

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is practical to do so, I'd prefer we put tests in a separate package (package tpmkey_test) rather than exercising via the unexported surfaces.

Here, I think it is only openOrCreate that we really need.

What do you think about making OpenOrCreate accept a device (a transport.TPM) rather than a path, and exporting OpenDevice (or maybe OpenDevicePath, since presumably we might get devices some other way on Windows or Darwin)? All we're really saving the caller is stitching those two together, and the fact that the tests immediately have to work around that makes me think it should be part of the API.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM; just made the change. I kept the openDevice / OpenDevicePath split so the godoc isn't duplicated in two locations

Comment thread tpmkey/tpmkey.go
Comment thread tpmkey/tpmkey.go
@andrew-d
andrew-d force-pushed the andrew/tpm branch 2 times, most recently from df16f88 to 6b34316 Compare July 21, 2026 00:49
Add a new tpmkey package that seals the key-encryption key to a TPM 2.0
device, as an alternative to a cloud KMS.

TPMs cannot perform AEAD operations directly, so instead a random
32-byte key is generated and sealed to the TPM, with the sealed blob
stored in a file on disk. At startup the blob is unsealed through the
TPM, and the key is used with a software AEAD.

The sealed blob can only be used by the TPM that created it, so a copy
of the database and key file together cannot be decrypted elsewhere.
This also works with virtual TPMs such as those QEMU (and thus Proxmox)
provide.
@andrew-d

Copy link
Copy Markdown
Member Author

Just tested this on a Proxmox VM with a vTPM installed, and it works great 😁

Comment thread tpmkey/tpmkey.go
"tailscale.com/atomicfile"
)

// A quick digression on how and why we "flush" something from the TPM:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is really helpful.

Comment thread tpmkey/tpmkey.go
@andrew-d
andrew-d merged commit dd66b9e into tailscale:main Jul 22, 2026
1 check passed
@andrew-d
andrew-d deleted the andrew/tpm branch July 22, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants