Skip to content

Commit

Permalink
Merge pull request #357 from chrisccoulson/tpm2-use-tpm-device
Browse files Browse the repository at this point in the history
tpm2: Use tpm2.TPMDevice abstraction.

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 #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 authored Jan 8, 2025
2 parents 082c84b + 5ce4b20 commit 31629d7
Show file tree
Hide file tree
Showing 19 changed files with 991 additions and 276 deletions.
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

0 comments on commit 31629d7

Please sign in to comment.