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 6164256e..70fdf51c 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) } @@ -126,7 +162,7 @@ func (t *wrappedTPM20) ekCertificates() ([]EK, error) { 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 } @@ -150,13 +186,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) } @@ -181,7 +218,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) { @@ -228,7 +269,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) } @@ -302,7 +343,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) } @@ -413,7 +454,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) @@ -428,7 +469,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 }