Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions attest/attest_simulated_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,60 @@ func TestSimEK(t *testing.T) {
}
}

func TestSimWrappedtpmEKCertificatesInternal(t *testing.T) {
sim, tpm := setupSimulatedTPM(t)
defer sim.Close()

eks, err := tpm.EKCertificates()
if err != nil {
t.Errorf("EKCertfificates() failed: %v", err)
}
if len(eks) != 0 {
t.Errorf("simlator returned an ekCertificate this should not happen")
}
// Since the tpmsimultor does not have ek certificates we will
// test some of the internal logic here, in particular search
// and injection of missing 2k rsa key.
// Because of this, the test dependent on internal apis which
// is not optimal.

// Use a wrappedTPM with the simulator as the tpm
wtpm := &wrappedTPM20{
interf: TPMInterfaceCommandChannel,
rwc: &fakeCmdChannel{sim},
}
eks, err = wtpm.ekCertificates()
if err != nil {
t.Errorf("wtpm ekCertificates failed")
}
if len(eks) != 0 {
t.Fatalf("should have returned with no eks ")
}
// Now we inject a single key and search for it
_, handleFoundMap, err := wtpm.getKeyHandleKeyMap()
if err != nil {
t.Fatal(err)
}
if len(handleFoundMap) != 0 {
t.Fatal("the simulator should be empty at this time")
}
injected2khandle, err := wtpm.create2048RSAEKInAvailableSlot(handleFoundMap)
if err != nil {
t.Fatal(err)
}
if injected2khandle != commonRSAEkEquivalentHandle {
t.Errorf("injected cert at not default handle when empty")
}
_, handleFoundMap, err = wtpm.getKeyHandleKeyMap()
if err != nil {
t.Fatal(err)
}
_, ok := handleFoundMap[commonRSAEkEquivalentHandle]
if !ok {
t.Fatalf("Injected key notfound")
}
}

func TestSimInfo(t *testing.T) {
sim, tpm := setupSimulatedTPM(t)
defer sim.Close()
Expand Down
19 changes: 19 additions & 0 deletions attest/attest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,25 @@ func TestEKs(t *testing.T) {
}
}

func TestEKCertificates(t *testing.T) {
if !*testLocal {
t.SkipNow()
}
tpm, err := OpenTPM(nil)
if err != nil {
t.Fatalf("OpenTPM() failed: %v", err)
}
defer tpm.Close()

eks, err := tpm.EKCertificates()
if err != nil {
t.Errorf("EKCertificates() failed: %v", err)
}
if len(eks) == 0 {
t.Log("EKCertificates() did not return anything. This could be an issue if an EK is present.")
}
}

func TestAKCreateAndLoad(t *testing.T) {
if !*testLocal {
t.SkipNow()
Expand Down
132 changes: 114 additions & 18 deletions attest/wrapped_tpm20.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -106,6 +108,21 @@ func (t *wrappedTPM20) info() (*TPMInfo, error) {
return &tInfo, nil
}

// createEK creates a persistent EK given an ek template and a handle location
func (t *wrappedTPM20) createEK(ekTemplate tpm2.Public, targetEKHandle tpmutil.Handle, rerr error) error {
keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", ekTemplate)
if err != nil {
return 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, targetEKHandle)
if err != nil {
return fmt.Errorf("EvictControl failed: %v", err)
}
return 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
Expand Down Expand Up @@ -138,15 +155,9 @@ func (t *wrappedTPM20) getEndorsementKeyHandle(ek *EK) (tpmutil.Handle, bool, er
}
rerr := err // Preserve this failure for later logging, if needed

keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", ekTemplate)
err = t.createEK(ekTemplate, ekHandle, rerr)
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 0, false, err
}

return ekHandle, true, nil
Expand All @@ -171,27 +182,112 @@ func (t *wrappedTPM20) getStorageRootKeyHandle(parent ParentKeyConfig) (tpmutil.
default:
return 0, false, fmt.Errorf("unsupported SRK algorithm: %v", parent.Algorithm)
}
keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", srkTemplate)
err = t.createEK(srkTemplate, srkHandle, rerr)
if err != nil {
return 0, false, fmt.Errorf("ReadPublic failed (%v), and then CreatePrimary failed: %v", rerr, err)
return 0, false, err
}
defer tpm2.FlushContext(t.rwc, keyHnd)
return srkHandle, true, nil
}

err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, srkHandle)
// returns the base64 encoding of the der encoding of a public key
func serializePublicKey(pub crypto.PublicKey) (string, error) {
derKey, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return 0, false, fmt.Errorf("EvictControl failed: %v", err)
return "", err
}
return base64.StdEncoding.EncodeToString(derKey), nil
}

