A cross-platform Go package for TPM 2.0 (Trusted Platform Module) key sealing and unsealing operations.
Status: Development - See ROADMAP.md for progress.
This module provides hardware-backed encryption key management using your computer's TPM chip. Keys sealed to the TPM:
- Cannot be extracted or copied
- Only work on the machine where they were created
- Are protected even if an attacker has full disk access
┌─────────────────────────────────────────────────────────────┐
│ Your Application │
├─────────────────────────────────────────────────────────────┤
│ sealbox package │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Initialize()│ │ GetSealed │ │ Reset() │ │
│ │ │ │ MasterKey() │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └─────────────┘ │
├─────────┼──────────────────┼────────────────────────────────┤
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ KeyManager │ │ KeyStore │ │
│ │ (TPM ops) │ │ (file ops) │ │
│ └──────┬──────┘ └──────┬──────┘ │
├─────────┼──────────────────┼────────────────────────────────┤
│ ▼ ▼ │
│ TPM 2.0 Chip Filesystem │
│ (hardware) (~/.config/app/) │
└─────────────────────────────────────────────────────────────┘
SEAL (one-time setup):
Random 32 bytes ──▶ TPM encrypts ──▶ Sealed blob ──▶ Save to disk
UNSEAL (every use):
Load from disk ──▶ TPM decrypts ──▶ Original key ──▶ Use in app
| Without TPM | With TPM (this module) |
|---|---|
| Key stored in plain file | Key encrypted by hardware |
| Attacker copies file = has key | Attacker copies file = useless blob |
| Key works on any machine | Key only works on THIS machine |
| Software-only protection | Hardware-backed protection |
- Password Manager - Derive master password from TPM-sealed key
- Credential Storage - Protect API tokens, SSH keys
- Disk Encryption - Seal disk encryption keys to TPM
- Application Secrets - Machine-bound secret storage
- Hardware-bound encryption - Keys are sealed to the TPM and cannot be extracted
- Cross-platform - Supports Linux and Windows (macOS Secure Enclave planned)
- PCR binding - Optional binding to Platform Configuration Register values
- Password protection - Optional password requirement for unsealing
- Secure key storage - Sealed blobs stored with platform-appropriate permissions
- Windows ACLs - Proper Windows file permissions (current user only)
- Memory zeroing - Utilities for clearing sensitive data from memory
- Simple API - Easy-to-use interfaces for key management
- Forked go-tpm - Includes forked google/go-tpm for customization
go get github.com/inovacc/sealbox- TPM 2.0 device (
/dev/tpmrm0) - User must have read/write access to TPM device
- Add user to
tssgroup:sudo usermod -aG tss $USER
- Windows 10/11 with TPM 2.0
- TPM enabled in BIOS/UEFI
- No administrator rights required
- Not yet supported (Secure Enclave integration planned)
package main
import (
"fmt"
"log"
"github.com/inovacc/sealbox"
)
func main() {
// Check if TPM is available
if !sealbox.IsAvailable() {
log.Fatal("TPM not available on this system")
}
// Configure storage path (required - no defaults)
opts := sealbox.WithStorePath("/path/to/sealed.key")
// Initialize: generate, seal, and store a new key
if err := sealbox.Initialize(opts); err != nil {
if err == sealbox.ErrKeyExists {
log.Println("Key already exists, skipping initialization")
} else {
log.Fatalf("Failed to initialize: %v", err)
}
}
// Later: retrieve the sealed key
key, err := sealbox.GetSealedMasterKey(opts)
if err != nil {
log.Fatalf("Failed to get sealed key: %v", err)
}
fmt.Printf("Unsealed key length: %d bytes\n", len(key))
}For more control, use the KeyManager and KeyStore interfaces directly:
km, err := sealbox.NewKeyManager()
if err != nil {
log.Fatal(err)
}
defer km.Close()
// Generate and seal a random key
sealed, err := km.GenerateAndSealKey()
if err != nil {
log.Fatalf("Failed to seal key: %v", err)
}
// Store the sealed data (path is required)
store, err := sealbox.NewKeyStore(sealbox.WithStorePath("/path/to/sealed.key"))
if err != nil {
log.Fatalf("Failed to create key store: %v", err)
}
if err := store.Save(sealed); err != nil {
log.Fatalf("Failed to save sealed key: %v", err)
}
fmt.Printf("Key sealed and stored at: %s\n", store.Path())For enhanced security, use policy-based sealing with PCR binding and/or password protection:
import "github.com/google/go-tpm/tpm2"
km, err := sealbox.NewKeyManager()
if err != nil {
log.Fatal(err)
}
defer km.Close()
// Seal with password protection
sealed, err := km.SealKeyWithOptions(myKey,
sealbox.WithPassword([]byte("my-secret-password")),
)
// Seal with PCR binding (key only works if PCRs match)
sealed, err := km.SealKeyWithOptions(myKey,
sealbox.WithPCRs(tpm2.TPMAlgSHA256, 0, 1, 7),
)
// Seal with both password AND PCR binding
sealed, err := km.SealKeyWithOptions(myKey,
sealbox.WithPassword([]byte("password")),
sealbox.WithPCRs(tpm2.TPMAlgSHA256, 0, 7),
)
// Unseal with password
key, err := km.UnsealKeyWithOptions(sealed,
sealbox.WithPassword([]byte("my-secret-password")),
)
// Read current PCR values
pcrValues, err := km.ReadPCRs(uint16(tpm2.TPMAlgSHA256), 0, 1, 7)Use the provided utilities to zero sensitive data after use:
// Zero a byte slice
sealbox.SecureZero(sensitiveKey)
// Use key with automatic cleanup
err := sealbox.WithKeyCleanup(key, func(k []byte) error {
// Use the key
return encrypt(data, k)
})
// Key is automatically zeroed after function returnsChecks if TPM hardware is accessible on the current system.
if sealbox.IsAvailable() {
fmt.Println("TPM is available")
}Creates a new TPM key manager for sealing/unsealing operations.
km, err := sealbox.NewKeyManager()
if err != nil {
log.Fatal(err)
}
defer km.Close()Creates a key store for persisting sealed data to disk. WithStorePath is required.
store, err := sealbox.NewKeyStore(sealbox.WithStorePath("/path/to/sealed.key"))Sets the storage path for the sealed key file. This option is required - there is no default path.
type KeyManager interface {
// SealKey seals arbitrary data to the TPM
SealKey(key []byte) (*SealedData, error)
// UnsealKey retrieves sealed data from TPM
UnsealKey(data *SealedData) ([]byte, error)
// GenerateAndSealKey creates and seals a random 32-byte key
GenerateAndSealKey() (*SealedData, error)
// Close releases TPM resources
Close() error
}Extended interface for policy-based sealing (PCR binding, passwords):
type KeyManagerWithOptions interface {
KeyManager
// SealKeyWithOptions seals with optional PCR binding and/or password
SealKeyWithOptions(key []byte, opts ...SealOption) (*SealedData, error)
// UnsealKeyWithOptions unseals data requiring policy authorization
UnsealKeyWithOptions(data *SealedData, opts ...SealOption) ([]byte, error)
// GenerateAndSealKeyWithOptions generates and seals with options
GenerateAndSealKeyWithOptions(opts ...SealOption) (*SealedData, error)
// ReadPCRs reads current PCR values
ReadPCRs(hash uint16, pcrs ...uint) ([][]byte, error)
}type KeyStore interface {
// Save stores sealed data to disk
Save(data *SealedData) error
// Load retrieves sealed data from disk
Load() (*SealedData, error)
// Exists checks if sealed data exists
Exists() bool
// Delete removes sealed data
Delete() error
// Path returns the storage path
Path() string
}type SealedData struct {
// Version indicates format (0/1=V1, 2=V2 with policy)
Version int `json:"version,omitempty"`
PublicArea []byte `json:"public_area"`
PrivateArea []byte `json:"private_area"`
SealedBlobPublic []byte `json:"sealed_blob_public"`
// V2 fields (policy metadata)
PolicyDigest []byte `json:"policy_digest,omitempty"`
PCRSelection *SealedPCRSelection `json:"pcr_selection,omitempty"`
HasPassword bool `json:"has_password,omitempty"`
}Functional options for sealing operations:
// Protect with password
sealbox.WithPassword(password []byte)
// Bind to PCR values (reads current values)
sealbox.WithPCRs(hash tpm2.TPMIAlgHash, pcrs ...uint)
// Bind to specific PCR digest
sealbox.WithPCRDigest(hash tpm2.TPMIAlgHash, digest []byte, pcrs ...uint)
// Enable session encryption
sealbox.WithSessionEncryption()This package protects against:
- Offline attacks - Sealed keys cannot be decrypted without the TPM
- Key extraction - Key material never leaves the TPM in plaintext
- Credential theft - Even with disk access, attacker cannot recover keys
- System state changes - PCR binding detects boot/config modifications
- Stolen sealed blobs - Password protection adds another layer
This package does NOT protect against:
- Physical TPM attacks - Advanced hardware attacks on the TPM chip
- Runtime memory attacks - Keys are briefly in memory during use (use
SecureZero()) - Malware with root access - Can use TPM while system is running
- No backup capability - TPM-sealed keys cannot be backed up by design
- Machine-bound - Keys only work on the machine where they were created
- BIOS updates - May invalidate sealed keys (re-seal after update)
- TPM clear - Clearing TPM in BIOS destroys all sealed keys
- Use PCR binding - Bind to PCRs 0, 1, 7 to detect boot changes
- Add password protection - Combine with PCR binding for defense in depth
- Zero sensitive memory - Use
SecureZero()orWithKeyCleanup()after use
For password-based applications (like KeePass), derive a password from the sealed key:
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func DerivePassword(key []byte, domain string) string {
h := hmac.New(sha256.New, key)
h.Write([]byte(domain))
return hex.EncodeToString(h.Sum(nil))
}
// Usage
key, _ := km.UnsealKey(sealed)
password := DerivePassword(key, "keepass")Uses forked go-tpm with the Linux TPM Resource Manager (/dev/tpmrm0).
# Check TPM availability
ls -la /dev/tpmrm0
# Add user to tss group for access
sudo usermod -aG tss $USER
newgrp tss # Apply immediatelyUses forked go-tpm with Windows TPM Base Services (TBS).
# Check TPM status
Get-Tpm
# Verify TPM is ready
(Get-Tpm).TpmReadyvar (
// TPM availability
ErrTPMNotAvailable = errors.New("TPM device not available")
ErrTPMNotSupported = errors.New("TPM not supported on this platform")
// Key operations
ErrNoSealedKey = errors.New("no sealed key found")
ErrKeyExists = errors.New("sealed key already exists")
ErrSealFailed = errors.New("failed to seal key to TPM")
ErrUnsealFailed = errors.New("failed to unseal key from TPM")
ErrKeyStoreNotInitialized = errors.New("key store not initialized")
ErrKeyTooLarge = errors.New("key exceeds maximum sealable size")
ErrKeyEmpty = errors.New("cannot seal empty key")
ErrInvalidSealedData = errors.New("sealed data has invalid fields")
// Policy errors (Phase 3)
ErrPCRMismatch = errors.New("PCR values do not match sealed policy")
ErrPasswordRequired = errors.New("password required to unseal this key")
ErrInvalidPassword = errors.New("invalid password for sealed key")
ErrPolicyFailed = errors.New("policy session failed")
ErrInvalidPCRSelection = errors.New("invalid PCR selection")
)# Run tests (requires TPM or simulator)
task test
# Or directly with go
go test -v ./...
# Run with software TPM simulator (Linux)
swtpm socket --tpmstate dir=/tmp/tpm --tpm2 --ctrl type=tcp,port=2322 &
TPM_DEVICE=/tmp/tpm go test -v ./...internal/tpm/- Forked from github.com/google/go-tpm- See internal/tpm/forked.md for 64 tracked upstream issues
- secret-store - TPM-backed secret storage CLI application
- ROADMAP.md - Development phases, local issues, timeline
- internal/tpm/forked.md - Upstream go-tpm issues by priority
MIT License - See LICENSE for details.
Contributions are welcome! Please see the ROADMAP.md for planned features and known issues.