From 48a29b4b89df88e02f6a29b5e802d9b4d22fb04a Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Sun, 18 Feb 2024 16:11:21 +0100 Subject: [PATCH 1/7] misc: update to go 1.22 Signed-off-by: Morten Linderud --- .github/workflows/test.yml | 2 +- go.mod | 2 +- go.sum | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb161f2..f938001 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - go: [1.20.x] + go: [1.22.x] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: diff --git a/go.mod b/go.mod index cf079a4..54f40d3 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/foxboron/ssh-tpm-agent -go 1.20 +go 1.22 require ( github.com/foxboron/swtpm_test v0.0.0-20230726224112-46aaafdf7006 diff --git a/go.sum b/go.sum index ff6aa9e..2b516ec 100644 --- a/go.sum +++ b/go.sum @@ -6,14 +6,19 @@ github.com/foxboron/swtpm_test v0.0.0-20230726224112-46aaafdf7006 h1:50sW4r0Pcvl github.com/foxboron/swtpm_test v0.0.0-20230726224112-46aaafdf7006/go.mod h1:eIXCMsMYCaqq9m1KSSxXwQG11krpuNPGP3k0uaWrbas= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2btFtk0= +github.com/google/go-sev-guest v0.6.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q= github.com/google/go-tpm v0.9.1-0.20230807150904-c49efc441a60 h1:WZpppXXHrrk6r76Vn2HPf1S/5MhuVjiaef3FHeLf0vA= github.com/google/go-tpm v0.9.1-0.20230807150904-c49efc441a60/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= 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= +github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -36,8 +41,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI= -golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -65,7 +68,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From ab5e213bb17f9ac0a84d1664e5dd782a6e6505e4 Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Sun, 18 Feb 2024 14:46:04 +0100 Subject: [PATCH 2/7] key: fetch supported ecdsa bit sizes Signed-off-by: Morten Linderud --- key/key.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/key/key.go b/key/key.go index 1171d0f..45724c8 100644 --- a/key/key.go +++ b/key/key.go @@ -549,3 +549,30 @@ func LoadKey(tpm transport.TPMCloser, key *Key) (*tpm2.AuthHandle, error) { return LoadKeyWithParent(tpm, *srkHandle, key) } + +func SupportedECCAlgorithms(tpm transport.TPMCloser) []int { + var getCapRsp *tpm2.GetCapabilityResponse + var supportedBitsizes []int + + eccCapCmd := tpm2.GetCapability{ + Capability: tpm2.TPMCapECCCurves, + PropertyCount: 100, + } + getCapRsp, err := eccCapCmd.Execute(tpm) + if err != nil { + return []int{} + } + curves, err := getCapRsp.CapabilityData.Data.ECCCurves() + if err != nil { + return []int{} + } + for _, curve := range curves.ECCCurves { + c, err := curve.Curve() + // if we fail here we are dealing with an unsupported curve + if err != nil { + continue + } + supportedBitsizes = append(supportedBitsizes, c.Params().BitSize) + } + return supportedBitsizes +} From e58264dc96dc7459058a71216b5062b92870af51 Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Sun, 18 Feb 2024 15:11:03 +0100 Subject: [PATCH 3/7] ssh-tpm-agent: support creation of p256 p384 and p512 keys 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 --- agent/agent_test.go | 2 +- cmd/ssh-tpm-agent/main_test.go | 10 +++--- cmd/ssh-tpm-keygen/main.go | 18 ++++++++--- key/key.go | 56 +++++++++++++++++++++++++--------- key/key_test.go | 12 ++++++-- signer/signer_test.go | 28 ++++++++++++++--- 6 files changed, 94 insertions(+), 32 deletions(-) diff --git a/agent/agent_test.go b/agent/agent_test.go index b4bb7da..e30860b 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -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) } diff --git a/cmd/ssh-tpm-agent/main_test.go b/cmd/ssh-tpm-agent/main_test.go index 277fb4c..d1546b9 100644 --- a/cmd/ssh-tpm-agent/main_test.go +++ b/cmd/ssh-tpm-agent/main_test.go @@ -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) } @@ -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) }) } diff --git a/cmd/ssh-tpm-keygen/main.go b/cmd/ssh-tpm-keygen/main.go index 34667b0..b9da4f1 100644 --- a/cmd/ssh-tpm-keygen/main.go +++ b/cmd/ssh-tpm-keygen/main.go @@ -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 | 521 -I, --import PATH Import existing key into ssh-tpm-agent. -A Generate host keys for all key types (rsa and ecdsa). @@ -97,6 +100,7 @@ func main() { var ( comment, outputFile, keyPin string keyType, importKey string + bits int swtpmFlag, hostKeys bool ) @@ -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") @@ -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) @@ -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) } @@ -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) } diff --git a/key/key.go b/key/key.go index 45724c8..b251f82 100644 --- a/key/key.go +++ b/key/key.go @@ -7,8 +7,10 @@ import ( "crypto/rsa" "encoding/binary" "encoding/pem" + "errors" "fmt" "math/big" + "slices" "strings" "github.com/foxboron/ssh-tpm-agent/utils" @@ -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, @@ -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( @@ -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, @@ -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, 521} + + 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") } @@ -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 == 521: + keyPublic = createECCKey(tpm2.TPMECCNistP521, tpm2.TPMAlgSHA256) + case keytype == tpm2.TPMAlgRSA: + keyPublic = createRSAKey(2048, tpm2.TPMAlgSHA256) } // Template for en ECDSA key for signing diff --git a/key/key_test.go b/key/key_test.go index e16b191..a86f6ae 100644 --- a/key/key_test.go +++ b/key/key_test.go @@ -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" @@ -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, }, } @@ -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) }) } } diff --git a/signer/signer_test.go b/signer/signer_test.go index 3e8bbbc..468ecc4 100644 --- a/signer/signer_test.go +++ b/signer/signer_test.go @@ -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 @@ -30,6 +32,8 @@ 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", @@ -37,6 +41,8 @@ func TestSigning(t *testing.T) { pin: []byte("123"), signpin: []byte("123"), keytype: tpm2.TPMAlgECDSA, + digest: crypto.SHA256, + bits: 256, }, { msg: "ecdsa - test encryption/decrypt - no pin for sign", @@ -44,6 +50,8 @@ func TestSigning(t *testing.T) { 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", @@ -51,11 +59,15 @@ func TestSigning(t *testing.T) { 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", @@ -63,6 +75,8 @@ func TestSigning(t *testing.T) { pin: []byte("123"), signpin: []byte("123"), keytype: tpm2.TPMAlgRSA, + digest: crypto.SHA256, + bits: 2048, }, { msg: "rsa - test encryption/decrypt - no pin for sign", @@ -70,6 +84,8 @@ func TestSigning(t *testing.T) { 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", @@ -77,6 +93,8 @@ func TestSigning(t *testing.T) { pin: []byte(""), signpin: []byte("123"), keytype: tpm2.TPMAlgRSA, + digest: crypto.SHA256, + bits: 2048, }, } @@ -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) } @@ -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 @@ -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) } } From f3f80e3c7b610117c43c2c83f49476f746a1785b Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Sun, 18 Feb 2024 16:07:10 +0100 Subject: [PATCH 4/7] key: Support importing p256, p386 and p512 Signed-off-by: Morten Linderud --- key/key.go | 53 +++++++++++++++--- key/key_test.go | 146 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 165 insertions(+), 34 deletions(-) diff --git a/key/key.go b/key/key.go index b251f82..f920469 100644 --- a/key/key.go +++ b/key/key.go @@ -57,9 +57,29 @@ func (k *Key) ecdsaPubKey() (*ecdsa.PublicKey, error) { return nil, err } - ecdsaKey := &ecdsa.PublicKey{Curve: elliptic.P256(), - X: big.NewInt(0).SetBytes(ecc.X.Buffer), - Y: big.NewInt(0).SetBytes(ecc.Y.Buffer), + eccdeets, err := pub.Parameters.ECCDetail() + if err != nil { + return nil, err + } + + var ecdsaKey *ecdsa.PublicKey + + switch eccdeets.CurveID { + case tpm2.TPMECCNistP256: + ecdsaKey = &ecdsa.PublicKey{Curve: elliptic.P256(), + X: big.NewInt(0).SetBytes(ecc.X.Buffer), + Y: big.NewInt(0).SetBytes(ecc.Y.Buffer), + } + case tpm2.TPMECCNistP384: + ecdsaKey = &ecdsa.PublicKey{Curve: elliptic.P384(), + X: big.NewInt(0).SetBytes(ecc.X.Buffer), + Y: big.NewInt(0).SetBytes(ecc.Y.Buffer), + } + case tpm2.TPMECCNistP521: + ecdsaKey = &ecdsa.PublicKey{Curve: elliptic.P521(), + X: big.NewInt(0).SetBytes(ecc.X.Buffer), + Y: big.NewInt(0).SetBytes(ecc.Y.Buffer), + } } return ecdsaKey, nil @@ -400,15 +420,30 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin, co } func ImportKey(tpm transport.TPMCloser, pk any, pin, comment []byte) (*Key, error) { - var public tpm2.TPMTPublic var sensitive tpm2.TPMTSensitive var unique tpm2.TPMUPublicID var keytype tpm2.TPMAlgID + supportedECCBitsizes := SupportedECCAlgorithms(tpm) + switch p := pk.(type) { case ecdsa.PrivateKey: + var curveid tpm2.TPMECCCurve + + if !slices.Contains(supportedECCBitsizes, p.Params().BitSize) { + return nil, fmt.Errorf("invalid ecdsa key length: TPM does not support %v bits", p.Params().BitSize) + } + + switch p.Params().BitSize { + case 256: + curveid = tpm2.TPMECCNistP256 + case 384: + curveid = tpm2.TPMECCNistP384 + case 521: + curveid = tpm2.TPMECCNistP521 + } keytype = tpm2.TPMAlgECDSA @@ -417,7 +452,7 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin, comment []byte) (*Key, erro SensitiveType: tpm2.TPMAlgECC, Sensitive: tpm2.NewTPMUSensitiveComposite( tpm2.TPMAlgECC, - &tpm2.TPM2BECCParameter{Buffer: p.D.FillBytes(make([]byte, 32))}, + &tpm2.TPM2BECCParameter{Buffer: p.D.FillBytes(make([]byte, len(p.D.Bytes())))}, ), } @@ -425,10 +460,10 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin, comment []byte) (*Key, erro tpm2.TPMAlgECC, &tpm2.TPMSECCPoint{ X: tpm2.TPM2BECCParameter{ - Buffer: p.X.FillBytes(make([]byte, 32)), + Buffer: p.X.FillBytes(make([]byte, len(p.X.Bytes()))), }, Y: tpm2.TPM2BECCParameter{ - Buffer: p.Y.FillBytes(make([]byte, 32)), + Buffer: p.Y.FillBytes(make([]byte, len(p.Y.Bytes()))), }, }, ) @@ -443,7 +478,7 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin, comment []byte) (*Key, erro Parameters: tpm2.NewTPMUPublicParms( tpm2.TPMAlgECC, &tpm2.TPMSECCParms{ - CurveID: tpm2.TPMECCNistP256, + CurveID: curveid, Scheme: tpm2.TPMTECCScheme{ Scheme: tpm2.TPMAlgECDSA, Details: tpm2.NewTPMUAsymScheme( @@ -459,6 +494,8 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin, comment []byte) (*Key, erro } case rsa.PrivateKey: + // TODO: Reject larger keys than 2048 + keytype = tpm2.TPMAlgRSA // Prepare RSA key for importing diff --git a/key/key_test.go b/key/key_test.go index a86f6ae..ca3bf15 100644 --- a/key/key_test.go +++ b/key/key_test.go @@ -25,6 +25,16 @@ func TestCreateKey(t *testing.T) { alg: tpm2.TPMAlgECDSA, bits: 256, }, + { + text: "p384", + alg: tpm2.TPMAlgECDSA, + bits: 384, + }, + { + text: "p521", + alg: tpm2.TPMAlgECDSA, + bits: 521, + }, { text: "rsa", alg: tpm2.TPMAlgRSA, @@ -150,50 +160,134 @@ func TestMarshalling(t *testing.T) { } } -func TestECDSAImportKey(t *testing.T) { - pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +func mkRSA(t *testing.T, bits int) rsa.PrivateKey { + t.Helper() + pk, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + t.Fatalf("failed to generate rsa key: %v", err) + } + return *pk +} + +func mkECDSA(t *testing.T, a elliptic.Curve) ecdsa.PrivateKey { + t.Helper() + pk, err := ecdsa.GenerateKey(a, rand.Reader) if err != nil { t.Fatalf("failed to generate ecdsa key: %v", err) } + return *pk +} +func TestImport(t *testing.T) { tpm, err := simulator.OpenSimulator() if err != nil { t.Fatal(err) } defer tpm.Close() - k, err := ImportKey(tpm, *pk, []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 - _, err = LoadKey(tpm, k) - if err != nil { - t.Fatalf("failed loading key: %v", err) - } -} + for _, c := range []struct { + text string + pk any + fail bool + }{ + { + text: "p256", + pk: mkECDSA(t, elliptic.P256()), + }, + { + text: "p384", + pk: mkECDSA(t, elliptic.P384()), + }, + { + text: "p521", + pk: mkECDSA(t, elliptic.P521()), + // Simulator doesn't like P521 + fail: true, + }, + { + text: "rsa2048", + pk: mkRSA(t, 2048), + }, + } { + t.Run(c.text, func(t *testing.T) { + k, err := ImportKey(tpm, c.pk, []byte(""), []byte("")) + if err != nil && c.fail { + return + } + if err != nil { + t.Fatalf("failed key import: %v", err) + } -func TestRSAImportKey(t *testing.T) { - pk, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatalf("failed to generate rsa key: %v", err) + // Test if we can load the key + // signer/signer_test.go tests the signing of the key + handle, err := LoadKey(tpm, k) + if err != nil { + t.Fatalf("failed loading key: %v", err) + } + utils.FlushHandle(tpm, handle) + }) } +} +func TestKeyPublickey(t *testing.T) { tpm, err := simulator.OpenSimulator() if err != nil { t.Fatal(err) } defer tpm.Close() - k, err := ImportKey(tpm, *pk, []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 - _, err = LoadKey(tpm, k) - if err != nil { - t.Fatalf("failed loading key: %v", err) + for _, c := range []struct { + text string + pk any + bitlength int + fail bool + }{ + { + text: "p256", + pk: mkECDSA(t, elliptic.P256()), + bitlength: 256, + }, + { + text: "p384", + pk: mkECDSA(t, elliptic.P384()), + bitlength: 384, + }, + { + text: "p521", + pk: mkECDSA(t, elliptic.P521()), + // Simulator doesn't like P521 + bitlength: 521, + fail: true, + }, + { + text: "rsa2048", + pk: mkRSA(t, 2048), + bitlength: 2048, + }, + } { + t.Run(c.text, func(t *testing.T) { + k, err := ImportKey(tpm, c.pk, []byte(""), []byte("")) + if err != nil && c.fail { + return + } + if err != nil { + t.Fatalf("failed key import: %v", err) + } + + pubkey, err := k.PublicKey() + if err != nil { + t.Fatalf("failed getting public key: %v", err) + } + switch pk := pubkey.(type) { + case *ecdsa.PublicKey: + if pk.Params().BitSize != c.bitlength { + t.Fatalf("wrong import, expected %v got %v bitlength", pk.Params().BitSize, c.bitlength) + } + case *rsa.PublicKey: + if pk.N.BitLen() != c.bitlength { + t.Fatalf("wrong import, expected %v got %v bitlength", pk.N.BitLen(), c.bitlength) + } + } + }) } } From fb875a074c713de9b0b3c0cec61377e6a13c56cc Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Sun, 18 Feb 2024 16:08:12 +0100 Subject: [PATCH 5/7] key: move to noalg keys to support different hashing algos Signed-off-by: Morten Linderud --- cmd/ssh-tpm-agent/main_test.go | 6 ++++++ key/key.go | 32 ++++---------------------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/cmd/ssh-tpm-agent/main_test.go b/cmd/ssh-tpm-agent/main_test.go index d1546b9..59bc8ba 100644 --- a/cmd/ssh-tpm-agent/main_test.go +++ b/cmd/ssh-tpm-agent/main_test.go @@ -190,4 +190,10 @@ func TestSSHAuth(t *testing.T) { t.Run("rsa - agent", func(t *testing.T) { runSSHAuth(t, tpm2.TPMAlgRSA, 2048) }) + t.Run("ecdsa p384 - agent", func(t *testing.T) { + runSSHAuth(t, tpm2.TPMAlgECDSA, 384) + }) + t.Run("ecdsa p521 - agent", func(t *testing.T) { + runSSHAuth(t, tpm2.TPMAlgECDSA, 521) + }) } diff --git a/key/key.go b/key/key.go index f920469..6e21bbb 100644 --- a/key/key.go +++ b/key/key.go @@ -291,13 +291,7 @@ func createECCKey(ecc tpm2.TPMECCCurve, sha tpm2.TPMAlgID) tpm2.TPM2B[tpm2.TPMTP &tpm2.TPMSECCParms{ CurveID: ecc, Scheme: tpm2.TPMTECCScheme{ - Scheme: tpm2.TPMAlgECDSA, - Details: tpm2.NewTPMUAsymScheme( - tpm2.TPMAlgECDSA, - &tpm2.TPMSSigSchemeECDSA{ - HashAlg: tpm2.TPMAlgSHA256, - }, - ), + Scheme: tpm2.TPMAlgNull, }, }, ), @@ -319,13 +313,7 @@ func createRSAKey(bits tpm2.TPMKeyBits, sha tpm2.TPMAlgID) tpm2.TPM2B[tpm2.TPMTP tpm2.TPMAlgRSA, &tpm2.TPMSRSAParms{ Scheme: tpm2.TPMTRSAScheme{ - Scheme: tpm2.TPMAlgRSASSA, - Details: tpm2.NewTPMUAsymScheme( - tpm2.TPMAlgRSASSA, - &tpm2.TPMSSigSchemeRSASSA{ - HashAlg: tpm2.TPMAlgSHA256, - }, - ), + Scheme: tpm2.TPMAlgNull, }, KeyBits: bits, }, @@ -480,13 +468,7 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin, comment []byte) (*Key, erro &tpm2.TPMSECCParms{ CurveID: curveid, Scheme: tpm2.TPMTECCScheme{ - Scheme: tpm2.TPMAlgECDSA, - Details: tpm2.NewTPMUAsymScheme( - tpm2.TPMAlgECDSA, - &tpm2.TPMSSigSchemeECDSA{ - HashAlg: tpm2.TPMAlgSHA256, - }, - ), + Scheme: tpm2.TPMAlgNull, }, }, ), @@ -523,13 +505,7 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin, comment []byte) (*Key, erro tpm2.TPMAlgRSA, &tpm2.TPMSRSAParms{ Scheme: tpm2.TPMTRSAScheme{ - Scheme: tpm2.TPMAlgRSASSA, - Details: tpm2.NewTPMUAsymScheme( - tpm2.TPMAlgRSASSA, - &tpm2.TPMSSigSchemeRSASSA{ - HashAlg: tpm2.TPMAlgSHA256, - }, - ), + Scheme: tpm2.TPMAlgNull, }, KeyBits: 2048, }, From d3c049b2169a5ed77d03d5ff7201cc6172dd7f16 Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Wed, 21 Feb 2024 20:57:42 +0100 Subject: [PATCH 6/7] signer: Implement support for multiple hashing algos Signed-off-by: Morten Linderud --- signer/signer.go | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/signer/signer.go b/signer/signer.go index 952b303..c177224 100644 --- a/signer/signer.go +++ b/signer/signer.go @@ -70,35 +70,50 @@ func encodeSignature(r, s []byte) ([]byte, error) { return b.Bytes() } -var ( - eccSigScheme = tpm2.TPMTSigScheme{ +func newECCSigScheme(digest tpm2.TPMAlgID) tpm2.TPMTSigScheme { + return tpm2.TPMTSigScheme{ Scheme: tpm2.TPMAlgECDSA, Details: tpm2.NewTPMUSigScheme( tpm2.TPMAlgECDSA, &tpm2.TPMSSchemeHash{ - HashAlg: tpm2.TPMAlgSHA256, + HashAlg: digest, }, ), } +} - rsaSigScheme = tpm2.TPMTSigScheme{ +func newRSASigScheme(digest tpm2.TPMAlgID) tpm2.TPMTSigScheme { + return tpm2.TPMTSigScheme{ Scheme: tpm2.TPMAlgRSASSA, Details: tpm2.NewTPMUSigScheme( tpm2.TPMAlgRSASSA, &tpm2.TPMSSchemeHash{ - HashAlg: tpm2.TPMAlgSHA256, + HashAlg: digest, }, ), } -) +} func (t *TPMSigner) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { - if opts.HashFunc() != crypto.SHA256 { + var digestlength int + var digestalg tpm2.TPMAlgID + + switch opts.HashFunc() { + case crypto.SHA256: + digestlength = 32 + digestalg = tpm2.TPMAlgSHA256 + case crypto.SHA384: + digestlength = 48 + digestalg = tpm2.TPMAlgSHA384 + case crypto.SHA512: + digestlength = 64 + digestalg = tpm2.TPMAlgSHA512 + default: return nil, fmt.Errorf("%s is not a supported hashing algorithm", opts.HashFunc()) } - if len(digest) != 32 { - return nil, fmt.Errorf("incorrect checksum length") + if len(digest) != digestlength { + return nil, fmt.Errorf("incorrect checksum length. expected %v got %v", digestlength, len(digest)) } tpm := t.getTPM() @@ -127,9 +142,9 @@ func (t *TPMSigner) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([] var sigscheme tpm2.TPMTSigScheme switch t.key.Type { case tpm2.TPMAlgECDSA: - sigscheme = eccSigScheme + sigscheme = newECCSigScheme(digestalg) case tpm2.TPMAlgRSA: - sigscheme = rsaSigScheme + sigscheme = newRSASigScheme(digestalg) } sign := tpm2.Sign{ From ae2e09673989fea5ca94e0eeb4d5a6e2c9bbb7fc Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Thu, 22 Feb 2024 18:55:07 +0100 Subject: [PATCH 7/7] ssh-tpm-keygen: add --supported switch to list supported bit lengths Signed-off-by: Morten Linderud --- cmd/ssh-tpm-keygen/main.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cmd/ssh-tpm-keygen/main.go b/cmd/ssh-tpm-keygen/main.go index b9da4f1..923d997 100644 --- a/cmd/ssh-tpm-keygen/main.go +++ b/cmd/ssh-tpm-keygen/main.go @@ -39,6 +39,7 @@ Options: ecdsa: 256 (default) | 384 | 521 -I, --import PATH Import existing key into ssh-tpm-agent. -A Generate host keys for all key types (rsa and ecdsa). + --supported List the supported keys of the TPM. Generate new TPM sealed keys for ssh-tpm-agent. @@ -102,6 +103,7 @@ func main() { keyType, importKey string bits int swtpmFlag, hostKeys bool + listsupported bool ) defaultComment := func() string { @@ -129,6 +131,7 @@ func main() { flag.StringVar(&importKey, "import", "", "import key") flag.BoolVar(&swtpmFlag, "swtpm", false, "use swtpm instead of actual tpm") flag.BoolVar(&hostKeys, "A", false, "generate host keys") + flag.BoolVar(&listsupported, "supported", false, "list tpm caps") flag.Parse() @@ -138,6 +141,16 @@ func main() { } defer tpm.Close() + if listsupported { + fmt.Printf("ecdsa bit lengths:") + for _, alg := range key.SupportedECCAlgorithms(tpm) { + fmt.Printf(" %d", alg) + } + fmt.Println() + fmt.Println("rsa bit lengths: 2048") + os.Exit(0) + } + // Generate host keys if hostKeys { // Mimics the `ssh-keygen -A -f ./something` behaviour