Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/pull/37'
Browse files Browse the repository at this point in the history
* origin/pull/37:
  add support for owner password
  • Loading branch information
Foxboron committed May 19, 2024
2 parents f1be81b + e3226d3 commit 1c7bced
Show file tree
Hide file tree
Showing 15 changed files with 317 additions and 65 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ $ export SSH_AUTH_SOCK="$(ssh-tpm-agent --print-socket)"
$ ssh git@github.com
```
**Note:** For `ssh-tpm-agent` you can specify the TPM owner password using the command line flags `-o` or `--owner-password`, which are preferred. Alternatively, you can use the environment variable `SSH_TPM_AGENT_OWNER_PASSWORD`.
### Import existing key
Useful if you want to back up the key to a remote secure storage while using the key day-to-day from the TPM.
Expand Down
6 changes: 4 additions & 2 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var SSH_TPM_AGENT_ADD = "tpm-add-key"
type Agent struct {
mu sync.Mutex
tpm func() transport.TPMCloser
op func() ([]byte, error)
pin func(*key.Key) ([]byte, error)
listener *net.UnixListener
quit chan interface{}
Expand Down Expand Up @@ -83,7 +84,7 @@ func (a *Agent) signers() ([]ssh.Signer, error) {
}

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

func NewAgent(listener *net.UnixListener, agents []agent.ExtendedAgent, tpmFetch func() transport.TPMCloser, pin func(*key.Key) ([]byte, error)) *Agent {
func NewAgent(listener *net.UnixListener, agents []agent.ExtendedAgent, tpmFetch func() transport.TPMCloser, ownerPassword func() ([]byte, error), pin func(*key.Key) ([]byte, error)) *Agent {
a := &Agent{
agents: agents,
tpm: tpmFetch,
op: ownerPassword,
listener: listener,
pin: pin,
quit: make(chan interface{}),
Expand Down
12 changes: 5 additions & 7 deletions agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@ func TestAddKey(t *testing.T) {
ag := NewAgent(unixList,
[]agent.ExtendedAgent{},
// TPM Callback
func() transport.TPMCloser {
return tpm
},
func() transport.TPMCloser { return tpm },
// Owner password
func() ([]byte, error) { return []byte(""), nil },
// PIN Callback
func(_ *key.Key) ([]byte, error) {
return []byte(""), nil
},
func(_ *key.Key) ([]byte, error) { return []byte(""), nil },
)
defer ag.Stop()

Expand All @@ -49,7 +47,7 @@ func TestAddKey(t *testing.T) {

client := agent.NewClient(conn)

k, err := key.CreateKey(tpm, tpm2.TPMAlgECC, 256, []byte(""), "")
k, err := key.CreateKey(tpm, tpm2.TPMAlgECC, 256, []byte(""), []byte(""), "")
if err != nil {
t.Fatal(err)
}
Expand Down
25 changes: 20 additions & 5 deletions cmd/ssh-tpm-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ Options:
--no-load Do not load TPM sealed keys by default.
-o, --owner-password Ask for the owner password.
-d Enable debug logging.
--install-user-units Installs systemd system units and sshd configs for using
Expand Down Expand Up @@ -98,10 +100,10 @@ func main() {
}

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

envSocketPath := func() string {
Expand All @@ -126,6 +128,8 @@ func main() {
flag.BoolVar(&installUserUnits, "install-user-units", false, "install systemd user units")
flag.BoolVar(&system, "install-system", false, "install systemd user units")
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.BoolVar(&debugMode, "d", false, "debug mode")
flag.Parse()

Expand Down Expand Up @@ -201,12 +205,23 @@ func main() {
return tpm
},

// Owner password
func() ([]byte, error) {
if askOwnerPassword {
return pinentry.GetOwnerPassword()
} else {
ownerPassword := os.Getenv("SSH_TPM_AGENT_OWNER_PASSWORD")

return []byte(ownerPassword), nil
}
},

// PIN Callback
func(key *key.Key) ([]byte, error) {
pbytes := tpm2.New2B(key.Pubkey)
keyHash := sha256.Sum256(pbytes.Bytes())
keyInfo := fmt.Sprintf("ssh-tpm-agent/%x", keyHash)
return pinentry.GetPinentry(keyInfo)
return pinentry.GetPin(keyInfo)
},
)

Expand Down
6 changes: 3 additions & 3 deletions cmd/ssh-tpm-agent/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID, bits int, pin []byte, keyfn
ag := agent.NewAgent(unixList,
[]sshagent.ExtendedAgent{},
// TPM Callback
func() transport.TPMCloser {
return tpm
},
func() transport.TPMCloser { return tpm },
// Owner password
func() ([]byte, error) { return []byte(""), nil },
// PIN Callback
func(_ *key.Key) ([]byte, error) {
return pin, nil
Expand Down
31 changes: 27 additions & 4 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:
ssh-tpm-keygen
Options:
-o, --owner-password Ask for the owner password.
-C Provide a comment with the key.
-f Output keyfile.
-N PIN for the key.
Expand Down Expand Up @@ -94,12 +95,24 @@ func getPin() []byte {
}
}

func getOwnerPassword() []byte {
fmt.Printf("Enter owner password: ")
password, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println("")
if err != nil {
log.Fatal(err)
}

return password
}

func main() {
flag.Usage = func() {
fmt.Println(usage)
}

var (
askOwnerPassword bool
comment, outputFile, keyPin string
keyType, importKey string
bits int
Expand All @@ -123,6 +136,8 @@ func main() {
return user.Username + "@" + host
}()

flag.BoolVar(&askOwnerPassword, "o", false, "ask for the owner password")
flag.BoolVar(&askOwnerPassword, "owner-password", false, "ask for the owner password")
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 Down Expand Up @@ -164,6 +179,14 @@ func main() {
os.Exit(0)
}

// Ask for owner password
var ownerPassword []byte
if askOwnerPassword {
ownerPassword = getOwnerPassword()
} else {
ownerPassword = []byte("")
}

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

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

k, err := key.CreateKey(tpm, t.alg, t.bits, []byte(""), defaultComment)
k, err := key.CreateKey(tpm, t.alg, t.bits, ownerPassword, []byte(""), defaultComment)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -275,7 +298,7 @@ func main() {
}
fmt.Println()

newkey, err := key.ChangeAuth(tpm, k, oldPin, newPin)
newkey, err := key.ChangeAuth(tpm, ownerPassword, k, oldPin, newPin)
if err != nil {
log.Fatal("Failed changing pin on the key.")
}
Expand Down Expand Up @@ -409,12 +432,12 @@ func main() {

if importKey != "" {
// TODO: Read public key for comment
k, err = key.ImportKey(tpm, toImportKey, pin, comment)
k, err = key.ImportKey(tpm, ownerPassword, toImportKey, pin, comment)
if err != nil {
log.Fatal(err)
}
} else {
k, err = key.CreateKey(tpm, tpmkeyType, bits, pin, comment)
k, err = key.CreateKey(tpm, tpmkeyType, bits, ownerPassword, pin, comment)
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22.0
require (
github.com/foxboron/go-tpm-keyfiles v0.0.0-20240225134915-950e719db3d9
github.com/foxboron/swtpm_test v0.0.0-20230726224112-46aaafdf7006
github.com/google/go-tpm v0.9.1-0.20240411180339-1fb84445f623
github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434
github.com/twpayne/go-pinentry v0.3.0
golang.org/x/crypto v0.21.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2bt
github.com/google/go-sev-guest v0.6.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q=
github.com/google/go-tpm v0.9.1-0.20240411180339-1fb84445f623 h1:LGYp08nFCGgxM/pRoE4etWElLB2WsrhJiBG4jK04MPE=
github.com/google/go-tpm v0.9.1-0.20240411180339-1fb84445f623/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434 h1:uPadaCeI0VnloLvthGLalr0Io0IDoI1VEQ95APzVAiw=
github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc=
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc=
github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
Expand Down
4 changes: 2 additions & 2 deletions internal/keytest/keytest.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func MkECDSA(t *testing.T, a elliptic.Curve) ecdsa.PrivateKey {
// Test helper for CreateKey
func MkKey(t *testing.T, tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []byte, comment string) (*key.Key, error) {
t.Helper()
return key.CreateKey(tpm, keytype, bits, pin, comment)
return key.CreateKey(tpm, keytype, bits, []byte(""), pin, comment)
}

// Helper to make an importable key
Expand All @@ -56,7 +56,7 @@ func MkImportableKey(t *testing.T, tpm transport.TPMCloser, keytype tpm2.TPMAlgI
case tpm2.TPMAlgRSA:
pk = MkRSA(t, bits)
}
return key.ImportKey(tpm, pk, pin, comment)
return key.ImportKey(tpm, []byte(""), pk, pin, comment)
}

// Give us some random bytes
Expand Down
34 changes: 18 additions & 16 deletions key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,16 @@ func DecodeKey(pemBytes []byte) (*Key, error) {
}

// Creates a Storage Key, or return the loaded storage key
func CreateSRK(tpm transport.TPMCloser) (*tpm2.AuthHandle, *tpm2.TPMTPublic, error) {
func CreateSRK(tpm transport.TPMCloser, ownerPassword []byte) (*tpm2.AuthHandle, *tpm2.TPMTPublic, error) {
srk := tpm2.CreatePrimary{
PrimaryHandle: tpm2.TPMRHOwner,
PrimaryHandle: tpm2.AuthHandle{
Handle: tpm2.TPMRHOwner,
Auth: tpm2.PasswordAuth(ownerPassword),
},
InSensitive: tpm2.TPM2BSensitiveCreate{
Sensitive: &tpm2.TPMSSensitiveCreate{
UserAuth: tpm2.TPM2BAuth{
Buffer: []byte(nil),
Buffer: []byte(""),
},
},
},
Expand Down Expand Up @@ -230,7 +233,7 @@ func createRSAKey(bits tpm2.TPMKeyBits, sha tpm2.TPMAlgID) tpm2.TPM2B[tpm2.TPMTP
})
}

func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []byte, comment string) (*Key, error) {
func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, ownerPassword []byte, pin []byte, comment string) (*Key, error) {
rsaBits := []int{2048}
ecdsaBits := []int{256, 384, 521}

Expand Down Expand Up @@ -258,7 +261,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []b
return nil, fmt.Errorf("unsupported key type")
}

srkHandle, srkPublic, err := CreateSRK(tpm)
srkHandle, srkPublic, err := CreateSRK(tpm, ownerPassword)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand Down Expand Up @@ -296,8 +299,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []b
emptyAuth = false
}

var createRsp *tpm2.CreateResponse
createRsp, err = createKey.Execute(tpm,
createRsp, err := createKey.Execute(tpm,
tpm2.HMAC(tpm2.TPMAlgSHA256, 16,
tpm2.AESEncryption(128, tpm2.EncryptIn),
tpm2.Salted(srkHandle.Handle, *srkPublic)))
Expand All @@ -315,7 +317,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []b
return &Key{tpmkey}, nil
}

func ImportKey(tpm transport.TPMCloser, pk any, pin []byte, comment string) (*Key, error) {
func ImportKey(tpm transport.TPMCloser, ownerPassword []byte, pk any, pin []byte, comment string) (*Key, error) {
var public tpm2.TPMTPublic
var sensitive tpm2.TPMTSensitive
var unique tpm2.TPMUPublicID
Expand Down Expand Up @@ -419,7 +421,7 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin []byte, comment string) (*Ke
return nil, fmt.Errorf("unsupported key type")
}

srkHandle, srkPublic, err := CreateSRK(tpm)
srkHandle, srkPublic, err := CreateSRK(tpm, ownerPassword)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand All @@ -434,7 +436,7 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin []byte, comment string) (*Ke
emptyAuth = false
}

// We need the size calcualted in the buffer, so we do this serialization dance
// We need the size calculated in the buffer, so we do this serialization dance
l := tpm2.Marshal(tpm2.TPM2BPrivate{Buffer: tpm2.Marshal(sensitive)})

pubbytes := tpm2.New2B(public)
Expand Down Expand Up @@ -483,8 +485,8 @@ func LoadKeyWithParent(tpm transport.TPMCloser, parent tpm2.AuthHandle, key *Key
}, nil
}

func LoadKey(tpm transport.TPMCloser, key *Key) (*tpm2.AuthHandle, error) {
srkHandle, _, err := CreateSRK(tpm)
func LoadKey(tpm transport.TPMCloser, ownerPassword []byte, key *Key) (*tpm2.AuthHandle, error) {
srkHandle, _, err := CreateSRK(tpm, ownerPassword)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -571,7 +573,7 @@ func newRSASigScheme(digest tpm2.TPMAlgID) tpm2.TPMTSigScheme {
}
}

func Sign(tpm transport.TPMCloser, key *Key, digest []byte, auth []byte, digestalg tpm2.TPMAlgID) ([]byte, error) {
func Sign(tpm transport.TPMCloser, ownerPassword []byte, key *Key, digest []byte, auth []byte, digestalg tpm2.TPMAlgID) ([]byte, error) {
var digestlength int

switch digestalg {
Expand All @@ -587,7 +589,7 @@ func Sign(tpm transport.TPMCloser, key *Key, digest []byte, auth []byte, digesta
return nil, fmt.Errorf("incorrect checksum length. expected %v got %v", digestlength, len(digest))
}

srkHandle, srkPublic, err := CreateSRK(tpm)
srkHandle, srkPublic, err := CreateSRK(tpm, ownerPassword)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand Down Expand Up @@ -649,10 +651,10 @@ func Sign(tpm transport.TPMCloser, key *Key, digest []byte, auth []byte, digesta

// ChangeAuth changes the object authn header to something else
// notice this changes the private blob inside the key in-place.
func ChangeAuth(tpm transport.TPMCloser, key *Key, oldpin, newpin []byte) (*Key, error) {
func ChangeAuth(tpm transport.TPMCloser, ownerPassword []byte, key *Key, oldpin, newpin []byte) (*Key, error) {
var err error

srkHandle, _, err := CreateSRK(tpm)
srkHandle, _, err := CreateSRK(tpm, ownerPassword)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand Down
Loading

0 comments on commit 1c7bced

Please sign in to comment.