Skip to content

Commit 05a477b

Browse files
authored
Dynamic search of ek keys with ekCertificates (#468)
* Dynamic search of ek keys with ekCertificates This patch allows ek keys to be searched in additional persistent key handle locations. And to return the actual Certificate and handle mapping when the keys in the handles do not match the default values. Unifies creation of EK and allows listing of high certs if present. Rationale: During the deployment of some newer lenovo systems the default rsa handle comes with a 3k key which prevents attestation from working as built. Thus the need for the library to create the 2k key in a separate location in case the default location is already occupied and there is no 2k on the standard locations. * addressing comments p1 * Adding test using local tpm. * Added documentation in the choice of keyhandles * Remove p384 and ek 3072 as not relevant to this change * Added test of EkCertificates with simulator Mostly used internal interfaces as simulator does not have an EkCert in nvram location. * Cleaned up comments and added more docs * fixing nits from liamjm's review
1 parent 2cb8754 commit 05a477b

File tree

3 files changed

+187
-18
lines changed

3 files changed

+187
-18
lines changed

attest/attest_simulated_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,60 @@ func TestSimEK(t *testing.T) {
6060
}
6161
}
6262

63+
func TestSimWrappedtpmEKCertificatesInternal(t *testing.T) {
64+
sim, tpm := setupSimulatedTPM(t)
65+
defer sim.Close()
66+
67+
eks, err := tpm.EKCertificates()
68+
if err != nil {
69+
t.Errorf("EKCertfificates() failed: %v", err)
70+
}
71+
if len(eks) != 0 {
72+
t.Errorf("simlator returned an ekCertificate this should not happen")
73+
}
74+
// Since the tpmsimulator does not have ek certificates we will
75+
// test some of the internal logic here, in particular search
76+
// and injection of missing 2k rsa key.
77+
// Because of this, the test dependent on internal apis which
78+
// is not optimal.
79+
80+
// Use a wrappedTPM with the simulator as the tpm
81+
wtpm := &wrappedTPM20{
82+
interf: TPMInterfaceCommandChannel,
83+
rwc: &fakeCmdChannel{sim},
84+
}
85+
eks, err = wtpm.ekCertificates()
86+
if err != nil {
87+
t.Errorf("wtpm ekCertificates failed")
88+
}
89+
if len(eks) != 0 {
90+
t.Fatalf("should have returned with no EKs")
91+
}
92+
// Now we inject a single key and search for it
93+
_, handleFoundMap, err := wtpm.getKeyHandleKeyMap()
94+
if err != nil {
95+
t.Fatal(err)
96+
}
97+
if len(handleFoundMap) != 0 {
98+
t.Fatal("the simulator should be empty at this time")
99+
}
100+
injected2khandle, err := wtpm.create2048RSAEKInAvailableSlot(handleFoundMap)
101+
if err != nil {
102+
t.Fatal(err)
103+
}
104+
if injected2khandle != commonRSAEkEquivalentHandle {
105+
t.Errorf("injected cert at not default handle when empty")
106+
}
107+
_, handleFoundMap, err = wtpm.getKeyHandleKeyMap()
108+
if err != nil {
109+
t.Fatal(err)
110+
}
111+
_, ok := handleFoundMap[commonRSAEkEquivalentHandle]
112+
if !ok {
113+
t.Fatalf("injected key notfound")
114+
}
115+
}
116+
63117
func TestSimInfo(t *testing.T) {
64118
sim, tpm := setupSimulatedTPM(t)
65119
defer sim.Close()

attest/attest_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,25 @@ func TestEKs(t *testing.T) {
8080
}
8181
}
8282

