Skip to content

Commit

Permalink
evetpm : update seal/unseal functions signature
Browse files Browse the repository at this point in the history
Update the Seal/Unseal disk key functions to be generic and accept TPM NV
indexes as parameters. These functions are not bound to just store and
retrive disk keys, and this modification makes it obvious.

Signed-off-by: Shahriyar Jalayeri <[email protected]>
  • Loading branch information
shjala committed Nov 27, 2024
1 parent 7151bd4 commit 4f21372
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 62 deletions.
2 changes: 1 addition & 1 deletion pkg/pillar/cmd/vaultmgr/vaultmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ func handleVaultKeyFromControllerImpl(ctxArg interface{}, key string,
}
// Try unlocking the vault now, in case it is not yet unlocked
log.Noticef("Vault is still locked, trying to unlock")
err = etpm.SealDiskKey(log, decryptedKey, etpm.DiskKeySealingPCRs)
err = etpm.TpmSealData(log, decryptedKey, etpm.DiskKeySealingPCRs, etpm.TpmSealedDiskPrivHdl, etpm.TpmSealedDiskPubHdl, true)
if err != nil {
log.Errorf("Failed to Seal key in TPM %v", err)
return
Expand Down
113 changes: 60 additions & 53 deletions pkg/pillar/evetpm/tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,8 @@ func FetchSealedVaultKey(log *base.LogObject) ([]byte, error) {
}

//gain some knowledge about existing environment
sealedKeyPresent := isSealedKeyPresent()
legacyKeyPresent := isLegacyKeyPresent()
sealedKeyPresent := isSealedDiskKeyPresent()
legacyKeyPresent := isLegacyDiskKeyPresent()

if !sealedKeyPresent && !legacyKeyPresent {
log.Noticef("neither legacy nor sealed disk key present, generating a fresh key")
Expand All @@ -669,7 +669,7 @@ func FetchSealedVaultKey(log *base.LogObject) ([]byte, error) {
if err != nil {
return nil, fmt.Errorf("GetRandom failed: %w", err)
}
err = SealDiskKey(log, key, DiskKeySealingPCRs)
err = TpmSealData(log, key, DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true)
if err != nil {
return nil, fmt.Errorf("sealing the fresh disk key failed: %w", err)
}
Expand All @@ -696,7 +696,7 @@ func FetchSealedVaultKey(log *base.LogObject) ([]byte, error) {

log.Noticef("try to convert the legacy key into a sealed key")

err = SealDiskKey(log, key, DiskKeySealingPCRs)
err = TpmSealData(log, key, DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true)
if err != nil {
return nil, fmt.Errorf("sealing the legacy disk key into TPM failed: %w", err)
}
Expand All @@ -707,7 +707,7 @@ func FetchSealedVaultKey(log *base.LogObject) ([]byte, error) {
log.Noticef("sealed disk key present int TPM, about to unseal it")
}
//By this, we have a key sealed into TPM
key, err := UnsealDiskKey(DiskKeySealingPCRs)
key, err := TpmUnsealData(DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true)
if err == nil {
// be more verbose, lets celebrate
log.Noticef("successfully unsealed the disk key from TPM")
Expand All @@ -716,19 +716,19 @@ func FetchSealedVaultKey(log *base.LogObject) ([]byte, error) {
return key, err
}

// SealDiskKey seals key into TPM2.0, with provided PCRs
func SealDiskKey(log *base.LogObject, key []byte, pcrSel tpm2.PCRSelection) error {
// TpmSealData seals private date (a key) into TPM2.0, with provided PCRs
func TpmSealData(log *base.LogObject, key []byte, pcrSel tpm2.PCRSelection, privHdl, pubHdl tpmutil.Handle, saveTpmLogs bool) error {
rw, err := tpm2.OpenTPM(TpmDevicePath)
if err != nil {
return err
}
defer rw.Close()

tpm2.NVUndefineSpace(rw, EmptyPassword,
tpm2.HandleOwner, TpmSealedDiskPubHdl)
tpm2.HandleOwner, pubHdl)

tpm2.NVUndefineSpace(rw, EmptyPassword,
tpm2.HandleOwner, TpmSealedDiskPrivHdl)
tpm2.HandleOwner, privHdl)

//Note on any abrupt power failure at this point, and the result of it
//We should be ok, since the key supplied here can be
Expand Down Expand Up @@ -759,68 +759,70 @@ func SealDiskKey(log *base.LogObject, key []byte, pcrSel tpm2.PCRSelection) erro
// Define space in NV storage and clean up afterwards or subsequent runs will fail.
if err := tpm2.NVDefineSpace(rw,
tpm2.HandleOwner,
TpmSealedDiskPrivHdl,
privHdl,
EmptyPassword,
EmptyPassword,
nil,
tpm2.AttrOwnerWrite|tpm2.AttrOwnerRead,
uint16(len(priv)),
); err != nil {
return fmt.Errorf("NVDefineSpace %v failed: %w", TpmSealedDiskPrivHdl, err)
return fmt.Errorf("NVDefineSpace %v failed: %w", privHdl, err)
}

// Write the private data
if err := tpm2.NVWrite(rw, tpm2.HandleOwner, TpmSealedDiskPrivHdl,
if err := tpm2.NVWrite(rw, tpm2.HandleOwner, privHdl,
EmptyPassword, priv, 0); err != nil {
return fmt.Errorf("NVWrite %v failed: %w", TpmSealedDiskPrivHdl, err)
return fmt.Errorf("NVWrite %v failed: %w", privHdl, err)
}

// Define space in NV storage
if err := tpm2.NVDefineSpace(rw,
tpm2.HandleOwner,
TpmSealedDiskPubHdl,
pubHdl,
EmptyPassword,
EmptyPassword,
nil,
tpm2.AttrOwnerWrite|tpm2.AttrOwnerRead,
uint16(len(public)),
); err != nil {
return fmt.Errorf("NVDefineSpace %v failed: %w", TpmSealedDiskPubHdl, err)
return fmt.Errorf("NVDefineSpace %v failed: %w", pubHdl, err)
}
// Write the public data
if err := tpm2.NVWrite(rw, tpm2.HandleOwner, TpmSealedDiskPubHdl,
if err := tpm2.NVWrite(rw, tpm2.HandleOwner, pubHdl,
EmptyPassword, public, 0); err != nil {
return fmt.Errorf("NVWrite %v failed: %w", TpmSealedDiskPubHdl, err)
return fmt.Errorf("NVWrite %v failed: %w", pubHdl, err)
}

// save a snapshot of current PCR values
if err := saveDiskKeySealingPCRs(); err != nil {
log.Warnf("saving snapshot of sealing PCRs failed: %s", err)
}
if saveTpmLogs {
// save a snapshot of current PCR values
if err := saveDiskKeySealingPCRs(); err != nil {
log.Warnf("saving snapshot of sealing PCRs failed: %s", err)
}

// In order to not lose the ability to diff and diagnose the issue,
// first backup the previous pair of logs (if any). This is needed because
// once the failing devices get connected to the controller to fetch the
// backup key, we end up here again and it'll override the MeasurementLogSealSuccess
// file content with current tpm measurement logs (which is same as the
// content of MeasurementLogSealFail).
if err := backupCopiedMeasurementLogs(); err != nil {
log.Warnf("copying previous snapshot of TPM event log failed: %s", err)
}
// In order to not lose the ability to diff and diagnose the issue,
// first backup the previous pair of logs (if any). This is needed because
// once the failing devices get connected to the controller to fetch the
// backup key, we end up here again and it'll override the MeasurementLogSealSuccess
// file content with current tpm measurement logs (which is same as the
// content of MeasurementLogSealFail).
if err := backupCopiedMeasurementLogs(); err != nil {
log.Warnf("copying previous snapshot of TPM event log failed: %s", err)
}

// fresh start, remove old copies of measurement logs.
removeCopiedMeasurementLogs()
// fresh start, remove old copies of measurement logs.
removeCopiedMeasurementLogs()

// save a copy of the current measurement log, this is also called
// if unseal fails to have copy when we fail to unlock the vault.
if err := copyMeasurementLog(measurementLogSealSuccess); err != nil {
log.Warnf("copying current TPM measurement log failed: %s", err)
// save a copy of the current measurement log, this is also called
// if unseal fails to have copy when we fail to unlock the vault.
if err := copyMeasurementLog(measurementLogSealSuccess); err != nil {
log.Warnf("copying current TPM measurement log failed: %s", err)
}
}

return nil
}

func isSealedKeyPresent() bool {
func isSealedDiskKeyPresent() bool {
rw, err := tpm2.OpenTPM(TpmDevicePath)
if err != nil {
return false
Expand All @@ -832,30 +834,30 @@ func isSealedKeyPresent() bool {
return err == nil
}

func isLegacyKeyPresent() bool {
func isLegacyDiskKeyPresent() bool {
_, err := readDiskKey()
return err == nil
}

// UnsealDiskKey unseals key from TPM2.0
func UnsealDiskKey(pcrSel tpm2.PCRSelection) ([]byte, error) {
// TpmUnsealData unseals private data (a key) from TPM2.0
func TpmUnsealData(pcrSel tpm2.PCRSelection, privHdl, pubHdl tpmutil.Handle, saveTpmLogs bool) ([]byte, error) {
rw, err := tpm2.OpenTPM(TpmDevicePath)
if err != nil {
return nil, err
}
defer rw.Close()

// Read all of the data with NVReadEx
priv, err := tpm2.NVReadEx(rw, TpmSealedDiskPrivHdl,
priv, err := tpm2.NVReadEx(rw, privHdl,
tpm2.HandleOwner, EmptyPassword, 0)
if err != nil {
return nil, fmt.Errorf("NVReadEx %v failed: %w", TpmSealedDiskPrivHdl, err)
return nil, fmt.Errorf("NVReadEx %v failed: %w", privHdl, err)
}
// Read all of the data with NVReadEx
pub, err := tpm2.NVReadEx(rw, TpmSealedDiskPubHdl,
pub, err := tpm2.NVReadEx(rw, pubHdl,
tpm2.HandleOwner, EmptyPassword, 0)
if err != nil {
return nil, fmt.Errorf("NVReadEx %v failed: %w", TpmSealedDiskPubHdl, err)
return nil, fmt.Errorf("NVReadEx %v failed: %w", pubHdl, err)
}

sealedObjHandle, _, err := tpm2.Load(rw, TpmSRKHdl, "", pub, priv)
Expand All @@ -872,13 +874,18 @@ func UnsealDiskKey(pcrSel tpm2.PCRSelection) ([]byte, error) {

key, err := tpm2.UnsealWithSession(rw, session, sealedObjHandle, EmptyPassword)
if err != nil {
// We get here mostly because of RCPolicyFail error, so first try to save
// a copy of TPM measurement log, it comes handy for diagnosing the issue.
evtLogStat := "copied (failed unseal) TPM measurement log"
if errEvtLog := copyMeasurementLog(measurementLogUnsealFail); errEvtLog != nil {
// just report the failure, still give findMismatchingPCRs a chance so
// we can at least have some partial information about why unseal failed.
evtLogStat = fmt.Sprintf("copying (failed unseal) TPM measurement log failed: %v", errEvtLog)
var evtLogStat string
if !saveTpmLogs {
evtLogStat = "TPM measurement log not copied (saveTpmLogs=false)"
} else {
// We get here mostly because of RCPolicyFail error, so first try to save
// a copy of TPM measurement log, it comes handy for diagnosing the issue.
evtLogStat = "copied (failed unseal) TPM measurement log"
if errEvtLog := copyMeasurementLog(measurementLogUnsealFail); errEvtLog != nil {
// just report the failure, still give findMismatchingPCRs a chance so
// we can at least have some partial information about why unseal failed.
evtLogStat = fmt.Sprintf("copying (failed unseal) TPM measurement log failed: %v", errEvtLog)
}
}

// try to find out the mismatching PCR index
Expand Down Expand Up @@ -926,15 +933,15 @@ func PolicyPCRSession(rw io.ReadWriteCloser, pcrSel tpm2.PCRSelection) (tpmutil.
// CompareLegacyandSealedKey compares legacy and sealed keys
// to record if we are using a new key for sealed vault
func CompareLegacyandSealedKey() SealedKeyType {
if !isSealedKeyPresent() {
if !isSealedDiskKeyPresent() {
return SealedKeyTypeUnprotected
}
legacyKey, err := readDiskKey()
if err != nil {
//no cloning case, return SealedKeyTypeNew
return SealedKeyTypeNew
}
unsealedKey, err := UnsealDiskKey(DiskKeySealingPCRs)
unsealedKey, err := TpmUnsealData(DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true)
if err != nil {
//key is present but can't unseal it
//but legacy key is present
Expand Down
16 changes: 8 additions & 8 deletions pkg/pillar/evetpm/tpm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TestMain(m *testing.M) {

func waitForTpmReadyState() error {
for i := 0; i < 10; i++ {
if err := SealDiskKey(log, []byte("secret"), DiskKeySealingPCRs); err != nil {
if err := TpmSealData(log, []byte("secret"), DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true); err != nil {
// this is RCRetry, so retry
if strings.Contains(err.Error(), "code 0x22") {
time.Sleep(100 * time.Millisecond)
Expand All @@ -73,11 +73,11 @@ func TestSealUnseal(t *testing.T) {
}

dataToSeal := []byte("secret")
if err := SealDiskKey(log, dataToSeal, DiskKeySealingPCRs); err != nil {
if err := TpmSealData(log, dataToSeal, DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true); err != nil {
t.Fatalf("Seal operation failed with err: %v", err)
}

unsealedData, err := UnsealDiskKey(DiskKeySealingPCRs)
unsealedData, err := TpmUnsealData(DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true)
if err != nil {
t.Fatalf("Unseal operation failed with err: %v", err)
}
Expand All @@ -99,7 +99,7 @@ func TestSealUnsealMismatchReport(t *testing.T) {
defer rw.Close()

dataToSeal := []byte("secret")
if err := SealDiskKey(log, dataToSeal, DiskKeySealingPCRs); err != nil {
if err := TpmSealData(log, dataToSeal, DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true); err != nil {
t.Fatalf("Seal operation failed with err: %v", err)
}

Expand All @@ -111,7 +111,7 @@ func TestSealUnsealMismatchReport(t *testing.T) {
}
}

_, err = UnsealDiskKey(DiskKeySealingPCRs)
_, err = TpmUnsealData(DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true)
if err == nil {
t.Fatalf("Expected error from UnsealDiskKey, got nil")
}
Expand All @@ -135,7 +135,7 @@ func TestSealUnsealTpmEventLogCollect(t *testing.T) {

// this should write tpm event log to measurementLogSealSuccess file
dataToSeal := []byte("secret")
if err := SealDiskKey(log, dataToSeal, DiskKeySealingPCRs); err != nil {
if err := TpmSealData(log, dataToSeal, DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true); err != nil {
t.Fatalf("Seal operation failed with err: %v", err)
}

Expand All @@ -146,7 +146,7 @@ func TestSealUnsealTpmEventLogCollect(t *testing.T) {
}

// this should fail and result in saving creating measurementLogUnsealFail
_, err = UnsealDiskKey(DiskKeySealingPCRs)
_, err = TpmUnsealData(DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true)
if err == nil {
t.Fatalf("Expected error from UnsealDiskKey, got nil")
}
Expand All @@ -159,7 +159,7 @@ func TestSealUnsealTpmEventLogCollect(t *testing.T) {
}

// this should trigger backing up previously saved tpm event logs
if err := SealDiskKey(log, dataToSeal, DiskKeySealingPCRs); err != nil {
if err := TpmSealData(log, dataToSeal, DiskKeySealingPCRs, TpmSealedDiskPrivHdl, TpmSealedDiskPubHdl, true); err != nil {
t.Fatalf("Seal operation failed with err: %v", err)
}

Expand Down

0 comments on commit 4f21372

Please sign in to comment.