1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
package key
import (
"errors"
"fmt"
"strings"
keyfile "github.com/foxboron/go-tpm-keyfiles"
"github.com/foxboron/ssh-tpm-agent/internal/keyring"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpm2/transport"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
var (
ErrOldKey = errors.New("old format on key")
)
type SSHTPMKeys interface {
Signer(*keyring.ThreadKeyring, func() ([]byte, error), func() transport.TPMCloser, func(*keyfile.TPMKey) ([]byte, error)) *SSHKeySigner
GetDescription() string
Fingerprint() string
AuthorizedKey() []byte
AgentKey() *agent.Key
GetTPMKey() *keyfile.TPMKey
}
// SSHTPMKey is a wrapper for TPMKey implementing the ssh.PublicKey specific parts
type SSHTPMKey struct {
*keyfile.TPMKey
PublicKey *ssh.PublicKey
Certificate *ssh.Certificate
}
func WrapTPMKey(k *keyfile.TPMKey) (*SSHTPMKey, error) {
pubkey, err := k.PublicKey()
if err != nil {
return nil, err
}
sshkey, err := ssh.NewPublicKey(pubkey)
if err != nil {
return nil, err
}
return &SSHTPMKey{k, &sshkey, nil}, nil
}
func NewSSHTPMKey(tpm transport.TPMCloser, alg tpm2.TPMAlgID, bits int, ownerauth []byte, fn ...keyfile.TPMKeyOption) (*SSHTPMKey, error) {
k, err := keyfile.NewLoadableKey(
tpm, alg, bits, ownerauth, fn...,
)
if err != nil {
return nil, err
}
return WrapTPMKey(k)
}
// This assumes we are just getting a local PK.
func NewImportedSSHTPMKey(tpm transport.TPMCloser, pk any, ownerauth []byte, fn ...keyfile.TPMKeyOption) (*SSHTPMKey, error) {
sess := keyfile.NewTPMSession(tpm)
srkHandle, srkPub, err := keyfile.CreateSRK(sess, tpm2.TPMRHOwner, ownerauth)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
sess.SetSalted(srkHandle.Handle, *srkPub)
defer sess.FlushHandle()
k, err := keyfile.NewImportablekey(
srkPub, pk, fn...)
if err != nil {
return nil, fmt.Errorf("failed failed creating importable key: %v", err)
}
k, err = keyfile.ImportTPMKey(tpm, k, ownerauth)
if err != nil {
return nil, fmt.Errorf("failed turning imported key to loadable key: %v", err)
}
pubkey, err := k.PublicKey()
if err != nil {
return nil, err
}
sshkey, err := ssh.NewPublicKey(pubkey)
if err != nil {
return nil, err
}
return &SSHTPMKey{k, &sshkey, nil}, nil
}
func (k *SSHTPMKey) Fingerprint() string {
return ssh.FingerprintSHA256(*k.PublicKey)
}
func (k *SSHTPMKey) AuthorizedKey() []byte {
return []byte(fmt.Sprintf("%s %s\n", strings.TrimSpace(string(ssh.MarshalAuthorizedKey(*k.PublicKey))), k.Description))
}
func (k *SSHTPMKey) GetDescription() string {
return k.Description
}
func (k *SSHTPMKey) AgentKey() *agent.Key {
if k.Certificate != nil {
return &agent.Key{
Format: k.Certificate.Type(),
Blob: k.Certificate.Marshal(),
Comment: k.Description,
}
}
return &agent.Key{
Format: (*k.PublicKey).Type(),
Blob: (*k.PublicKey).Marshal(),
Comment: k.Description,
}
}
func (k *SSHTPMKey) GetTPMKey() *keyfile.TPMKey {
return k.TPMKey
}
func (k *SSHTPMKey) Signer(keyring *keyring.ThreadKeyring, ownerAuth func() ([]byte, error), tpm func() transport.TPMCloser, auth func(*keyfile.TPMKey) ([]byte, error)) *SSHKeySigner {
return NewSSHKeySigner(k, keyring, ownerAuth, tpm, auth)
}
func Decode(b []byte) (*SSHTPMKey, error) {
k, err := keyfile.Decode(b)
if err != nil {
return nil, err
}
return WrapTPMKey(k)
}
|