From d29df3055302816a3940dd18304c6cfaee4a62a2 Mon Sep 17 00:00:00 2001 From: zhsh Date: Fri, 23 Jun 2023 06:17:47 +1000 Subject: [PATCH] Add EK as a field to AK struct. (#332) The change is a no-op for existing clients, and it will simplify adding the support for ECC EKs. The activation code no longer makes assumptions about EK's type and handle (i.e. RSA and 0x81010001), and instead relies on TPM.EKs() to provide the EK's details. --- attest/attest.go | 19 +++++-- attest/attest_simulated_tpm20_test.go | 77 +++++++++++++++++++++------ attest/attest_test.go | 9 ++-- attest/attest_tpm12_test.go | 2 +- attest/key_linux.go | 2 +- attest/key_windows.go | 4 +- attest/wrapped_tpm20.go | 71 ++++++++++++++++++------ 7 files changed, 140 insertions(+), 44 deletions(-) diff --git a/attest/attest.go b/attest/attest.go index 28afb75f..8408a834 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -25,6 +25,7 @@ import ( "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpm" + "github.com/google/go-tpm/tpmutil" ) // TPMVersion is used to configure a preference in @@ -101,7 +102,7 @@ const ( type ak interface { close(tpmBase) error marshal() ([]byte, error) - activateCredential(tpm tpmBase, in EncryptedCredential) ([]byte, error) + activateCredential(tpm tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) quote(t tpmBase, nonce []byte, alg HashAlg) (*Quote, error) attestationParameters() AttestationParameters certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) @@ -110,6 +111,10 @@ type ak interface { // AK represents a key which can be used for attestation. type AK struct { ak ak + + // The EK that will be used for attestation. + // If nil, an RSA EK with handle 0x81010001 will be used. + ek *EK } // Close unloads the AK from the system. @@ -130,7 +135,7 @@ func (k *AK) Marshal() ([]byte, error) { // // This operation is synonymous with TPM2_ActivateCredential. func (k *AK) ActivateCredential(tpm *TPM, in EncryptedCredential) (secret []byte, err error) { - return k.ak.activateCredential(tpm.tpm, in) + return k.ak.activateCredential(tpm.tpm, in, k.ek) } // Quote returns a quote over the platform state, signed by the AK. @@ -155,9 +160,12 @@ func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, er return k.ak.certify(tpm.tpm, handle) } -// AKConfig encapsulates parameters for minting keys. This type is defined -// now (despite being empty) for future interface compatibility. +// AKConfig encapsulates parameters for minting keys. type AKConfig struct { + // The EK that will be used for attestation. + // If nil, an RSA EK with handle 0x81010001 will be used. + // If not nil, it must be one of EKs returned from TPM.EKs(). + EK *EK } // EncryptedCredential represents encrypted parameters which must be activated @@ -205,6 +213,9 @@ type EK struct { // Public key. Clients or servers can perform an HTTP GET to this URL, and // use ParseEKCertificate on the response body. CertificateURL string + + // The EK persistent handle. + handle tpmutil.Handle } // AttestationParameters describes information about a key which is necessary diff --git a/attest/attest_simulated_tpm20_test.go b/attest/attest_simulated_tpm20_test.go index 77cb01e1..af01f395 100644 --- a/attest/attest_simulated_tpm20_test.go +++ b/attest/attest_simulated_tpm20_test.go @@ -98,25 +98,37 @@ func TestSimTPM20AKCreateAndLoad(t *testing.T) { } func TestSimTPM20ActivateCredential(t *testing.T) { + testActivateCredential(t, false) +} + +func TestSimTPM20ActivateCredentialWithEK(t *testing.T) { + testActivateCredential(t, true) +} + +func testActivateCredential(t *testing.T, useEK bool) { sim, tpm := setupSimulatedTPM(t) defer sim.Close() - ak, err := tpm.NewAK(nil) - if err != nil { - t.Fatalf("NewAK() failed: %v", err) - } - defer ak.Close(tpm) - EKs, err := tpm.EKs() if err != nil { t.Fatalf("EKs() failed: %v", err) } ek := chooseEK(t, EKs) + var akConfig *AKConfig + if useEK { + akConfig = &AKConfig{EK: &ek} + } + ak, err := tpm.NewAK(akConfig) + if err != nil { + t.Fatalf("NewAK() failed: %v", err) + } + defer ak.Close(tpm) + ap := ActivationParameters{ TPMVersion: TPMVersion20, AK: ak.AttestationParameters(), - EK: ek, + EK: ek.Public, } secret, challenge, err := ap.Generate() if err != nil { @@ -246,24 +258,57 @@ func TestSimTPM20PCRs(t *testing.T) { } } -func TestSimTPM20Persistence(t *testing.T) { +func TestSimTPM20PersistenceSRK(t *testing.T) { sim, tpm := setupSimulatedTPM(t) defer sim.Close() - ekHnd, _, err := tpm.tpm.(*wrappedTPM20).getPrimaryKeyHandle(commonRSAEkEquivalentHandle) + srkHnd, _, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(commonSrkEquivalentHandle) + if err != nil { + t.Fatalf("getStorageRootKeyHandle() failed: %v", err) + } + if srkHnd != commonSrkEquivalentHandle { + t.Fatalf("bad SRK-equivalent handle: got 0x%x, wanted 0x%x", srkHnd, commonSrkEquivalentHandle) + } + + srkHnd, p, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(commonSrkEquivalentHandle) + if err != nil { + t.Fatalf("second getStorageRootKeyHandle() failed: %v", err) + } + if srkHnd != commonSrkEquivalentHandle { + t.Fatalf("bad SRK-equivalent handle: got 0x%x, wanted 0x%x", srkHnd, commonSrkEquivalentHandle) + } + if p { + t.Fatalf("generated a new key the second time; that shouldn't happen") + } +} + +func TestSimTPM20PersistenceEK(t *testing.T) { + sim, tpm := setupSimulatedTPM(t) + defer sim.Close() + + eks, err := tpm.EKs() + if err != nil { + t.Errorf("EKs() failed: %v", err) + } + if len(eks) == 0 || (eks[0].Public == nil) { + t.Errorf("EKs() = %v, want at least 1 EK with populated fields", eks) + } + + ek := eks[0] + ekHnd, _, err := tpm.tpm.(*wrappedTPM20).getEndorsementKeyHandle(&ek) if err != nil { - t.Fatalf("getPrimaryKeyHandle() failed: %v", err) + t.Fatalf("getStorageRootKeyHandle() failed: %v", err) } - if ekHnd != commonRSAEkEquivalentHandle { - t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonRSAEkEquivalentHandle) + if ekHnd != ek.handle { + t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, ek.handle) } - ekHnd, p, err := tpm.tpm.(*wrappedTPM20).getPrimaryKeyHandle(commonRSAEkEquivalentHandle) + ekHnd, p, err := tpm.tpm.(*wrappedTPM20).getEndorsementKeyHandle(&ek) if err != nil { - t.Fatalf("second getPrimaryKeyHandle() failed: %v", err) + t.Fatalf("second getEndorsementKeyHandle() failed: %v", err) } - if ekHnd != commonRSAEkEquivalentHandle { - t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonRSAEkEquivalentHandle) + if ekHnd != ek.handle { + t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, ek.handle) } if p { t.Fatalf("generated a new key the second time; that shouldn't happen") diff --git a/attest/attest_test.go b/attest/attest_test.go index 8a2b812e..2d7a2fb9 100644 --- a/attest/attest_test.go +++ b/attest/attest_test.go @@ -16,7 +16,6 @@ package attest import ( "bytes" - "crypto" "flag" "fmt" "reflect" @@ -119,16 +118,16 @@ func TestAKCreateAndLoad(t *testing.T) { } } -// chooseEK selects the EK public which will be activated against. -func chooseEK(t *testing.T, eks []EK) crypto.PublicKey { +// chooseEK selects the EK which will be activated against. +func chooseEK(t *testing.T, eks []EK) EK { t.Helper() for _, ek := range eks { - return ek.Public + return ek } t.Fatalf("No suitable EK found") - return nil + return EK{} } func TestPCRs(t *testing.T) { diff --git a/attest/attest_tpm12_test.go b/attest/attest_tpm12_test.go index 7acd1723..627de331 100644 --- a/attest/attest_tpm12_test.go +++ b/attest/attest_tpm12_test.go @@ -151,7 +151,7 @@ func TestTPMActivateCredential(t *testing.T) { ap := ActivationParameters{ TPMVersion: TPMVersion12, AK: ak.AttestationParameters(), - EK: ek, + EK: ek.Public, } secret, challenge, err := ap.Generate() if err != nil { diff --git a/attest/key_linux.go b/attest/key_linux.go index ac09e491..1e56b4fc 100644 --- a/attest/key_linux.go +++ b/attest/key_linux.go @@ -52,7 +52,7 @@ func (k *trousersKey12) close(tpm tpmBase) error { return nil // No state for tpm 1.2. } -func (k *trousersKey12) activateCredential(tb tpmBase, in EncryptedCredential) ([]byte, error) { +func (k *trousersKey12) activateCredential(tb tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) { t, ok := tb.(*trousersTPM) if !ok { return nil, fmt.Errorf("expected *linuxTPM, got %T", tb) diff --git a/attest/key_windows.go b/attest/key_windows.go index 6c79b5f1..aab867f3 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -49,7 +49,7 @@ func (k *windowsKey12) marshal() ([]byte, error) { return out.Serialize() } -func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) { +func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) { tpm, ok := t.(*windowsTPM) if !ok { return nil, fmt.Errorf("expected *windowsTPM, got %T", t) @@ -152,7 +152,7 @@ func (k *windowsKey20) marshal() ([]byte, error) { return out.Serialize() } -func (k *windowsKey20) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) { +func (k *windowsKey20) activateCredential(t tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) { tpm, ok := t.(*windowsTPM) if !ok { return nil, fmt.Errorf("expected *windowsTPM, got %T", t) diff --git a/attest/wrapped_tpm20.go b/attest/wrapped_tpm20.go index a563fff0..d4efb97d 100644 --- a/attest/wrapped_tpm20.go +++ b/attest/wrapped_tpm20.go @@ -83,8 +83,50 @@ func (t *wrappedTPM20) info() (*TPMInfo, error) { return &tInfo, nil } +// Return value: handle, whether we generated a new one, error. +func (t *wrappedTPM20) getEndorsementKeyHandle(ek *EK) (tpmutil.Handle, bool, error) { + var ekHandle tpmutil.Handle + var ekTemplate tpm2.Public + + if ek == nil { + // The default is RSA for backward compatibility. + ekHandle = commonRSAEkEquivalentHandle + ekTemplate = t.rsaEkTemplate() + } else { + ekHandle = ek.handle + switch pub := ek.Public.(type) { + case *rsa.PublicKey: + ekTemplate = t.rsaEkTemplate() + case *ecdsa.PublicKey: + return 0, false, errors.New("ECC EKs are not supported") + default: + return 0, false, fmt.Errorf("unsupported public key type %T", pub) + } + } + + _, _, _, err := tpm2.ReadPublic(t.rwc, ekHandle) + if err == nil { + // Found the persistent handle, assume it's the key we want. + return ekHandle, false, nil + } + rerr := err // Preserve this failure for later logging, if needed + + keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", ekTemplate) + if err != nil { + return 0, false, fmt.Errorf("ReadPublic failed (%v), and then CreatePrimary failed: %v", rerr, err) + } + defer tpm2.FlushContext(t.rwc, keyHnd) + + err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, ekHandle) + if err != nil { + return 0, false, fmt.Errorf("EvictControl failed: %v", err) + } + + return ekHandle, true, nil +} + // Return value: handle, whether we generated a new one, error -func (t *wrappedTPM20) getPrimaryKeyHandle(pHnd tpmutil.Handle) (tpmutil.Handle, bool, error) { +func (t *wrappedTPM20) getStorageRootKeyHandle(pHnd tpmutil.Handle) (tpmutil.Handle, bool, error) { _, _, _, err := tpm2.ReadPublic(t.rwc, pHnd) if err == nil { // Found the persistent handle, assume it's the key we want. @@ -92,13 +134,7 @@ func (t *wrappedTPM20) getPrimaryKeyHandle(pHnd tpmutil.Handle) (tpmutil.Handle, } rerr := err // Preserve this failure for later logging, if needed - var keyHnd tpmutil.Handle - switch pHnd { - case commonSrkEquivalentHandle: - keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", defaultSRKTemplate) - case commonRSAEkEquivalentHandle: - keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", t.rsaEkTemplate()) - } + keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", defaultSRKTemplate) if err != nil { return 0, false, fmt.Errorf("ReadPublic failed (%v), and then CreatePrimary failed: %v", rerr, err) } @@ -115,7 +151,7 @@ func (t *wrappedTPM20) getPrimaryKeyHandle(pHnd tpmutil.Handle) (tpmutil.Handle, func (t *wrappedTPM20) eks() ([]EK, error) { if cert, err := readEKCertFromNVRAM20(t.rwc, nvramRSACertIndex); err == nil { return []EK{ - {Public: crypto.PublicKey(cert.PublicKey), Certificate: cert}, + {Public: crypto.PublicKey(cert.PublicKey), Certificate: cert, handle: commonRSAEkEquivalentHandle}, }, nil } @@ -139,13 +175,14 @@ func (t *wrappedTPM20) eks() ([]EK, error) { E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus(), }, + handle: commonRSAEkEquivalentHandle, }, }, nil } func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) { // TODO(jsonp): Abstract choice of hierarchy & parent. - srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle) + srk, _, err := t.getStorageRootKeyHandle(commonSrkEquivalentHandle) if err != nil { return nil, fmt.Errorf("failed to get SRK handle: %v", err) } @@ -170,7 +207,11 @@ func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) { if err != nil { return nil, fmt.Errorf("CertifyCreation failed: %v", err) } - return &AK{ak: newWrappedAK20(keyHandle, blob, pub, creationData, attestation, sig)}, nil + var ek *EK + if opts != nil { + ek = opts.EK + } + return &AK{ak: newWrappedAK20(keyHandle, blob, pub, creationData, attestation, sig), ek: ek}, nil } func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) { @@ -217,7 +258,7 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) { } func createKey(t *wrappedTPM20, opts *KeyConfig) (tpmutil.Handle, []byte, []byte, []byte, error) { - srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle) + srk, _, err := t.getStorageRootKeyHandle(commonSrkEquivalentHandle) if err != nil { return 0, nil, nil, nil, fmt.Errorf("failed to get SRK handle: %v", err) } @@ -291,7 +332,7 @@ func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *s return 0, nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding) } - srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle) + srk, _, err := t.getStorageRootKeyHandle(commonSrkEquivalentHandle) if err != nil { return 0, nil, fmt.Errorf("failed to get SRK handle: %v", err) } @@ -402,7 +443,7 @@ func (k *wrappedKey20) close(t tpmBase) error { return tpm2.FlushContext(tpm.rwc, k.hnd) } -func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([]byte, error) { +func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) { t, ok := tb.(*wrappedTPM20) if !ok { return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb) @@ -417,7 +458,7 @@ func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([ } secret := in.Secret[2:] - ekHnd, _, err := t.getPrimaryKeyHandle(commonRSAEkEquivalentHandle) + ekHnd, _, err := t.getEndorsementKeyHandle(ek) if err != nil { return nil, err }