83+
func TestEKCertificates(t *testing.T) {
84+
if !*testLocal {
85+
t.SkipNow()
86+
}
87+
tpm, err := OpenTPM(nil)
88+
if err != nil {
89+
t.Fatalf("OpenTPM() failed: %v", err)
90+
}
91+
defer tpm.Close()
92+
93+
eks, err := tpm.EKCertificates()
94+
if err != nil {
95+
t.Errorf("EKCertificates() failed: %v", err)
96+
}
97+
if len(eks) == 0 {
98+
t.Log("EKCertificates() did not return anything. This could be an issue if an EK is present.")
99+
}
100+
}
101+
83102
func TestAKCreateAndLoad(t *testing.T) {
84103
if !*testLocal {
85104
t.SkipNow()

attest/wrapped_tpm20.go

Lines changed: 114 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import (
2020
"crypto/ecdsa"
2121
"crypto/elliptic"
2222
"crypto/rsa"
23+
"crypto/x509"
2324
"encoding/asn1"
25+
"encoding/base64"
2426
"errors"
2527
"fmt"
2628
"io"
@@ -106,6 +108,21 @@ func (t *wrappedTPM20) info() (*TPMInfo, error) {
106108
return &tInfo, nil
107109
}
108110

111+
// createEK creates a persistent EK given an ek template and a handle location
112+
func (t *wrappedTPM20) createEK(ekTemplate tpm2.Public, targetEKHandle tpmutil.Handle, rerr error) error {
113+
keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", ekTemplate)
114+
if err != nil {
115+
return fmt.Errorf("ReadPublic failed (%v), and then CreatePrimary failed: %v", rerr, err)
116+
}
117+
defer tpm2.FlushContext(t.rwc, keyHnd)
118+
119+
err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, targetEKHandle)
120+
if err != nil {
121+
return fmt.Errorf("EvictControl failed: %v", err)
122+
}
123+
return nil
124+
}
125+
109126
// Return value: handle, whether we generated a new one, error.
110127
func (t *wrappedTPM20) getEndorsementKeyHandle(ek *EK) (tpmutil.Handle, bool, error) {
111128
var ekHandle tpmutil.Handle
@@ -138,15 +155,9 @@ func (t *wrappedTPM20) getEndorsementKeyHandle(ek *EK) (tpmutil.Handle, bool, er
138155
}
139156
rerr := err // Preserve this failure for later logging, if needed
140157

141-
keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", ekTemplate)
158+
err = t.createEK(ekTemplate, ekHandle, rerr)
142159
if err != nil {
143-
return 0, false, fmt.Errorf("ReadPublic failed (%v), and then CreatePrimary failed: %v", rerr, err)
144-
}
145-
defer tpm2.FlushContext(t.rwc, keyHnd)
146-
147-
err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, ekHandle)
148-
if err != nil {
149-
return 0, false, fmt.Errorf("EvictControl failed: %v", err)
160+
return 0, false, err
150161
}
151162

152163
return ekHandle, true, nil
@@ -171,27 +182,112 @@ func (t *wrappedTPM20) getStorageRootKeyHandle(parent ParentKeyConfig) (tpmutil.
171182
default:
172183
return 0, false, fmt.Errorf("unsupported SRK algorithm: %v", parent.Algorithm)
173184
}
174-
keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", srkTemplate)
185+
err = t.createEK(srkTemplate, srkHandle, rerr)
175186
if err != nil {
176-
return 0, false, fmt.Errorf("ReadPublic failed (%v), and then CreatePrimary failed: %v", rerr, err)
187+
return 0, false, err
177188
}
178-
defer tpm2.FlushContext(t.rwc, keyHnd)
189+
return srkHandle, true, nil
190+
}
179191

180-
err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, srkHandle)
192+
// returns the base64 encoding of the der encoding of a public key
193+
func serializePublicKey(pub crypto.PublicKey) (string, error) {
194+
derKey, err := x509.MarshalPKIXPublicKey(pub)
181195
if err != nil {
182-
return 0, false, fmt.Errorf("EvictControl failed: %v", err)
196+
return "", err
183197
}
198+
return base64.StdEncoding.EncodeToString(derKey), nil
199+
}
184200