return srkHandle, true, nil
// Unfortunatelly some TPMs have a non rsa2048 key in the commonRSAEkEquivalentHandle
// handle location. Thus we need an alternative handle to use for both creating
// and searching for the rsa2048 ek.
// The "Registry-of-Reserved-TPM-2.0-Handles-and-Localities-Version 1.2" section 2.3.4
// asserts that persistent EK handles should be in the range 0x8101000-0x810100FF
// Thus any value in this range is acceptable, so we arbitrarily chose
// a value inmediatelly after the ECC (p256) handle.
const altRSAEkEquivalentHandle = commonECCEkEquivalentHandle + 1

// creates a map of a base64 pkcs8 encoding of public keys to handles
func (t *wrappedTPM20) getKeyHandleKeyMap() (map[string]tpmutil.Handle, map[tpmutil.Handle]struct{}, error) {
key2Handle := make(map[string]tpmutil.Handle)
handleFound := make(map[tpmutil.Handle]struct{})
// The handles to searh today are the nvram locations where we expect to find keys
// we could augment this list int the future, by including the equivalent of
// "tpm2_getcap handles-persistent". However we want to limit the number of locations
// to probe as accessing the tpm is a relatively slow path.
knownHandlesToSearch := []tpmutil.Handle{
commonRSAEkEquivalentHandle,
commonECCEkEquivalentHandle,
altRSAEkEquivalentHandle,
}
for _, keyHandle := range knownHandlesToSearch {
pub, _, _, err := tpm2.ReadPublic(t.rwc, keyHandle)
if err != nil {
continue
}
if pub.RSAParameters != nil || pub.ECCParameters != nil {
key, err := pub.Key()
if err != nil {
return nil, nil, err
}
serializedKey, err := serializePublicKey(key)
if err != nil {
return nil, nil, err
}
key2Handle[serializedKey] = keyHandle
handleFound[keyHandle] = struct{}{}
}
}
return key2Handle, handleFound, nil
}

func (t *wrappedTPM20) create2048RSAEKInAvailableSlot(handleFoundMap map[tpmutil.Handle]struct{}) (tpmutil.Handle, error) {
rsakeyHandles := []tpmutil.Handle{
commonRSAEkEquivalentHandle,
altRSAEkEquivalentHandle,
}
for _, targetHandle := range rsakeyHandles {
_, handleInUse := handleFoundMap[targetHandle]
if handleInUse {
continue
}
ekTemplate := t.rsaEkTemplate()
err := t.createEK(ekTemplate, targetHandle, fmt.Errorf(""))
if err != nil {
return tpmutil.Handle(0), err
}
return targetHandle, nil
}
return tpmutil.Handle(0), fmt.Errorf("could not create rsa 2048 key in persistent handle")
}

func (t *wrappedTPM20) ekCertificates() ([]EK, error) {
var res []EK
if rsaCert, err := readEKCertFromNVRAM20(t.rwc, nvramRSACertIndex); err == nil {
res = append(res, EK{Public: crypto.PublicKey(rsaCert.PublicKey), Certificate: rsaCert, handle: commonRSAEkEquivalentHandle})
certIndexes := []int{nvramRSACertIndex, nvramECCCertIndex}
keyHandleMap, handleFoundMap, err := t.getKeyHandleKeyMap()
if err != nil {
return nil, err
}
if eccCert, err := readEKCertFromNVRAM20(t.rwc, nvramECCCertIndex); err == nil {
res = append(res, EK{Public: crypto.PublicKey(eccCert.PublicKey), Certificate: eccCert, handle: commonECCEkEquivalentHandle})
for _, certIndex := range certIndexes {
if cert, err := readEKCertFromNVRAM20(t.rwc, tpmutil.Handle(certIndex)); err == nil {
serializedKey, err := serializePublicKey(cert.PublicKey)
if err != nil {
return nil, err
}

handleToUse, keyfound := keyHandleMap[serializedKey]
if !keyfound && certIndex == nvramRSACertIndex {
handleToUse, err = t.create2048RSAEKInAvailableSlot(handleFoundMap)
if err != nil {
return nil, err
}
keyfound = true
}
if !keyfound {
continue
}
res = append(res, EK{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert, handle: handleToUse})
}
}
return res, nil
}
Expand Down