From 1b202b12e80969eb8c5933a9197c16f5f7529d37 Mon Sep 17 00:00:00 2001 From: Evgeny Shatokhin Date: Fri, 3 Jan 2025 15:28:59 +1100 Subject: [PATCH] attest: Support creation of ECC AK. --- attest/attest.go | 3 ++ attest/attest_simulated_tpm20_test.go | 69 ++++++++++++++++-------- attest/attest_test.go | 78 +++++++++++++++++---------- attest/tpm.go | 18 ++++++- attest/wrapped_tpm20.go | 12 ++++- 5 files changed, 128 insertions(+), 52 deletions(-) diff --git a/attest/attest.go b/attest/attest.go index b305c8b..2584819 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -194,6 +194,9 @@ type AKConfig struct { // If nil, the default SRK (i.e. RSA with handle 0x81000001) is assumed. // Supported only by TPM 2.0 on Linux. Parent *ParentKeyConfig + + // If not specified, the default algorithm (RSA) is assumed. + Algorithm Algorithm } // EncryptedCredential represents encrypted parameters which must be activated diff --git a/attest/attest_simulated_tpm20_test.go b/attest/attest_simulated_tpm20_test.go index 8ad2c3c..976e1b1 100644 --- a/attest/attest_simulated_tpm20_test.go +++ b/attest/attest_simulated_tpm20_test.go @@ -67,33 +67,56 @@ func TestSimTPM20Info(t *testing.T) { func TestSimTPM20AKCreateAndLoad(t *testing.T) { sim, tpm := setupSimulatedTPM(t) defer sim.Close() + for _, test := range []struct { + name string + opts *AKConfig + }{ + { + name: "NoConfig", + opts: nil, + }, + { + name: "EmptyConfig", + opts: &AKConfig{}, + }, + { + name: "RSA", + opts: &AKConfig{Algorithm: RSA}, + }, + { + name: "ECDSA", + opts: &AKConfig{Algorithm: ECDSA}, + }, + } { + t.Run(test.name, func(t *testing.T) { + ak, err := tpm.NewAK(test.opts) + if err != nil { + t.Fatalf("NewAK() failed: %v", err) + } - ak, err := tpm.NewAK(nil) - if err != nil { - t.Fatalf("NewAK() failed: %v", err) - } + enc, err := ak.Marshal() + if err != nil { + ak.Close(tpm) + t.Fatalf("ak.Marshal() failed: %v", err) + } + if err := ak.Close(tpm); err != nil { + t.Fatalf("ak.Close() failed: %v", err) + } - enc, err := ak.Marshal() - if err != nil { - ak.Close(tpm) - t.Fatalf("ak.Marshal() failed: %v", err) - } - if err := ak.Close(tpm); err != nil { - t.Fatalf("ak.Close() failed: %v", err) - } + loaded, err := tpm.LoadAK(enc) + if err != nil { + t.Fatalf("LoadAK() failed: %v", err) + } + defer loaded.Close(tpm) - loaded, err := tpm.LoadAK(enc) - if err != nil { - t.Fatalf("LoadKey() failed: %v", err) - } - defer loaded.Close(tpm) + k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20) - k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20) - - if !bytes.Equal(k1.public, k2.public) { - t.Error("Original & loaded AK public blobs did not match.") - t.Logf("Original = %v", k1.public) - t.Logf("Loaded = %v", k2.public) + if !bytes.Equal(k1.public, k2.public) { + t.Error("Original & loaded AK public blobs did not match.") + t.Logf("Original = %v", k1.public) + t.Logf("Loaded = %v", k2.public) + } + }) } } diff --git a/attest/attest_test.go b/attest/attest_test.go index 2d7a2fb..43ca9a1 100644 --- a/attest/attest_test.go +++ b/attest/attest_test.go @@ -83,38 +83,62 @@ func TestAKCreateAndLoad(t *testing.T) { if !*testLocal { t.SkipNow() } - tpm, err := OpenTPM(nil) - if err != nil { - t.Fatalf("OpenTPM() failed: %v", err) - } - defer tpm.Close() + for _, test := range []struct { + name string + opts *AKConfig + }{ + { + name: "NoConfig", + opts: nil, + }, + { + name: "EmptyConfig", + opts: &AKConfig{}, + }, + { + name: "RSA", + opts: &AKConfig{Algorithm: RSA}, + }, + { + name: "ECDSA", + opts: &AKConfig{Algorithm: ECDSA}, + }, + } { + t.Run(test.name, func(t *testing.T) { + tpm, err := OpenTPM(nil) + if err != nil { + t.Fatalf("OpenTPM() failed: %v", err) + } + defer tpm.Close() - ak, err := tpm.NewAK(nil) - if err != nil { - t.Fatalf("NewAK() failed: %v", err) - } + ak, err := tpm.NewAK(test.opts) + if err != nil { + t.Fatalf("NewAK() failed: %v", err) + } - enc, err := ak.Marshal() - if err != nil { - ak.Close(tpm) - t.Fatalf("ak.Marshal() failed: %v", err) - } - if err := ak.Close(tpm); err != nil { - t.Fatalf("ak.Close() failed: %v", err) - } + enc, err := ak.Marshal() + if err != nil { + ak.Close(tpm) + t.Fatalf("ak.Marshal() failed: %v", err) + } + if err := ak.Close(tpm); err != nil { + t.Fatalf("ak.Close() failed: %v", err) + } - loaded, err := tpm.LoadAK(enc) - if err != nil { - t.Fatalf("LoadKey() failed: %v", err) - } - defer loaded.Close(tpm) + loaded, err := tpm.LoadAK(enc) + if err != nil { + t.Fatalf("LoadAK() failed: %v", err) + } + defer loaded.Close(tpm) - k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20) + k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20) - if !bytes.Equal(k1.public, k2.public) { - t.Error("Original & loaded AK public blobs did not match.") - t.Logf("Original = %v", k1.public) - t.Logf("Loaded = %v", k2.public) + if !bytes.Equal(k1.public, k2.public) { + t.Error("Original & loaded AK public blobs did not match.") + t.Logf("Original = %v", k1.public) + t.Logf("Loaded = %v", k2.public) + } + }) } } diff --git a/attest/tpm.go b/attest/tpm.go index e7f121f..2f376d1 100644 --- a/attest/tpm.go +++ b/attest/tpm.go @@ -48,7 +48,7 @@ const ( ) var ( - akTemplate = tpm2.Public{ + akTemplateRSA = tpm2.Public{ Type: tpm2.AlgRSA, NameAlg: tpm2.AlgSHA256, Attributes: tpm2.FlagSignerDefault | tpm2.FlagNoDA, @@ -60,6 +60,22 @@ var ( KeyBits: 2048, }, } + akTemplateECC = tpm2.Public{ + Type: tpm2.AlgECC, + NameAlg: tpm2.AlgSHA256, + Attributes: tpm2.FlagSignerDefault | tpm2.FlagNoDA, + ECCParameters: &tpm2.ECCParams{ + Sign: &tpm2.SigScheme{ + Alg: tpm2.AlgECDSA, + Hash: tpm2.AlgSHA256, + }, + CurveID: tpm2.CurveNISTP256, + Point: tpm2.ECPoint{ + XRaw: make([]byte, 32), + YRaw: make([]byte, 32), + }, + }, + } defaultRSASRKTemplate = tpm2.Public{ Type: tpm2.AlgRSA, NameAlg: tpm2.AlgSHA256, diff --git a/attest/wrapped_tpm20.go b/attest/wrapped_tpm20.go index 1468c1b..afd3c07 100644 --- a/attest/wrapped_tpm20.go +++ b/attest/wrapped_tpm20.go @@ -246,6 +246,16 @@ func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) { return nil, fmt.Errorf("failed to get SRK handle: %v", err) } + var akTemplate tpm2.Public + var sigScheme *tpm2.SigScheme + // The default is RSA. + if opts != nil && opts.Algorithm == ECDSA { + akTemplate = akTemplateECC + sigScheme = akTemplateECC.ECCParameters.Sign + } else { + akTemplate = akTemplateRSA + sigScheme = akTemplateRSA.RSAParameters.Sign + } blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", akTemplate) if err != nil { return nil, fmt.Errorf("CreateKeyEx() failed: %v", err) @@ -262,7 +272,7 @@ func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) { }() // We can only certify the creation immediately afterwards, so we cache the result. - attestation, sig, err := tpm2.CertifyCreation(t.rwc, "", keyHandle, keyHandle, nil, creationHash, tpm2.SigScheme{Alg: tpm2.AlgRSASSA, Hash: tpm2.AlgSHA256, Count: 0}, tix) + attestation, sig, err := tpm2.CertifyCreation(t.rwc, "", keyHandle, keyHandle, nil, creationHash, *sigScheme, tix) if err != nil { return nil, fmt.Errorf("CertifyCreation failed: %v", err) }