blob: f20bfea80f8033b51d128c7e30479d6db0643c97 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package hwsec
/*
This file implements the TPM clear tool in remote tast.
*/
import (
"context"
"os"
"regexp"
"strings"
"chromiumos/tast/common/hwsec"
"chromiumos/tast/errors"
"chromiumos/tast/testing"
)
const (
systemKeyBackupFileName = "/mnt/stateful_partition/unencrypted/preserve/system.key"
)
// TPMClearer clear the TPM via crossystem, this would only work on TPM2.0.
type TPMClearer struct {
cmdRunner hwsec.CmdRunner
daemonController *hwsec.DaemonController
hasSysKey bool
}
// NewTPMClearer creates a new TPMClearer object, where r is used to run the command internally.
func NewTPMClearer(cmdRunner hwsec.CmdRunner, daemonController *hwsec.DaemonController) *TPMClearer {
return &TPMClearer{cmdRunner, daemonController, false}
}
// PreClearTPM backups the system key.
func (tc *TPMClearer) PreClearTPM(ctx context.Context) error {
// Makes sure this is a TPM 2.0 device.
version, err := tc.getTPMVersion(ctx)
if err != nil {
return errors.Wrap(err, "failed to get TPM version")
} else if version != "2.0" {
return errors.Errorf("we don't support TPM version %s for TPM soft-clearing yet", version)
}
// Checks if system key exists in NVRAM.
hasSysKey, err := tc.hasSystemKey(ctx)
if err != nil {
return errors.Wrap(err, "failed to check system key in NVRAM")
}
tc.hasSysKey = hasSysKey
// If system key exists, checks if we also have the system key backup.
if tc.hasSysKey {
if hasSysKeyBackup, err := tc.hasSystemKeyBackup(); err != nil {
return errors.Wrap(err, "failed to check the system key backup file")
} else if !hasSysKeyBackup {
return errors.New("there is a system key but not its backup; we shouldn't soft-clear the TPM")
}
}
return nil
}
// ClearTPM soft clears the TPM.
func (tc *TPMClearer) ClearTPM(ctx context.Context) error {
// Using soft clear to clear the TPM
if _, err := tc.cmdRunner.Run(ctx, "tpm_softclear"); err != nil {
return errors.Wrap(err, "failed to soft clear the TPM")
}
return nil
}
// PostClearTPM restores the system key and ensures TPM daemon is up.
func (tc *TPMClearer) PostClearTPM(ctx context.Context) error {
// Stop the TPM daemon
if err := tc.daemonController.TryStop(ctx, hwsec.TrunksDaemon); err != nil {
return errors.Wrap(err, "failed to try to stop TPM daemon")
}
defer func() {
if err := tc.daemonController.Ensure(ctx, hwsec.TrunksDaemon); err != nil {
testing.ContextLog(ctx, "Failed to ensure TPM daemon: ", err)
}
}()
if tc.hasSysKey {
if err := tc.restoreSystemKey(ctx); err != nil {
testing.ContextLog(ctx, "Failed to restore system key: ", err)
// Continues to reset daemons and system states even if we failed to restore system key,
// since the TPM is already cleared.
}
}
return nil
}
// hasSystemKey returns if the system key for encstateful exists in NVRAM or an error on any failure.
func (tc *TPMClearer) hasSystemKey(ctx context.Context) (spaceExists bool, err error) {
var nvSpaceInfoRegexp = regexp.MustCompile(`(?m)^\s*result:\s*NVRAM_RESULT_SUCCESS\s*$`)
out, err := tc.cmdRunner.Run(ctx, "tpm_manager_client", "get_space_info", "--index=0x800005")
return nvSpaceInfoRegexp.Match(out), err
}
// hasSystemKeyBackup returns if the system key backup exists on the disk or an error on any failure.
func (tc *TPMClearer) hasSystemKeyBackup() (backupExists bool, err error) {
fileInfo, err := os.Stat(systemKeyBackupFileName)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, err
}
if fileInfo.IsDir() {
return false, errors.Errorf("%s is a dir", systemKeyBackupFileName)
}
return true, nil
}
// restoreSystemKey restores encstateful system key to NVRAM.
func (tc *TPMClearer) restoreSystemKey(ctx context.Context) error {
if _, err := tc.cmdRunner.Run(ctx, "mount-encrypted", "set", systemKeyBackupFileName); err != nil {
return errors.Wrapf(err, "failed to restore system key into NVRAM from %s", systemKeyBackupFileName)
}
return nil
}
// getTPMVersion would rteurn the TPM version, for example: "1.2", "2.0"
func (tc *TPMClearer) getTPMVersion(ctx context.Context) (string, error) {
out, err := tc.cmdRunner.Run(ctx, "tpmc", "tpmver")
// Trailing newline char is trimmed.
return strings.TrimSpace(string(out)), err
}