This library wraps the configfs/tsm Linux subsystem for Trusted Security Module operations.
This library wraps the configfs/tsm/report subsystem for safely generating attestation reports.
The TSM report
subsystem provides a vendor-agnostic interface for collecting a
signed document for the Trusted Execution Environment's (TEE) state for remote
verification. For simplicity, we call this document an "attestation report",
though other sources may sometimes refer to it as a "quote".
Signing keys are expected to be rooted back to the manufacturer. Certificates
may be present in the auxblob
attribute or as part of the report in outblob
.
The core functionality of attestation report interaction is nonce in, report out. For testability, we abstract the file operations that are needed for creating configfs report entries, reading and writing attributes, and final reclaiming of resources.
func Get(client configfsi.Client, req *report.Request) (*report.Response, error)
Where
type Request struct {
InBlob []byte
Privilege *Privilege
GetAuxBlob bool
}
type Response struct {
Provider string
OutBlob []byte
AuxBlob []byte
}
type Privilege struct {
Level int
}
The provider may not implement an AuxBlob
delivery mechanism, so if
GetAuxBlob
is true, then AuxBlob
still must be checked for length 0.
Since this is a file-based system, there's always a chance that an operation may fail with a permission error. By default, the TSM system requires root access.
The host may also add rate limiting to requests, such that an outblob read fails
with EBUSY
. The kernel may or may not try again on behalf of the user.
Finally, due to the fact that the TSM report system only requests an attestation
report when reading outblob
or auxblob
, there is a chance the input
attributes may have been changed to unexpected values from an interfering
process. This interference is a bug in user space that the kernel does not block
for simplicity. Interference is evident through the generation
attribute. When
generation
does not match the expectations that the report
package tracks,
report.Get
returns a *report.GenerationErr
or an error that wraps
*report.GenerationErr
.
Use func GetGenerationErr(error) *GenerationErr
to extract a *GenerationErr
from an error if it is or contains a *GenerationErr
. If present, the caller
should try to identify the source of interference and remove it. Meanwhile, the
caller may try again.
Most users will only want to use the client from linuxtsm.MakeClient
.
A client on real hardware is just the filesystem, since the configfs interactions will interact with the hardware. In unit tests though, we can emulate the behavior that has been proposed in v7 of the patch series
type Client interface {
MkdirTemp(dir, pattern string) (string, error)
ReadFile(name string) ([]byte, error)
WriteFile(name string, contents []byte) error
RemoveAll(path string) error
}
The RemoveAll
function is the only oddly named method, since the real
interface would just rmdir
the report directory
(os.Remove
in Golang), even when there are
apparent files underneath. Non-empty directory removal is generally not allowed,
so the RemoveAll
name is clearer with what it does.
The linuxtsm
package defines an implementation of configfsi.Client
with
func MakeClient() (configfsi.Client, error)
For further convenience, linuxtsm
provides an alias for MakeClient
combined with report.Get
as
func GetReport(req *report.Request) (*report.Response, error)
The usage is the same as for report.Get
.
The faketsm.Client
implementation allows tests to provide custom behavior for subsystems by name:
type Client struct {
Subsystems map[string]configfsi.Client
}
The faketsm.ReportSubsystem
type implements a client that emulates the
concurrent behavior and generation
attribute semantics. To test negative
behavior as well, the subsystem allows the user to override Mkdir
, ReadFile
,
existing entries' values, and the error behavior of WriteFile
.
This is not an officially supported Google product.