Skip to content

Commit 15e38e3

Browse files
authored
Merge branch 'master' into ecc-point
2 parents db1bb31 + 0b33a14 commit 15e38e3

File tree

7 files changed

+233
-32
lines changed

7 files changed

+233
-32
lines changed

attest/application_key_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030
"encoding/asn1"
3131
"math/big"
3232
"testing"
33+
34+
"github.com/google/go-cmp/cmp"
3335
)
3436

3537
func TestSimKeyCreateAndLoad(t *testing.T) {
@@ -678,3 +680,28 @@ func testKeyOpts(t *testing.T, tpm *TPM) {
678680
})
679681
}
680682
}
683+
684+
func TestOptsSetDefault(t *testing.T) {
685+
if optsSetDefault(nil) != defaultConfig {
686+
t.Errorf("expecting optsSetDefault(nil) to return the defaultConfig")
687+
}
688+
689+
normalOpts := KeyConfig{
690+
Algorithm: RSA,
691+
Size: 2048,
692+
}
693+
if optsSetDefault(&normalOpts) != &normalOpts {
694+
t.Errorf("expecting optsSetDefault() to return the same pointer for normal options")
695+
}
696+
697+
optsMissingAlgorithmAndSize := KeyConfig{
698+
QualifyingData: []byte("hello"),
699+
}
700+
optsAfter := optsSetDefault(&optsMissingAlgorithmAndSize)
701+
if optsAfter == &optsMissingAlgorithmAndSize {
702+
t.Errorf("expecting optsSetDefault() to return a pointer to a copy")
703+
}
704+
if diff := cmp.Diff(optsAfter, &KeyConfig{Algorithm: ECDSA, Size: 256, QualifyingData: []byte("hello")}); diff != "" {
705+
t.Errorf("optsSetDefault() mismatch (-want +got):\n%s", diff)
706+
}
707+
}

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/tpm.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -488,15 +488,25 @@ func (t *TPM) NewAK(opts *AKConfig) (*AK, error) {
488488
return t.tpm.newAK(opts)
489489
}
490490

491-
// NewKey creates an application key certified by the attestation key. If opts is nil
492-
// then DefaultConfig is used.
493-
func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) {
491+
// Return a KeyConfig with default values if appropriate. It never modifies the
492+
// incoming pointer.
493+
func optsSetDefault(opts *KeyConfig) *KeyConfig {
494494
if opts == nil {
495-
opts = defaultConfig
495+
return defaultConfig
496496
}
497497
if opts.Algorithm == "" && opts.Size == 0 {
498-
opts = defaultConfig
498+
optsCopy := *opts
499+
optsCopy.Algorithm = defaultConfig.Algorithm
500+
optsCopy.Size = defaultConfig.Size
501+
return &optsCopy
499502
}
503+
return opts
504+
}
505+
506+
// NewKey creates an application key certified by the attestation key. If opts is nil
507+
// then DefaultConfig is used.
508+
func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) {
509+
opts = optsSetDefault(opts)
500510
return t.tpm.newKey(ak, opts)
501511
}
502512

@@ -506,12 +516,7 @@ func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) {
506516
// Thus it can be used in cases where the attestation key was not created
507517
// by go-attestation library. If opts is nil then DefaultConfig is used.
508518
func (t *TPM) NewKeyCertifiedByKey(akHandle tpmutil.Handle, akAlg Algorithm, opts *KeyConfig) (*Key, error) {
509-
if opts == nil {
510-
opts = defaultConfig
511-
}
512-
if opts.Algorithm == "" && opts.Size == 0 {
513-
opts = defaultConfig
514-
}
519+
opts = optsSetDefault(opts)
515520
ck := certifyingKey{handle: akHandle, alg: akAlg}
516521
return t.tpm.newKeyCertifiedByKey(ck, opts)
517522
}

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)
142-
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)
158+
err = t.createEK(ekTemplate, ekHandle, rerr)
148159
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
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/google/go-cmp v0.7.0
99
github.com/google/go-tpm v0.9.7
1010
github.com/google/go-tpm-tools v0.4.7
11-
golang.org/x/sys v0.38.0
11+
golang.org/x/sys v0.39.0
1212
)
1313

1414
require golang.org/x/crypto v0.45.0 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
1818
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
1919
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
2020
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
21-
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
22-
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
21+
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
22+
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
2323
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
2424
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=

0 commit comments

Comments
 (0)