Skip to content

Commit

Permalink
Merge branch 'morten/cli-tests'
Browse files Browse the repository at this point in the history
* morten/cli-tests:
  ssh-tpm-add: include the description in the add output
  Use key.Decode instead of keyfile.Decode to get the wrapped type
  ssh-tpm-keygen: remove duplicate code
  Implement testscript for integration tests
  ssh-tpm-keygen: ensure we always create the ~/.ssh directory
  key: implement a wrap function to wrap a TPMKey
  utils/tpm: include a fixed seed simulator
  • Loading branch information
Foxboron committed Jan 6, 2025
2 parents 77eeb8f + 9b4d2ae commit d579cf8
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 79 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ BINDIR := $(PREFIX)/bin
LIBDIR := $(PREFIX)/lib
SHRDIR := $(PREFIX)/share
MANDIR := $(PREFIX)/share/man
BINS = $(notdir $(wildcard cmd/*))
BINS = $(filter-out %_test.go,$(notdir $(wildcard cmd/*)))
TAG = $(shell git describe --abbrev=0 --tags)
VERSION = $(shell git describe --abbrev=7 | sed 's/-/./g;s/^v//;')

Expand Down
34 changes: 8 additions & 26 deletions agent/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,25 +63,23 @@ func MarshalTPMKeyMsg(cert *sshagent.AddedKey) []byte {
}

func ParseTPMKeyMsg(req []byte) (*key.SSHTPMKey, error) {
var k TPMKeyMsg

var retkey key.SSHTPMKey
var tpmkey *keyfile.TPMKey
var err error
var (
k TPMKeyMsg
tpmkey *key.SSHTPMKey
err error
)

if err := ssh.Unmarshal(req, &k); err != nil {
return nil, err
}

if len(k.PrivateKey) != 0 {
tpmkey, err = keyfile.Decode(k.PrivateKey)
tpmkey, err = key.Decode(k.PrivateKey)
if err != nil {
return nil, err
}
}

retkey.TPMKey = tpmkey

if len(k.CertBytes) != 0 {
pubKey, err := ssh.ParsePublicKey(k.CertBytes)
if err != nil {
Expand All @@ -91,24 +89,8 @@ func ParseTPMKeyMsg(req []byte) (*key.SSHTPMKey, error) {
if !ok {
return nil, errors.New("agent: bad tpm thing")
}
retkey.Certificate = cert
}

pubkey, err := tpmkey.PublicKey()
if err != nil {
return nil, err
tpmkey.Certificate = cert
}
sshkey, err := ssh.NewPublicKey(pubkey)
if err != nil {
return nil, err
}

retkey.PublicKey = &sshkey

// TODO: We need constraints on our key as well
// if err := setConstraints(addedKey, k.Constraints); err != nil {
// return nil, err
// }

return &retkey, nil
return tpmkey, nil
}
99 changes: 99 additions & 0 deletions cmd/scripts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package script_tests

import (
"encoding/pem"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"testing"
"time"

keyfile "github.com/foxboron/go-tpm-keyfiles"
"github.com/foxboron/go-tpm-keyfiles/pkix"
"github.com/foxboron/ssh-tpm-agent/utils"
"github.com/google/go-tpm/tpm2"
"github.com/rogpeppe/go-internal/testscript"
)

func ScriptsWithPath(t *testing.T, path string) {
tmp := t.TempDir()
fmt.Println("built")
c := exec.Command("go", "build", "-buildmode=pie", "-o", tmp, "../cmd/...")
out, err := c.CombinedOutput()
if err != nil {
t.Fatal(string(out))
}
testscript.Run(t, testscript.Params{
Deadline: time.Now().Add(5 * time.Second),
Setup: func(e *testscript.Env) error {
e.Setenv("PATH", tmp+string(filepath.ListSeparator)+e.Getenv("PATH"))
e.Vars = append(e.Vars, "SSH_TPM_AGENT_SIMULATOR=1")
e.Vars = append(e.Vars, fmt.Sprintf("SSH_AUTH_SOCK=%s/agent.sock", e.WorkDir))
e.Vars = append(e.Vars, fmt.Sprintf("SSH_TPM_AUTH_SOCK=%s/agent.sock", e.WorkDir))
e.Vars = append(e.Vars, fmt.Sprintf("HOME=%s", e.WorkDir))
return nil
},
Dir: path,
Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
// Create an EK certificate from our fixed seed simulator
"getekcert": func(ts *testscript.TestScript, neg bool, args []string) {
tpm, err := utils.GetFixedSim()
if err != nil {
t.Fatal(err)
}
defer tpm.Close()
rsp, err := tpm2.CreatePrimary{
PrimaryHandle: tpm2.AuthHandle{
Handle: tpm2.TPMRHOwner,
Auth: tpm2.PasswordAuth([]byte(nil)),
},
InSensitive: tpm2.TPM2BSensitiveCreate{
Sensitive: &tpm2.TPMSSensitiveCreate{
UserAuth: tpm2.TPM2BAuth{
Buffer: []byte(nil),
},
},
},
InPublic: tpm2.New2B(keyfile.ECCSRK_H2_Template),
}.Execute(tpm)
if err != nil {
log.Fatalf("failed creating primary key: %v", err)
}
keyfile.FlushHandle(tpm, rsp.ObjectHandle)
srkPublic, err := rsp.OutPublic.Contents()
if err != nil {
log.Fatalf("failed getting srk public content: %v", err)
}
b, err := pkix.FromTPMPublic(srkPublic)
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile(ts.MkAbs("srk.pem"),
pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: b,
}), 0664); err != nil {
log.Fatal(err)
}
},
},
})
}

func TestAgent(t *testing.T) {
ScriptsWithPath(t, "ssh-tpm-agent/testdata/script")
}

func TestKeygen(t *testing.T) {
ScriptsWithPath(t, "ssh-tpm-keygen/testdata/script")
}

// func TestAdd(t *testing.T) {
// ScriptsWithPath(t, "ssh-tpm-add/testdata/script")
// }

// func TestHostkeys(t *testing.T) {
// ScriptsWithPath(t, "ssh-tpm-hostkeys/testdata/script")
// }
8 changes: 4 additions & 4 deletions cmd/ssh-tpm-add/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"path/filepath"
"strings"

keyfile "github.com/foxboron/go-tpm-keyfiles"
"github.com/foxboron/ssh-tpm-agent/agent"
"github.com/foxboron/ssh-tpm-agent/key"
"github.com/foxboron/ssh-tpm-agent/utils"
"github.com/foxboron/ssh-tpm-ca-authority/client"
"github.com/google/go-tpm/tpm2/transport"
Expand Down Expand Up @@ -110,7 +110,7 @@ func main() {
log.Fatal(err)
}

k, err := keyfile.Decode(b)
k, err := key.Decode(b)
if err != nil {
log.Fatal(err)
}
Expand All @@ -125,7 +125,7 @@ func main() {
)); err != nil {
log.Fatal(err)
}
fmt.Printf("Identity added: %s\n", path)
fmt.Printf("Identity added: %s (%s)\n", path, k.Description)

certStr := fmt.Sprintf("%s-cert.pub", strings.TrimSuffix(path, filepath.Ext(path)))
if _, err := os.Stat(certStr); !errors.Is(err, os.ErrNotExist) {
Expand All @@ -151,7 +151,7 @@ func main() {
)); err != nil {
log.Fatal(err)
}
fmt.Printf("Identity added: %s\n", certStr)
fmt.Printf("Identity added: %s (%s)\n", certStr, k.Description)
}

}
Expand Down
76 changes: 76 additions & 0 deletions cmd/ssh-tpm-agent/testdata/script/agent.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Ensure we can run the agent
exec ssh-tpm-agent -d --no-load &agent&
exec sleep .2s
exec ssh-tpm-keygen
exec ssh-tpm-keygen -t rsa
exec ssh-tpm-add
stdout id_ecdsa.tpm
stdout id_rsa.tpm
exec ssh-add -l
stdout ECDSA
stdout RSA
exec ssh-add -D


# ssh sign file - ecdsa
exec ssh-tpm-add .ssh/id_ecdsa.tpm
exec ssh-add -l
stdout ECDSA
exec ssh-keygen -Y sign -n file -f .ssh/id_ecdsa.pub file_to_sign.txt
stdin file_to_sign.txt
exec ssh-keygen -Y check-novalidate -n file -f .ssh/id_ecdsa.pub -s file_to_sign.txt.sig
exists file_to_sign.txt.sig
exec ssh-add -D
rm file_to_sign.txt.sig


# ssh sign file - rsa
exec ssh-tpm-add .ssh/id_rsa.tpm
exec ssh-add -l
stdout RSA
exec ssh-keygen -Y sign -n file -f .ssh/id_rsa.pub file_to_sign.txt
stdin file_to_sign.txt
exec ssh-keygen -Y check-novalidate -n file -f .ssh/id_rsa.pub -s file_to_sign.txt.sig
exists file_to_sign.txt.sig
rm file_to_sign.txt.sig
exec ssh-add -D


# ssh create a certificate - ecdsa
exec ssh-keygen -t ecdsa -f id_ca -N ''
exec ssh-keygen -s id_ca -n fox -I 'cert' -z '0001' .ssh/id_ecdsa.pub
exists .ssh/id_ecdsa-cert.pub
exec ssh-tpm-add .ssh/id_ecdsa.tpm
stdout id_ecdsa.tpm
stdout id_ecdsa-cert.pub
exec ssh-add -l
stdout \(ECDSA\)
stdout \(ECDSA-CERT\)
exec ssh-keygen -Y sign -n file -f .ssh/id_ecdsa-cert.pub file_to_sign.txt
stdin file_to_sign.txt
exec ssh-keygen -Y check-novalidate -n file -f .ssh/id_ecdsa-cert.pub -s file_to_sign.txt.sig
exists file_to_sign.txt.sig
rm file_to_sign.txt.sig
exec ssh-add -D
rm id_ca id_ca.pub


# ssh create a certificate - rsa
exec ssh-keygen -t rsa -f id_ca -N ''
exec ssh-keygen -s id_ca -n fox -I 'cert' -z '0001' .ssh/id_rsa.pub
exists .ssh/id_rsa-cert.pub
exec ssh-tpm-add .ssh/id_rsa.tpm
exec ssh-add -l
stdout \(RSA\)
stdout \(RSA-CERT\)
exec ssh-keygen -Y sign -n file -f .ssh/id_rsa-cert.pub file_to_sign.txt
stdin file_to_sign.txt
exec ssh-keygen -Y check-novalidate -n file -f .ssh/id_rsa-cert.pub -s file_to_sign.txt.sig
exists file_to_sign.txt.sig
rm file_to_sign.txt.sig
exec ssh-add -D
rm id_ca id_ca.pub


-- file_to_sign.txt --
Hello World
41 changes: 20 additions & 21 deletions cmd/ssh-tpm-keygen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,21 +178,6 @@ func main() {

supportedECCBitsizes := keyfile.SupportedECCAlgorithms(tpm)

if printPubkey != "" {
f, err := os.ReadFile(printPubkey)
if err != nil {
log.Fatalf("failed reading TPM key %s: %v", printPubkey, err)
}

k, err := key.Decode(f)
if err != nil {
log.Fatal(err)
}
fmt.Print(string(k.AuthorizedKey()))

os.Exit(0)
}

if printPubkey != "" {
f, err := os.ReadFile(printPubkey)
if err != nil {
Expand Down Expand Up @@ -261,7 +246,10 @@ func main() {
log.Fatal(err)
}

sshkey := key.SSHTPMKey{TPMKey: k}
sshkey, err := key.WrapTPMKey(k)
if err != nil {
log.Fatal(err)
}

if err := os.WriteFile(pubkeyFilename, sshkey.AuthorizedKey(), 0o600); err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -290,6 +278,13 @@ func main() {
}
}

if !utils.FileExists(utils.SSHDir()) {
if err := os.Mkdir(utils.SSHDir(), 0700); err != nil {
log.Fatalf("Could not create directory %s", utils.SSHDir())
os.Exit(1)
}
}

// Wrapping of keyfile for import
if wrap != "" {
if wrapWith == "" {
Expand Down Expand Up @@ -372,7 +367,10 @@ func main() {
}

// Write out the public key
sshkey := &key.SSHTPMKey{TPMKey: k}
sshkey, err := key.WrapTPMKey(k)
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile(pubkeyFilename, sshkey.AuthorizedKey(), 0o600); err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -406,13 +404,11 @@ func main() {
log.Fatal(err)
}

parsedk, err := keyfile.Decode(b)
k, err := key.Decode(b)
if err != nil {
log.Fatal(err)
}

k := &key.SSHTPMKey{TPMKey: parsedk}

if k.Description != "" {
fmt.Printf("Key has comment '%s'\n", k.Description)
}
Expand Down Expand Up @@ -594,7 +590,10 @@ func main() {
if err != nil {
log.Fatal(err)
}
k = &key.SSHTPMKey{TPMKey: tkey}
k, err = key.WrapTPMKey(tkey)
if err != nil {
log.Fatal(err)
}
importKey = ""
} else if importKey != "" {
k, err = key.NewImportedSSHTPMKey(tpm, toImportKey, ownerPassword,
Expand Down
Loading

0 comments on commit d579cf8

Please sign in to comment.