Skip to content

Commit 2aae6d8

Browse files
authored
Merge pull request #358 from smallstep/mariano/tss2-integration
TSS2 integration
2 parents 5ea629b + bbaee93 commit 2aae6d8

File tree

14 files changed

+845
-75
lines changed

14 files changed

+845
-75
lines changed

kms/tpmkms/testdata/ec-tss2.pem

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN TSS2 PRIVATE KEY-----
2+
MIHyBgZngQUKAQOgAwEB/wIEQAAAAQRaAFgAIwALAAQAcgAAABAAGAALAAMAEAAg
3+
ebLnGlDAN5aHdkffRTqBdsQNnO60aY+Xvg5u80sIauMAIM0E3zndp5392TPBroKz
4+
PLHEwLiUVeBmOhBG3kss/uICBIGAAH4AIIMO322TFYndManhpvTgLMiFd6Vs3HVs
5+
OrHb15qLZTDQABBMcF+L3C2322FU060DHXv56Uq87uMu/qWE3HU+r5856+70P94I
6+
0z3Plxwln2iGhhbKZ8gQQNKhiOdE4MYDPnN1uqvcwJwd7NZ1fqnwBKks6E1vgSne
7+
tYeNooQ=
8+
-----END TSS2 PRIVATE KEY-----

kms/tpmkms/tpmkms.go

Lines changed: 137 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@ import (
1010
"crypto/sha256"
1111
"crypto/x509"
1212
"encoding/base64"
13+
"encoding/pem"
1314
"errors"
1415
"fmt"
1516
"net/url"
17+
"os"
1618
"time"
1719

1820
"go.step.sm/crypto/kms/apiv1"
1921
"go.step.sm/crypto/kms/uri"
2022
"go.step.sm/crypto/tpm"
2123
"go.step.sm/crypto/tpm/attestation"
2224
"go.step.sm/crypto/tpm/storage"
25+
"go.step.sm/crypto/tpm/tss2"
2326
)
2427

