Skip to content

Commit

Permalink
ssh-tpm-agent: support creation of p256 p384 and p512 keys
Browse files Browse the repository at this point in the history
Implements support for the *creation* of different bit sizes of keys.
Note it's dependant on the TPM availability.

This commit alone doesn't support such keys in the agent.

Signed-off-by: Morten Linderud <morten@linderud.pw>
  • Loading branch information
Foxboron committed Feb 18, 2024
1 parent 069e9d1 commit 9bced17
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 32 deletions.
2 changes: 1 addition & 1 deletion agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestAddKey(t *testing.T) {

client := agent.NewClient(conn)

k, err := key.CreateKey(tpm, tpm2.TPMAlgECDSA, []byte(""), []byte(""))
k, err := key.CreateKey(tpm, tpm2.TPMAlgECDSA, 256, []byte(""), []byte(""))
if err != nil {
t.Fatal(err)
}
Expand Down
10 changes: 5 additions & 5 deletions cmd/ssh-tpm-agent/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ func setupServer(listener net.Listener, clientKey ssh.PublicKey) (hostkey ssh.Pu
return hostSigner.PublicKey(), msgSent
}

func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID) {
func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID, bits int) {
tpm, err := simulator.OpenSimulator()
if err != nil {
t.Fatal(err)
}

k, err := key.CreateKey(tpm, keytype, []byte(""), []byte(""))
k, err := key.CreateKey(tpm, keytype, bits, []byte(""), []byte(""))
if err != nil {
t.Fatalf("failed creating key: %v", err)
}
Expand Down Expand Up @@ -184,10 +184,10 @@ func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID) {
}

