Skip to content

Commit

Permalink
add support for persistent srk
Browse files Browse the repository at this point in the history
* Persisting the SRK allows the creation and use of individual keys
  without specifying the owner password
  • Loading branch information
novag committed Jan 28, 2024
1 parent ef5f5ad commit 63f6f85
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 41 deletions.
7 changes: 5 additions & 2 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/rand"
"errors"
"fmt"
"github.com/google/go-tpm/tpm2"
"io"
"io/fs"
"net"
Expand All @@ -30,6 +31,7 @@ type Agent struct {
mu sync.Mutex
tpm func() transport.TPMCloser
op []byte
srk tpm2.TPMHandle
pin func(*key.Key) ([]byte, error)
listener *net.UnixListener
quit chan interface{}
Expand Down Expand Up @@ -84,7 +86,7 @@ func (a *Agent) signers() ([]ssh.Signer, error) {
}

for _, k := range a.keys {
s, err := ssh.NewSignerFromSigner(signer.NewTPMSigner(k, a.op, a.tpm, a.pin))
s, err := ssh.NewSignerFromSigner(signer.NewTPMSigner(k, a.op, a.srk, a.tpm, a.pin))
if err != nil {
return nil, fmt.Errorf("failed to prepare signer: %w", err)
}
Expand Down Expand Up @@ -312,11 +314,12 @@ func LoadKeys(keyDir string) (map[string]*key.Key, error) {
return keys, err
}

func NewAgent(listener *net.UnixListener, agents []agent.ExtendedAgent, ownerPassword []byte, tpmFetch func() transport.TPMCloser, pin func(*key.Key) ([]byte, error)) *Agent {
func NewAgent(listener *net.UnixListener, agents []agent.ExtendedAgent, ownerPassword []byte, srkHandle tpm2.TPMHandle, tpmFetch func() transport.TPMCloser, pin func(*key.Key) ([]byte, error)) *Agent {
a := &Agent{
agents: agents,
tpm: tpmFetch,
op: ownerPassword,
srk: srkHandle,
listener: listener,
pin: pin,
quit: make(chan interface{}),
Expand Down
3 changes: 2 additions & 1 deletion agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestAddKey(t *testing.T) {
ag := NewAgent(unixList,
[]agent.ExtendedAgent{},
[]byte(nil),
0x0,
// TPM Callback
func() transport.TPMCloser {
return tpm
Expand All @@ -50,7 +51,7 @@ func TestAddKey(t *testing.T) {

client := agent.NewClient(conn)

k, err := key.CreateKey(tpm, tpm2.TPMAlgECDSA, []byte(nil), []byte(""), []byte(""))
k, err := key.CreateKey(tpm, tpm2.TPMAlgECDSA, []byte(nil), 0x0, []byte(""), []byte(""))
if err != nil {
t.Fatal(err)
}
Expand Down
17 changes: 12 additions & 5 deletions cmd/ssh-tpm-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Options:
-o, --owner-password Ask for the owner password.
-s, --srk-handle Persist the storage root key at the specified handle.
-d Enable debug logging.
--install-user-units Installs systemd system units and sshd configs for using
Expand Down Expand Up @@ -99,10 +101,10 @@ func main() {
}

var (
socketPath, keyDir string
swtpmFlag, printSocketFlag bool
installUserUnits, system, noLoad bool
askOwnerPassword, debugMode bool
socketPath, keyDir, srkHandleInput string
swtpmFlag, printSocketFlag bool
installUserUnits, system, noLoad bool
askOwnerPassword, debugMode bool
)

envSocketPath := func() string {
Expand All @@ -129,6 +131,8 @@ func main() {
flag.BoolVar(&noLoad, "no-load", false, "don't load TPM sealed keys")
flag.BoolVar(&askOwnerPassword, "o", false, "ask for the owner password")
flag.BoolVar(&askOwnerPassword, "owner-password", false, "ask for the owner password")
flag.StringVar(&srkHandleInput, "s", "", "persist the SRK to the specified handle")
flag.StringVar(&srkHandleInput, "srk-handle", "", "persist the SRK to the specified handle")
flag.BoolVar(&debugMode, "d", false, "debug mode")
flag.Parse()

Expand All @@ -152,6 +156,9 @@ func main() {
ownerPassword = []byte(nil)
}

// Parse srk handle
srkHandle, err := utils.ParseHexHandle(srkHandleInput)

if installUserUnits {
if err := utils.InstallUserUnits(system); err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -200,7 +207,7 @@ func main() {
os.Exit(1)
}

agent := agent.NewAgent(listener, agents, ownerPassword,
agent := agent.NewAgent(listener, agents, ownerPassword, srkHandle,

// TPM Callback
func() (tpm transport.TPMCloser) {
Expand Down
3 changes: 2 additions & 1 deletion cmd/ssh-tpm-agent/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID) {
t.Fatal(err)
}

k, err := key.CreateKey(tpm, keytype, []byte(nil), []byte(""), []byte(""))
k, err := key.CreateKey(tpm, keytype, []byte(nil), 0x0, []byte(""), []byte(""))
if err != nil {
t.Fatalf("failed creating key: %v", err)
}
Expand Down Expand Up @@ -138,6 +138,7 @@ func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID) {
ag := agent.NewAgent(unixList,
[]sshagent.ExtendedAgent{},
[]byte(nil),
0x0,
// TPM Callback
func() transport.TPMCloser {
return tpm
Expand Down
20 changes: 13 additions & 7 deletions cmd/ssh-tpm-keygen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const usage = `Usage:
Options:
-o, --owner-password Ask for the owner password.
-s, --srk-handle Persist the storage root key at the specified handle.
-C Provide a comment with the key.
-f Output keyfile.
-N PIN for the key.
Expand Down Expand Up @@ -96,10 +97,10 @@ func main() {
}

var (
askOwnerPassword bool
comment, outputFile, keyPin string
keyType, importKey string
swtpmFlag, hostKeys bool
askOwnerPassword bool
srkHandleInput, comment, outputFile, keyPin string
keyType, importKey string
swtpmFlag, hostKeys bool
)

defaultComment := func() string {
Expand All @@ -120,6 +121,8 @@ func main() {

flag.BoolVar(&askOwnerPassword, "o", false, "ask for the owner password")
flag.BoolVar(&askOwnerPassword, "owner-password", false, "ask for the owner password")
flag.StringVar(&srkHandleInput, "s", "", "persist the SRK to the specified handle")
flag.StringVar(&srkHandleInput, "srk-handle", "", "persist the SRK to the specified handle")
flag.StringVar(&comment, "C", defaultComment, "provide a comment, default to user@host")
flag.StringVar(&outputFile, "f", "", "output keyfile")
flag.StringVar(&keyPin, "N", "", "new pin for the key")
Expand All @@ -145,6 +148,9 @@ func main() {
ownerPassword = []byte(nil)
}

// Parse srk handle
srkHandle, err := utils.ParseHexHandle(srkHandleInput)

// Generate host keys
if hostKeys {
// Mimics the `ssh-keygen -A -f ./something` behaviour
Expand All @@ -168,7 +174,7 @@ func main() {

slog.Info("Generating new host key", slog.String("algorithm", strings.ToUpper(n)))

k, err := key.CreateKey(tpm, t, ownerPassword, []byte(""), []byte(defaultComment))
k, err := key.CreateKey(tpm, t, ownerPassword, srkHandle, []byte(""), []byte(defaultComment))
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -318,12 +324,12 @@ func main() {

if importKey != "" {
// TODO: Read public key for comment
k, err = key.ImportKey(tpm, ownerPassword, toImportKey, pin, []byte(comment))
k, err = key.ImportKey(tpm, ownerPassword, srkHandle, toImportKey, pin, []byte(comment))
if err != nil {
log.Fatal(err)
}
} else {
k, err = key.CreateKey(tpm, tpmkeyType, ownerPassword, pin, []byte(comment))
k, err = key.CreateKey(tpm, tpmkeyType, ownerPassword, srkHandle, pin, []byte(comment))
if err != nil {
log.Fatal(err)
}
Expand Down
116 changes: 106 additions & 10 deletions key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,75 @@ func DecodeKey(pemBytes []byte) (*Key, error) {
}
}

func DoesHandleExist(tpm transport.TPMCloser, handle tpm2.TPMHandle) (bool, error) {
cap := tpm2.GetCapability{
Capability: tpm2.TPMCapHandles,
Property: uint32(handle),
PropertyCount: 1,
}

rsp, err := cap.Execute(tpm)
if err != nil {
return false, fmt.Errorf("failed getting capability: %v", err)
}

handles, err := rsp.CapabilityData.Data.Handles()
if err != nil {
return false, fmt.Errorf("failed getting handles: %v", err)
}

if len(handles.Handle) == 0 || handles.Handle[0] != handle {
return false, nil
}

return true, nil
}

func PersistSRK(tpm transport.TPMCloser, ownerPassword []byte, authHandle *tpm2.AuthHandle, handle tpm2.TPMHandle) error {
evict := tpm2.EvictControl{
Auth: tpm2.AuthHandle{
Handle: tpm2.TPMRHOwner,
Auth: tpm2.PasswordAuth(ownerPassword),
},
ObjectHandle: &tpm2.NamedHandle{
Handle: authHandle.Handle,
Name: authHandle.Name,
},
PersistentHandle: handle,
}

_, err := evict.Execute(tpm)
if err != nil {
return fmt.Errorf("failed persisting primary key: %v", err)
}

return nil
}

// ReadSRK Loads a persistent Storage Key
func ReadSRK(tpm transport.TPMCloser, handle tpm2.TPMHandle) (*tpm2.AuthHandle, *tpm2.TPMTPublic, error) {
srk := tpm2.ReadPublic{
ObjectHandle: handle,
}

var rsp *tpm2.ReadPublicResponse
rsp, err := srk.Execute(tpm)
if err != nil {
return nil, nil, fmt.Errorf("failed reading primary key: %v", err)
}

srkPublic, err := rsp.OutPublic.Contents()
if err != nil {
return nil, nil, fmt.Errorf("failed getting srk public content: %v", err)
}

return &tpm2.AuthHandle{
Handle: handle,
Name: rsp.Name,
Auth: tpm2.PasswordAuth(nil),
}, srkPublic, nil
}

// Creates a Storage Key, or return the loaded storage key
func CreateSRK(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, ownerPassword []byte) (*tpm2.AuthHandle, *tpm2.TPMTPublic, error) {

Expand Down Expand Up @@ -256,6 +325,33 @@ func CreateSRK(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, ownerPassword []b
}, srkPublic, nil
}

func GetOrCreateSRK(tpm transport.TPMCloser, handle tpm2.TPMHandle, keytype tpm2.TPMAlgID, ownerPassword []byte) (*tpm2.AuthHandle, *tpm2.TPMTPublic, error) {
if handle == 0x0 {
return CreateSRK(tpm, keytype, ownerPassword)
} else {
doesHandleExist, err := DoesHandleExist(tpm, handle)
if err != nil {
return nil, nil, err
}

if doesHandleExist {
return ReadSRK(tpm, handle)
} else {
authHandle, public, err := CreateSRK(tpm, keytype, ownerPassword)
if err != nil {
return nil, nil, err
}

err = PersistSRK(tpm, ownerPassword, authHandle, handle)
if err != nil {
return nil, nil, err
}

return authHandle, public, nil
}
}
}

var (
eccPublic = tpm2.New2B(tpm2.TPMTPublic{
Type: tpm2.TPMAlgECC,
Expand Down Expand Up @@ -312,15 +408,15 @@ var (
})
)

func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, ownerPassword []byte, pin, comment []byte) (*Key, error) {
func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, ownerPassword []byte, srkHandle tpm2.TPMHandle, pin, comment []byte) (*Key, error) {
switch keytype {
case tpm2.TPMAlgECDSA:
case tpm2.TPMAlgRSA:
default:
return nil, fmt.Errorf("unsupported key type")
}

srkHandle, srkPublic, err := CreateSRK(tpm, keytype, ownerPassword)
srkAuthHandle, srkPublic, err := GetOrCreateSRK(tpm, srkHandle, keytype, ownerPassword)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand All @@ -338,7 +434,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, ownerPassword []b

// Template for en ECDSA key for signing
createKey := tpm2.Create{
ParentHandle: srkHandle,
ParentHandle: srkAuthHandle,
InPublic: keyPublic,
}

Expand All @@ -358,7 +454,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, ownerPassword []b
createRsp, err := createKey.Execute(tpm,
tpm2.HMAC(tpm2.TPMAlgSHA256, 16,
tpm2.AESEncryption(128, tpm2.EncryptIn),
tpm2.Salted(srkHandle.Handle, *srkPublic)))
tpm2.Salted(srkAuthHandle.Handle, *srkPublic)))
if err != nil {
return nil, fmt.Errorf("failed creating TPM key: %v", err)
}
Expand All @@ -373,7 +469,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, ownerPassword []b
}, nil
}

func ImportKey(tpm transport.TPMCloser, ownerPassword []byte, pk any, pin, comment []byte) (*Key, error) {
func ImportKey(tpm transport.TPMCloser, ownerPassword []byte, srkHandle tpm2.TPMHandle, pk any, pin, comment []byte) (*Key, error) {

var public tpm2.TPMTPublic
var sensitive tpm2.TPMTSensitive
Expand Down Expand Up @@ -478,7 +574,7 @@ func ImportKey(tpm transport.TPMCloser, ownerPassword []byte, pk any, pin, comme
return nil, fmt.Errorf("unsupported key type")
}

srkHandle, srkPublic, err := CreateSRK(tpm, keytype, ownerPassword)
srkAuthHandle, srkPublic, err := GetOrCreateSRK(tpm, srkHandle, keytype, ownerPassword)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand Down Expand Up @@ -507,7 +603,7 @@ func ImportKey(tpm transport.TPMCloser, ownerPassword []byte, pk any, pin, comme
importRsp, err = importCmd.Execute(tpm,
tpm2.HMAC(tpm2.TPMAlgSHA256, 16,
tpm2.AESEncryption(128, tpm2.EncryptIn),
tpm2.Salted(srkHandle.Handle, *srkPublic)))
tpm2.Salted(srkAuthHandle.Handle, *srkPublic)))
if err != nil {
return nil, fmt.Errorf("failed creating TPM key: %v", err)
}
Expand Down Expand Up @@ -541,13 +637,13 @@ func LoadKeyWithParent(tpm transport.TPMCloser, parent tpm2.AuthHandle, key *Key
}, nil
}

func LoadKey(tpm transport.TPMCloser, ownerPassword []byte, key *Key) (*tpm2.AuthHandle, error) {
srkHandle, _, err := CreateSRK(tpm, key.Type, ownerPassword)
func LoadKey(tpm transport.TPMCloser, ownerPassword []byte, srkHandle tpm2.TPMHandle, key *Key) (*tpm2.AuthHandle, error) {
srkAuthHandle, _, err := GetOrCreateSRK(tpm, srkHandle, key.Type, ownerPassword)
if err != nil {
return nil, err
}

defer utils.FlushHandle(tpm, srkHandle)

return LoadKeyWithParent(tpm, *srkHandle, key)
return LoadKeyWithParent(tpm, *srkAuthHandle, key)
}
Loading

0 comments on commit 63f6f85

Please sign in to comment.