2528
func init() {
@@ -188,6 +191,7 @@ func New(_ context.Context, opts apiv1.Options) (kms *TPMKMS, err error) {
188191
//
189192
// - name=<name>: specify the name to identify the key with
190193
// - ak=true: if set to true, an Attestation Key (AK) will be created instead of an application key
194+
// - tss2=true: is set to true, the PrivateKey response will contain a [tss2.TPMKey].
191195
// - attest-by=<akName>: attest an application key at creation time with the AK identified by `akName`
192196
// - qualifying-data=<random>: hexadecimal coded binary data that can be used to guarantee freshness when attesting creation of a key
193197
//
@@ -240,6 +244,8 @@ func (k *TPMKMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespons
240244
}
241245

242246
ctx := context.Background()
247+
248+
var privateKey any
243249
if properties.ak {
244250
ak, err := k.tpm.CreateAK(ctx, properties.name) // NOTE: size is never passed for AKs; it's hardcoded to 2048 in lower levels.
245251
if err != nil {
@@ -248,10 +254,20 @@ func (k *TPMKMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespons
248254
}
249255
return nil, fmt.Errorf("failed creating AK: %w", err)
250256
}
257+
258+
if properties.tss2 {
259+
tpmKey, err := ak.ToTSS2(ctx)
260+
if err != nil {
261+
return nil, fmt.Errorf("failed exporting AK to TSS2: %w", err)
262+
}
263+
privateKey = tpmKey
264+
}
265+
251266
createdAKURI := fmt.Sprintf("tpmkms:name=%s;ak=true", ak.Name())
252267
return &apiv1.CreateKeyResponse{
253-
Name: createdAKURI,
254-
PublicKey: ak.Public(),
268+
Name: createdAKURI,
269+
PublicKey: ak.Public(),
270+
PrivateKey: privateKey,
255271
}, nil
256272
}
257273

@@ -283,6 +299,14 @@ func (k *TPMKMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespons
283299
}
284300
}
285301

302+
if properties.tss2 {
303+
tpmKey, err := key.ToTSS2(ctx)
304+
if err != nil {
305+
return nil, fmt.Errorf("failed exporting key to TSS2: %w", err)
306+
}
307+
privateKey = tpmKey
308+
}
309+
286310
signer, err := key.Signer(ctx)
287311
if err != nil {
288312
return nil, fmt.Errorf("failed getting signer for key: %w", err)
@@ -294,8 +318,9 @@ func (k *TPMKMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespons
294318
}
295319

296320
return &apiv1.CreateKeyResponse{
297-
Name: createdKeyURI,
298-
PublicKey: signer.Public(),
321+
Name: createdKeyURI,
322+
PublicKey: signer.Public(),
323+
PrivateKey: privateKey,
299324
CreateSignerRequest: apiv1.CreateSignerRequest{
300325
SigningKey: createdKeyURI,
301326
Signer: signer,
@@ -304,39 +329,76 @@ func (k *TPMKMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespons
304329
}
305330

306331
// CreateSigner creates a signer using a key present in the TPM KMS.
332+
//
333+
// The `signingKey` in the [apiv1.CreateSignerRequest] can be used to specify
334+
// some key properties. These are as follows:
335+
//
336+
// - name=<name>: specify the name to identify the key with
337+
// - path=<file>: specify the TSS2 PEM file to use
307338
func (k *TPMKMS) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error) {
308339
if req.Signer != nil {
309340
return req.Signer, nil
310341
}
311342

312-
if req.SigningKey == "" {
313-
return nil, errors.New("createSignerRequest 'signingKey' cannot be empty")
314-
}
343+
var pemBytes []byte
315344

316-
properties, err := parseNameURI(req.SigningKey)
317-
if err != nil {
318-
return nil, fmt.Errorf("failed parsing %q: %w", req.SigningKey, err)
319-
}
345+
switch {
346+
case req.SigningKey != "":
347+
properties, err := parseNameURI(req.SigningKey)
348+
if err != nil {
349+
return nil, fmt.Errorf("failed parsing %q: %w", req.SigningKey, err)
350+
}
351+
if properties.ak {
352+
return nil, fmt.Errorf("signing with an AK currently not supported")
353+
}
320354

321-
if properties.ak {
322-
return nil, fmt.Errorf("signing with an AK currently not supported")
355+
switch {
356+
case properties.name != "":
357+
ctx := context.Background()
358+
key, err := k.tpm.GetKey(ctx, properties.name)
359+
if err != nil {
360+
return nil, err
361+
}
362+
signer, err := key.Signer(ctx)
363+
if err != nil {
364+
return nil, fmt.Errorf("failed getting signer for key %q: %w", properties.name, err)
365+
}
366+
return signer, nil
367+
case properties.path != "":
368+
if pemBytes, err = os.ReadFile(properties.path); err != nil {
369+
return nil, fmt.Errorf("failed reading key from %q: %w", properties.path, err)
370+
}
371+
default:
372+
return nil, fmt.Errorf("failed parsing %q: name and path cannot be empty", req.SigningKey)
373+
}
374+
case len(req.SigningKeyPEM) > 0:
375+
pemBytes = req.SigningKeyPEM
376+
default:
377+
return nil, errors.New("createSignerRequest 'signingKey' and 'signingKeyPEM' cannot be empty")
323378
}
324379

325-
ctx := context.Background()
326-
key, err := k.tpm.GetKey(ctx, properties.name)
380+
// Create a signer from a TSS2 PEM block
381+
key, err := parseTSS2(pemBytes)
327382
if err != nil {
328383
return nil, err
329384
}
330385

331-
signer, err := key.Signer(ctx)
386+
ctx := context.Background()
387+
signer, err := tpm.CreateTSS2Signer(ctx, k.tpm, key)
332388
if err != nil {
333-
return nil, fmt.Errorf("failed getting signer for key %q: %w", properties.name, err)
389+
return nil, fmt.Errorf("failed getting signer for TSS2 PEM: %w", err)
334390
}
335-
336391
return signer, nil
337392
}
338393

339-
// GetPublicKey returns the public key ....
394+
// GetPublicKey returns the public key present in the TPM KMS.
395+
//
396+
// The `name` in the [apiv1.GetPublicKeyRequest] can be used to specify some key
397+
// properties. These are as follows:
398+
//
399+
// - name=<name>: specify the name to identify the key with
400+
// - ak=true: if set to true, an Attestation Key (AK) will be read instead of an application key
401+
// - path=<file>: specify the TSS2 PEM file to read from
340402
func (k *TPMKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) {
341403
if req.Name == "" {
342404
return nil, errors.New("getPublicKeyRequest 'name' cannot be empty")
@@ -348,29 +410,48 @@ func (k *TPMKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey,
348410
}
349411

350412
ctx := context.Background()
351-
if properties.ak {
352-
ak, err := k.tpm.GetAK(ctx, properties.name)
413+
switch {
414+
case properties.name != "":
415+
if properties.ak {
416+
ak, err := k.tpm.GetAK(ctx, properties.name)
417+
if err != nil {
418+
return nil, err
419+
}
420+
akPub := ak.Public()
421+
if akPub == nil {
422+
return nil, errors.New("failed getting AK public key")
423+
}
424+
return akPub, nil
425+
}
426+
427+
key, err := k.tpm.GetKey(ctx, properties.name)
353428
if err != nil {
354429
return nil, err
355430
}
356-
akPub := ak.Public()
357-
if akPub == nil {
358-
return nil, errors.New("failed getting AK public key")
359-
}
360-
return akPub, nil
361-
}
362431

363-
key, err := k.tpm.GetKey(ctx, properties.name)
364-
if err != nil {
365-
return nil, err
366-
}
432+
signer, err := key.Signer(ctx)
433+
if err != nil {
434+
return nil, fmt.Errorf("failed getting signer for key %q: %w", properties.name, err)
435+
}
367436

368-
signer, err := key.Signer(ctx)
369-
if err != nil {
370-
return nil, fmt.Errorf("failed getting signer for key %q: %w", properties.name, err)
437+
return signer.Public(), nil
438+
case properties.path != "":
439+
pemBytes, err := os.ReadFile(properties.path)
440+
if err != nil {
441+
return nil, fmt.Errorf("failed reading key from %q: %w", properties.path, err)
442+
}
443+
key, err := parseTSS2(pemBytes)
444+
if err != nil {
445+
return nil, err
446+
}
447+
pub, err := key.Public()
448+
if err != nil {
449+
return nil, fmt.Errorf("error decoding public key from %q: %w", properties.path, err)
450+
}
451+
return pub, nil
452+
default:
453+
return nil, fmt.Errorf("failed parsing %q: name and path cannot be empty", req.Name)
371454
}
372-
373-
return signer.Public(), nil
374455
}
375456

376457
// LoadCertificate loads the certificate for the key identified by name from the TPMKMS.
@@ -757,6 +838,26 @@ func ekURL(keyID []byte) *url.URL {
757838
}
758839
}
759840

841+
func parseTSS2(pemBytes []byte) (*tss2.TPMKey, error) {
842+
var block *pem.Block
843+
for len(pemBytes) > 0 {
844+
block, pemBytes = pem.Decode(pemBytes)
845+
if block == nil {
846+
break
847+
}
848+
if block.Type != "TSS2 PRIVATE KEY" {
849+
continue
850+
}
851+
852+
key, err := tss2.ParsePrivateKey(block.Bytes)
853+
if err != nil {
854+
return nil, fmt.Errorf("failed parsing TSS2 PEM: %w", err)
855+
}
856+
return key, nil
857+
}
858+
return nil, fmt.Errorf("failed parsing TSS2 PEM: block not found")
859+
}
860+
760861
var _ apiv1.KeyManager = (*TPMKMS)(nil)
761862
var _ apiv1.Attester = (*TPMKMS)(nil)
762863
var _ apiv1.CertificateManager = (*TPMKMS)(nil)

0 commit comments

Comments
 (0)