func TestSSHAuth(t *testing.T) {
t.Run("ecdsa - agent", func(t *testing.T) {
runSSHAuth(t, tpm2.TPMAlgECDSA)
t.Run("ecdsa p256 - agent", func(t *testing.T) {
runSSHAuth(t, tpm2.TPMAlgECDSA, 256)
})
t.Run("rsa - agent", func(t *testing.T) {
runSSHAuth(t, tpm2.TPMAlgRSA)
runSSHAuth(t, tpm2.TPMAlgRSA, 2048)
})
}
18 changes: 13 additions & 5 deletions cmd/ssh-tpm-keygen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ Options:
-f Output keyfile.
-N PIN for the key.
-t ecdsa | rsa Specify the type of key to create. Defaults to ecdsa
-b bits Number of bits in the key to create.
rsa: 2048 (default)
ecdsa: 256 (default) | 384 | 512
-I, --import PATH Import existing key into ssh-tpm-agent.
-A Generate host keys for all key types (rsa and ecdsa).
Expand Down Expand Up @@ -97,6 +100,7 @@ func main() {
var (
comment, outputFile, keyPin string
keyType, importKey string
bits int
swtpmFlag, hostKeys bool
)

Expand All @@ -120,6 +124,7 @@ func main() {
flag.StringVar(&outputFile, "f", "", "output keyfile")
flag.StringVar(&keyPin, "N", "", "new pin for the key")
flag.StringVar(&keyType, "t", "ecdsa", "key to create")
flag.IntVar(&bits, "b", 0, "number of bits")
flag.StringVar(&importKey, "I", "", "import key")
flag.StringVar(&importKey, "import", "", "import key")
flag.BoolVar(&swtpmFlag, "swtpm", false, "use swtpm instead of actual tpm")
Expand All @@ -141,9 +146,12 @@ func main() {
outputPath = path.Join(outputFile, outputPath)
}

lookup := map[string]tpm2.TPMAlgID{
"rsa": tpm2.TPMAlgRSA,
"ecdsa": tpm2.TPMAlgECDSA,
lookup := map[string]struct {
alg tpm2.TPMAlgID
bits int
}{
"rsa": {alg: tpm2.TPMAlgRSA, bits: 2048},
"ecdsa": {alg: tpm2.TPMAlgECDSA, bits: 256},
}
for n, t := range lookup {
filename := fmt.Sprintf("ssh_tpm_host_%s_key", n)
Expand All @@ -156,7 +164,7 @@ func main() {

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

k, err := key.CreateKey(tpm, t, []byte(""), []byte(defaultComment))
k, err := key.CreateKey(tpm, t.alg, t.bits, []byte(""), []byte(defaultComment))
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -311,7 +319,7 @@ func main() {
log.Fatal(err)
}
} else {
k, err = key.CreateKey(tpm, tpmkeyType, pin, []byte(comment))
k, err = key.CreateKey(tpm, tpmkeyType, bits, pin, []byte(comment))
if err != nil {
log.Fatal(err)
}
Expand Down
56 changes: 42 additions & 14 deletions key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import (
"crypto/rsa"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"math/big"
"slices"
"strings"

"github.com/foxboron/ssh-tpm-agent/utils"
Expand Down Expand Up @@ -253,10 +255,10 @@ func CreateSRK(tpm transport.TPMCloser, keytype tpm2.TPMAlgID) (*tpm2.AuthHandle
}, srkPublic, nil
}

var (
eccPublic = tpm2.New2B(tpm2.TPMTPublic{
func createECCKey(ecc tpm2.TPMECCCurve, sha tpm2.TPMAlgID) tpm2.TPM2B[tpm2.TPMTPublic, *tpm2.TPMTPublic] {
return tpm2.New2B(tpm2.TPMTPublic{
Type: tpm2.TPMAlgECC,
NameAlg: tpm2.TPMAlgSHA256,
NameAlg: sha,
ObjectAttributes: tpm2.TPMAObject{
SignEncrypt: true,
FixedTPM: true,
Expand All @@ -267,7 +269,7 @@ var (
Parameters: tpm2.NewTPMUPublicParms(
tpm2.TPMAlgECC,
&tpm2.TPMSECCParms{
CurveID: tpm2.TPMECCNistP256,
CurveID: ecc,
Scheme: tpm2.TPMTECCScheme{
Scheme: tpm2.TPMAlgECDSA,
Details: tpm2.NewTPMUAsymScheme(
Expand All @@ -280,10 +282,12 @@ var (
},
),
})
}

rsaPublic = tpm2.New2B(tpm2.TPMTPublic{
func createRSAKey(bits tpm2.TPMKeyBits, sha tpm2.TPMAlgID) tpm2.TPM2B[tpm2.TPMTPublic, *tpm2.TPMTPublic] {
return tpm2.New2B(tpm2.TPMTPublic{
Type: tpm2.TPMAlgRSA,
NameAlg: tpm2.TPMAlgSHA256,
NameAlg: sha,
ObjectAttributes: tpm2.TPMAObject{
SignEncrypt: true,
FixedTPM: true,
Expand All @@ -303,16 +307,36 @@ var (
},
),
},
KeyBits: 2048,
KeyBits: bits,
},
),
})
)
}

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

supportedECCBitsizes := SupportedECCAlgorithms(tpm)

func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, pin, comment []byte) (*Key, error) {
switch keytype {
case tpm2.TPMAlgECDSA:
if bits == 0 {
bits = ecdsaBits[0]
}
if !slices.Contains(ecdsaBits, bits) {
return nil, errors.New("invalid ecdsa key length: valid length are 256, 384 or 512 bits")
}
if !slices.Contains(supportedECCBitsizes, bits) {
return nil, fmt.Errorf("invalid ecdsa key length: TPM does not support %v bits", bits)
}
case tpm2.TPMAlgRSA:
if bits == 0 {
bits = rsaBits[0]
}
if !slices.Contains(rsaBits, bits) {
return nil, errors.New("invalid rsa key length: only 2048 is supported")
}
default:
return nil, fmt.Errorf("unsupported key type")
}
Expand All @@ -326,11 +350,15 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, pin, comment []by

var keyPublic tpm2.TPM2BPublic

switch keytype {
case tpm2.TPMAlgECDSA:
keyPublic = eccPublic
case tpm2.TPMAlgRSA:
keyPublic = rsaPublic
switch {
case keytype == tpm2.TPMAlgECDSA && bits == 256:
keyPublic = createECCKey(tpm2.TPMECCNistP256, tpm2.TPMAlgSHA256)
case keytype == tpm2.TPMAlgECDSA && bits == 384:
keyPublic = createECCKey(tpm2.TPMECCNistP384, tpm2.TPMAlgSHA256)
case keytype == tpm2.TPMAlgECDSA && bits == 512:
keyPublic = createECCKey(tpm2.TPMECCNistP521, tpm2.TPMAlgSHA256)
case keytype == tpm2.TPMAlgRSA:
keyPublic = createRSAKey(2048, tpm2.TPMAlgSHA256)
}

// Template for en ECDSA key for signing
Expand Down
12 changes: 9 additions & 3 deletions key/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"reflect"
"testing"

"github.com/foxboron/ssh-tpm-agent/utils"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpm2/transport/simulator"

Expand All @@ -17,14 +18,17 @@ func TestCreateKey(t *testing.T) {
cases := []struct {
text string
alg tpm2.TPMAlgID
bits int
}{
{
text: "ecdsa",
text: "p256",
alg: tpm2.TPMAlgECDSA,
bits: 256,
},
{
text: "rsa",
alg: tpm2.TPMAlgRSA,
bits: 2048,
},
}

Expand All @@ -36,16 +40,18 @@ func TestCreateKey(t *testing.T) {

for _, c := range cases {
t.Run(c.text, func(t *testing.T) {
k, err := CreateKey(tpm, c.alg, []byte(""), []byte(""))
k, err := CreateKey(tpm, c.alg, c.bits, []byte(""), []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
if _, err = LoadKey(tpm, k); err != nil {
handle, err := LoadKey(tpm, k)
if err != nil {
t.Fatalf("failed loading key: %v", err)
}
utils.FlushHandle(tpm, handle)
})
}
}
Expand Down
28 changes: 24 additions & 4 deletions signer/signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ func TestSigning(t *testing.T) {
cases := []struct {
msg string
keytype tpm2.TPMAlgID
bits int
digest crypto.Hash
filekey []byte
pin []byte
signpin []byte
Expand All @@ -30,53 +32,69 @@ func TestSigning(t *testing.T) {
msg: "ecdsa - test encryption/decrypt - no pin",
filekey: []byte("this is a test filekey"),
keytype: tpm2.TPMAlgECDSA,
digest: crypto.SHA256,
bits: 256,
},
{
msg: "ecdsa - test encryption/decrypt - pin",
filekey: []byte("this is a test filekey"),
pin: []byte("123"),
signpin: []byte("123"),
keytype: tpm2.TPMAlgECDSA,
digest: crypto.SHA256,
bits: 256,
},
{
msg: "ecdsa - test encryption/decrypt - no pin for sign",
filekey: []byte("this is a test filekey"),
pin: []byte("123"),
shouldfail: true,
keytype: tpm2.TPMAlgECDSA,
digest: crypto.SHA256,
bits: 256,
},
{
msg: "ecdsa - test encryption/decrypt - no pin for key, pin for sign",
filekey: []byte("this is a test filekey"),
pin: []byte(""),
signpin: []byte("123"),
keytype: tpm2.TPMAlgECDSA,
digest: crypto.SHA256,
bits: 256,
},
{
msg: "rsa - test encryption/decrypt - no pin",
filekey: []byte("this is a test filekey"),
keytype: tpm2.TPMAlgRSA,
digest: crypto.SHA256,
bits: 2048,
},
{
msg: "rsa - test encryption/decrypt - pin",
filekey: []byte("this is a test filekey"),
pin: []byte("123"),
signpin: []byte("123"),
keytype: tpm2.TPMAlgRSA,
digest: crypto.SHA256,
bits: 2048,
},
{
msg: "rsa - test encryption/decrypt - no pin for sign",
filekey: []byte("this is a test filekey"),
pin: []byte("123"),
shouldfail: true,
keytype: tpm2.TPMAlgRSA,
digest: crypto.SHA256,
bits: 2048,
},
{
msg: "rsa - test encryption/decrypt - no pin for key, pin for sign",
filekey: []byte("this is a test filekey"),
pin: []byte(""),
signpin: []byte("123"),
keytype: tpm2.TPMAlgRSA,
digest: crypto.SHA256,
bits: 2048,
},
}

Expand All @@ -90,9 +108,11 @@ func TestSigning(t *testing.T) {
}
defer tpm.Close()

b := sha256.Sum256([]byte("heyho"))
h := c.digest.New()
h.Write([]byte("heyho"))
b := h.Sum(nil)

k, err := key.CreateKey(tpm, c.keytype, c.pin, []byte(""))
k, err := key.CreateKey(tpm, c.keytype, c.bits, c.pin, []byte(""))
if err != nil {
t.Fatalf("%v", err)
}
Expand All @@ -105,7 +125,7 @@ func TestSigning(t *testing.T) {
// Empty reader, we don't use this
var r io.Reader

sig, err := signer.Sign(r, b[:], crypto.SHA256)
sig, err := signer.Sign(r, b[:], c.digest)
if err != nil {
if c.shouldfail {
return
Expand Down Expand Up @@ -138,7 +158,7 @@ func TestSigning(t *testing.T) {
t.Fatalf("invalid signature")
}
case *rsa.PublicKey:
if err := rsa.VerifyPKCS1v15(pk, crypto.SHA256, b[:], sig); err != nil {
if err := rsa.VerifyPKCS1v15(pk, c.digest, b[:], sig); err != nil {
t.Errorf("Signature verification failed: %v", err)
}
}
Expand Down

0 comments on commit 9bced17

Please sign in to comment.