Skip to content

Commit

Permalink
signer: implement test for key import
Browse files Browse the repository at this point in the history
Signed-off-by: Morten Linderud <[email protected]>
  • Loading branch information
Foxboron committed Aug 7, 2023
1 parent 6609b57 commit c7ca080
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 25 deletions.
114 changes: 92 additions & 22 deletions cmd/ssh-tpm-keygen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"bufio"
"bytes"
"crypto/ecdsa"
"crypto/x509"
"errors"
"flag"
"fmt"
Expand All @@ -26,10 +28,11 @@ const usage = `Usage:
ssh-tpm-keygen
Options:
-C Comment WIP
-f Output keyfile WIP
-N PIN for the key WIP
-t ecdsa | rsa Specify the type of key to create. Defaults to ecdsa
-C Comment WIP
-f Output keyfile WIP
-N PIN for the key WIP
-t ecdsa | rsa Specify the type of key to create. Defaults to ecdsa
-I, --import PATH Import existing key into ssh-tpm-agent.
Generate new TPM sealed keys for ssh-tpm-agent.
Expand Down Expand Up @@ -98,20 +101,25 @@ func main() {

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

flag.StringVar(&comment, "C", "", "provide a comment with the key")
flag.StringVar(&outputFile, "f", "", "output keyfile")
flag.StringVar(&keyPin, "N", "", "new pin for the key")
flag.StringVar(&keyType, "t", "ecdsa", "key to create")
flag.StringVar(&importKey, "I", "", "import key")
flag.StringVar(&importKey, "import", "", "import key")
flag.BoolVar(&swtpmFlag, "swtpm", false, "use swtpm instead of actual tpm")

flag.Parse()

var tpmkeyType tpm2.TPMAlgID
var sshKey ssh.PublicKey
var filename string
var privatekeyFilename string
var pubkeyFilename string

switch keyType {
case "ecdsa":
Expand All @@ -122,20 +130,67 @@ func main() {
filename = "id_rsa"
}

fmt.Printf("Generating a sealed public/private %s key pair.\n", keyType)
// Only used with -I/--import
var toImportKey *ecdsa.PrivateKey

filename = path.Join(utils.GetSSHDir(), filename)
if importKey != "" {
fmt.Println("Sealing an existing public/private ecdsa key pair.")

filename = importKey

pem, err := os.ReadFile(importKey)
if err != nil {
log.Fatal(err)
}

var kerr *ssh.PassphraseMissingError

var rawKey any

rawKey, err = ssh.ParseRawPrivateKey(pem)
if errors.As(err, &kerr) {
for {
fmt.Printf("Enter existing password (empty for no pin): ")
pin, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println("")
if err != nil {
log.Fatal(err)
}
rawKey, err = ssh.ParseRawPrivateKeyWithPassphrase(pem, pin)
if err == nil {
break
} else if errors.Is(err, x509.IncorrectPasswordError) {
fmt.Println("Wrong password, try again.")
continue
} else {
log.Fatal(err)
}
}
}

switch key := rawKey.(type) {
case *ecdsa.PrivateKey:
toImportKey = key
default:
log.Fatal("unsupported key type")
}

} else {
fmt.Printf("Generating a sealed public/private %s key pair.\n", keyType)

filename = path.Join(utils.GetSSHDir(), filename)
filenameInput, err := getStdin("Enter file in which to save the key (%s): ", filename)
if err != nil {
log.Fatal(err)
}
if filenameInput != "" {
filename = filenameInput
}

filenameInput, err := getStdin("Enter file in which to save the key (%s): ", filename)
if err != nil {
log.Fatal(err)
}
if filenameInput != "" {
filename = filenameInput
}

privatekeyFilename := filename + ".tpm"
pubkeyFilename := filename + ".pub"
privatekeyFilename = filename + ".tpm"
pubkeyFilename = filename + ".pub"

if fileExists(privatekeyFilename) {
fmt.Printf("%s already exists.\n", privatekeyFilename)
Expand All @@ -147,6 +202,7 @@ func main() {
return
}
}

if fileExists(pubkeyFilename) {
fmt.Printf("%s already exists.\n", pubkeyFilename)
s, err := getStdin("Overwrite (y/n)?")
Expand All @@ -169,26 +225,40 @@ func main() {
log.Fatal(err)
}
defer tpm.Close()
k, err := key.CreateKey(tpm, tpmkeyType, pin)
if err != nil {
log.Fatal(err)

var k *key.Key

if importKey != "" {
k, err = key.ImportKey(tpm, *toImportKey, pin)
if err != nil {
log.Fatal(err)
}
} else {
k, err = key.CreateKey(tpm, tpmkeyType, pin)
if err != nil {
log.Fatal(err)
}
}

sshKey, err := k.SSHPublicKey()
sshKey, err = k.SSHPublicKey()
if err != nil {
log.Fatal(err)
}

if err := os.WriteFile(pubkeyFilename, ssh.MarshalAuthorizedKey(sshKey), 0644); err != nil {
log.Fatal(err)
if importKey == "" {
if err := os.WriteFile(pubkeyFilename, ssh.MarshalAuthorizedKey(sshKey), 0644); err != nil {
log.Fatal(err)
}
}

if err := os.WriteFile(privatekeyFilename, key.EncodeKey(k), 0600); err != nil {
log.Fatal(err)
}

fmt.Printf("Your identification has been saved in %s\n", privatekeyFilename)
fmt.Printf("Your public key has been saved in %s\n", pubkeyFilename)
if importKey == "" {
fmt.Printf("Your public key has been saved in %s\n", pubkeyFilename)
}
fmt.Printf("The key fingerprint is:\n")
fmt.Println(ssh.FingerprintSHA256(sshKey))
fmt.Println("The key's randomart image is the color of television, tuned to a dead channel.")
Expand Down
5 changes: 2 additions & 3 deletions key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,12 @@ func DecodeKey(pemBytes []byte) (*Key, error) {

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

var public tpm2.TPM2BPublic
switch keytype {
case tpm2.TPMAlgECDSA:
public = tpm2.New2B(tpm2.ECCSRKTemplate)
case tpm2.TPMAlgRSA:
public = tpm2.New2B(tpm2.RSASRKTemplate)

}

srk := tpm2.CreatePrimary{
Expand Down Expand Up @@ -329,7 +327,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, pin []byte) (*Key
}

func ImportKey(tpm transport.TPMCloser, pk ecdsa.PrivateKey, pin []byte) (*Key, error) {
srkHandle, srkPublic, err := CreateSRK(tpm)
srkHandle, srkPublic, err := CreateSRK(tpm, tpm2.TPMAlgECDSA)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand Down Expand Up @@ -412,6 +410,7 @@ func ImportKey(tpm transport.TPMCloser, pk ecdsa.PrivateKey, pin []byte) (*Key,
PIN: pinstatus,
Private: importRsp.OutPrivate,
Public: eccPublicImport,
Type: tpm2.TPMAlgECDSA,
}, nil
}

Expand Down
28 changes: 28 additions & 0 deletions key/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import (

"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpm2/transport/simulator"

"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
)

func TestECDSACreateKey(t *testing.T) {
Expand Down Expand Up @@ -110,3 +114,27 @@ func TestMarshalling(t *testing.T) {
}
}
}

func TestImportKey(t *testing.T) {
pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("failed to generate ecdsa key: %v", err)
}

tpm, err := simulator.OpenSimulator()
if err != nil {
t.Fatal(err)
}
defer tpm.Close()
k, err := ImportKey(tpm, *pk, []byte(""))
if err != nil {
t.Fatalf("failed key import: %v", err)
}

// Test if we can load the key
// signer/signer_test.go tests the signing of the key
_, err = LoadKey(tpm, k)
if err != nil {
t.Fatalf("failed loading key: %v", err)
}
}
106 changes: 106 additions & 0 deletions signer/signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package signer
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"fmt"
Expand Down Expand Up @@ -143,3 +145,107 @@ func TestSigning(t *testing.T) {
})
}
}

func TestSigningWithImportedKey(t *testing.T) {
cases := []struct {
msg string
filekey []byte
pin []byte
signpin []byte
shouldfail bool
}{
{
msg: "test encryption/decrypt - no pin",
filekey: []byte("this is a test filekey"),
},
{
msg: "test encryption/decrypt - pin",
filekey: []byte("this is a test filekey"),
pin: []byte("123"),
signpin: []byte("123"),
},
{
msg: "test encryption/decrypt - no pin for sign",
filekey: []byte("this is a test filekey"),
pin: []byte("123"),
shouldfail: true,
},
{
msg: "test encryption/decrypt - no pin for key, pin for sign",
filekey: []byte("this is a test filekey"),
pin: []byte(""),
signpin: []byte("123"),
},
}

for n, c := range cases {
t.Run(fmt.Sprintf("case %d, %s", n, c.msg), func(t *testing.T) {
// Always re-init simulator as the Signer is going to close it,
// and we can't retain state.
tpm, err := simulator.OpenSimulator()
if err != nil {
t.Fatal(err)
}
defer tpm.Close()

b := sha256.Sum256([]byte("heyho"))

pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("failed to generate ecdsa key: %v", err)
}

k, err := key.ImportKey(tpm, *pk, c.pin)
if err != nil {
t.Fatalf("failed key import: %v", err)
}

signer := NewTPMSigner(k,
func() transport.TPMCloser { return tpm },
func(_ *key.Key) ([]byte, error) { return c.signpin, nil },
)

// Empty reader, we don't use this
var r io.Reader

sig, err := signer.Sign(r, b[:], crypto.SHA256)
if err != nil {
if c.shouldfail {
return
}
t.Fatalf("%v", err)
}

pubkey, err := k.PublicKey()
if err != nil {
if c.shouldfail {
return
}
t.Fatalf("failed getting pubkey: %v", err)
}

if err != nil {
if c.shouldfail {
return
}
t.Fatalf("failed test: %v", err)
}

if c.shouldfail {
t.Fatalf("test should be failing")
}

switch pk := pubkey.(type) {
case *ecdsa.PublicKey:
if !ecdsa.VerifyASN1(pk, b[:], sig) {
t.Fatalf("invalid signature")
}
case *rsa.PublicKey:
if err := rsa.VerifyPKCS1v15(pk, crypto.SHA256, b[:], sig); err != nil {
t.Errorf("Signature verification failed: %v", err)
}
}

})
}
}

0 comments on commit c7ca080

Please sign in to comment.