From 3058f62805a8a7168bc8261a1bcfce7f5b9bdb03 Mon Sep 17 00:00:00 2001 From: Spyros Seimenis Date: Fri, 23 Sep 2022 18:21:22 +0300 Subject: [PATCH 1/2] tpm2: Refactor loadForUnseal() loadForUnseal() will now try all the persistent SRKs stored in NV before falling back to creating a transient SRK. Any errors during that process are returned as an ErrTPMProvisioning error to the caller. --- tpm2/unseal.go | 69 ++++++++++++++++++-------------------------------- 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/tpm2/unseal.go b/tpm2/unseal.go index 6c6698c3..a466bf90 100644 --- a/tpm2/unseal.go +++ b/tpm2/unseal.go @@ -20,82 +20,63 @@ package tpm2 import ( - "fmt" - "github.com/canonical/go-tpm2" "golang.org/x/xerrors" - - "github.com/snapcore/secboot/internal/tcg" ) const ( tryPersistentSRK = iota tryTransientSRK - tryMax ) // loadForUnseal loads the sealed key object into the TPM and returns a context -// for it. It first tries by using the persistent shared SRK at the well known -// handle as the parent object. If this object doesn't exist or loading fails with -// an error indicating that the supplied sealed key object data is invalid, this -// function will try to create a transient SRK and then retry loading of the sealed -// key object by specifying the newly created transient object as the parent. +// for it. It first tries by going through all the peristent handles. If none can be +// used, this function will try to create a transient SRK and then retry loading of +// the sealed key object by specifying the newly created transient object as parent. // -// If both attempts to load the sealed key object fail, or if the first attempt fails -// and a transient SRK cannot be created, an error will be returned. +// If all attempts to load the sealed key object fail and a transient SRK cannot be +// created, an error will be returned. // // If a transient SRK is created, it is flushed from the TPM before this function // returns. func (k *SealedKeyObject) loadForUnseal(tpm *tpm2.TPMContext, session tpm2.SessionContext) (tpm2.ResourceContext, error) { var lastError error - for try := tryPersistentSRK; try <= tryMax; try++ { + + handles, err := tpm.GetCapabilityHandles(tpm2.HandleTypePersistent.BaseHandle(), tpm2.CapabilityMaxProperties) + if err != nil { + return nil, err + } + + tries := map[int]int{ + tryPersistentSRK: len(handles), + tryTransientSRK: len(handles) + 1, + } + + for try := 0; try < tries[tryTransientSRK]; try++ { var srk tpm2.ResourceContext - if try == tryPersistentSRK { - var err error - srk, err = tpm.CreateResourceContextFromTPM(tcg.SRKHandle) - if tpm2.IsResourceUnavailableError(err, tcg.SRKHandle) { - // No SRK - save the error and try creating a transient - lastError = ErrTPMProvisioning - continue - } else if err != nil { - // This is an unexpected error - return nil, xerrors.Errorf("cannot create context for SRK: %w", err) - } + var err error + if try < tries[tryPersistentSRK] { + srk, err = tpm.CreateResourceContextFromTPM(handles[try]) } else { - var err error srk, _, _, _, _, err = tpm.CreatePrimary(tpm.OwnerHandleContext(), nil, selectSrkTemplate(tpm, session), nil, nil, session) - if isAuthFailError(err, tpm2.CommandCreatePrimary, 1) { - // We don't know the authorization value for the storage hierarchy - ignore - // this so we end up returning the last error. - continue - } else if err != nil { - // This is an unexpected error - return nil, xerrors.Errorf("cannot create transient SRK: %w", err) - } defer tpm.FlushContext(srk) } + if err != nil { + lastError = ErrTPMProvisioning + continue + } // Load the key data keyObject, err := k.load(tpm, srk, session) - if isLoadInvalidParamError(err) || isImportInvalidParamError(err) { - // The supplied key data is invalid or is not protected by the supplied SRK. - lastError = InvalidKeyDataError{ - fmt.Sprintf("cannot load sealed key object into TPM: %v. Either the sealed key object is bad or the TPM owner has changed", err)} - continue - } else if isLoadInvalidParentError(err) || isImportInvalidParentError(err) { - // The supplied SRK is not a valid storage parent. + if err != nil { lastError = ErrTPMProvisioning continue - } else if err != nil { - // This is an unexpected error - return nil, xerrors.Errorf("cannot load sealed key object into TPM: %w", err) } return keyObject, nil } - // No more attempts left - return the last error return nil, lastError } From 61c71deeb7c1ea7e5011e867a01bf0dd3dfdf6c3 Mon Sep 17 00:00:00 2001 From: Spyros Seimenis Date: Mon, 26 Sep 2022 13:15:28 +0300 Subject: [PATCH 2/2] tpm2: Skip EK when trying all persistent SRK --- tpm2/unseal.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tpm2/unseal.go b/tpm2/unseal.go index a466bf90..95e8be06 100644 --- a/tpm2/unseal.go +++ b/tpm2/unseal.go @@ -21,6 +21,7 @@ package tpm2 import ( "github.com/canonical/go-tpm2" + "github.com/snapcore/secboot/internal/tcg" "golang.org/x/xerrors" ) @@ -31,7 +32,7 @@ const ( ) // loadForUnseal loads the sealed key object into the TPM and returns a context -// for it. It first tries by going through all the peristent handles. If none can be +// for it. It first tries by going through all the peristent SRK handles. If none can be // used, this function will try to create a transient SRK and then retry loading of // the sealed key object by specifying the newly created transient object as parent. // @@ -56,7 +57,12 @@ func (k *SealedKeyObject) loadForUnseal(tpm *tpm2.TPMContext, session tpm2.Sessi for try := 0; try < tries[tryTransientSRK]; try++ { var srk tpm2.ResourceContext var err error + if try < tries[tryPersistentSRK] { + if handles[try] == tcg.EKHandle { + continue + } + srk, err = tpm.CreateResourceContextFromTPM(handles[try]) } else { srk, _, _, _, _, err = tpm.CreatePrimary(tpm.OwnerHandleContext(), nil, selectSrkTemplate(tpm, session), nil, nil, session)