From 046550658becea5c28bee4857d62e927373d7419 Mon Sep 17 00:00:00 2001 From: zhsh Date: Wed, 12 Jul 2023 00:14:13 +1000 Subject: [PATCH] attest: Create keys under non-default SRKs (#342) --- attest/application_key.go | 4 ++ attest/attest.go | 19 ++++++++- attest/attest_simulated_tpm20_test.go | 24 +++++++++--- attest/tpm.go | 28 +++++++++++++- attest/tpm12_linux.go | 8 ++++ attest/tpm_windows.go | 8 ++++ attest/wrapped_tpm20.go | 55 ++++++++++++++++++++------- 7 files changed, 123 insertions(+), 23 deletions(-) diff --git a/attest/application_key.go b/attest/application_key.go index 2ee710d..71163b8 100644 --- a/attest/application_key.go +++ b/attest/application_key.go @@ -72,6 +72,10 @@ type KeyConfig struct { // Size is used to specify the bit size of the key or elliptic curve. For // example, '256' is used to specify curve P-256. Size int + // Parent describes the Storage Root Key that will be used as a parent. + // If nil, the default SRK (i.e. RSA with handle 0x81000001) is assumed. + // Supported only by TPM 2.0 on Linux. + Parent *ParentKeyConfig } // defaultConfig is used when no other configuration is specified. diff --git a/attest/attest.go b/attest/attest.go index 7d38d01..fb9b6b2 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -99,6 +99,18 @@ const ( keyEncodingParameterized ) +// ParentKeyConfig describes the Storage Root Key that is used +// as a parent for new keys. +type ParentKeyConfig struct { + Algorithm Algorithm + Handle tpmutil.Handle +} + +var defaultParentConfig = ParentKeyConfig{ + Algorithm: RSA, + Handle: 0x81000001, +} + type ak interface { close(tpmBase) error marshal() ([]byte, error) @@ -176,9 +188,12 @@ func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, er return k.ak.certify(tpm.tpm, handle) } -// AKConfig encapsulates parameters for minting keys. This type is defined -// now (despite being empty) for future interface compatibility. +// AKConfig encapsulates parameters for minting keys. type AKConfig struct { + // Parent describes the Storage Root Key that will be used as a parent. + // If nil, the default SRK (i.e. RSA with handle 0x81000001) is assumed. + // Supported only by TPM 2.0 on Linux. + Parent *ParentKeyConfig } // EncryptedCredential represents encrypted parameters which must be activated diff --git a/attest/attest_simulated_tpm20_test.go b/attest/attest_simulated_tpm20_test.go index 74ff7f7..8ad2c3c 100644 --- a/attest/attest_simulated_tpm20_test.go +++ b/attest/attest_simulated_tpm20_test.go @@ -260,23 +260,35 @@ func TestSimTPM20PCRs(t *testing.T) { } func TestSimTPM20PersistenceSRK(t *testing.T) { + testPersistenceSRK(t, defaultParentConfig) +} + +func TestSimTPM20PersistenceECCSRK(t *testing.T) { + parentConfig := ParentKeyConfig{ + Algorithm: ECDSA, + Handle: 0x81000002, + } + testPersistenceSRK(t, parentConfig) +} + +func testPersistenceSRK(t *testing.T, parentConfig ParentKeyConfig) { sim, tpm := setupSimulatedTPM(t) defer sim.Close() - srkHnd, _, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(commonSrkEquivalentHandle) + srkHnd, _, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(parentConfig) if err != nil { t.Fatalf("getStorageRootKeyHandle() failed: %v", err) } - if srkHnd != commonSrkEquivalentHandle { - t.Fatalf("bad SRK-equivalent handle: got 0x%x, wanted 0x%x", srkHnd, commonSrkEquivalentHandle) + if srkHnd != parentConfig.Handle { + t.Fatalf("bad SRK-equivalent handle: got 0x%x, wanted 0x%x", srkHnd, parentConfig.Handle) } - srkHnd, p, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(commonSrkEquivalentHandle) + srkHnd, p, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(parentConfig) if err != nil { t.Fatalf("second getStorageRootKeyHandle() failed: %v", err) } - if srkHnd != commonSrkEquivalentHandle { - t.Fatalf("bad SRK-equivalent handle: got 0x%x, wanted 0x%x", srkHnd, commonSrkEquivalentHandle) + if srkHnd != parentConfig.Handle { + t.Fatalf("bad SRK-equivalent handle: got 0x%x, wanted 0x%x", srkHnd, parentConfig.Handle) } if p { t.Fatalf("generated a new key the second time; that shouldn't happen") diff --git a/attest/tpm.go b/attest/tpm.go index b1a3625..3121772 100644 --- a/attest/tpm.go +++ b/attest/tpm.go @@ -43,7 +43,6 @@ const ( nvramECCEkNonceIndex = 0x1c0000b // Defined in "Registry of reserved TPM 2.0 handles and localities", and checked on a glinux machine. - commonSrkEquivalentHandle = 0x81000001 commonRSAEkEquivalentHandle = 0x81010001 commonECCEkEquivalentHandle = 0x81010002 ) @@ -61,7 +60,7 @@ var ( KeyBits: 2048, }, } - defaultSRKTemplate = tpm2.Public{ + defaultRSASRKTemplate = tpm2.Public{ Type: tpm2.AlgRSA, NameAlg: tpm2.AlgSHA256, Attributes: tpm2.FlagStorageDefault | tpm2.FlagNoDA, @@ -75,6 +74,23 @@ var ( KeyBits: 2048, }, } + defaultECCSRKTemplate = tpm2.Public{ + Type: tpm2.AlgECC, + NameAlg: tpm2.AlgSHA256, + Attributes: tpm2.FlagStorageDefault | tpm2.FlagNoDA, + ECCParameters: &tpm2.ECCParams{ + Symmetric: &tpm2.SymScheme{ + Alg: tpm2.AlgAES, + KeyBits: 128, + Mode: tpm2.AlgCFB, + }, + CurveID: tpm2.CurveNISTP256, + Point: tpm2.ECPoint{ + XRaw: make([]byte, 32), + YRaw: make([]byte, 32), + }, + }, + } // Default RSA and ECC EK templates defined in: // https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf defaultRSAEKTemplate = tpm2.Public{ @@ -327,8 +343,10 @@ type tpmBase interface { info() (*TPMInfo, error) loadAK(opaqueBlob []byte) (*AK, error) + loadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error) newAK(opts *AKConfig) (*AK, error) loadKey(opaqueBlob []byte) (*Key, error) + loadKeyWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*Key, error) newKey(ak *AK, opts *KeyConfig) (*Key, error) pcrs(alg HashAlg) ([]PCR, error) measurementLog() ([]byte, error) @@ -370,6 +388,12 @@ func (t *TPM) LoadAK(opaqueBlob []byte) (*AK, error) { return t.tpm.loadAK(opaqueBlob) } +// LoadAKWithParent loads a previously-created ak into the TPM +// under the given parent for use. +func (t *TPM) LoadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error) { + return t.tpm.loadAKWithParent(opaqueBlob, parent) +} + // MeasurementLog returns the present value of the System Measurement Log. // // This is a low-level API. Consumers seeking to attest the state of the diff --git a/attest/tpm12_linux.go b/attest/tpm12_linux.go index 5059496..ad56816 100644 --- a/attest/tpm12_linux.go +++ b/attest/tpm12_linux.go @@ -116,6 +116,10 @@ func (t *trousersTPM) loadKey(opaqueBlob []byte) (*Key, error) { return nil, fmt.Errorf("not implemented") } +func (t *trousersTPM) loadKeyWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*Key, error) { + return nil, fmt.Errorf("not implemented") +} + func (t *trousersTPM) newAK(opts *AKConfig) (*AK, error) { pub, blob, err := attestation.CreateAIK(t.ctx) if err != nil { @@ -136,6 +140,10 @@ func (t *trousersTPM) loadAK(opaqueBlob []byte) (*AK, error) { return &AK{ak: newTrousersKey12(sKey.Blob, sKey.Public)}, nil } +func (t *trousersTPM) loadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error) { + return nil, fmt.Errorf("not implemented") +} + // allPCRs12 returns a map of all the PCR values on the TPM func allPCRs12(ctx *tspi.Context) (map[uint32][]byte, error) { tpm := ctx.GetTPM() diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index 6bbdcf1..ccaf1ee 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -337,6 +337,10 @@ func (t *windowsTPM) loadAK(opaqueBlob []byte) (*AK, error) { } } +func (t *windowsTPM) loadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error) { + return nil, fmt.Errorf("not implemented") +} + func (t *windowsTPM) newKey(*AK, *KeyConfig) (*Key, error) { return nil, fmt.Errorf("not implemented") } @@ -345,6 +349,10 @@ func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) { return nil, fmt.Errorf("not implemented") } +func (t *windowsTPM) loadKeyWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*Key, error) { + return nil, fmt.Errorf("not implemented") +} + func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) { numPCRs := 24 out := map[uint32][]byte{} diff --git a/attest/wrapped_tpm20.go b/attest/wrapped_tpm20.go index e39c133..f7a3cac 100644 --- a/attest/wrapped_tpm20.go +++ b/attest/wrapped_tpm20.go @@ -148,26 +148,36 @@ func (t *wrappedTPM20) getEndorsementKeyHandle(ek *EK) (tpmutil.Handle, bool, er } // Return value: handle, whether we generated a new one, error -func (t *wrappedTPM20) getStorageRootKeyHandle(pHnd tpmutil.Handle) (tpmutil.Handle, bool, error) { - _, _, _, err := tpm2.ReadPublic(t.rwc, pHnd) +func (t *wrappedTPM20) getStorageRootKeyHandle(parent ParentKeyConfig) (tpmutil.Handle, bool, error) { + srkHandle := parent.Handle + _, _, _, err := tpm2.ReadPublic(t.rwc, srkHandle) if err == nil { // Found the persistent handle, assume it's the key we want. - return pHnd, false, nil + return srkHandle, false, nil } rerr := err // Preserve this failure for later logging, if needed - keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", defaultSRKTemplate) + var srkTemplate tpm2.Public + switch parent.Algorithm { + case RSA: + srkTemplate = defaultRSASRKTemplate + case ECDSA: + srkTemplate = defaultECCSRKTemplate + default: + return 0, false, fmt.Errorf("unsupported SRK algorithm: %v", parent.Algorithm) + } + keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", srkTemplate) 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, pHnd) + err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, srkHandle) if err != nil { return 0, false, fmt.Errorf("EvictControl failed: %v", err) } - return pHnd, true, nil + return srkHandle, true, nil } func (t *wrappedTPM20) ekCertificates() ([]EK, error) { @@ -214,8 +224,13 @@ func (t *wrappedTPM20) eks() ([]EK, error) { } func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) { - // TODO(jsonp): Abstract choice of hierarchy & parent. - srk, _, err := t.getStorageRootKeyHandle(commonSrkEquivalentHandle) + var parent ParentKeyConfig + if opts != nil && opts.Parent != nil { + parent = *opts.Parent + } else { + parent = defaultParentConfig + } + srk, _, err := t.getStorageRootKeyHandle(parent) if err != nil { return nil, fmt.Errorf("failed to get SRK handle: %v", err) } @@ -287,7 +302,13 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) { } func createKey(t *wrappedTPM20, opts *KeyConfig) (tpmutil.Handle, []byte, []byte, []byte, error) { - srk, _, err := t.getStorageRootKeyHandle(commonSrkEquivalentHandle) + var parent ParentKeyConfig + if opts != nil && opts.Parent != nil { + parent = *opts.Parent + } else { + parent = defaultParentConfig + } + srk, _, err := t.getStorageRootKeyHandle(parent) if err != nil { return 0, nil, nil, nil, fmt.Errorf("failed to get SRK handle: %v", err) } @@ -352,7 +373,7 @@ func templateFromConfig(opts *KeyConfig) (tpm2.Public, error) { return tmpl, nil } -func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *serializedKey, error) { +func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte, parent ParentKeyConfig) (tpmutil.Handle, *serializedKey, error) { sKey, err := deserializeKey(opaqueBlob, TPMVersion20) if err != nil { return 0, nil, fmt.Errorf("deserializeKey() failed: %v", err) @@ -361,7 +382,7 @@ func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *s return 0, nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding) } - srk, _, err := t.getStorageRootKeyHandle(commonSrkEquivalentHandle) + srk, _, err := t.getStorageRootKeyHandle(parent) if err != nil { return 0, nil, fmt.Errorf("failed to get SRK handle: %v", err) } @@ -373,7 +394,11 @@ func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *s } func (t *wrappedTPM20) loadAK(opaqueBlob []byte) (*AK, error) { - hnd, sKey, err := t.deserializeAndLoad(opaqueBlob) + return t.loadAKWithParent(opaqueBlob, defaultParentConfig) +} + +func (t *wrappedTPM20) loadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error) { + hnd, sKey, err := t.deserializeAndLoad(opaqueBlob, parent) if err != nil { return nil, fmt.Errorf("cannot load attestation key: %v", err) } @@ -381,7 +406,11 @@ func (t *wrappedTPM20) loadAK(opaqueBlob []byte) (*AK, error) { } func (t *wrappedTPM20) loadKey(opaqueBlob []byte) (*Key, error) { - hnd, sKey, err := t.deserializeAndLoad(opaqueBlob) + return t.loadKeyWithParent(opaqueBlob, defaultParentConfig) +} + +func (t *wrappedTPM20) loadKeyWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*Key, error) { + hnd, sKey, err := t.deserializeAndLoad(opaqueBlob, parent) if err != nil { return nil, fmt.Errorf("cannot load signing key: %v", err) }