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.
This commit is contained in:
zhsh 2023-06-23 06:17:47 +10:00 committed by GitHub
parent 63dd90f699
commit d29df30553
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 140 additions and 44 deletions

View File

@ -25,6 +25,7 @@ import (
"github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/legacy/tpm2"
"github.com/google/go-tpm/tpm" "github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpmutil"
) )
// TPMVersion is used to configure a preference in // TPMVersion is used to configure a preference in
@ -101,7 +102,7 @@ const (
type ak interface { type ak interface {
close(tpmBase) error close(tpmBase) error
marshal() ([]byte, 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) quote(t tpmBase, nonce []byte, alg HashAlg) (*Quote, error)
attestationParameters() AttestationParameters attestationParameters() AttestationParameters
certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error)
@ -110,6 +111,10 @@ type ak interface {
// AK represents a key which can be used for attestation. // AK represents a key which can be used for attestation.
type AK struct { type AK struct {
ak ak 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. // Close unloads the AK from the system.
@ -130,7 +135,7 @@ func (k *AK) Marshal() ([]byte, error) {
// //
// This operation is synonymous with TPM2_ActivateCredential. // This operation is synonymous with TPM2_ActivateCredential.
func (k *AK) ActivateCredential(tpm *TPM, in EncryptedCredential) (secret []byte, err error) { 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. // 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) return k.ak.certify(tpm.tpm, handle)
} }
// AKConfig encapsulates parameters for minting keys. This type is defined // AKConfig encapsulates parameters for minting keys.
// now (despite being empty) for future interface compatibility.
type AKConfig struct { 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 // 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 // Public key. Clients or servers can perform an HTTP GET to this URL, and
// use ParseEKCertificate on the response body. // use ParseEKCertificate on the response body.
CertificateURL string CertificateURL string
// The EK persistent handle.
handle tpmutil.Handle
} }
// AttestationParameters describes information about a key which is necessary // AttestationParameters describes information about a key which is necessary

View File

@ -98,25 +98,37 @@ func TestSimTPM20AKCreateAndLoad(t *testing.T) {
} }
func TestSimTPM20ActivateCredential(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) sim, tpm := setupSimulatedTPM(t)
defer sim.Close() 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() EKs, err := tpm.EKs()
if err != nil { if err != nil {
t.Fatalf("EKs() failed: %v", err) t.Fatalf("EKs() failed: %v", err)
} }
ek := chooseEK(t, EKs) 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{ ap := ActivationParameters{
TPMVersion: TPMVersion20, TPMVersion: TPMVersion20,
AK: ak.AttestationParameters(), AK: ak.AttestationParameters(),
EK: ek, EK: ek.Public,
} }
secret, challenge, err := ap.Generate() secret, challenge, err := ap.Generate()
if err != nil { 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) sim, tpm := setupSimulatedTPM(t)
defer sim.Close() defer sim.Close()
ekHnd, _, err := tpm.tpm.(*wrappedTPM20).getPrimaryKeyHandle(commonRSAEkEquivalentHandle) srkHnd, _, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(commonSrkEquivalentHandle)
if err != nil { if err != nil {
t.Fatalf("getPrimaryKeyHandle() failed: %v", err) t.Fatalf("getStorageRootKeyHandle() failed: %v", err)
} }
if ekHnd != commonRSAEkEquivalentHandle { if srkHnd != commonSrkEquivalentHandle {
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonRSAEkEquivalentHandle) t.Fatalf("bad SRK-equivalent handle: got 0x%x, wanted 0x%x", srkHnd, commonSrkEquivalentHandle)
} }
ekHnd, p, err := tpm.tpm.(*wrappedTPM20).getPrimaryKeyHandle(commonRSAEkEquivalentHandle) srkHnd, p, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(commonSrkEquivalentHandle)
if err != nil { if err != nil {
t.Fatalf("second getPrimaryKeyHandle() failed: %v", err) t.Fatalf("second getStorageRootKeyHandle() failed: %v", err)
} }
if ekHnd != commonRSAEkEquivalentHandle { if srkHnd != commonSrkEquivalentHandle {
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonRSAEkEquivalentHandle) 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("getStorageRootKeyHandle() failed: %v", err)
}
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).getEndorsementKeyHandle(&ek)
if err != nil {
t.Fatalf("second getEndorsementKeyHandle() failed: %v", err)
}
if ekHnd != ek.handle {
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, ek.handle)
} }
if p { if p {
t.Fatalf("generated a new key the second time; that shouldn't happen") t.Fatalf("generated a new key the second time; that shouldn't happen")

View File

@ -16,7 +16,6 @@ package attest
import ( import (
"bytes" "bytes"
"crypto"
"flag" "flag"
"fmt" "fmt"
"reflect" "reflect"
@ -119,16 +118,16 @@ func TestAKCreateAndLoad(t *testing.T) {
} }
} }
// chooseEK selects the EK public which will be activated against. // chooseEK selects the EK which will be activated against.
func chooseEK(t *testing.T, eks []EK) crypto.PublicKey { func chooseEK(t *testing.T, eks []EK) EK {
t.Helper() t.Helper()
for _, ek := range eks { for _, ek := range eks {
return ek.Public return ek
} }
t.Fatalf("No suitable EK found") t.Fatalf("No suitable EK found")
return nil return EK{}
} }
func TestPCRs(t *testing.T) { func TestPCRs(t *testing.T) {

View File

@ -151,7 +151,7 @@ func TestTPMActivateCredential(t *testing.T) {
ap := ActivationParameters{ ap := ActivationParameters{
TPMVersion: TPMVersion12, TPMVersion: TPMVersion12,
AK: ak.AttestationParameters(), AK: ak.AttestationParameters(),
EK: ek, EK: ek.Public,
} }
secret, challenge, err := ap.Generate() secret, challenge, err := ap.Generate()
if err != nil { if err != nil {

View File

@ -52,7 +52,7 @@ func (k *trousersKey12) close(tpm tpmBase) error {
return nil // No state for tpm 1.2. 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) t, ok := tb.(*trousersTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb) return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)

View File

@ -49,7 +49,7 @@ func (k *windowsKey12) marshal() ([]byte, error) {
return out.Serialize() 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) tpm, ok := t.(*windowsTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", t) return nil, fmt.Errorf("expected *windowsTPM, got %T", t)
@ -152,7 +152,7 @@ func (k *windowsKey20) marshal() ([]byte, error) {
return out.Serialize() 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) tpm, ok := t.(*windowsTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", t) return nil, fmt.Errorf("expected *windowsTPM, got %T", t)

View File

@ -83,8 +83,50 @@ func (t *wrappedTPM20) info() (*TPMInfo, error) {
return &tInfo, nil 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 // 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) _, _, _, err := tpm2.ReadPublic(t.rwc, pHnd)
if err == nil { if err == nil {
// Found the persistent handle, assume it's the key we want. // 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 rerr := err // Preserve this failure for later logging, if needed
var keyHnd tpmutil.Handle keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", defaultSRKTemplate)
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())
}
if err != nil { if err != nil {
return 0, false, fmt.Errorf("ReadPublic failed (%v), and then CreatePrimary failed: %v", rerr, err) 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) { func (t *wrappedTPM20) eks() ([]EK, error) {
if cert, err := readEKCertFromNVRAM20(t.rwc, nvramRSACertIndex); err == nil { if cert, err := readEKCertFromNVRAM20(t.rwc, nvramRSACertIndex); err == nil {
return []EK{ return []EK{
{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert}, {Public: crypto.PublicKey(cert.PublicKey), Certificate: cert, handle: commonRSAEkEquivalentHandle},
}, nil }, nil
} }
@ -139,13 +175,14 @@ func (t *wrappedTPM20) eks() ([]EK, error) {
E: int(pub.RSAParameters.Exponent()), E: int(pub.RSAParameters.Exponent()),
N: pub.RSAParameters.Modulus(), N: pub.RSAParameters.Modulus(),
}, },
handle: commonRSAEkEquivalentHandle,
}, },
}, nil }, nil
} }
func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) { func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) {
// TODO(jsonp): Abstract choice of hierarchy & parent. // TODO(jsonp): Abstract choice of hierarchy & parent.
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle) srk, _, err := t.getStorageRootKeyHandle(commonSrkEquivalentHandle)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get SRK handle: %v", err) 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 { if err != nil {
return nil, fmt.Errorf("CertifyCreation failed: %v", err) 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) { 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) { 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 { if err != nil {
return 0, nil, nil, nil, fmt.Errorf("failed to get SRK handle: %v", err) 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) return 0, nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
} }
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle) srk, _, err := t.getStorageRootKeyHandle(commonSrkEquivalentHandle)
if err != nil { if err != nil {
return 0, nil, fmt.Errorf("failed to get SRK handle: %v", err) 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) 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) t, ok := tb.(*wrappedTPM20)
if !ok { if !ok {
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb) 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:] secret := in.Secret[2:]
ekHnd, _, err := t.getPrimaryKeyHandle(commonRSAEkEquivalentHandle) ekHnd, _, err := t.getEndorsementKeyHandle(ek)
if err != nil { if err != nil {
return nil, err return nil, err
} }