diff --git a/efi/preinstall/check_tcglog.go b/efi/preinstall/check_tcglog.go index 0fc67bad..a4106e10 100644 --- a/efi/preinstall/check_tcglog.go +++ b/efi/preinstall/check_tcglog.go @@ -578,7 +578,7 @@ func (t *tcglogPhaseTracker) reachedOSPresent() bool { // PCRs set, but the errors will be accessible on the returned results struct. // // The returned results struct indicates the best PCR bank to use and specifies the TPM startup locality as well. -func checkFirmwareLogAndChoosePCRBank(tpm *tpm2.TPMContext, log *tcglog.Log, mandatoryPcrs tpm2.HandleList) (results *pcrBankResults, err error) { +func checkFirmwareLogAndChoosePCRBank(tpm *tpm2.TPMContext, log *tcglog.Log, mandatoryPcrs tpm2.HandleList, permitEmptyPCRBanks bool) (results *pcrBankResults, err error) { // Make sure it's a crypto-agile log if !log.Spec.IsEFI_2() { return nil, errors.New("invalid log spec") @@ -601,6 +601,23 @@ func checkFirmwareLogAndChoosePCRBank(tpm *tpm2.TPMContext, log *tcglog.Log, man results, err := checkFirmwareLogAgainstTPMForAlg(tpm, log, alg, mandatoryPcrs) switch { + case errors.Is(err, ErrPCRBankMissingFromLog): + if !permitEmptyPCRBanks { + // Make sure that the TPM PCR bank is not enabled + pcrs, err := tpm.GetCapabilityPCRs() + if err != nil { + return nil, fmt.Errorf("cannot obtain active PCRs: %w", err) + } + for _, selection := range pcrs { + if selection.Hash == alg && len(selection.Select) > 0 { + // This bank is missing from the log but enabled on the TPM. + // This is very bad for remote attestation (not so bad for FDE), but treat + // this as a serious error nonetheless. + return nil, &EmptyPCRBankError{alg} + } + } + } + fallthrough case err != nil: // This entire bank is bad mainErr.setBankErr(alg, err) diff --git a/efi/preinstall/check_tcglog_test.go b/efi/preinstall/check_tcglog_test.go index 95281640..591c396c 100644 --- a/efi/preinstall/check_tcglog_test.go +++ b/efi/preinstall/check_tcglog_test.go @@ -222,6 +222,7 @@ type testCheckFirmwareLogAndChoosePCRBankParams struct { startupLocality uint8 disallowPreOSVerification bool mandatoryPcrs tpm2.HandleList + permitEmptyPCRBanks bool expectedAlg tpm2.HashAlgorithmId } @@ -235,7 +236,7 @@ func (s *tcglogSuite) testCheckFirmwareLogAndChoosePCRBank(c *C, params *testChe DisallowPreOSVerification: params.disallowPreOSVerification, }) s.resetTPMAndReplayLog(c, log, params.logAlgs...) - result, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, params.mandatoryPcrs) + result, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, params.mandatoryPcrs, params.permitEmptyPCRBanks) c.Assert(err, IsNil) c.Check(result.Alg, Equals, params.expectedAlg) c.Check(result.StartupLocality, Equals, params.startupLocality) @@ -331,7 +332,8 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankSHA256WithEmptySHA384B internal_efi.PlatformManufacturerPCR, internal_efi.SecureBootPolicyPCR, }, - expectedAlg: tpm2.HashAlgorithmSHA256, + permitEmptyPCRBanks: true, + expectedAlg: tpm2.HashAlgorithmSHA256, }) } @@ -452,7 +454,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankUnexpectedStartupLocal _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, tpm2.HandleList{ internal_efi.PlatformFirmwarePCR, - }) + }, false) c.Check(err, ErrorMatches, `no suitable PCR algorithm available: - TPM_ALG_SHA512: the PCR bank is missing from the TCG log. - TPM_ALG_SHA384: the PCR bank is missing from the TCG log. @@ -531,7 +533,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankOutOfPlaceStartupLocal _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, tpm2.HandleList{ internal_efi.PlatformFirmwarePCR, - }) + }, false) c.Check(err, ErrorMatches, `no suitable PCR algorithm available: - TPM_ALG_SHA512: the PCR bank is missing from the TCG log. - TPM_ALG_SHA384: the PCR bank is missing from the TCG log. @@ -574,7 +576,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankInvalidStartupLocality _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, tpm2.HandleList{ internal_efi.PlatformFirmwarePCR, - }) + }, false) c.Check(err, ErrorMatches, `no suitable PCR algorithm available: - TPM_ALG_SHA512: the PCR bank is missing from the TCG log. - TPM_ALG_SHA384: the PCR bank is missing from the TCG log. @@ -600,7 +602,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankPCRMismatchMandatory(c }) _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, tpm2.HandleList{ internal_efi.PlatformFirmwarePCR, - }) + }, false) c.Check(err, ErrorMatches, `no suitable PCR algorithm available: - TPM_ALG_SHA512: the PCR bank is missing from the TCG log. - TPM_ALG_SHA384: the PCR bank is missing from the TCG log. @@ -634,6 +636,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankPCRMismatchNonMandator internal_efi.PlatformManufacturerPCR, internal_efi.SecureBootPolicyPCR, }, + false, ) c.Assert(err, IsNil) c.Check(results.Alg, Equals, tpm2.HashAlgorithmSHA256) @@ -673,6 +676,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankPCRMismatchMandatoryIn internal_efi.PlatformManufacturerPCR, internal_efi.SecureBootPolicyPCR, }, + false, ) c.Assert(err, IsNil) c.Check(results.Alg, Equals, tpm2.HashAlgorithmSHA256) @@ -708,6 +712,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankPCRMismatchNonMandator internal_efi.PlatformManufacturerPCR, internal_efi.SecureBootPolicyPCR, }, + false, ) c.Assert(err, IsNil) c.Check(results.Alg, Equals, tpm2.HashAlgorithmSHA384) @@ -766,6 +771,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankSecureBootConfigJumpsT internal_efi.PlatformManufacturerPCR, internal_efi.SecureBootPolicyPCR, }, + false, ) c.Assert(err, IsNil) c.Check(results.Alg, Equals, tpm2.HashAlgorithmSHA256) @@ -782,7 +788,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankBadSpec(c *C) { Minor: 2, Errata: 0, } - _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil) + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil, false) c.Check(err, ErrorMatches, `invalid log spec`) } @@ -815,7 +821,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankPreOSMeasurementToNonT log.Events = eventsCopy s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) - _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil) + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil, false) c.Check(err, ErrorMatches, `measurements were made by firmware from pre-OS environment to non-TCG defined PCR 8`) } @@ -839,7 +845,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankSeparatorDecodeError(c } s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) - _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil) + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil, false) c.Check(err, ErrorMatches, `invalid event data for EV_SEPARATOR event in PCR 7: some error`) } @@ -863,7 +869,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankSeparatorError(c *C) { } s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) - _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil) + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil, false) c.Check(err, ErrorMatches, `EV_SEPARATOR event for PCR 7 indicates an error occurred \(error code in log: 67305985\)`) } @@ -899,7 +905,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankUnexpectedSuccessfulSe s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) - _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil) + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil, false) c.Check(err, ErrorMatches, `unexpected normal EV_SEPARATOR event in PCR 0`) } @@ -928,7 +934,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankMissingSeparators(c *C log.Events = eventsCopy s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) - _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil) + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil, false) c.Check(err, ErrorMatches, `unexpected EV_EFI_VARIABLE_AUTHORITY event in PCR 7 whilst transitioning to OS-present \(expected EV_SEPARATOR\)`) } @@ -954,7 +960,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankMultipleSeparatorsForS log.Events = eventsCopy s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) - _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil) + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil, false) c.Check(err, ErrorMatches, `more than one EV_SEPARATOR event exists for PCR 7`) } @@ -978,6 +984,96 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankTruncatedLog(c *C) { log.Events = eventsCopy s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) - _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil) + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, nil, false) c.Check(err, ErrorMatches, `reached the end of the log without seeing EV_SEPARATOR events in all TCG defined PCRs`) } + +func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankEmptyPCRBankNotAllowed(c *C) { + s.RequireAlgorithm(c, tpm2.AlgorithmSHA384) + s.allocatePCRBanks(c, tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA384) + + log := efitest.NewLog(c, &efitest.LogOptions{ + Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}, + StartupLocality: 3, + }) + s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) + + // This will make the PCR 0 calculation wrong + log = efitest.NewLog(c, &efitest.LogOptions{ + Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}, + }) + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, + tpm2.HandleList{ + internal_efi.PlatformConfigPCR, + internal_efi.DriversAndAppsPCR, + internal_efi.DriversAndAppsConfigPCR, + internal_efi.BootManagerCodePCR, + internal_efi.BootManagerConfigPCR, + internal_efi.PlatformManufacturerPCR, + internal_efi.SecureBootPolicyPCR, + }, + false, + ) + c.Check(err, ErrorMatches, `the PCR bank for TPM_ALG_SHA384 is missing from the TCG log but is active on the TPM`) + + var emptyPCRErr *EmptyPCRBankError + c.Check(errors.As(err, &emptyPCRErr), testutil.IsTrue) +} + +func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankEmptyPCRBankAllowed(c *C) { + s.RequireAlgorithm(c, tpm2.AlgorithmSHA384) + s.allocatePCRBanks(c, tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA384) + + log := efitest.NewLog(c, &efitest.LogOptions{ + Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}, + StartupLocality: 3, + }) + s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) + + // This will make the PCR 0 calculation wrong + log = efitest.NewLog(c, &efitest.LogOptions{ + Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}, + }) + const permitEmptyPCRBanks = true + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, + tpm2.HandleList{ + internal_efi.PlatformConfigPCR, + internal_efi.DriversAndAppsPCR, + internal_efi.DriversAndAppsConfigPCR, + internal_efi.BootManagerCodePCR, + internal_efi.BootManagerConfigPCR, + internal_efi.PlatformManufacturerPCR, + internal_efi.SecureBootPolicyPCR, + }, + permitEmptyPCRBanks, + ) + c.Assert(err, IsNil) +} + +func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankSHA256WithEmptySHA384BankBad(c *C) { + s.RequireAlgorithm(c, tpm2.AlgorithmSHA384) + s.allocatePCRBanks(c, tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA384) + + log := efitest.NewLog(c, &efitest.LogOptions{ + Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}, + StartupLocality: 3, + }) + s.resetTPMAndReplayLog(c, log, tpm2.HashAlgorithmSHA256) + + _, err := CheckFirmwareLogAndChoosePCRBank(s.TPM, log, + tpm2.HandleList{ + internal_efi.PlatformConfigPCR, + internal_efi.DriversAndAppsPCR, + internal_efi.DriversAndAppsConfigPCR, + internal_efi.BootManagerCodePCR, + internal_efi.BootManagerConfigPCR, + internal_efi.PlatformManufacturerPCR, + internal_efi.SecureBootPolicyPCR, + }, + false, + ) + c.Check(err, ErrorMatches, `the PCR bank for TPM_ALG_SHA384 is missing from the TCG log but is active on the TPM`) + + var bankErr *EmptyPCRBankError + c.Check(errors.As(err, &bankErr), testutil.IsTrue) +} diff --git a/efi/preinstall/checks.go b/efi/preinstall/checks.go index f3cd9386..3e049cc2 100644 --- a/efi/preinstall/checks.go +++ b/efi/preinstall/checks.go @@ -142,6 +142,12 @@ const ( // regards to firmware updates because db has to be changed accordingly each time, so this is not // advisable. PermitPreOSVerificationUsingDigests + + // PermitEmptyPCRBanks will prevent RunChecks from returning an error if there are any PCR banks + // (those are PCR banks that are enabled but which firmware doesn't perform measurements to). This + // is generally ok for full-disk encryption, but completely breaks the remote attestation model + // because it allows an adversary to trivially spoof an entire trusted platform from software. + PermitEmptyPCRBanks ) var ( @@ -236,11 +242,17 @@ func RunChecks(ctx context.Context, flags CheckFlags, loadedImages []secboot_efi mandatoryPcrs = append(mandatoryPcrs, internal_efi.SecureBootPolicyPCR) } - logResults, err := checkFirmwareLogAndChoosePCRBank(tpm, log, mandatoryPcrs) + permitEmptyPCRBanks := flags&PermitEmptyPCRBanks > 0 + logResults, err := checkFirmwareLogAndChoosePCRBank(tpm, log, mandatoryPcrs, permitEmptyPCRBanks) switch { case tpm2.IsTPMError(err, tpm2.AnyErrorCode, tpm2.AnyCommandCode): return nil, &TPM2DeviceError{err} case err != nil: + var pcrBankErr *EmptyPCRBankError + if errors.As(err, &pcrBankErr) { + // return this one unwrapped + return nil, err + } return nil, &TCGLogError{err} } diff --git a/efi/preinstall/checks_test.go b/efi/preinstall/checks_test.go index 8eeba0fc..167d606c 100644 --- a/efi/preinstall/checks_test.go +++ b/efi/preinstall/checks_test.go @@ -344,6 +344,7 @@ C7E003CB &mockImage{contents: []byte("mock grub executable"), digest: testutil.DecodeHexString(c, "d5a9780e9f6a43c2e53fe9fda547be77f7783f31aea8013783242b040ff21dc0")}, &mockImage{contents: []byte("mock kernel executable"), digest: testutil.DecodeHexString(c, "2ddfbd91fa1698b0d133c38ba90dbba76c9e08371ff83d03b5fb4c2e56d7e81f")}, }, + flags: PermitEmptyPCRBanks, expectedPcrAlg: tpm2.HashAlgorithmSHA256, expectedUsedSecureBootCAs: []*X509CertificateID{NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert))}, expectedFlags: NoPlatformConfigProfileSupport | NoDriversAndAppsConfigProfileSupport | NoBootManagerConfigProfileSupport, @@ -3190,3 +3191,70 @@ C7E003CB err = rce.Errs[0] c.Check(errors.Is(err, ErrTPMStartupLocalityNotProtected), testutil.IsTrue) } + +func (s *runChecksSuite) TestRunChecksBadEmptySHA384(c *C) { + meiAttrs := map[string][]byte{ + "fw_ver": []byte(`0:16.1.27.2176 +0:16.1.27.2176 +0:16.0.15.1624 +`), + "fw_status": []byte(`94000245 +09F10506 +00000020 +00004000 +00041F03 +C7E003CB +`), + } + devices := map[string][]internal_efi.SysfsDevice{ + "iommu": []internal_efi.SysfsDevice{ + efitest.NewMockSysfsDevice("dmar0", "/sys/devices/virtual/iommu/dmar0", "iommu", nil), + efitest.NewMockSysfsDevice("dmar1", "/sys/devices/virtual/iommu/dmar1", "iommu", nil), + }, + "mei": []internal_efi.SysfsDevice{ + efitest.NewMockSysfsDevice("mei0", "/sys/devices/pci0000:00/0000:00:16.0/mei/mei0", "mei", meiAttrs), + }, + } + + _, err := s.testRunChecks(c, &testRunChecksParams{ + env: efitest.NewMockHostEnvironmentWithOpts( + efitest.WithVirtMode(internal_efi.VirtModeNone, internal_efi.DetectVirtModeAll), + efitest.WithTPMDevice(tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)), + efitest.WithLog(efitest.NewLog(c, &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}})), + efitest.WithAMD64Environment("GenuineIntel", []uint64{cpuid.SDBG, cpuid.SMX}, 4, map[uint32]uint64{0xc80: 0x40000000}), + efitest.WithSysfsDevices(devices), + efitest.WithMockVars(efitest.MockVars{ + {Name: "AuditMode", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeNonVolatile | efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x0}}, + {Name: "BootCurrent", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x3, 0x0}}, + {Name: "BootOptionSupport", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x13, 0x03, 0x00, 0x00}}, + {Name: "DeployedMode", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeNonVolatile | efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x1}}, + {Name: "SetupMode", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x0}}, + {Name: "OsIndicationsSupported", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + }.SetSecureBoot(true).SetPK(c, efitest.NewSignatureListX509(c, snakeoilCert, efi.MakeGUID(0x03f66fa4, 0x5eee, 0x479c, 0xa408, [...]uint8{0xc4, 0xdc, 0x0a, 0x33, 0xfc, 0xde})))), + ), + tpmPropertyModifiers: map[tpm2.Property]uint32{ + tpm2.PropertyNVCountersMax: 0, + tpm2.PropertyPSFamilyIndicator: 1, + tpm2.PropertyManufacturer: uint32(tpm2.TPMManufacturerINTC), + }, + enabledBanks: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA384}, + loadedImages: []secboot_efi.Image{ + &mockImage{ + contents: []byte("mock shim executable"), + digest: testutil.DecodeHexString(c, "25e1b08db2f31ff5f5d2ea53e1a1e8fda6e1d81af4f26a7908071f1dec8611b7"), + signatures: []*efi.WinCertificateAuthenticode{ + efitest.ReadWinCertificateAuthenticodeDetached(c, shimUbuntuSig4), + }, + }, + &mockImage{contents: []byte("mock grub executable"), digest: testutil.DecodeHexString(c, "d5a9780e9f6a43c2e53fe9fda547be77f7783f31aea8013783242b040ff21dc0")}, + &mockImage{contents: []byte("mock kernel executable"), digest: testutil.DecodeHexString(c, "2ddfbd91fa1698b0d133c38ba90dbba76c9e08371ff83d03b5fb4c2e56d7e81f")}, + }, + expectedPcrAlg: tpm2.HashAlgorithmSHA256, + expectedUsedSecureBootCAs: []*X509CertificateID{NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert))}, + expectedFlags: NoPlatformConfigProfileSupport | NoDriversAndAppsConfigProfileSupport | NoBootManagerConfigProfileSupport, + }) + c.Assert(err, ErrorMatches, `the PCR bank for TPM_ALG_SHA384 is missing from the TCG log but is active on the TPM`) + + var be *EmptyPCRBankError + c.Check(errors.As(err, &be), testutil.IsTrue) +} diff --git a/efi/preinstall/errors.go b/efi/preinstall/errors.go index cc5e5be7..064e7a49 100644 --- a/efi/preinstall/errors.go +++ b/efi/preinstall/errors.go @@ -249,7 +249,7 @@ var ( ErrTPMDisabled = errors.New("TPM2 device is present but is currently disabled by the platform firmware") ) -// TPMHierarchyOwnedError is returned wrapped in TPM2DeviceError if the authorization value +// TPMHierarchyOwnedError is returned wrapped in [TPM2DeviceError] if the authorization value // for the specified hierarchy is set, but the PostInstallChecks flag isn't set. If a // hierarchy is owned during pre-install, the TPM will most likely need to be cleared. type TPM2HierarchyOwnedError struct { @@ -293,6 +293,22 @@ func (e *PCRValueMismatchError) Error() string { return fmt.Sprintf("PCR value mismatch (actual from TPM %#x, reconstructed from log %#x)", e.PCRValue, e.LogValue) } +// EmptyPCRBankError may be returned unwrapped in the event where a PCR bank seems +// to be active but not extended by firmware and not present in the log. This doesn't matter so +// much for FDE because we can select a good bank, but is a serious firmware bug for any scenario +// that requires remote attestation, because it permits an entire trusted computing environment to +// be spoofed by an adversary in software. +// +// This error can be ignored by passing the PermitEmptyPCRBanks flag to [RunChecks]. This is +// generally ok, as long as the device is not going to be used for any kind of remote attestation. +type EmptyPCRBankError struct { + Alg tpm2.HashAlgorithmId +} + +func (e *EmptyPCRBankError) Error() string { + return fmt.Sprintf("the PCR bank for %v is missing from the TCG log but is active on the TPM", e.Alg) +} + // NoSuitablePCRAlgorithmError is returned wrapped in [TCGLogError] if there is no suitable PCR // bank where the log matches the TPM values when reconstructed. As multiple errors can occur // during testing (multiple banks and multiple PCRs), this error wraps each individual error