Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tpm2: Use tpm2.TPMDevice abstraction #357

Merged
merged 3 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions efi/preinstall/check_tpm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceGoodPreInstallNoVMInfiniteCountersD
tpm2.PropertyManufacturer: uint32(tpm2.TPMManufacturerNTC),
})

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
tpm, discreteTPM, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, IsNil)
Expand All @@ -117,7 +117,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceGoodPreInstallNoVMInfiniteCountersF
tpm2.PropertyManufacturer: uint32(tpm2.TPMManufacturerINTC),
})

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
tpm, discreteTPM, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, IsNil)
Expand All @@ -137,7 +137,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceGoodPreInstallNoVMFiniteCountersDis
tpm2.PropertyManufacturer: uint32(tpm2.TPMManufacturerNTC),
})

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
tpm, discreteTPM, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, IsNil)
Expand All @@ -157,7 +157,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceGoodPostInstallNoVMCountersCheckSki
tpm2.PropertyManufacturer: uint32(tpm2.TPMManufacturerNTC),
})

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
tpm, discreteTPM, err := OpenAndCheckTPM2Device(env, CheckTPM2DevicePostInstall)
c.Check(err, IsNil)
Expand All @@ -176,7 +176,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceGoodPreInstallVMInfiniteCounters(c
tpm2.PropertyManufacturer: uint32(tpm2.TPMManufacturerMSFT),
})

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
tpm, discreteTPM, err := OpenAndCheckTPM2Device(env, CheckTPM2DeviceInVM)
c.Check(err, IsNil)
Expand All @@ -199,7 +199,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceGoodPostInstallNoVMLockoutCheckSkip
// Trip the DA logic by setting newMaxTries to 0
c.Assert(s.TPM.DictionaryAttackParameters(s.TPM.LockoutHandleContext(), 0, 10000, 10000, nil), IsNil)

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
tpm, discreteTPM, err := OpenAndCheckTPM2Device(env, CheckTPM2DevicePostInstall)
c.Check(err, IsNil)
Expand All @@ -221,7 +221,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceGoodPostInstallNoVMLockoutOwnedChec
// Set the lockout hierarchy auth value.
c.Assert(s.TPM.HierarchyChangeAuth(s.TPM.LockoutHandleContext(), []byte{1, 2, 3, 4}, nil), IsNil)

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
tpm, discreteTPM, err := OpenAndCheckTPM2Device(env, CheckTPM2DevicePostInstall)
c.Check(err, IsNil)
Expand All @@ -243,7 +243,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceGoodPostInstallNoVMOwnerOwnedCheckS
// Set the owner hierarchy auth value.
c.Assert(s.TPM.HierarchyChangeAuth(s.TPM.OwnerHandleContext(), []byte{1, 2, 3, 4}, nil), IsNil)

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
tpm, discreteTPM, err := OpenAndCheckTPM2Device(env, CheckTPM2DevicePostInstall)
c.Check(err, IsNil)
Expand All @@ -265,7 +265,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceGoodPostInstallNoVMEndorsmentOwnedC
// Set the endorsement hierarchy auth value.
c.Assert(s.TPM.HierarchyChangeAuth(s.TPM.EndorsementHandleContext(), []byte{1, 2, 3, 4}, nil), IsNil)

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
tpm, discreteTPM, err := OpenAndCheckTPM2Device(env, CheckTPM2DevicePostInstall)
c.Check(err, IsNil)
Expand All @@ -288,7 +288,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceDisabled(c *C) {
c.Assert(s.TPM.HierarchyControl(s.TPM.OwnerHandleContext(), tpm2.HandleOwner, false, nil), IsNil)
c.Assert(s.TPM.HierarchyControl(s.TPM.EndorsementHandleContext(), tpm2.HandleEndorsement, false, nil), IsNil)

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
_, _, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, Equals, ErrTPMDisabled)
Expand All @@ -304,7 +304,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceLockout(c *C) {
// Trip the DA logic by setting newMaxTries to 0
c.Assert(s.TPM.DictionaryAttackParameters(s.TPM.LockoutHandleContext(), 0, 10000, 10000, nil), IsNil)

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
_, _, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, Equals, ErrTPMLockout)
Expand All @@ -320,7 +320,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceAlreadyOwnedLockout(c *C) {
// Set the lockout hierarchy auth value so we get an error indicating that the TPM is already owned.
c.Assert(s.TPM.HierarchyChangeAuth(s.TPM.LockoutHandleContext(), []byte{1, 2, 3, 4}, nil), IsNil)

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
_, _, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, ErrorMatches, `TPM lockout hierarchy is currently owned`)
Expand All @@ -338,7 +338,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceAlreadyOwnedOwner(c *C) {
// Set the owner hierarchy auth value so we get an error indicating that the TPM is already owned.
c.Assert(s.TPM.HierarchyChangeAuth(s.TPM.OwnerHandleContext(), []byte{1, 2, 3, 4}, nil), IsNil)

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
_, _, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, ErrorMatches, `TPM owner hierarchy is currently owned`)
Expand All @@ -356,7 +356,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceAlreadyOwnedEndorsement(c *C) {
// Set the endorsement hierarchy auth value so we get an error indicating that the TPM is already owned.
c.Assert(s.TPM.HierarchyChangeAuth(s.TPM.EndorsementHandleContext(), []byte{1, 2, 3, 4}, nil), IsNil)

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
_, _, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, ErrorMatches, `TPM endorsement hierarchy is currently owned`)
Expand All @@ -370,7 +370,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceIsNotPCClient(c *C) {
tpm2.PropertyPSFamilyIndicator: 2, // This is defined as PDA in the reference library specs
})

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
_, _, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, Equals, ErrNoPCClientTPM)
Expand All @@ -382,7 +382,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceIsNotPCClientWithSWTPMWorkaround(c
tpm2.PropertyPSFamilyIndicator: 2, // This is defined as PDA in the reference library specs
})

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
_, _, err := OpenAndCheckTPM2Device(env, CheckTPM2DeviceInVM)
c.Check(err, Equals, ErrNoPCClientTPM)
Expand All @@ -396,7 +396,7 @@ func (s *tpmSuite) TestOpenAndCheckTPM2DeviceInsufficientNVCountersPreInstall(c
tpm2.PropertyPSFamilyIndicator: 1,
})

dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false)
dev := tpm2_testutil.NewTransportBackedDevice(s.Transport, false, 1)
env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithTPMDevice(dev))
_, _, err := OpenAndCheckTPM2Device(env, 0)
c.Check(err, Equals, ErrTPMInsufficientNVCounters)
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/canonical/go-efilib v1.4.1
github.com/canonical/go-sp800.108-kdf v0.0.0-20210315104021-ead800bbf9a0
github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3
github.com/canonical/go-tpm2 v1.7.6
github.com/canonical/go-tpm2 v1.10.0
github.com/canonical/tcglog-parser v0.0.0-20240924110432-d15eaf652981
github.com/snapcore/snapd v0.0.0-20220714152900-4a1f4c93fc85
golang.org/x/crypto v0.21.0
Expand All @@ -19,6 +19,7 @@ require (
)

require (
github.com/canonical/go-kbkdf v0.0.0-20250104172618-3b1308f9acf9 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/kr/pretty v0.2.2-0.20200810074440-814ac30b4b18 // indirect
github.com/kr/text v0.1.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ github.com/canonical/cpuid v0.0.0-20220614022739-219e067757cb/go.mod h1:6j8Sw3dw
github.com/canonical/go-efilib v0.0.0-20210909101908-41435fa545d4/go.mod h1:9Sr9kd7IhQPYqaU5nut8Ky97/CtlhHDzQncQnrULgDM=
github.com/canonical/go-efilib v1.4.1 h1:/VMNCypz+iVmnNuMcsm7WvmDMI1ObkEP2W1h8Ls7OyM=
github.com/canonical/go-efilib v1.4.1/go.mod h1:n0Ttsy1JuHAvqaFbZBs6PAzoiiJdfkHsAmDOEbexYEQ=
github.com/canonical/go-kbkdf v0.0.0-20250104172618-3b1308f9acf9 h1:Twk1ZSTWRClfGShP16ePf2JIiayqWS4ix1rkAR6baag=
github.com/canonical/go-kbkdf v0.0.0-20250104172618-3b1308f9acf9/go.mod h1:IneQ5/yQcfPXrGekEXpR6yeea55ZD24N5+kHzeDseOM=
github.com/canonical/go-sp800.108-kdf v0.0.0-20210314145419-a3359f2d21b9/go.mod h1:Zrs3YjJr+w51u0R/dyLh/oWt/EcBVdLPCVFYC4daW5s=
github.com/canonical/go-sp800.108-kdf v0.0.0-20210315104021-ead800bbf9a0 h1:ZE2XMRFHcwlib3uU9is37+pKkkMloVoEPWmgQ6GK1yo=
github.com/canonical/go-sp800.108-kdf v0.0.0-20210315104021-ead800bbf9a0/go.mod h1:Zrs3YjJr+w51u0R/dyLh/oWt/EcBVdLPCVFYC4daW5s=
github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3 h1:oe6fCvaEpkhyW3qAicT0TnGtyht/UrgvOwMcEgLb7Aw=
github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3/go.mod h1:qdP0gaj0QtgX2RUZhnlVrceJ+Qln8aSlDyJwelLLFeM=
github.com/canonical/go-tpm2 v0.0.0-20210827151749-f80ff5afff61/go.mod h1:vG41hdbBjV4+/fkubTT1ENBBqSkLwLr7mCeW9Y6kpZY=
github.com/canonical/go-tpm2 v1.7.6 h1:9k9OAEEp9xKp4h2WJwfTUNivblJi4L5Wjx7Q/LkSTSQ=
github.com/canonical/go-tpm2 v1.7.6/go.mod h1:Dz0PQRmoYrmk/4BLILjRA+SFzuqEo1etAvYeAJiMhYU=
github.com/canonical/go-tpm2 v1.10.0 h1:yo37mU1OdSd3Rx2bYsAvVUlWrHNukFC7gWMkWOOt5Us=
github.com/canonical/go-tpm2 v1.10.0/go.mod h1:zK+qESVwu78XyX+NPhiBdN+zwPPDoKk4rYlQ7VUsRp4=
github.com/canonical/tcglog-parser v0.0.0-20210824131805-69fa1e9f0ad2/go.mod h1:QoW2apR2tBl6T/4czdND/EHjL1Ia9cCmQnIj9Xe0Kt8=
github.com/canonical/tcglog-parser v0.0.0-20240924110432-d15eaf652981 h1:vrUzSfbhl8mzdXPzjxq4jXZPCCNLv18jy6S7aVTS2tI=
github.com/canonical/tcglog-parser v0.0.0-20240924110432-d15eaf652981/go.mod h1:ywdPBqUGkuuiitPpVWCfilf2/gq+frhq4CNiNs9KyHU=
Expand Down
97 changes: 97 additions & 0 deletions internal/tpm2_device/device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2024 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package tpm2_device

import (
"errors"

"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/ppi"
)

// DeviceMode describes the mode to select the default device.
type DeviceMode int

const (
// DeviceModeDirect requests the most direct TPM2 device, without
// the use of a resource manager. These devices cannot be opened more
// than once and don't permit the TPM to be shared.
DeviceModeDirect DeviceMode = iota

// DeviceModeResourceManaged requests a resource managed TPM2 device.
// These devices can be opened more than once and shared, relying on
// the resource manager to handle context switching between users
// (although they can't be shared with a direct device).
DeviceModeResourceManaged

// DeviceModeTryResourceManaged is like DeviceModeResourceManaged except
// it will return a direct device if a resource managed device is not
// available. Some older linux kernels do not support an in-kernel resource
// manager.
DeviceModeTryResourceManaged
)

var (
// ErrNoTPM2Device indicates that no TPM2 device is available.
ErrNoTPM2Device = errors.New("no TPM2 device is available")

// ErrNoResourceManagedTPM2Device indicates that there is no resource
// managed TPM2 device option available.
ErrNoResourceManagedTPM2Device = errors.New("no resource managed TPM2 device available")

// ErrNoPPI indicates that no physical presence interface is available.
ErrNoPPI = errors.New("no physical presence interface available")
)

type tpmDevice struct {
tpm2.TPMDevice

mode DeviceMode

ppi ppi.PPI
ppiErr error
}

func (d *tpmDevice) Mode() DeviceMode {
return d.mode
}

func (d *tpmDevice) PPI() (ppi.PPI, error) {
if d.ppiErr != nil {
return nil, d.ppiErr
}
if d.ppi == nil {
return nil, ErrNoPPI
}
return d.ppi, nil
}

// TPMDevice corresponds to a [tpm2.TPMDevice] with some extra features.
type TPMDevice interface {
tpm2.TPMDevice
Mode() DeviceMode // either DeviceModeDirect or DeviceModeResourceManaged
PPI() (ppi.PPI, error) // provide access to the physical presence interface
}

// DefaultDevice returns the default TPM device. The specified mode controls what kind
// of device to return, if available.
var DefaultDevice = func(DeviceMode) (TPMDevice, error) {
return nil, ErrNoTPM2Device
}
92 changes: 92 additions & 0 deletions internal/tpm2_device/device_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2024 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package tpm2_device

import (
"errors"

"github.com/canonical/go-tpm2/linux"
)

var (
linuxDefaultTPM2Device = linux.DefaultTPM2Device
linuxRawDeviceResourceManagedDevice = (*linux.RawDevice).ResourceManagedDevice
linuxRawDevicePhysicalPresenceInterface = (*linux.RawDevice).PhysicalPresenceInterface
linuxRMDeviceRawDevice = (*linux.RMDevice).RawDevice
)

func newTpmDeviceDirect(dev *linux.RawDevice) TPMDevice {
ppi, err := linuxRawDevicePhysicalPresenceInterface(dev)
if errors.Is(err, linux.ErrNoPhysicalPresenceInterface) {
err = nil
}
return &tpmDevice{
TPMDevice: dev,
mode: DeviceModeDirect,
ppi: ppi,
ppiErr: err,
}
}

func newTpmDeviceRM(dev *linux.RMDevice) TPMDevice {
ppi, err := linuxRawDevicePhysicalPresenceInterface(linuxRMDeviceRawDevice(dev))
if errors.Is(err, linux.ErrNoPhysicalPresenceInterface) {
err = nil
}
return &tpmDevice{
TPMDevice: dev,
mode: DeviceModeResourceManaged,
ppi: ppi,
ppiErr: err,
}
}

func init() {
DefaultDevice = func(mode DeviceMode) (TPMDevice, error) {
rawDev, err := linuxDefaultTPM2Device()
switch {
case errors.Is(err, linux.ErrDefaultNotTPM2Device) || errors.Is(err, linux.ErrNoTPMDevices):
// Either there are no TPM devices or the default device is a TPM1.2 device
return nil, ErrNoTPM2Device
case err != nil:
return nil, err
}

if mode == DeviceModeDirect {
// Return the direct device
return newTpmDeviceDirect(rawDev), nil
}

rmDev, err := linuxRawDeviceResourceManagedDevice(rawDev)
switch {
case errors.Is(err, linux.ErrNoResourceManagedDevice) && mode == DeviceModeTryResourceManaged:
// No in-kernel resource manager, but the mode allows us to return the direct device
return newTpmDeviceDirect(rawDev), nil
case errors.Is(err, linux.ErrNoResourceManagedDevice):
// No in-kernel resource manager, return an error
return nil, ErrNoResourceManagedTPM2Device
case err != nil:
return nil, err
}

// Return the resource managed device
return newTpmDeviceRM(rmDev), nil
}
}
Loading
Loading