From c7aee80c5d760a084adf51055b748d04a3187818 Mon Sep 17 00:00:00 2001 From: Evgeny Shatokhin Date: Mon, 6 Jan 2025 16:26:38 +1100 Subject: [PATCH] attest: Support certification by ECC AKs. --- attest/certification.go | 19 +++++---- attest/certification_test.go | 77 +++++++++++++++++++++++++++--------- attest/wrapped_tpm20.go | 27 +++++++++++-- 3 files changed, 94 insertions(+), 29 deletions(-) diff --git a/attest/certification.go b/attest/certification.go index 809fac67..c30d3e39 100644 --- a/attest/certification.go +++ b/attest/certification.go @@ -17,6 +17,7 @@ package attest import ( "bytes" "crypto" + "crypto/ecdsa" "crypto/rand" "crypto/rsa" "errors" @@ -164,11 +165,6 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error { } // Check the signature over the attestation data verifies correctly. - // TODO: Support ECC certifying keys - pk, ok := opts.Public.(*rsa.PublicKey) - if !ok { - return fmt.Errorf("only RSA verification keys are supported") - } if !opts.Hash.Available() { return fmt.Errorf("hash function is unavailable") } @@ -184,8 +180,17 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error { return fmt.Errorf("DecodeSignature() failed: %v", err) } - if err := rsa.VerifyPKCS1v15(pk, opts.Hash, hsh.Sum(nil), sig.RSA.Signature); err != nil { - return fmt.Errorf("could not verify attestation: %v", err) + switch pk := opts.Public.(type) { + case *rsa.PublicKey: + if err := rsa.VerifyPKCS1v15(pk, opts.Hash, hsh.Sum(nil), sig.RSA.Signature); err != nil { + return fmt.Errorf("could not verify attestation: %v", err) + } + case *ecdsa.PublicKey: + if ok := ecdsa.Verify(pk, hsh.Sum(nil), sig.ECC.R, sig.ECC.S); !ok { + return fmt.Errorf("could not verify ECC attestation") + } + default: + return fmt.Errorf("unsupported public key type: %T", pub) } return nil diff --git a/attest/certification_test.go b/attest/certification_test.go index d1c8acfc..df84155c 100644 --- a/attest/certification_test.go +++ b/attest/certification_test.go @@ -31,13 +31,19 @@ import ( "github.com/google/go-tpm/legacy/tpm2" ) -func TestSimTPM20CertificationParameters(t *testing.T) { +func TestSimTPM20CertificationParametersRSA(t *testing.T) { sim, tpm := setupSimulatedTPM(t) defer sim.Close() - testCertificationParameters(t, tpm) + testCertificationParameters(t, tpm, RSA) } -func TestTPM20CertificationParameters(t *testing.T) { +func TestSimTPM20CertificationParametersECC(t *testing.T) { + sim, tpm := setupSimulatedTPM(t) + defer sim.Close() + testCertificationParameters(t, tpm, ECDSA) +} + +func TestTPM20CertificationParametersRSA(t *testing.T) { if !*testLocal { t.SkipNow() } @@ -46,11 +52,23 @@ func TestTPM20CertificationParameters(t *testing.T) { t.Fatalf("OpenTPM() failed: %v", err) } defer tpm.Close() - testCertificationParameters(t, tpm) + testCertificationParameters(t, tpm, RSA) } -func testCertificationParameters(t *testing.T, tpm *TPM) { - ak, err := tpm.NewAK(nil) +func TestTPM20CertificationParametersECC(t *testing.T) { + if !*testLocal { + t.SkipNow() + } + tpm, err := OpenTPM(nil) + if err != nil { + t.Fatalf("OpenTPM() failed: %v", err) + } + defer tpm.Close() + testCertificationParameters(t, tpm, ECDSA) +} + +func testCertificationParameters(t *testing.T, tpm *TPM, akAlg Algorithm) { + ak, err := tpm.NewAK(&AKConfig{Algorithm: akAlg}) if err != nil { t.Fatal(err) } @@ -59,12 +77,12 @@ func testCertificationParameters(t *testing.T, tpm *TPM) { if err != nil { t.Fatal(err) } - if pub.Type != tpm2.AlgRSA { - t.Fatal("non-RSA verifying key") - } - pk := &rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()} - hash, err := pub.RSAParameters.Sign.Hash.Hash() + pk, err := pub.Key() + if err != nil { + t.Fatal(err) + } + hash, err := pub.NameAlg.Hash() if err != nil { t.Fatal(err) } @@ -170,13 +188,19 @@ func testCertificationParameters(t *testing.T, tpm *TPM) { } } -func TestSimTPM20KeyCertification(t *testing.T) { +func TestSimTPM20KeyCertificationRSA(t *testing.T) { + sim, tpm := setupSimulatedTPM(t) + defer sim.Close() + testKeyCertification(t, tpm, RSA) +} + +func TestSimTPM20KeyCertificationECC(t *testing.T) { sim, tpm := setupSimulatedTPM(t) defer sim.Close() - testKeyCertification(t, tpm) + testKeyCertification(t, tpm, ECDSA) } -func TestTPM20KeyCertification(t *testing.T) { +func TestTPM20KeyCertificationRSA(t *testing.T) { if !*testLocal { t.SkipNow() } @@ -185,11 +209,23 @@ func TestTPM20KeyCertification(t *testing.T) { t.Fatalf("OpenTPM() failed: %v", err) } defer tpm.Close() - testKeyCertification(t, tpm) + testKeyCertification(t, tpm, RSA) } -func testKeyCertification(t *testing.T, tpm *TPM) { - ak, err := tpm.NewAK(nil) +func TestTPM20KeyCertificationECC(t *testing.T) { + if !*testLocal { + t.SkipNow() + } + tpm, err := OpenTPM(nil) + if err != nil { + t.Fatalf("OpenTPM() failed: %v", err) + } + defer tpm.Close() + testKeyCertification(t, tpm, ECDSA) +} + +func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) { + ak, err := tpm.NewAK(&AKConfig{Algorithm: akAlg}) if err != nil { t.Fatalf("NewAK() failed: %v", err) } @@ -198,8 +234,11 @@ func testKeyCertification(t *testing.T, tpm *TPM) { if err != nil { t.Fatalf("DecodePublic() failed: %v", err) } - pk := &rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()} - hash, err := pub.RSAParameters.Sign.Hash.Hash() + pk, err := pub.Key() + if err != nil { + t.Fatalf("pub.Key() failed: %v", err) + } + hash, err := pub.NameAlg.Hash() if err != nil { t.Fatalf("cannot access AK's hash function: %v", err) } diff --git a/attest/wrapped_tpm20.go b/attest/wrapped_tpm20.go index afd3c076..19ca9fa8 100644 --- a/attest/wrapped_tpm20.go +++ b/attest/wrapped_tpm20.go @@ -566,6 +566,27 @@ func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential, ek }, k.hnd, ekHnd, credential, secret) } +func sigSchemeFromPublicKey(pub []byte) (tpm2.SigScheme, error) { + tpmPub, err := tpm2.DecodePublic(pub) + if err != nil { + return tpm2.SigScheme{}, fmt.Errorf("decode public key: %v", err) + } + switch tpmPub.Type { + case tpm2.AlgRSA: + return tpm2.SigScheme{ + Alg: tpm2.AlgRSASSA, + Hash: tpm2.AlgSHA256, + }, nil + case tpm2.AlgECC: + return tpm2.SigScheme{ + Alg: tpm2.AlgECDSA, + Hash: tpm2.AlgSHA256, + }, nil + default: + return tpm2.SigScheme{}, fmt.Errorf("public key of alg 0x%x not supported", tpmPub.Type) + } +} + func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) { t, ok := tb.(*wrappedTPM20) if !ok { @@ -575,9 +596,9 @@ func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa if !ok { return nil, fmt.Errorf("expected tpmutil.Handle, got %T", handle) } - scheme := tpm2.SigScheme{ - Alg: tpm2.AlgRSASSA, - Hash: tpm2.AlgSHA256, + scheme, err := sigSchemeFromPublicKey(k.public) + if err != nil { + return nil, fmt.Errorf("get signature scheme: %v", err) } return certify(t.rwc, hnd, k.hnd, scheme) }