Skip to content

Commit

Permalink
tpm2: Use tpm2.TPMDevice abstraction
Browse files Browse the repository at this point in the history
Secboot currently opens a transport to the Linux character device at a
hardcoded path (/dev/tpm0) and passes the opened transport to the
deprecated tpm2.NewTPMContext API. The replacement API
(tpm2.OpenTPMDevice) makes use of a new abstraction (tpm2.TPMDevice).

The linux sub-package provides a way of enumerating TPM devices or
providing access to the default one. It also provides access to the
resource managed device, which we might want to use in secboot in the
future, and it provides access to the physical presence interface for
performing actions that are implemented in platform firmware.

Implementing this now is required to write test cases for
canonical#353, as the current test
harness will permit the code under test to open as many TPM connections
as it likes, when in reality, it can only have a single open connection
if it is using the direct character device (/dev/tpm0).
  • Loading branch information
chrisccoulson committed Jan 7, 2025
1 parent 082c84b commit 0687f8d
Show file tree
Hide file tree
Showing 17 changed files with 979 additions and 253 deletions.
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.8.1-0.20250106220815-bd8d7cc9ba17
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.8.1-0.20250106220815-bd8d7cc9ba17 h1:bzX4uvym3OoyKAQd2fTUOP4tAuHbQktwsezk6KTXAgc=
github.com/canonical/go-tpm2 v1.8.1-0.20250106220815-bd8d7cc9ba17/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 sharedi, 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

0 comments on commit 0687f8d

Please sign in to comment.