185-
return srkHandle, true, nil
201+
// Unfortunatelly some TPMs have a non rsa2048 key in the commonRSAEkEquivalentHandle
202+
// handle location. Thus we need an alternative handle to use for both creating
203+
// and searching for the rsa2048 ek.
204+
// The "Registry-of-Reserved-TPM-2.0-Handles-and-Localities-Version 1.2" section 2.3.1
205+
// asserts that persistent EK handles should be in the range 0x8101000-0x810100FF
206+
// Thus any value in this range is acceptable, so we arbitrarily chose
207+
// a value inmediatelly after the ECC (p256) handle.
208+
const altRSAEkEquivalentHandle = commonECCEkEquivalentHandle + 1
209+
210+
// creates a map of a base64 pkcs8 encoding of public keys to handles
211+
func (t *wrappedTPM20) getKeyHandleKeyMap() (map[string]tpmutil.Handle, map[tpmutil.Handle]struct{}, error) {
212+
key2Handle := make(map[string]tpmutil.Handle)
213+
handleFound := make(map[tpmutil.Handle]struct{})
214+
// The handles to searh today are the nvram locations where we expect to find keys
215+
// we could augment this list int the future, by including the equivalent of
216+
// "tpm2_getcap handles-persistent". However we want to limit the number of locations
217+
// to probe as accessing the tpm is a relatively slow path.
218+
knownHandlesToSearch := []tpmutil.Handle{
219+
commonRSAEkEquivalentHandle,
220+
commonECCEkEquivalentHandle,
221+
altRSAEkEquivalentHandle,
222+
}
223+
for _, keyHandle := range knownHandlesToSearch {
224+
pub, _, _, err := tpm2.ReadPublic(t.rwc, keyHandle)
225+
if err != nil {
226+
continue
227+
}
228+
if pub.RSAParameters != nil || pub.ECCParameters != nil {
229+
key, err := pub.Key()
230+
if err != nil {
231+
return nil, nil, fmt.Errorf("failed to obtain public key for handle %x: %w", keyHandle, err)
232+
}
233+
serializedKey, err := serializePublicKey(key)
234+
if err != nil {
235+
return nil, nil, fmt.Errorf("failed to serialize public key for handle %x: %w", keyHandle, err)
236+
}
237+
key2Handle[serializedKey] = keyHandle
238+
handleFound[keyHandle] = struct{}{}
239+
}
240+
}
241+
return key2Handle, handleFound, nil
242+
}
243+
244+
func (t *wrappedTPM20) create2048RSAEKInAvailableSlot(handleFoundMap map[tpmutil.Handle]struct{}) (tpmutil.Handle, error) {
245+
rsakeyHandles := []tpmutil.Handle{
246+
commonRSAEkEquivalentHandle,
247+
altRSAEkEquivalentHandle,
248+
}
249+
for _, targetHandle := range rsakeyHandles {
250+
_, handleInUse := handleFoundMap[targetHandle]
251+
if handleInUse {
252+
continue
253+
}
254+
ekTemplate := t.rsaEkTemplate()
255+
err := t.createEK(ekTemplate, targetHandle, fmt.Errorf(""))
256+
if err != nil {
257+
return tpmutil.Handle(0), err
258+
}
259+
return targetHandle, nil
260+
}
261+
return tpmutil.Handle(0), fmt.Errorf("no available handles to create RSA 2048 key in persistent handle")
186262
}
187263

188264
func (t *wrappedTPM20) ekCertificates() ([]EK, error) {
189265
var res []EK
190-
if rsaCert, err := readEKCertFromNVRAM20(t.rwc, nvramRSACertIndex); err == nil {
191-
res = append(res, EK{Public: crypto.PublicKey(rsaCert.PublicKey), Certificate: rsaCert, handle: commonRSAEkEquivalentHandle})
266+
certIndexes := []int{nvramRSACertIndex, nvramECCCertIndex}
267+
keyHandleMap, handleFoundMap, err := t.getKeyHandleKeyMap()
268+
if err != nil {
269+
return nil, err
192270
}
193-
if eccCert, err := readEKCertFromNVRAM20(t.rwc, nvramECCCertIndex); err == nil {
194-
res = append(res, EK{Public: crypto.PublicKey(eccCert.PublicKey), Certificate: eccCert, handle: commonECCEkEquivalentHandle})
271+
for _, certIndex := range certIndexes {
272+
if cert, err := readEKCertFromNVRAM20(t.rwc, tpmutil.Handle(certIndex)); err == nil {
273+
serializedKey, err := serializePublicKey(cert.PublicKey)
274+
if err != nil {
275+
return nil, err
276+
}
277+
278+
handleToUse, keyfound := keyHandleMap[serializedKey]
279+
if !keyfound && certIndex == nvramRSACertIndex {
280+
handleToUse, err = t.create2048RSAEKInAvailableSlot(handleFoundMap)
281+
if err != nil {
282+
return nil, err
283+
}
284+
keyfound = true
285+
}
286+
if !keyfound {
287+
continue
288+
}
289+
res = append(res, EK{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert, handle: handleToUse})
290+
}
195291
}
196292
return res, nil
197293
}

0 commit comments

Comments
 (0)