./ 0000775 0000000 0000000 00000000000 00000000000 007344 5 ustar 00 0000000 0000000 ./github.com/ 0000775 0000000 0000000 00000000000 00000000000 011403 5 ustar 00 0000000 0000000 ./github.com/canonical/ 0000775 0000000 0000000 00000000000 00000000000 013332 5 ustar 00 0000000 0000000 ./github.com/canonical/go-efilib/ 0000775 0000000 0000000 00000000000 00000000000 015167 5 ustar 00 0000000 0000000 ./github.com/canonical/go-efilib/.gitignore 0000664 0000000 0000000 00000000042 00000000000 017153 0 ustar 00 0000000 0000000 cmd/efi_devicepath/efi_devicepath
./github.com/canonical/go-efilib/LICENSE 0000664 0000000 0000000 00000021501 00000000000 016173 0 ustar 00 0000000 0000000 All files in this repository are licensed as follows. If you contribute
to this repository, it is assumed that you license your contribution
under the same license unless you state otherwise.
All files Copyright (C) 2020 Canonical Ltd. unless otherwise specified in the file.
This software is licensed under the LGPLv3, included below.
As a special exception to the GNU Lesser General Public License version 3
("LGPL3"), the copyright holders of this Library give you permission to
convey to a third party a Combined Work that links statically or dynamically
to this Library without providing any Minimal Corresponding Source or
Minimal Application Code as set out in 4d or providing the installation
information set out in section 4e, provided that you comply with the other
provisions of LGPL3 and provided that you meet, for the Application the
terms and conditions of the license(s) which apply to the Application.
Except as stated in this special exception, the provisions of LGPL3 will
continue to comply in full to this Library. If you modify this Library, you
may apply this exception to your version of this Library, but you are not
obliged to do so. If you do not wish to do so, delete this exception
statement from your version. This exception does not (and cannot) modify any
license terms which apply to the Application, with which you must still
comply.
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
./github.com/canonical/go-efilib/authvars.go 0000664 0000000 0000000 00000024256 00000000000 017364 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"bytes"
"encoding/binary"
"errors"
"io"
"time"
"github.com/canonical/go-efilib/internal/ioerr"
"github.com/canonical/go-efilib/internal/uefi"
)
// VariableAuthentication correspond to the EFI_VARIABLE_AUTHENTICATION type and is provided as a header when updating a variable with
// the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute set.
type VariableAuthentication struct {
MonotonicCount uint64
AuthInfo WinCertificateGUID
}
func (a *VariableAuthentication) Write(w io.Writer) error {
desc := uefi.EFI_VARIABLE_AUTHENTICATION{
MonotonicCount: a.MonotonicCount,
AuthInfo: *a.AuthInfo.toUefiType()}
return binary.Write(w, binary.LittleEndian, desc)
}
// ReadVariableAuthentication decodes a header for updating a variable with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute
// set.
func ReadVariableAuthentication(r io.Reader) (*VariableAuthentication, error) {
desc, err := uefi.Read_EFI_VARIABLE_AUTHENTICATION(r)
if err != nil {
return nil, err
}
return &VariableAuthentication{
MonotonicCount: desc.MonotonicCount,
AuthInfo: *newWinCertificateGUID(&desc.AuthInfo)}, nil
}
// VariableAuthentication2 correspond to the EFI_VARIABLE_AUTHENTICATION_2 type and is provided as a header when updating a variable
// with the EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute set.
type VariableAuthentication2 struct {
TimeStamp time.Time
AuthInfo WinCertificateGUID
}
func (a *VariableAuthentication2) Write(w io.Writer) error {
desc := uefi.EFI_VARIABLE_AUTHENTICATION_2{
TimeStamp: *uefi.New_EFI_TIME(a.TimeStamp),
AuthInfo: *a.AuthInfo.toUefiType()}
return binary.Write(w, binary.LittleEndian, desc)
}
// ReadTimeBasedVariableAuthentication decodes the header for updating a variable with the
// EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute set.
func ReadTimeBasedVariableAuthentication(r io.Reader) (*VariableAuthentication2, error) {
desc, err := uefi.Read_EFI_VARIABLE_AUTHENTICATION_2(r)
if err != nil {
return nil, err
}
return &VariableAuthentication2{
TimeStamp: desc.TimeStamp.GoTime(),
AuthInfo: *newWinCertificateGUID(&desc.AuthInfo)}, nil
}
// VariableAuthentication3 represents the header for updating a variable with the EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS
// attribute set.
type VariableAuthentication3 interface{}
// VariableAuthentication3Timestamp corresponds to the header for updating a variable with the
// EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS attribute set, and a type of EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE.
type VariableAuthentication3Timestamp struct {
TimeStamp time.Time
NewCert *WinCertificateGUID
SigningCert WinCertificateGUID
}
func (a *VariableAuthentication3Timestamp) Write(w io.Writer) error {
var buf bytes.Buffer
t := uefi.New_EFI_TIME(a.TimeStamp)
if err := binary.Write(&buf, binary.LittleEndian, &t); err != nil {
panic(err)
}
if a.NewCert != nil {
if err := binary.Write(&buf, binary.LittleEndian, a.NewCert.toUefiType()); err != nil {
panic(err)
}
}
if err := binary.Write(&buf, binary.LittleEndian, a.SigningCert.toUefiType()); err != nil {
panic(err)
}
hdr := uefi.EFI_VARIABLE_AUTHENTICATION_3{
Version: 1,
Type: uefi.EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE}
hdr.MetadataSize = uint32(binary.Size(hdr) + buf.Len())
if a.NewCert != nil {
hdr.Flags = 1
}
if err := binary.Write(w, binary.LittleEndian, &hdr); err != nil {
return err
}
_, err := buf.WriteTo(w)
return err
}
// VariableAuthentication3Nonce corresponds to the header for updating a variable with the
// EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS attribute set, and a type of EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE.
type VariableAuthentication3Nonce struct {
Nonce []byte
NewCert *WinCertificateGUID
SigningCert WinCertificateGUID
}
func (a *VariableAuthentication3Nonce) Write(w io.Writer) error {
var buf bytes.Buffer
n := uefi.EFI_VARIABLE_AUTHENTICATION_3_NONCE{
NonceSize: uint32(len(a.Nonce)),
Nonce: a.Nonce}
if err := binary.Write(&buf, binary.LittleEndian, &n); err != nil {
panic(err)
}
if a.NewCert != nil {
if err := binary.Write(&buf, binary.LittleEndian, a.NewCert.toUefiType()); err != nil {
panic(err)
}
}
if err := binary.Write(&buf, binary.LittleEndian, a.SigningCert.toUefiType()); err != nil {
panic(err)
}
hdr := uefi.EFI_VARIABLE_AUTHENTICATION_3{
Version: 1,
Type: uefi.EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE}
hdr.MetadataSize = uint32(binary.Size(hdr) + buf.Len())
if a.NewCert != nil {
hdr.Flags = 1
}
if err := binary.Write(w, binary.LittleEndian, &hdr); err != nil {
return err
}
_, err := buf.WriteTo(w)
return err
}
// ReadEnhancedVariableAuthentication decodes the header for updating a variable with the
// EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS attribute set.
func ReadEnhancedVariableAuthentication(r io.Reader) (VariableAuthentication3, error) {
var hdr uefi.EFI_VARIABLE_AUTHENTICATION_3
if err := binary.Read(r, binary.LittleEndian, &hdr); err != nil {
return nil, err
}
if hdr.Version != 1 {
return nil, errors.New("unexpected version")
}
lr := io.LimitReader(r, int64(hdr.MetadataSize)-int64(binary.Size(hdr)))
switch hdr.Type {
case uefi.EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE:
var t uefi.EFI_TIME
if err := binary.Read(lr, binary.LittleEndian, &t); err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read timestamp authentication: %w", err)
}
var newCert *uefi.WIN_CERTIFICATE_UEFI_GUID
if hdr.Flags&1 > 0 {
cert, err := uefi.Read_WIN_CERTIFICATE_UEFI_GUID(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read timestamp authentication: %w", err)
}
newCert = cert
}
signingCert, err := uefi.Read_WIN_CERTIFICATE_UEFI_GUID(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read timestamp authentication: %w", err)
}
out := &VariableAuthentication3Timestamp{
TimeStamp: t.GoTime(),
SigningCert: *newWinCertificateGUID(signingCert)}
if newCert != nil {
out.NewCert = newWinCertificateGUID(newCert)
}
return out, nil
case uefi.EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE:
n, err := uefi.Read_EFI_VARIABLE_AUTHENTICATION_3_NONCE(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read nonce authentication: %w", err)
}
var newCert *uefi.WIN_CERTIFICATE_UEFI_GUID
if hdr.Flags&1 > 0 {
cert, err := uefi.Read_WIN_CERTIFICATE_UEFI_GUID(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read nonce authentication: %w", err)
}
newCert = cert
}
signingCert, err := uefi.Read_WIN_CERTIFICATE_UEFI_GUID(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read nonce authentication: %w", err)
}
out := &VariableAuthentication3Nonce{
Nonce: n.Nonce,
SigningCert: *newWinCertificateGUID(signingCert)}
if newCert != nil {
out.NewCert = newWinCertificateGUID(newCert)
}
return out, nil
default:
return nil, errors.New("unexpected type")
}
}
// VariableAuthentication3Descriptor corresponds to the authentication descriptor provided when reading the payload of a variable
// with the EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS attribute set.
type VariableAuthentication3Descriptor interface{}
const (
VariableAuthentication3CertIDSHA256 = uefi.EFI_VARIABLE_AUTHENTICATION_3_CERT_ID_SHA256
)
type VariableAuthentication3CertId struct {
Type uint8
Id []byte
}
func newVariableAuthentication3CertId(id *uefi.EFI_VARIABLE_AUTHENTICATION_3_CERT_ID) *VariableAuthentication3CertId {
return &VariableAuthentication3CertId{
Type: id.Type,
Id: id.Id}
}
// VariableAuthentication3TimestampDescriptor corresponds to the authentication descriptor provided when reading the payload of a
// variable with the EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS attribute set, and a type of
// EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE.
type VariableAuthentication3TimestampDescriptor struct {
TimeStamp time.Time
VariableAuthentication3CertId
}
// VariableAuthentication3NonceDescriptor corresponds to the authentication descriptor provided when reading the payload of a
// variable with the EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS attribute set, and a type of
// EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE.
type VariableAuthentication3NonceDescriptor struct {
Nonce []byte
VariableAuthentication3CertId
}
// ReadEnhancedAuthenticationDescriptor decodes the enhanced authentication descriptor from the supplied io.Reader. The supplied
// reader will typically read from the payload area of a variable with the EFI_VARIABLE_ENHANCED_AUTHENTICATION_ACCESS attribute
// set.
func ReadEnhancedAuthenticationDescriptor(r io.Reader) (VariableAuthentication3Descriptor, error) {
var hdr uefi.EFI_VARIABLE_AUTHENTICATION_3
if err := binary.Read(r, binary.LittleEndian, &hdr); err != nil {
return nil, err
}
if hdr.Version != 1 {
return nil, errors.New("unexpected version")
}
lr := io.LimitReader(r, int64(hdr.MetadataSize)-int64(binary.Size(hdr)))
switch hdr.Type {
case uefi.EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE:
var t uefi.EFI_TIME
if err := binary.Read(lr, binary.LittleEndian, &t); err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read timestamp descriptor: %w", err)
}
id, err := uefi.Read_EFI_VARIABLE_AUTHENTICATION_3_CERT_ID(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read timestamp descriptor: %w", err)
}
return &VariableAuthentication3TimestampDescriptor{
TimeStamp: t.GoTime(),
VariableAuthentication3CertId: *newVariableAuthentication3CertId(id)}, nil
case uefi.EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE:
n, err := uefi.Read_EFI_VARIABLE_AUTHENTICATION_3_NONCE(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read nonce descriptor: %w", err)
}
id, err := uefi.Read_EFI_VARIABLE_AUTHENTICATION_3_CERT_ID(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read nonce descriptor: %w", err)
}
return &VariableAuthentication3NonceDescriptor{
Nonce: n.Nonce,
VariableAuthentication3CertId: *newVariableAuthentication3CertId(id)}, nil
default:
return nil, errors.New("unexpected type")
}
}
./github.com/canonical/go-efilib/db.go 0000664 0000000 0000000 00000010770 00000000000 016110 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"bytes"
"crypto"
"crypto/x509"
"encoding/binary"
"fmt"
"io"
"github.com/canonical/go-efilib/internal/uefi"
"golang.org/x/xerrors"
)
// SignatureData corresponds to the EFI_SIGNATURE_DATA type.
type SignatureData struct {
Owner GUID
Data []byte
}
func (d *SignatureData) toUefiType() *uefi.EFI_SIGNATURE_DATA {
return &uefi.EFI_SIGNATURE_DATA{
SignatureOwner: uefi.EFI_GUID(d.Owner),
SignatureData: d.Data}
}
// Write serializes this signature data to w.
func (d *SignatureData) Write(w io.Writer) error {
return d.toUefiType().Write(w)
}
// Equal determines whether other is equal to this SignatureData
func (d *SignatureData) Equal(other *SignatureData) bool {
if d.Owner != other.Owner {
return false
}
return bytes.Equal(d.Data, other.Data)
}
// SignatureList corresponds to the EFI_SIGNATURE_LIST type.
type SignatureList struct {
Type GUID
Header []byte
Signatures []*SignatureData
}
func (l *SignatureList) toUefiType() (out *uefi.EFI_SIGNATURE_LIST, err error) {
out = &uefi.EFI_SIGNATURE_LIST{
SignatureType: uefi.EFI_GUID(l.Type),
SignatureHeaderSize: uint32(len(l.Header)),
SignatureHeader: l.Header}
for i, s := range l.Signatures {
sig := s.toUefiType()
sz := uint32(binary.Size(sig.SignatureOwner) + len(sig.SignatureData))
if i == 0 {
out.SignatureSize = sz
}
if sz != out.SignatureSize {
// EFI_SIGNATURE_LIST cannot contain EFI_SIGNATURE_DATA entries with different
// sizes - they must go in their own list.
return nil, fmt.Errorf("signature %d contains the wrong size", i)
}
out.Signatures = append(out.Signatures, *sig)
}
out.SignatureListSize = uefi.ESLHeaderSize + out.SignatureHeaderSize + (out.SignatureSize * uint32(len(out.Signatures)))
return out, nil
}
func (l *SignatureList) String() string {
var b bytes.Buffer
fmt.Fprintf(&b, "EFI_SIGNATURE_LIST{ SignatureType: %v, SignatureHeader: %x, Signatures: [", l.Type, l.Header)
for _, d := range l.Signatures {
fmt.Fprintf(&b, "\n\tEFI_SIGNATURE_DATA{ SignatureOwner: %v, Details: {", d.Owner)
switch l.Type {
case CertSHA1Guid, CertSHA256Guid, CertSHA224Guid, CertSHA384Guid, CertSHA512Guid:
fmt.Fprintf(&b, "\n\t\tHash: %x", d.Data)
case CertX509Guid:
cert, err := x509.ParseCertificate(d.Data)
if err != nil {
fmt.Fprintf(&b, "%v", err)
}
h := crypto.SHA256.New()
h.Write(cert.RawTBSCertificate)
fmt.Fprintf(&b, "\n\t\tSubject: %v\n\t\tIssuer: %v\n\t\tSHA256 fingerprint: %x", cert.Subject, cert.Issuer, h.Sum(nil))
default:
fmt.Fprintf(&b, "")
}
fmt.Fprintf(&b, "}}")
}
fmt.Fprintf(&b, "]")
return b.String()
}
// Write serializes this signature list to w.
func (l *SignatureList) Write(w io.Writer) error {
list, err := l.toUefiType()
if err != nil {
return err
}
return list.Write(w)
}
// ReadSignatureList decodes a single EFI_SIGNATURE_LIST from r.
func ReadSignatureList(r io.Reader) (*SignatureList, error) {
l, err := uefi.Read_EFI_SIGNATURE_LIST(r)
if err != nil {
return nil, err
}
list := &SignatureList{Type: GUID(l.SignatureType), Header: l.SignatureHeader}
for _, s := range l.Signatures {
list.Signatures = append(list.Signatures, &SignatureData{Owner: GUID(s.SignatureOwner), Data: s.SignatureData})
}
return list, nil
}
// SignatureDatabase corresponds to a list of EFI_SIGNATURE_LIST structures.
type SignatureDatabase []*SignatureList
func (db SignatureDatabase) String() string {
var s string
for _, l := range db {
s = s + "\n" + l.String() + "\n"
}
return s
}
// Bytes returns the serialized form of this signature database.
func (db SignatureDatabase) Bytes() ([]byte, error) {
w := new(bytes.Buffer)
if err := db.Write(w); err != nil {
return nil, err
}
return w.Bytes(), nil
}
// Write serializes this signature database to w.
func (db SignatureDatabase) Write(w io.Writer) error {
for i, l := range db {
if err := l.Write(w); err != nil {
return xerrors.Errorf("cannot encode signature list %d: %w", i, err)
}
}
return nil
}
// ReadSignatureDatabase decodes a list of EFI_SIGNATURE_LIST structures from r.
func ReadSignatureDatabase(r io.Reader) (SignatureDatabase, error) {
var db SignatureDatabase
for i := 0; ; i++ {
l, err := ReadSignatureList(r)
if err != nil {
if err == io.EOF {
break
}
return nil, xerrors.Errorf("cannot read EFI_SIGNATURE_LIST %d: %w", i, err)
}
db = append(db, l)
}
return db, nil
}
./github.com/canonical/go-efilib/devicepath.go 0000664 0000000 0000000 00000110546 00000000000 017641 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/canonical/go-efilib/internal/ioerr"
"github.com/canonical/go-efilib/internal/uefi"
"github.com/canonical/go-efilib/mbr"
"golang.org/x/xerrors"
)
// DevicePathType is the type of a device path node.
type DevicePathType uint8
func (t DevicePathType) String() string {
switch t {
case HardwareDevicePath:
return "HardwarePath"
case ACPIDevicePath:
return "AcpiPath"
case MessagingDevicePath:
return "Msg"
case MediaDevicePath:
return "MediaPath"
case BBSDevicePath:
return "BbsPath"
default:
return fmt.Sprintf("Path[%02x]", uint8(t))
}
}
const (
HardwareDevicePath DevicePathType = uefi.HARDWARE_DEVICE_PATH
ACPIDevicePath DevicePathType = uefi.ACPI_DEVICE_PATH
MessagingDevicePath DevicePathType = uefi.MESSAGING_DEVICE_PATH
MediaDevicePath DevicePathType = uefi.MEDIA_DEVICE_PATH
BBSDevicePath DevicePathType = uefi.BBS_DEVICE_PATH
)
// DevicePathSubType is the sub-type of a device path node.
type DevicePathSubType uint8
// DevicePathToStringFlags defines flags for DevicePath.ToString and
// DevicePathNode.ToString.
type DevicePathToStringFlags int
func (f DevicePathToStringFlags) DisplayOnly() bool {
return f&DevicePathDisplayOnly > 0
}
const (
// DevicePathDisplayOnly indicates that each node is converted
// to the shorter text representation.
DevicePathDisplayOnly DevicePathToStringFlags = 1 << 0
)
// DevicePathNode represents a single node in a device path.
type DevicePathNode interface {
fmt.Stringer
ToString(flags DevicePathToStringFlags) string
Write(w io.Writer) error
}
// DevicePath represents a complete device path with the first node
// representing the root.
type DevicePath []DevicePathNode
// ToString returns a string representation of this device path with the
// supplied flags.
func (p DevicePath) ToString(flags DevicePathToStringFlags) string {
s := new(bytes.Buffer)
for _, node := range p {
fmt.Fprintf(s, "\\%s", node.ToString(flags))
}
return s.String()
}
func (p DevicePath) String() string {
return p.ToString(DevicePathDisplayOnly)
}
// Bytes returns the serialized form of this device path.
func (p DevicePath) Bytes() ([]byte, error) {
w := new(bytes.Buffer)
if err := p.Write(w); err != nil {
return nil, err
}
return w.Bytes(), nil
}
// Write serializes the complete device path to w.
func (p DevicePath) Write(w io.Writer) error {
for i, node := range p {
if err := node.Write(w); err != nil {
return xerrors.Errorf("cannot write node %d: %w", i, err)
}
}
end := uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uefi.END_DEVICE_PATH_TYPE,
SubType: uefi.END_ENTIRE_DEVICE_PATH_SUBTYPE,
Length: 4}
return binary.Write(w, binary.LittleEndian, &end)
}
// GenericDevicePathNode corresponds to a device path nodes with an unhandled type.
type GenericDevicePathNode struct {
Type DevicePathType
SubType DevicePathSubType
Data []byte
}
func (d *GenericDevicePathNode) ToString(_ DevicePathToStringFlags) string {
var builder bytes.Buffer
switch d.Type {
case HardwareDevicePath, ACPIDevicePath, MessagingDevicePath,
MediaDevicePath, BBSDevicePath:
fmt.Fprintf(&builder, "%s(", d.Type)
default:
fmt.Fprintf(&builder, "Path(%d,", d.Type)
}
fmt.Fprintf(&builder, "%d", d.SubType)
if len(d.Data) > 0 {
fmt.Fprintf(&builder, ",%x", d.Data)
}
fmt.Fprintf(&builder, ")")
return builder.String()
}
func (d *GenericDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *GenericDevicePathNode) Write(w io.Writer) error {
data := uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(d.Type),
SubType: uint8(d.SubType)}
if len(d.Data) > math.MaxUint16-binary.Size(data) {
return errors.New("Data too large")
}
data.Length = uint16(binary.Size(data) + len(d.Data))
if err := binary.Write(w, binary.LittleEndian, &data); err != nil {
return err
}
_, err := w.Write(d.Data)
return err
}
// PCIDevicePathNode corresponds to a PCI device path node.
type PCIDevicePathNode struct {
Function uint8
Device uint8
}
func (d *PCIDevicePathNode) ToString(_ DevicePathToStringFlags) string {
return fmt.Sprintf("Pci(0x%x,0x%x)", d.Device, d.Function)
}
func (d *PCIDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *PCIDevicePathNode) Write(w io.Writer) error {
data := uefi.PCI_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.HARDWARE_DEVICE_PATH),
SubType: uint8(uefi.HW_PCI_DP)},
Function: d.Function,
Device: d.Device}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
type VendorDevicePathNode struct {
Type DevicePathType
GUID GUID
Data []byte
}
func (d *VendorDevicePathNode) ToString(_ DevicePathToStringFlags) string {
var t string
switch d.Type {
case HardwareDevicePath:
t = "Hw"
case MessagingDevicePath:
t = "Msg"
case MediaDevicePath:
t = "Media"
default:
t = "?"
}
var s bytes.Buffer
fmt.Fprintf(&s, "Ven%s(%s", t, d.GUID)
if len(d.Data) > 0 {
fmt.Fprintf(&s, ",%x", d.Data)
}
fmt.Fprintf(&s, ")")
return s.String()
}
func (d *VendorDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *VendorDevicePathNode) Write(w io.Writer) error {
var subType uint8
switch d.Type {
case HardwareDevicePath:
subType = uefi.HW_VENDOR_DP
case MessagingDevicePath:
subType = uefi.MSG_VENDOR_DP
case MediaDevicePath:
subType = uefi.MEDIA_VENDOR_DP
default:
return errors.New("invalid device path type")
}
data := uefi.VENDOR_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(d.Type),
SubType: subType},
Guid: uefi.EFI_GUID(d.GUID)}
if len(d.Data) > math.MaxUint16-binary.Size(data) {
return errors.New("Data too large")
}
data.Header.Length = uint16(binary.Size(data) + len(d.Data))
if err := binary.Write(w, binary.LittleEndian, &data); err != nil {
return err
}
_, err := w.Write(d.Data)
return err
}
func readVendorDevicePathNode(r io.Reader) (out *VendorDevicePathNode, err error) {
var n uefi.VENDOR_DEVICE_PATH
if err := binary.Read(r, binary.LittleEndian, &n); err != nil {
return nil, err
}
out = &VendorDevicePathNode{
Type: DevicePathType(n.Header.Type),
GUID: GUID(n.Guid)}
data, _ := ioutil.ReadAll(r)
out.Data = data
return out, nil
}
// EISAID represents a compressed EISA PNP ID
type EISAID uint32
// Vendor returns the 3-letter vendor ID.
func (id EISAID) Vendor() string {
return fmt.Sprintf("%c%c%c",
((id>>10)&0x1f)+'A'-1,
((id>>5)&0x1f)+'A'-1,
(id&0x1f)+'A'-1)
}
// Product returns the product ID.
func (id EISAID) Product() uint16 {
return uint16(id >> 16)
}
func (id EISAID) String() string {
if id == 0 {
return "0"
}
return fmt.Sprintf("%s%04x", id.Vendor(), id.Product())
}
func NewEISAID(vendor string, product uint16) (EISAID, error) {
if len(vendor) != 3 {
return 0, errors.New("invalid vendor length")
}
var out EISAID
out |= EISAID((vendor[0]-'A'+1)&0x1f) << 10
out |= EISAID((vendor[1]-'A'+1)&0x1f) << 5
out |= EISAID((vendor[2] - 'A' + 1) & 0x1f)
out |= EISAID(product) << 16
return out, nil
}
// ACPIDevicePathNode corresponds to an ACPI device path node.
type ACPIDevicePathNode struct {
HID EISAID
UID uint32
}
func (d *ACPIDevicePathNode) ToString(_ DevicePathToStringFlags) string {
if d.HID.Vendor() == "PNP" {
switch d.HID.Product() {
case 0x0a03:
return fmt.Sprintf("PciRoot(0x%x)", d.UID)
case 0x0a08:
return fmt.Sprintf("PcieRoot(0x%x)", d.UID)
case 0x0604:
return fmt.Sprintf("Floppy(0x%x)", d.UID)
case 0x0301:
return fmt.Sprintf("Keyboard(0x%x)", d.UID)
case 0x0501:
return fmt.Sprintf("Serial(0x%x)", d.UID)
case 0x0401:
return fmt.Sprintf("ParallelPort(0x%x)", d.UID)
}
}
return fmt.Sprintf("Acpi(%s,0x%x)", d.HID, d.UID)
}
func (d *ACPIDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *ACPIDevicePathNode) Write(w io.Writer) error {
data := uefi.ACPI_HID_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.ACPI_DEVICE_PATH),
SubType: uint8(uefi.ACPI_DP)},
HID: uint32(d.HID),
UID: uint32(d.UID)}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
type ACPIExtendedDevicePathNode struct {
HID EISAID
UID uint32
CID EISAID
HIDStr string
UIDStr string
CIDStr string
}
func (d *ACPIExtendedDevicePathNode) ToString(flags DevicePathToStringFlags) string {
switch {
case d.HIDStr == "" && d.CIDStr == "" && d.UIDStr != "":
return fmt.Sprintf("AcpiExp(%s,%s,%s)", d.HID, d.CID, d.UIDStr)
case flags.DisplayOnly() && d.HID.Vendor() == "PNP" && (d.HID.Product() == 0x0a03 || (d.CID.Product() == 0x0a03 && d.HID.Product() != 0x0a08)):
if d.UIDStr != "" {
return fmt.Sprintf("PciRoot(%s)", d.UIDStr)
}
return fmt.Sprintf("PciRoot(0x%x)", d.UID)
case flags.DisplayOnly() && d.HID.Vendor() == "PNP" && (d.HID.Product() == 0x0a08 || d.CID.Product() == 0x0a08):
if d.UIDStr != "" {
return fmt.Sprintf("PcieRoot(%s)", d.UIDStr)
}
return fmt.Sprintf("PcieRoot(0x%x)", d.UID)
}
if !flags.DisplayOnly() {
hidStr := d.HIDStr
if hidStr == "" {
hidStr = ""
}
cidStr := d.CIDStr
if cidStr == "" {
cidStr = ""
}
uidStr := d.UIDStr
if uidStr == "" {
uidStr = ""
}
return fmt.Sprintf("AcpiEx(%s,%s,0x%x,%s,%s,%s)", d.HID, d.CID, d.UID, hidStr, cidStr, uidStr)
}
hidText := d.HID.String()
if d.HIDStr != "" {
hidText = d.HIDStr
}
cidText := d.CID.String()
if d.CIDStr != "" {
cidText = d.CIDStr
}
if d.UIDStr != "" {
return fmt.Sprintf("AcpiEx(%s,%s,%s)", hidText, cidText, d.UIDStr)
}
return fmt.Sprintf("AcpiEx(%s,%s,0x%x)", hidText, cidText, d.UID)
}
func (d *ACPIExtendedDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *ACPIExtendedDevicePathNode) Write(w io.Writer) error {
data := uefi.ACPI_EXTENDED_HID_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.ACPI_DEVICE_PATH),
SubType: uint8(uefi.ACPI_EXTENDED_DP)},
HID: uint32(d.HID),
UID: d.UID,
CID: uint32(d.CID)}
// Set a reasonable limit on each string field
for _, s := range []string{d.HIDStr, d.UIDStr, d.CIDStr} {
if len(s) > math.MaxUint16-(binary.Size(data)+3) {
return errors.New("string field too large")
}
}
// This can't overflow int
length := binary.Size(data) + len(d.HIDStr) + len(d.UIDStr) + len(d.CIDStr) + 3
if length > math.MaxUint16 {
return errors.New("too large")
}
data.Header.Length = uint16(length)
if err := binary.Write(w, binary.LittleEndian, &data); err != nil {
return err
}
for _, s := range []string{d.HIDStr, d.UIDStr, d.CIDStr} {
if _, err := io.WriteString(w, s); err != nil {
return err
}
w.Write([]byte{0x00})
}
return nil
}
type ATAPIControllerRole uint8
func (r ATAPIControllerRole) String() string {
switch r {
case ATAPIControllerPrimary:
return "Primary"
case ATAPIControllerSecondary:
return "Secondary"
default:
return strconv.FormatUint(uint64(r), 10)
}
}
const (
ATAPIControllerPrimary ATAPIControllerRole = 0
ATAPIControllerSecondary ATAPIControllerRole = 1
)
type ATAPIDriveRole uint8
func (r ATAPIDriveRole) String() string {
switch r {
case ATAPIDriveMaster:
return "Master"
case ATAPIDriveSlave:
return "Slave"
default:
return strconv.FormatUint(uint64(r), 10)
}
}
const (
ATAPIDriveMaster ATAPIDriveRole = 0
ATAPIDriveSlave ATAPIDriveRole = 1
)
// ATAPIDevicePathNode corresponds to an ATA device path node.
type ATAPIDevicePathNode struct {
Controller ATAPIControllerRole
Drive ATAPIDriveRole
LUN uint16
}
func (d *ATAPIDevicePathNode) ToString(flags DevicePathToStringFlags) string {
if flags.DisplayOnly() {
return fmt.Sprintf("Ata(0x%x)", d.LUN)
}
return fmt.Sprintf("Ata(%s,%s,0x%x)", d.Controller, d.Drive, d.LUN)
}
func (d *ATAPIDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *ATAPIDevicePathNode) Write(w io.Writer) error {
data := uefi.ATAPI_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MESSAGING_DEVICE_PATH),
SubType: uint8(uefi.MSG_ATAPI_DP)},
PrimarySecondary: uint8(d.Controller),
SlaveMaster: uint8(d.Drive),
Lun: d.LUN}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
// SCSIDevicePathNode corresponds to a SCSI device path node.
type SCSIDevicePathNode struct {
PUN uint16
LUN uint16
}
func (d *SCSIDevicePathNode) ToString(_ DevicePathToStringFlags) string {
return fmt.Sprintf("Scsi(0x%x,0x%x)", d.PUN, d.LUN)
}
func (d *SCSIDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *SCSIDevicePathNode) Write(w io.Writer) error {
data := uefi.SCSI_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MESSAGING_DEVICE_PATH),
SubType: uint8(uefi.MSG_SCSI_DP)},
Pun: d.PUN,
Lun: d.LUN}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
// USBDevicePathNode corresponds to a USB device path node.
type USBDevicePathNode struct {
ParentPortNumber uint8
InterfaceNumber uint8
}
func (d *USBDevicePathNode) ToString(_ DevicePathToStringFlags) string {
return fmt.Sprintf("USB(0x%x,0x%x)", d.ParentPortNumber, d.InterfaceNumber)
}
func (d *USBDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *USBDevicePathNode) Write(w io.Writer) error {
data := uefi.USB_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MESSAGING_DEVICE_PATH),
SubType: uint8(uefi.MSG_USB_DP)},
ParentPortNumber: d.ParentPortNumber,
InterfaceNumber: d.InterfaceNumber}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
type USBClass uint8
const (
USBClassAudio USBClass = 0x01
USBClassCDCControl USBClass = 0x02
USBClassHID USBClass = 0x03
USBClassImage USBClass = 0x06
USBClassPrinter USBClass = 0x07
USBClassMassStorage USBClass = 0x08
USBClassHub USBClass = 0x09
USBClassCDCData USBClass = 0x0a
USBClassSmartCard USBClass = 0x0b
USBClassVideo USBClass = 0x0e
USBClassDiagnostic USBClass = 0xdc
USBClassWireless USBClass = 0xe0
)
// USBClassDevicePathNode corresponds to a USB class device path node.
type USBClassDevicePathNode struct {
VendorId uint16
ProductId uint16
DeviceClass USBClass
DeviceSubClass uint8
DeviceProtocol uint8
}
func (d *USBClassDevicePathNode) ToString(_ DevicePathToStringFlags) string {
var builder bytes.Buffer
switch d.DeviceClass {
case USBClassAudio:
fmt.Fprintf(&builder, "UsbAudio")
case USBClassCDCControl:
fmt.Fprintf(&builder, "UsbCDCControl")
case USBClassHID:
fmt.Fprintf(&builder, "UsbHID")
case USBClassImage:
fmt.Fprintf(&builder, "UsbImage")
case USBClassPrinter:
fmt.Fprintf(&builder, "UsbPrinter")
case USBClassMassStorage:
fmt.Fprintf(&builder, "UsbMassStorage")
case USBClassHub:
fmt.Fprintf(&builder, "UsbHub")
case USBClassCDCData:
fmt.Fprintf(&builder, "UsbCDCData")
case USBClassSmartCard:
fmt.Fprintf(&builder, "UsbSmartCard")
case USBClassVideo:
fmt.Fprintf(&builder, "UsbVideo")
case USBClassDiagnostic:
fmt.Fprintf(&builder, "UsbDiagnostic")
case USBClassWireless:
fmt.Fprintf(&builder, "UsbWireless")
default:
return fmt.Sprintf("UsbClass(0x%x,0x%x,0x%x,0x%x,0x%x)", d.VendorId, d.ProductId, d.DeviceClass, d.DeviceSubClass, d.DeviceProtocol)
}
fmt.Fprintf(&builder, "(0x%x,0x%x,0x%x,0x%x)", d.VendorId, d.ProductId, d.DeviceSubClass, d.DeviceProtocol)
return builder.String()
}
func (d *USBClassDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *USBClassDevicePathNode) Write(w io.Writer) error {
data := uefi.USB_CLASS_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MESSAGING_DEVICE_PATH),
SubType: uint8(uefi.MSG_USB_CLASS_DP)},
VendorId: d.VendorId,
ProductId: d.ProductId,
DeviceClass: uint8(d.DeviceClass),
DeviceSubClass: d.DeviceSubClass,
DeviceProtocol: d.DeviceProtocol}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
// USBWWIDDevicePathNode corresponds to a USB WWID device path node.
type USBWWIDDevicePathNode struct {
InterfaceNumber uint16
VendorId uint16
ProductId uint16
SerialNumber string
}
func (d *USBWWIDDevicePathNode) ToString(_ DevicePathToStringFlags) string {
return fmt.Sprintf("UsbWwid(0x%x,0x%x,0x%x,\"%s\"", d.VendorId, d.ProductId, d.InterfaceNumber, d.SerialNumber)
}
func (d *USBWWIDDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *USBWWIDDevicePathNode) Write(w io.Writer) error {
data := uefi.USB_WWID_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MESSAGING_DEVICE_PATH),
SubType: uint8(uefi.MSG_USB_WWID_DP)},
InterfaceNumber: d.InterfaceNumber,
VendorId: d.VendorId,
ProductId: d.ProductId,
SerialNumber: ConvertUTF8ToUTF16(d.SerialNumber)}
l := binary.Size(data.Header) + binary.Size(data.InterfaceNumber) + binary.Size(data.VendorId) + binary.Size(data.ProductId)
if binary.Size(data.SerialNumber) > math.MaxUint16-l {
return errors.New("SerialNumber too long")
}
data.Header.Length = uint16(l + binary.Size(data.SerialNumber))
return binary.Write(w, binary.LittleEndian, &data)
}
type DeviceLogicalUnitDevicePathNode struct {
LUN uint8
}
func (d *DeviceLogicalUnitDevicePathNode) ToString(_ DevicePathToStringFlags) string {
return fmt.Sprintf("Unit(0x%x)", d.LUN)
}
func (d *DeviceLogicalUnitDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *DeviceLogicalUnitDevicePathNode) Write(w io.Writer) error {
data := uefi.DEVICE_LOGICAL_UNIT_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MESSAGING_DEVICE_PATH),
SubType: uint8(uefi.MSG_DEVICE_LOGICAL_UNIT_DP)},
Lun: d.LUN}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
// SATADevicePathNode corresponds to a SATA device path node.
type SATADevicePathNode struct {
HBAPortNumber uint16
PortMultiplierPortNumber uint16
LUN uint16
}
func (d *SATADevicePathNode) ToString(_ DevicePathToStringFlags) string {
return fmt.Sprintf("Sata(0x%x,0x%x,0x%x)", d.HBAPortNumber, d.PortMultiplierPortNumber, d.LUN)
}
func (d *SATADevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *SATADevicePathNode) Write(w io.Writer) error {
data := uefi.SATA_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MESSAGING_DEVICE_PATH),
SubType: uint8(uefi.MSG_SATA_DP)},
HBAPortNumber: d.HBAPortNumber,
PortMultiplierPortNumber: d.PortMultiplierPortNumber,
Lun: d.LUN}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
// NVMENamespaceDevicePathNode corresponds to a NVME namespace device path node.
type NVMENamespaceDevicePathNode struct {
NamespaceID uint32
NamespaceUUID uint64
}
func (d *NVMENamespaceDevicePathNode) ToString(_ DevicePathToStringFlags) string {
var uuid [8]uint8
binary.BigEndian.PutUint64(uuid[:], d.NamespaceUUID)
return fmt.Sprintf("NVMe(0x%x,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)", d.NamespaceID,
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7])
}
func (d *NVMENamespaceDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *NVMENamespaceDevicePathNode) Write(w io.Writer) error {
data := uefi.NVME_NAMESPACE_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MESSAGING_DEVICE_PATH),
SubType: uint8(uefi.MSG_NVME_NAMESPACE_DP)},
NamespaceId: d.NamespaceID,
NamespaceUuid: d.NamespaceUUID}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
type MBRType uint8
func (t MBRType) String() string {
switch t {
case LegacyMBR:
return "MBR"
case GPT:
return "GPT"
default:
return strconv.FormatUint(uint64(t), 10)
}
}
const (
LegacyMBR MBRType = 1
GPT = 2
)
type HardDriveSignatureType uint8
func (t HardDriveSignatureType) String() string {
switch t {
case uefi.SIGNATURE_TYPE_MBR:
return "MBR"
case uefi.SIGNATURE_TYPE_GUID:
return "GPT"
default:
return strconv.FormatUint(uint64(t), 10)
}
}
type HardDriveSignature interface {
fmt.Stringer
Data() [16]uint8
Type() HardDriveSignatureType
}
type GUIDHardDriveSignature GUID
func (s GUIDHardDriveSignature) String() string {
return GUID(s).String()
}
func (s GUIDHardDriveSignature) Data() (out [16]uint8) {
copy(out[:], s[:])
return out
}
func (GUIDHardDriveSignature) Type() HardDriveSignatureType {
return HardDriveSignatureType(uefi.SIGNATURE_TYPE_GUID)
}
type MBRHardDriveSignature uint32
func (s MBRHardDriveSignature) String() string {
return fmt.Sprintf("0x%08x", uint32(s))
}
func (s MBRHardDriveSignature) Data() (out [16]uint8) {
binary.LittleEndian.PutUint32(out[:], uint32(s))
return out
}
func (s MBRHardDriveSignature) Type() HardDriveSignatureType {
return HardDriveSignatureType(uefi.SIGNATURE_TYPE_MBR)
}
type genericHardDriveSignature struct {
typ HardDriveSignatureType
data [16]uint8
}
func (s *genericHardDriveSignature) String() string {
return fmt.Sprintf("%x", s.data)
}
func (s *genericHardDriveSignature) Data() [16]uint8 {
return s.data
}
func (s *genericHardDriveSignature) Type() HardDriveSignatureType {
return s.typ
}
// HardDriveDevicePathNode corresponds to a hard drive device path node.
type HardDriveDevicePathNode struct {
PartitionNumber uint32
PartitionStart uint64
PartitionSize uint64
Signature HardDriveSignature
MBRType MBRType
}
func (d *HardDriveDevicePathNode) ToString(flags DevicePathToStringFlags) string {
var builder bytes.Buffer
switch d.Signature.Type() {
default:
fmt.Fprintf(&builder, "HD(%d,%d,0", d.PartitionNumber, d.MBRType)
case uefi.SIGNATURE_TYPE_MBR, uefi.SIGNATURE_TYPE_GUID:
fmt.Fprintf(&builder, "HD(%d,%s,%s", d.PartitionNumber, d.Signature.Type(), d.Signature)
}
if !flags.DisplayOnly() {
fmt.Fprintf(&builder, ",0x%x,0x%x", d.PartitionStart, d.PartitionSize)
}
fmt.Fprintf(&builder, ")")
return builder.String()
}
func (d *HardDriveDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *HardDriveDevicePathNode) Write(w io.Writer) error {
data := uefi.HARDDRIVE_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MEDIA_DEVICE_PATH),
SubType: uint8(uefi.MEDIA_HARDDRIVE_DP)},
PartitionNumber: d.PartitionNumber,
PartitionStart: d.PartitionStart,
PartitionSize: d.PartitionSize,
MBRType: uint8(d.MBRType)}
if d.Signature != nil {
data.SignatureType = uint8(d.Signature.Type())
switch d.Signature.Type() {
case uefi.NO_DISK_SIGNATURE:
if d.Signature.Data() != data.Signature {
return errors.New("inconsistent signature and signature type")
}
default:
data.Signature = d.Signature.Data()
}
}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
// NewHardDriveDevicePathNodeFromDevice constructs a HardDriveDevicePathNode for the
// specified partition on the supplied device reader. The device's total size and
// logical block size must be supplied.
func NewHardDriveDevicePathNodeFromDevice(r io.ReaderAt, totalSz, blockSz int64, part int) (*HardDriveDevicePathNode, error) {
if part < 1 {
return nil, errors.New("invalid partition number")
}
table, err := ReadPartitionTable(r, totalSz, blockSz, PrimaryPartitionTable, true)
switch {
case err == ErrNoProtectiveMBR:
record, err := mbr.ReadRecord(io.NewSectionReader(r, 0, totalSz))
if err != nil {
return nil, err
}
if part > 4 {
return nil, fmt.Errorf("invalid partition number %d for MBR", part)
}
entry := record.Partitions[part-1]
return &HardDriveDevicePathNode{
PartitionNumber: uint32(part),
PartitionStart: uint64(entry.StartingLBA),
PartitionSize: uint64(entry.NumberOfSectors),
Signature: MBRHardDriveSignature(record.UniqueSignature),
MBRType: LegacyMBR}, nil
case err != nil:
return nil, err
default:
if part > len(table.Entries) {
return nil, fmt.Errorf("invalid partition number %d: device only has %d partitions", part, len(table.Entries))
}
entry := table.Entries[part-1]
if entry.PartitionTypeGUID == UnusedPartitionType {
return nil, errors.New("requested partition is unused")
}
return &HardDriveDevicePathNode{
PartitionNumber: uint32(part),
PartitionStart: uint64(entry.StartingLBA),
PartitionSize: uint64(entry.EndingLBA - entry.StartingLBA + 1),
Signature: GUIDHardDriveSignature(entry.UniquePartitionGUID),
MBRType: GPT}, nil
}
}
// CDROMDevicePathNode corresponds to a CDROM device path node.
type CDROMDevicePathNode struct {
BootEntry uint32
PartitionStart uint64
PartitionSize uint64
}
func (d *CDROMDevicePathNode) ToString(flags DevicePathToStringFlags) string {
if flags.DisplayOnly() {
return fmt.Sprintf("CDROM(0x%x)", d.BootEntry)
}
return fmt.Sprintf("CDROM(0x%x,0x%x,0x%x)", d.BootEntry, d.PartitionStart, d.PartitionSize)
}
func (d *CDROMDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *CDROMDevicePathNode) Write(w io.Writer) error {
data := uefi.CDROM_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MEDIA_DEVICE_PATH),
SubType: uint8(uefi.MEDIA_CDROM_DP)},
BootEntry: d.BootEntry,
PartitionStart: d.PartitionStart,
PartitionSize: d.PartitionSize}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
// FilePathDevicePathNode corresponds to a file path device path node.
type FilePathDevicePathNode string
func (d FilePathDevicePathNode) ToString(_ DevicePathToStringFlags) string {
return string(d)
}
func (d FilePathDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d FilePathDevicePathNode) Write(w io.Writer) error {
data := uefi.FILEPATH_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MEDIA_DEVICE_PATH),
SubType: uint8(uefi.MEDIA_FILEPATH_DP)},
PathName: ConvertUTF8ToUTF16(string(d) + "\x00")}
if binary.Size(data.PathName) > math.MaxUint16-binary.Size(data.Header) {
return errors.New("PathName too large")
}
data.Header.Length = uint16(binary.Size(data.Header) + binary.Size(data.PathName))
return data.Write(w)
}
// NewFilePathDevicePathNode constructs a new FilePathDevicePathNode from the supplied
// path, converting the OS native separators to EFI separators ("\") and prepending
// a separator to the start of the path if one doesn't already exist.
func NewFilePathDevicePathNode(path string) (out FilePathDevicePathNode) {
components := strings.Split(path, string(os.PathSeparator))
if !filepath.IsAbs(path) {
out = FilePathDevicePathNode("\\")
}
return out + FilePathDevicePathNode(strings.Join(components, "\\"))
}
// MediaFvFileDevicePathNode corresponds to a firmware volume file device path node.
type MediaFvFileDevicePathNode GUID
func (d MediaFvFileDevicePathNode) ToString(_ DevicePathToStringFlags) string {
return fmt.Sprintf("FvFile(%s)", GUID(d))
}
func (d MediaFvFileDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d MediaFvFileDevicePathNode) Write(w io.Writer) error {
data := uefi.MEDIA_FW_VOL_FILEPATH_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MEDIA_DEVICE_PATH),
SubType: uint8(uefi.MEDIA_PIWG_FW_FILE_DP)},
FvFileName: uefi.EFI_GUID(d)}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
// MediaFvDevicePathNode corresponds to a firmware volume device path node.
type MediaFvDevicePathNode GUID
func (d MediaFvDevicePathNode) ToString(_ DevicePathToStringFlags) string {
return fmt.Sprintf("Fv(%s)", GUID(d))
}
func (d MediaFvDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d MediaFvDevicePathNode) Write(w io.Writer) error {
data := uefi.MEDIA_FW_VOL_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MEDIA_DEVICE_PATH),
SubType: uint8(uefi.MEDIA_PIWG_FW_VOL_DP)},
FvName: uefi.EFI_GUID(d)}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
type MediaRelOffsetRangeDevicePathNode struct {
StartingOffset uint64
EndingOffset uint64
}
func (d *MediaRelOffsetRangeDevicePathNode) ToString(_ DevicePathToStringFlags) string {
return fmt.Sprintf("Offset(0x%x,0x%x)", d.StartingOffset, d.EndingOffset)
}
func (d *MediaRelOffsetRangeDevicePathNode) String() string {
return d.ToString(DevicePathDisplayOnly)
}
func (d *MediaRelOffsetRangeDevicePathNode) Write(w io.Writer) error {
data := uefi.MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH{
Header: uefi.EFI_DEVICE_PATH_PROTOCOL{
Type: uint8(uefi.MEDIA_DEVICE_PATH),
SubType: uint8(uefi.MEDIA_RELATIVE_OFFSET_RANGE_DP)},
StartingOffset: d.StartingOffset,
EndingOffset: d.EndingOffset}
data.Header.Length = uint16(binary.Size(data))
return binary.Write(w, binary.LittleEndian, &data)
}
func decodeDevicePathNode(r io.Reader) (out DevicePathNode, err error) {
buf := new(bytes.Buffer)
r2 := io.TeeReader(r, buf)
var h uefi.EFI_DEVICE_PATH_PROTOCOL
if err := binary.Read(r2, binary.LittleEndian, &h); err != nil {
return nil, err
}
if h.Length < 4 {
return nil, fmt.Errorf("invalid length %d bytes (too small)", h.Length)
}
if _, err := io.CopyN(buf, r, int64(h.Length-4)); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
defer func() {
switch {
case err == io.EOF:
fallthrough
case xerrors.Is(err, io.ErrUnexpectedEOF):
err = fmt.Errorf("invalid length %d bytes (too small)", h.Length)
case err != nil:
case buf.Len() > 0:
err = fmt.Errorf("invalid length %d bytes (too large)", h.Length)
}
}()
switch h.Type {
case uefi.HARDWARE_DEVICE_PATH:
switch h.SubType {
case uefi.HW_PCI_DP:
var n uefi.PCI_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &PCIDevicePathNode{Function: n.Function, Device: n.Device}, nil
case uefi.HW_VENDOR_DP:
return readVendorDevicePathNode(buf)
}
case uefi.ACPI_DEVICE_PATH:
switch h.SubType {
case uefi.ACPI_DP:
var n uefi.ACPI_HID_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &ACPIDevicePathNode{HID: EISAID(n.HID), UID: n.UID}, nil
case uefi.ACPI_EXTENDED_DP:
var n uefi.ACPI_EXTENDED_HID_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
node := &ACPIExtendedDevicePathNode{HID: EISAID(n.HID), UID: n.UID, CID: EISAID(n.CID)}
for _, s := range []*string{&node.HIDStr, &node.UIDStr, &node.CIDStr} {
v, err := buf.ReadString('\x00')
if err != nil {
return nil, err
}
*s = v[:len(v)-1]
}
return node, nil
}
case uefi.MESSAGING_DEVICE_PATH:
switch h.SubType {
case uefi.MSG_ATAPI_DP:
var n uefi.ATAPI_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &ATAPIDevicePathNode{
Controller: ATAPIControllerRole(n.PrimarySecondary),
Drive: ATAPIDriveRole(n.SlaveMaster),
LUN: n.Lun}, nil
case uefi.MSG_SCSI_DP:
var n uefi.SCSI_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &SCSIDevicePathNode{PUN: n.Pun, LUN: n.Lun}, nil
case uefi.MSG_USB_DP:
var n uefi.USB_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &USBDevicePathNode{ParentPortNumber: n.ParentPortNumber, InterfaceNumber: n.InterfaceNumber}, nil
case uefi.MSG_USB_CLASS_DP:
var n uefi.USB_CLASS_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &USBClassDevicePathNode{
VendorId: n.VendorId,
ProductId: n.ProductId,
DeviceClass: USBClass(n.DeviceClass),
DeviceSubClass: n.DeviceSubClass,
DeviceProtocol: n.DeviceProtocol}, nil
case uefi.MSG_VENDOR_DP:
return readVendorDevicePathNode(buf)
case uefi.MSG_USB_WWID_DP:
n, err := uefi.Read_USB_WWID_DEVICE_PATH(buf)
if err != nil {
return nil, err
}
return &USBWWIDDevicePathNode{
InterfaceNumber: n.InterfaceNumber,
VendorId: n.VendorId,
ProductId: n.ProductId,
SerialNumber: ConvertUTF16ToUTF8(n.SerialNumber)}, nil
case uefi.MSG_DEVICE_LOGICAL_UNIT_DP:
var n uefi.DEVICE_LOGICAL_UNIT_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &DeviceLogicalUnitDevicePathNode{LUN: n.Lun}, nil
case uefi.MSG_SATA_DP:
var n uefi.SATA_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &SATADevicePathNode{
HBAPortNumber: n.HBAPortNumber,
PortMultiplierPortNumber: n.PortMultiplierPortNumber,
LUN: n.Lun}, nil
case uefi.MSG_NVME_NAMESPACE_DP:
var n uefi.NVME_NAMESPACE_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &NVMENamespaceDevicePathNode{
NamespaceID: n.NamespaceId,
NamespaceUUID: n.NamespaceUuid}, nil
}
case uefi.MEDIA_DEVICE_PATH:
switch h.SubType {
case uefi.MEDIA_HARDDRIVE_DP:
var n uefi.HARDDRIVE_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
var signature HardDriveSignature
switch n.SignatureType {
case uefi.NO_DISK_SIGNATURE:
case uefi.SIGNATURE_TYPE_MBR:
signature = MBRHardDriveSignature(binary.LittleEndian.Uint32(n.Signature[:]))
case uefi.SIGNATURE_TYPE_GUID:
signature = GUIDHardDriveSignature(n.Signature)
default:
signature = &genericHardDriveSignature{
typ: HardDriveSignatureType(n.SignatureType),
data: n.Signature}
}
return &HardDriveDevicePathNode{
PartitionNumber: n.PartitionNumber,
PartitionStart: n.PartitionStart,
PartitionSize: n.PartitionSize,
Signature: signature,
MBRType: MBRType(n.MBRType)}, nil
case uefi.MEDIA_CDROM_DP:
var n uefi.CDROM_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &CDROMDevicePathNode{
BootEntry: n.BootEntry,
PartitionStart: n.PartitionStart,
PartitionSize: n.PartitionSize}, nil
case uefi.MEDIA_VENDOR_DP:
return readVendorDevicePathNode(buf)
case uefi.MEDIA_FILEPATH_DP:
n, err := uefi.Read_FILEPATH_DEVICE_PATH(buf)
if err != nil {
return nil, err
}
return FilePathDevicePathNode(ConvertUTF16ToUTF8(n.PathName)), nil
case uefi.MEDIA_PIWG_FW_FILE_DP:
var n uefi.MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return MediaFvFileDevicePathNode(GUID(n.FvFileName)), nil
case uefi.MEDIA_PIWG_FW_VOL_DP:
var n uefi.MEDIA_FW_VOL_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return MediaFvDevicePathNode(GUID(n.FvName)), nil
case uefi.MEDIA_RELATIVE_OFFSET_RANGE_DP:
var n uefi.MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
return &MediaRelOffsetRangeDevicePathNode{StartingOffset: n.StartingOffset, EndingOffset: n.EndingOffset}, nil
}
case uefi.END_DEVICE_PATH_TYPE:
buf.Reset()
return nil, nil
}
var n uefi.EFI_DEVICE_PATH_PROTOCOL
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
return nil, err
}
data, _ := ioutil.ReadAll(buf)
return &GenericDevicePathNode{Type: DevicePathType(n.Type), SubType: DevicePathSubType(n.SubType), Data: data}, nil
}
// ReadDevicePath decodes a device path from the supplied io.Reader.
func ReadDevicePath(r io.Reader) (out DevicePath, err error) {
for i := 0; ; i++ {
node, err := decodeDevicePathNode(r)
switch {
case err != nil && i == 0:
return nil, ioerr.PassRawEOF("cannot decode node %d: %w", i, err)
case err != nil:
return nil, ioerr.EOFIsUnexpected("cannot decode node: %d: %w", i, err)
}
if node == nil {
break
}
out = append(out, node)
}
return out, nil
}
./github.com/canonical/go-efilib/gpt.go 0000664 0000000 0000000 00000023733 00000000000 016320 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"io"
"golang.org/x/xerrors"
"github.com/canonical/go-efilib/internal/uefi"
"github.com/canonical/go-efilib/mbr"
)
var (
ErrCRCCheck = errors.New("CRC check failed")
ErrNoProtectiveMBR = errors.New("no protective master boot record found")
// UnusedPartitionType is the type GUID of an unused partition entry.
UnusedPartitionType GUID
)
type InvalidGPTHeaderError string
func (e InvalidGPTHeaderError) Error() string {
return "invalid GPT header: " + string(e)
}
// PartitionTableHeader correponds to the EFI_PARTITION_TABLE_HEADER type.
type PartitionTableHeader struct {
HeaderSize uint32
MyLBA LBA
AlternateLBA LBA
FirstUsableLBA LBA
LastUsableLBA LBA
DiskGUID GUID
PartitionEntryLBA LBA
NumberOfPartitionEntries uint32
SizeOfPartitionEntry uint32
PartitionEntryArrayCRC32 uint32
}
// Write serializes this PartitionTableHeader to w. The CRC field is
// computed automatically.
func (h *PartitionTableHeader) Write(w io.Writer) error {
hdr := uefi.EFI_PARTITION_TABLE_HEADER{
Hdr: uefi.EFI_TABLE_HEADER{
Signature: uefi.EFI_PTAB_HEADER_ID,
Revision: 0x10000,
HeaderSize: h.HeaderSize},
MyLBA: uefi.EFI_LBA(h.MyLBA),
AlternateLBA: uefi.EFI_LBA(h.AlternateLBA),
FirstUsableLBA: uefi.EFI_LBA(h.FirstUsableLBA),
LastUsableLBA: uefi.EFI_LBA(h.LastUsableLBA),
DiskGUID: uefi.EFI_GUID(h.DiskGUID),
PartitionEntryLBA: uefi.EFI_LBA(h.PartitionEntryLBA),
NumberOfPartitionEntries: h.NumberOfPartitionEntries,
SizeOfPartitionEntry: h.SizeOfPartitionEntry,
PartitionEntryArrayCRC32: h.PartitionEntryArrayCRC32}
hdrSize := binary.Size(hdr)
if h.HeaderSize < uint32(hdrSize) {
return errors.New("invalid HeaderSize")
}
reserved := make([]byte, int(h.HeaderSize)-hdrSize)
crc := crc32.NewIEEE()
binary.Write(crc, binary.LittleEndian, &hdr)
crc.Write(reserved)
hdr.Hdr.CRC = crc.Sum32()
if err := binary.Write(w, binary.LittleEndian, &hdr); err != nil {
return err
}
_, err := w.Write(reserved)
return err
}
func (h *PartitionTableHeader) String() string {
return fmt.Sprintf("EFI_PARTITION_TABLE_HEADER{ MyLBA: 0x%x, AlternateLBA: 0x%x, FirstUsableLBA: 0x%x, "+
"LastUsableLBA: 0x%x, DiskGUID: %v, PartitionEntryLBA: 0x%x, NumberOfPartitionEntries: %d, "+
"SizeOfPartitionEntry: 0x%x, PartitionEntryArrayCRC32: 0x%08x }",
h.MyLBA, h.AlternateLBA, h.FirstUsableLBA, h.LastUsableLBA, h.DiskGUID, h.PartitionEntryLBA,
h.NumberOfPartitionEntries, h.SizeOfPartitionEntry, h.PartitionEntryArrayCRC32)
}
// ReadPartitionTableHeader reads a EFI_PARTITION_TABLE_HEADER from the supplied io.Reader.
// If the header signature or revision is incorrect, an error will be returned. If
// checkCrc is true and the header has an invalid CRC, an error will be returned.
// If checkCrc is false, then a CRC check is not performed.
func ReadPartitionTableHeader(r io.Reader, checkCrc bool) (*PartitionTableHeader, error) {
hdr, crc, err := uefi.Read_EFI_PARTITION_TABLE_HEADER(r)
if err != nil {
return nil, err
}
if hdr.Hdr.Signature != uefi.EFI_PTAB_HEADER_ID {
return nil, InvalidGPTHeaderError("invalid signature")
}
if hdr.Hdr.Revision != 0x10000 {
return nil, InvalidGPTHeaderError("unexpected revision")
}
if checkCrc && hdr.Hdr.CRC != crc {
return nil, ErrCRCCheck
}
return &PartitionTableHeader{
HeaderSize: hdr.Hdr.HeaderSize,
MyLBA: LBA(hdr.MyLBA),
AlternateLBA: LBA(hdr.AlternateLBA),
FirstUsableLBA: LBA(hdr.FirstUsableLBA),
LastUsableLBA: LBA(hdr.LastUsableLBA),
DiskGUID: GUID(hdr.DiskGUID),
PartitionEntryLBA: LBA(hdr.PartitionEntryLBA),
NumberOfPartitionEntries: hdr.NumberOfPartitionEntries,
SizeOfPartitionEntry: hdr.SizeOfPartitionEntry,
PartitionEntryArrayCRC32: hdr.PartitionEntryArrayCRC32}, nil
}
// PartitionEntry corresponds to the EFI_PARTITION_ENTRY type.
type PartitionEntry struct {
PartitionTypeGUID GUID
UniquePartitionGUID GUID
StartingLBA LBA
EndingLBA LBA
Attributes uint64
PartitionName string
}
func (e *PartitionEntry) String() string {
return fmt.Sprintf("EFI_PARTITION_ENTRY{ PartitionTypeGUID: %s, UniquePartitionGUID: %s, StartingLBA: 0x%x, "+
"EndingLBA: 0x%x, Attributes: 0x%016x, PartitionName: \"%s\" }",
e.PartitionTypeGUID, e.UniquePartitionGUID, e.StartingLBA, e.EndingLBA, e.Attributes, e.PartitionName)
}
// Write serializes this PartitionEntry to w. Note that it doesn't write
// any bytes beyond the end of the EFI_PARTITION_ENTRY structure, so if the
// caller is writing several entries and the partition table header defines
// an entry size of greater than 128 bytes, the caller is responsible for
// inserting the 0 padding bytes.
func (e *PartitionEntry) Write(w io.Writer) error {
entry := uefi.EFI_PARTITION_ENTRY{
PartitionTypeGUID: uefi.EFI_GUID(e.PartitionTypeGUID),
UniquePartitionGUID: uefi.EFI_GUID(e.UniquePartitionGUID),
StartingLBA: uefi.EFI_LBA(e.StartingLBA),
EndingLBA: uefi.EFI_LBA(e.EndingLBA),
Attributes: e.Attributes}
partitionName := ConvertUTF8ToUTF16(e.PartitionName)
if len(partitionName) > len(entry.PartitionName) {
return errors.New("PartitionName is too long")
}
copy(entry.PartitionName[:], partitionName)
return binary.Write(w, binary.LittleEndian, &entry)
}
// ReadPartitionEntry reads a single EFI_PARTITION_ENTRY from r.
func ReadPartitionEntry(r io.Reader) (*PartitionEntry, error) {
var e uefi.EFI_PARTITION_ENTRY
if err := binary.Read(r, binary.LittleEndian, &e); err != nil {
return nil, err
}
return &PartitionEntry{
PartitionTypeGUID: GUID(e.PartitionTypeGUID),
UniquePartitionGUID: GUID(e.UniquePartitionGUID),
StartingLBA: LBA(e.StartingLBA),
EndingLBA: LBA(e.EndingLBA),
Attributes: e.Attributes,
PartitionName: ConvertUTF16ToUTF8(e.PartitionName[:])}, nil
}
func readPartitionEntries(r io.Reader, num, sz, expectedCrc uint32, checkCrc bool) (out []*PartitionEntry, err error) {
crc := crc32.NewIEEE()
r2 := io.TeeReader(r, crc)
b := new(bytes.Buffer)
for i := uint32(0); i < num; i++ {
b.Reset()
if _, err := io.CopyN(b, r2, int64(sz)); err != nil {
switch {
case err == io.EOF && i == 0:
return nil, err
case err == io.EOF:
err = io.ErrUnexpectedEOF
}
return nil, xerrors.Errorf("cannot read entry %d: %w", i, err)
}
e, err := ReadPartitionEntry(b)
if err != nil {
return nil, err
}
out = append(out, e)
}
if checkCrc && crc.Sum32() != expectedCrc {
return nil, ErrCRCCheck
}
return out, nil
}
// ReadPartitionEntries reads the specified number of EFI_PARTITION_ENTRY structures
// of the specified size from the supplied io.Reader. The number and size are typically
// defined by the partition table header.
func ReadPartitionEntries(r io.Reader, num, sz uint32) ([]*PartitionEntry, error) {
return readPartitionEntries(r, num, sz, 0, false)
}
var emptyPartitionType GUID
// PartitionTableRole describes the role of a partition table.
type PartitionTableRole int
const (
PrimaryPartitionTable PartitionTableRole = iota
BackupPartitionTable
)
// PartitionTable describes a complete GUID partition table.
type PartitionTable struct {
Hdr *PartitionTableHeader
Entries []*PartitionEntry
}
func (t *PartitionTable) String() string {
b := new(bytes.Buffer)
fmt.Fprintf(b, "GPT{\n\tHdr: %s,\n\tEntries: [", t.Hdr)
for _, entry := range t.Entries {
fmt.Fprintf(b, "\n\t\t%s", entry)
}
fmt.Fprintf(b, "\n\t]\n}")
return b.String()
}
// ReadPartitionTable reads a complete GUID partition table from the supplied
// io.Reader. The total size and logical block size of the device must be
// supplied - the logical block size is 512 bytes for a file, but must be
// obtained from the kernel for a block device.
//
// This function expects the device to have a valid protective MBR.
//
// If role is PrimaryPartitionTable, this will read the primary partition
// table that is located immediately after the protective MBR. If role is
// BackupPartitionTable, this will read the backup partition table that is
// located at the end of the device.
//
// If checkCrc is true and either CRC check fails, an error will be returned.
// Setting checkCrc to false disables the CRC checks.
func ReadPartitionTable(r io.ReaderAt, totalSz, blockSz int64, role PartitionTableRole, checkCrc bool) (*PartitionTable, error) {
r2 := io.NewSectionReader(r, 0, totalSz)
record, err := mbr.ReadRecord(r2)
if err != nil {
return nil, err
}
validPmbr := false
for _, p := range record.Partitions {
if p.Type == 0xee {
validPmbr = true
break
}
}
if !validPmbr {
return nil, ErrNoProtectiveMBR
}
var offset int64
var whence int
switch role {
case PrimaryPartitionTable:
offset = blockSz
whence = io.SeekStart
case BackupPartitionTable:
if _, err := r2.Seek(blockSz, io.SeekStart); err != nil {
return nil, err
}
hdr, err := ReadPartitionTableHeader(r2, checkCrc)
if err != nil {
offset = -blockSz
whence = io.SeekEnd
} else {
offset = int64(hdr.AlternateLBA) * blockSz
whence = io.SeekStart
}
default:
panic("invalid role")
}
if _, err := r2.Seek(offset, whence); err != nil {
return nil, err
}
hdr, err := ReadPartitionTableHeader(r2, checkCrc)
switch {
case err == io.EOF:
return nil, io.ErrUnexpectedEOF
case err != nil:
return nil, err
}
if _, err := r2.Seek(int64(hdr.PartitionEntryLBA)*blockSz, io.SeekStart); err != nil {
return nil, err
}
entries, err := readPartitionEntries(r2, hdr.NumberOfPartitionEntries, hdr.SizeOfPartitionEntry, hdr.PartitionEntryArrayCRC32, checkCrc)
switch {
case err == io.EOF:
return nil, io.ErrUnexpectedEOF
case err != nil:
return nil, err
}
return &PartitionTable{hdr, entries}, nil
}
./github.com/canonical/go-efilib/guid.go 0000664 0000000 0000000 00000004435 00000000000 016454 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"regexp"
"github.com/canonical/go-efilib/internal/uefi"
)
// GUID corresponds to the EFI_GUID type.
type GUID [16]byte
func (guid GUID) A() uint32 {
return binary.LittleEndian.Uint32(guid[0:4])
}
func (guid GUID) B() uint16 {
return binary.LittleEndian.Uint16(guid[4:6])
}
func (guid GUID) C() uint16 {
return binary.LittleEndian.Uint16(guid[6:8])
}
func (guid GUID) D() uint16 {
return binary.BigEndian.Uint16(guid[8:10])
}
func (guid GUID) E() [6]uint8 {
var out [6]uint8
copy(out[:], guid[10:16])
return out
}
func (guid GUID) String() string {
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", guid.A(), guid.B(), guid.C(), guid.D(), guid.E())
}
// MakeGUID makes a new GUID from the supplied arguments.
func MakeGUID(a uint32, b, c, d uint16, e [6]uint8) GUID {
return GUID(uefi.New_EFI_GUID(a, b, c, d, e))
}
// ReadGUID reads a EFI_GUID from the supplied io.Reader.
func ReadGUID(r io.Reader) (out GUID, err error) {
_, err = io.ReadFull(r, out[:])
return
}
var guidRe = regexp.MustCompile(`\{?([[:xdigit:]]{8})-([[:xdigit:]]{4})-([[:xdigit:]]{4})-([[:xdigit:]]{4})-([[:xdigit:]]{12})\}?`)
func decodeStringUint32(s string) (uint32, error) {
h, err := hex.DecodeString(s)
if err != nil {
return 0, err
}
if len(h) > 4 {
return 0, errors.New("invalid length")
}
return binary.BigEndian.Uint32(h), nil
}
func decodeStringUint16(s string) (uint16, error) {
h, err := hex.DecodeString(s)
if err != nil {
return 0, err
}
if len(h) > 2 {
return 0, errors.New("invalid length")
}
return binary.BigEndian.Uint16(h), nil
}
// DecodeGUIDString decodes the supplied GUID string. The string must have
// the format "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" and may be surrounded
// by curly braces.
func DecodeGUIDString(s string) (GUID, error) {
m := guidRe.FindStringSubmatch(s)
if m == nil {
return GUID{}, errors.New("invalid format")
}
a, _ := decodeStringUint32(m[1])
b, _ := decodeStringUint16(m[2])
c, _ := decodeStringUint16(m[3])
d, _ := decodeStringUint16(m[4])
e, _ := hex.DecodeString(m[5])
var e2 [6]uint8
copy(e2[:], e)
return MakeGUID(a, b, c, d, e2), nil
}
./github.com/canonical/go-efilib/ids.go 0000664 0000000 0000000 00000002723 00000000000 016301 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"github.com/canonical/go-efilib/internal/uefi"
)
var (
HashAlgorithmSHA1Guid = GUID(uefi.EFI_HASH_ALGORITHM_SHA1_GUID)
HashAlgorithmSHA256Guid = GUID(uefi.EFI_HASH_ALGORITHM_SHA256_GUID)
HashAlgorithmSHA224Guid = GUID(uefi.EFI_HASH_ALGORITHM_SHA224_GUID)
HashAlgorithmSHA384Guid = GUID(uefi.EFI_HASH_ALGORITHM_SHA384_GUID)
HashAlgorithmSHA512Guid = GUID(uefi.EFI_HASH_ALGORITHM_SHA512_GUID)
CertTypeRSA2048SHA256Guid = GUID(uefi.EFI_CERT_TYPE_RSA2048_SHA256_GUID)
CertTypePKCS7Guid = GUID(uefi.EFI_CERT_TYPE_PKCS7_GUID)
CertSHA1Guid = GUID(uefi.EFI_CERT_SHA1_GUID)
CertSHA256Guid = GUID(uefi.EFI_CERT_SHA256_GUID)
CertSHA224Guid = GUID(uefi.EFI_CERT_SHA224_GUID)
CertSHA384Guid = GUID(uefi.EFI_CERT_SHA384_GUID)
CertSHA512Guid = GUID(uefi.EFI_CERT_SHA512_GUID)
CertRSA2048Guid = GUID(uefi.EFI_CERT_RSA2048_GUID)
CertRSA2048SHA1Guid = GUID(uefi.EFI_CERT_RSA2048_SHA1_GUID)
CertRSA2048SHA256Guid = GUID(uefi.EFI_CERT_RSA2048_SHA256_GUID)
CertX509Guid = GUID(uefi.EFI_CERT_X509_GUID)
CertX509SHA256Guid = GUID(uefi.EFI_CERT_X509_SHA256_GUID)
CertX509SHA384Guid = GUID(uefi.EFI_CERT_X509_SHA384_GUID)
CertX509SHA512Guid = GUID(uefi.EFI_CERT_X509_SHA512_GUID)
GlobalVariable = GUID(uefi.EFI_GLOBAL_VARIABLE)
ImageSecurityDatabaseGuid = GUID(uefi.EFI_IMAGE_SECURITY_DATABASE_GUID)
)
./github.com/canonical/go-efilib/internal/ 0000775 0000000 0000000 00000000000 00000000000 017003 5 ustar 00 0000000 0000000 ./github.com/canonical/go-efilib/internal/ioerr/ 0000775 0000000 0000000 00000000000 00000000000 020123 5 ustar 00 0000000 0000000 ./github.com/canonical/go-efilib/internal/ioerr/ioerr.go 0000664 0000000 0000000 00000004734 00000000000 021602 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package ioerr
import (
"io"
"unicode"
"unicode/utf8"
"golang.org/x/xerrors"
)
// Return the index of the first %w in format, or -1 if none.
// TODO: handle "%[N]w".
func parsePercentW(format string) int {
// Loosely copied from golang.org/x/xerrors/fmt.go.
n := 0
sz := 0
var isW bool
for i := 0; i < len(format); i += sz {
if format[i] != '%' {
sz = 1
continue
}
// "%%" is not a format directive.
if i+1 < len(format) && format[i+1] == '%' {
sz = 2
continue
}
sz, isW = parsePrintfVerb(format[i:])
if isW {
return n
}
n++
}
return -1
}
// Parse the printf verb starting with a % at s[0].
// Return how many bytes it occupies and whether the verb is 'w'.
func parsePrintfVerb(s string) (int, bool) {
// Assume only that the directive is a sequence of non-letters followed by a single letter.
sz := 0
var r rune
for i := 1; i < len(s); i += sz {
r, sz = utf8.DecodeRuneInString(s[i:])
if unicode.IsLetter(r) {
return i + sz, r == 'w'
}
}
return len(s), false
}
// EOFIsUnexpected converts io.EOF errors into io.ErrUnexpected, which is
// useful when using binary.Read to decode aprts of a structure that aren't
// at the start and when a io.EOF error is not expected.
//
// It can be called in one of 2 ways - either with a single argument which
// must be an error, or with a format string and an arbitrary number of
// arguments. In this second mode, the function is a wrapper around
// xerrors.Errorf.
//
// This only works on raw io.EOF errors - ie, it won't work on errors that
// have been wrapped.
func EOFIsUnexpected(args ...interface{}) error {
switch {
case len(args) > 1:
format := args[0].(string)
idx := parsePercentW(format)
if idx >= 0 {
if err, isErr := args[idx+1].(error); isErr && err == io.EOF {
args[idx+1] = io.ErrUnexpectedEOF
}
}
return xerrors.Errorf(format, args[1:]...)
case len(args) == 1:
switch err := args[0].(type) {
case error:
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
case nil:
return nil
default:
panic("invalid type")
}
default:
panic("no arguments")
}
}
// PassRawEOF is a wrapper around xerrors.Errorf that will return a raw
// io.EOF if this is the error.
func PassRawEOF(format string, args ...interface{}) error {
err := xerrors.Errorf(format, args...)
if xerrors.Is(err, io.EOF) {
return io.EOF
}
return err
}
./github.com/canonical/go-efilib/internal/pe1.14/ 0000775 0000000 0000000 00000000000 00000000000 017713 5 ustar 00 0000000 0000000 ./github.com/canonical/go-efilib/internal/pe1.14/README 0000664 0000000 0000000 00000000672 00000000000 020600 0 ustar 00 0000000 0000000 This is a copy of debug/pe from go 1.14.2 (96745b980cfde139e8611772e2bc0c59a8e6cdf7)
It exists here because debug/pe in go versions before 1.14 did not handle variable
length optional headers (where the data directory is less than 16 entries) and
therefore doesn't correctly decode kernel EFI images. Copying the code here avoids
a hard dependency on go 1.14.
See https://github.com/golang/go/commit/3b92f36d15c868e856be71c0fadfc7ff97039b96
./github.com/canonical/go-efilib/internal/pe1.14/file.go 0000664 0000000 0000000 00000041270 00000000000 021165 0 ustar 00 0000000 0000000 // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package pe implements access to PE (Microsoft Windows Portable Executable) files.
package pe
import (
"bytes"
"compress/zlib"
"debug/dwarf"
"encoding/binary"
"fmt"
"io"
"os"
"strings"
)
// Avoid use of post-Go 1.4 io features, to make safe for toolchain bootstrap.
const seekStart = 0
// A File represents an open PE file.
type File struct {
FileHeader
OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
Sections []*Section
Symbols []*Symbol // COFF symbols with auxiliary symbol records removed
COFFSymbols []COFFSymbol // all COFF symbols (including auxiliary symbol records)
StringTable StringTable
closer io.Closer
}
// Open opens the named file using os.Open and prepares it for use as a PE binary.
func Open(name string) (*File, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
ff, err := NewFile(f)
if err != nil {
f.Close()
return nil, err
}
ff.closer = f
return ff, nil
}
// Close closes the File.
// If the File was created using NewFile directly instead of Open,
// Close has no effect.
func (f *File) Close() error {
var err error
if f.closer != nil {
err = f.closer.Close()
f.closer = nil
}
return err
}
// TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance)
// NewFile creates a new File for accessing a PE binary in an underlying reader.
func NewFile(r io.ReaderAt) (*File, error) {
f := new(File)
sr := io.NewSectionReader(r, 0, 1<<63-1)
var dosheader [96]byte
if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
return nil, err
}
var base int64
if dosheader[0] == 'M' && dosheader[1] == 'Z' {
signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
var sign [4]byte
r.ReadAt(sign[:], signoff)
if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
return nil, fmt.Errorf("Invalid PE COFF file signature of %v.", sign)
}
base = signoff + 4
} else {
base = int64(0)
}
sr.Seek(base, seekStart)
if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
return nil, err
}
switch f.FileHeader.Machine {
case IMAGE_FILE_MACHINE_UNKNOWN,
IMAGE_FILE_MACHINE_ARMNT, IMAGE_FILE_MACHINE_ARM64,
IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386,
IMAGE_FILE_MACHINE_RISCV64:
default:
return nil, fmt.Errorf("Unrecognised COFF file header machine value of 0x%x.", f.FileHeader.Machine)
}
var err error
// Read string table.
f.StringTable, err = readStringTable(&f.FileHeader, sr)
if err != nil {
return nil, err
}
// Read symbol table.
f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
if err != nil {
return nil, err
}
f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable)
if err != nil {
return nil, err
}
// Seek past file header.
_, err = sr.Seek(base+int64(binary.Size(f.FileHeader)), seekStart)
if err != nil {
return nil, fmt.Errorf("failure to seek past the file header: %v", err)
}
// Read optional header.
f.OptionalHeader, err = readOptionalHeader(sr, f.FileHeader.SizeOfOptionalHeader)
if err != nil {
return nil, err
}
// Process sections.
f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
sh := new(SectionHeader32)
if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
return nil, err
}
name, err := sh.fullName(f.StringTable)
if err != nil {
return nil, err
}
s := new(Section)
s.SectionHeader = SectionHeader{
Name: name,
VirtualSize: sh.VirtualSize,
VirtualAddress: sh.VirtualAddress,
Size: sh.SizeOfRawData,
Offset: sh.PointerToRawData,
PointerToRelocations: sh.PointerToRelocations,
PointerToLineNumbers: sh.PointerToLineNumbers,
NumberOfRelocations: sh.NumberOfRelocations,
NumberOfLineNumbers: sh.NumberOfLineNumbers,
Characteristics: sh.Characteristics,
}
r2 := r
if sh.PointerToRawData == 0 { // .bss must have all 0s
r2 = zeroReaderAt{}
}
s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
s.ReaderAt = s.sr
f.Sections[i] = s
}
for i := range f.Sections {
var err error
f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
if err != nil {
return nil, err
}
}
return f, nil
}
// zeroReaderAt is ReaderAt that reads 0s.
type zeroReaderAt struct{}
// ReadAt writes len(p) 0s into p.
func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
for i := range p {
p[i] = 0
}
return len(p), nil
}
// getString extracts a string from symbol string table.
func getString(section []byte, start int) (string, bool) {
if start < 0 || start >= len(section) {
return "", false
}
for end := start; end < len(section); end++ {
if section[end] == 0 {
return string(section[start:end]), true
}
}
return "", false
}
// Section returns the first section with the given name, or nil if no such
// section exists.
func (f *File) Section(name string) *Section {
for _, s := range f.Sections {
if s.Name == name {
return s
}
}
return nil
}
func (f *File) DWARF() (*dwarf.Data, error) {
dwarfSuffix := func(s *Section) string {
switch {
case strings.HasPrefix(s.Name, ".debug_"):
return s.Name[7:]
case strings.HasPrefix(s.Name, ".zdebug_"):
return s.Name[8:]
default:
return ""
}
}
// sectionData gets the data for s and checks its size.
sectionData := func(s *Section) ([]byte, error) {
b, err := s.Data()
if err != nil && uint32(len(b)) < s.Size {
return nil, err
}
if 0 < s.VirtualSize && s.VirtualSize < s.Size {
b = b[:s.VirtualSize]
}
if len(b) >= 12 && string(b[:4]) == "ZLIB" {
dlen := binary.BigEndian.Uint64(b[4:12])
dbuf := make([]byte, dlen)
r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
if err != nil {
return nil, err
}
if _, err := io.ReadFull(r, dbuf); err != nil {
return nil, err
}
if err := r.Close(); err != nil {
return nil, err
}
b = dbuf
}
return b, nil
}
// There are many other DWARF sections, but these
// are the ones the debug/dwarf package uses.
// Don't bother loading others.
var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
for _, s := range f.Sections {
suffix := dwarfSuffix(s)
if suffix == "" {
continue
}
if _, ok := dat[suffix]; !ok {
continue
}
b, err := sectionData(s)
if err != nil {
return nil, err
}
dat[suffix] = b
}
d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
if err != nil {
return nil, err
}
// Look for DWARF4 .debug_types sections.
for i, s := range f.Sections {
suffix := dwarfSuffix(s)
if suffix != "types" {
continue
}
b, err := sectionData(s)
if err != nil {
return nil, err
}
err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
if err != nil {
return nil, err
}
}
return d, nil
}
// TODO(brainman): document ImportDirectory once we decide what to do with it.
type ImportDirectory struct {
OriginalFirstThunk uint32
TimeDateStamp uint32
ForwarderChain uint32
Name uint32
FirstThunk uint32
dll string
}
// ImportedSymbols returns the names of all symbols
// referred to by the binary f that are expected to be
// satisfied by other libraries at dynamic load time.
// It does not return weak symbols.
func (f *File) ImportedSymbols() ([]string, error) {
if f.OptionalHeader == nil {
return nil, nil
}
var pe64 bool
switch f.Machine {
case IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_ARM64, IMAGE_FILE_MACHINE_RISCV64:
pe64 = true
}
// grab the number of data directory entries
var dd_length uint32
if pe64 {
dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
} else {
dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
}
// check that the length of data directory entries is large
// enough to include the imports directory.
if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
return nil, nil
}
// grab the import data directory entry
var idd DataDirectory
if pe64 {
idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
} else {
idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
}
// figure out which section contains the import directory table
var ds *Section
ds = nil
for _, s := range f.Sections {
if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize {
ds = s
break
}
}
// didn't find a section, so no import libraries were found
if ds == nil {
return nil, nil
}
d, err := ds.Data()
if err != nil {
return nil, err
}
// seek to the virtual address specified in the import data directory
d = d[idd.VirtualAddress-ds.VirtualAddress:]
// start decoding the import directory
var ida []ImportDirectory
for len(d) >= 20 {
var dt ImportDirectory
dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
dt.Name = binary.LittleEndian.Uint32(d[12:16])
dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
d = d[20:]
if dt.OriginalFirstThunk == 0 {
break
}
ida = append(ida, dt)
}
// TODO(brainman): this needs to be rewritten
// ds.Data() returns contents of section containing import table. Why store in variable called "names"?
// Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere.
// getString does not extracts a string from symbol string table (as getString doco says).
// Why ds.Data() called again and again in the loop?
// Needs test before rewrite.
names, _ := ds.Data()
var all []string
for _, dt := range ida {
dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
d, _ = ds.Data()
// seek to OriginalFirstThunk
d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
for len(d) > 0 {
if pe64 { // 64bit
va := binary.LittleEndian.Uint64(d[0:8])
d = d[8:]
if va == 0 {
break
}
if va&0x8000000000000000 > 0 { // is Ordinal
// TODO add dynimport ordinal support.
} else {
fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
all = append(all, fn+":"+dt.dll)
}
} else { // 32bit
va := binary.LittleEndian.Uint32(d[0:4])
d = d[4:]
if va == 0 {
break
}
if va&0x80000000 > 0 { // is Ordinal
// TODO add dynimport ordinal support.
//ord := va&0x0000FFFF
} else {
fn, _ := getString(names, int(va-ds.VirtualAddress+2))
all = append(all, fn+":"+dt.dll)
}
}
}
}
return all, nil
}
// ImportedLibraries returns the names of all libraries
// referred to by the binary f that are expected to be
// linked with the binary at dynamic link time.
func (f *File) ImportedLibraries() ([]string, error) {
// TODO
// cgo -dynimport don't use this for windows PE, so just return.
return nil, nil
}
// FormatError is unused.
// The type is retained for compatibility.
type FormatError struct {
}
func (e *FormatError) Error() string {
return "unknown error"
}
// readOptionalHeader accepts a io.ReadSeeker pointing to optional header in the PE file
// and its size as seen in the file header.
// It parses the given size of bytes and returns optional header. It infers whether the
// bytes being parsed refer to 32 bit or 64 bit version of optional header.
func readOptionalHeader(r io.ReadSeeker, sz uint16) (interface{}, error) {
// If optional header size is 0, return empty optional header.
if sz == 0 {
return nil, nil
}
var (
// First couple of bytes in option header state its type.
// We need to read them first to determine the type and
// validity of optional header.
ohMagic uint16
ohMagicSz = binary.Size(ohMagic)
)
// If optional header size is greater than 0 but less than its magic size, return error.
if sz < uint16(ohMagicSz) {
return nil, fmt.Errorf("optional header size is less than optional header magic size")
}
// read reads from io.ReadSeeke, r, into data.
var err error
read := func(data interface{}) bool {
err = binary.Read(r, binary.LittleEndian, data)
return err == nil
}
if !read(&ohMagic) {
return nil, fmt.Errorf("failure to read optional header magic: %v", err)
}
switch ohMagic {
case 0x10b: // PE32
var (
oh32 OptionalHeader32
// There can be 0 or more data directories. So the minimum size of optional
// header is calculated by subtracting oh32.DataDirectory size from oh32 size.
oh32MinSz = binary.Size(oh32) - binary.Size(oh32.DataDirectory)
)
if sz < uint16(oh32MinSz) {
return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) of PE32 optional header", sz, oh32MinSz)
}
// Init oh32 fields
oh32.Magic = ohMagic
if !read(&oh32.MajorLinkerVersion) ||
!read(&oh32.MinorLinkerVersion) ||
!read(&oh32.SizeOfCode) ||
!read(&oh32.SizeOfInitializedData) ||
!read(&oh32.SizeOfUninitializedData) ||
!read(&oh32.AddressOfEntryPoint) ||
!read(&oh32.BaseOfCode) ||
!read(&oh32.BaseOfData) ||
!read(&oh32.ImageBase) ||
!read(&oh32.SectionAlignment) ||
!read(&oh32.FileAlignment) ||
!read(&oh32.MajorOperatingSystemVersion) ||
!read(&oh32.MinorOperatingSystemVersion) ||
!read(&oh32.MajorImageVersion) ||
!read(&oh32.MinorImageVersion) ||
!read(&oh32.MajorSubsystemVersion) ||
!read(&oh32.MinorSubsystemVersion) ||
!read(&oh32.Win32VersionValue) ||
!read(&oh32.SizeOfImage) ||
!read(&oh32.SizeOfHeaders) ||
!read(&oh32.CheckSum) ||
!read(&oh32.Subsystem) ||
!read(&oh32.DllCharacteristics) ||
!read(&oh32.SizeOfStackReserve) ||
!read(&oh32.SizeOfStackCommit) ||
!read(&oh32.SizeOfHeapReserve) ||
!read(&oh32.SizeOfHeapCommit) ||
!read(&oh32.LoaderFlags) ||
!read(&oh32.NumberOfRvaAndSizes) {
return nil, fmt.Errorf("failure to read PE32 optional header: %v", err)
}
dd, err := readDataDirectories(r, sz-uint16(oh32MinSz), oh32.NumberOfRvaAndSizes)
if err != nil {
return nil, err
}
copy(oh32.DataDirectory[:], dd)
return &oh32, nil
case 0x20b: // PE32+
var (
oh64 OptionalHeader64
// There can be 0 or more data directories. So the minimum size of optional
// header is calculated by subtracting oh64.DataDirectory size from oh64 size.
oh64MinSz = binary.Size(oh64) - binary.Size(oh64.DataDirectory)
)
if sz < uint16(oh64MinSz) {
return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) for PE32+ optional header", sz, oh64MinSz)
}
// Init oh64 fields
oh64.Magic = ohMagic
if !read(&oh64.MajorLinkerVersion) ||
!read(&oh64.MinorLinkerVersion) ||
!read(&oh64.SizeOfCode) ||
!read(&oh64.SizeOfInitializedData) ||
!read(&oh64.SizeOfUninitializedData) ||
!read(&oh64.AddressOfEntryPoint) ||
!read(&oh64.BaseOfCode) ||
!read(&oh64.ImageBase) ||
!read(&oh64.SectionAlignment) ||
!read(&oh64.FileAlignment) ||
!read(&oh64.MajorOperatingSystemVersion) ||
!read(&oh64.MinorOperatingSystemVersion) ||
!read(&oh64.MajorImageVersion) ||
!read(&oh64.MinorImageVersion) ||
!read(&oh64.MajorSubsystemVersion) ||
!read(&oh64.MinorSubsystemVersion) ||
!read(&oh64.Win32VersionValue) ||
!read(&oh64.SizeOfImage) ||
!read(&oh64.SizeOfHeaders) ||
!read(&oh64.CheckSum) ||
!read(&oh64.Subsystem) ||
!read(&oh64.DllCharacteristics) ||
!read(&oh64.SizeOfStackReserve) ||
!read(&oh64.SizeOfStackCommit) ||
!read(&oh64.SizeOfHeapReserve) ||
!read(&oh64.SizeOfHeapCommit) ||
!read(&oh64.LoaderFlags) ||
!read(&oh64.NumberOfRvaAndSizes) {
return nil, fmt.Errorf("failure to read PE32+ optional header: %v", err)
}
dd, err := readDataDirectories(r, sz-uint16(oh64MinSz), oh64.NumberOfRvaAndSizes)
if err != nil {
return nil, err
}
copy(oh64.DataDirectory[:], dd)
return &oh64, nil
default:
return nil, fmt.Errorf("optional header has unexpected Magic of 0x%x", ohMagic)
}
}
// readDataDirectories accepts a io.ReadSeeker pointing to data directories in the PE file,
// its size and number of data directories as seen in optional header.
// It parses the given size of bytes and returns given number of data directories.
func readDataDirectories(r io.ReadSeeker, sz uint16, n uint32) ([]DataDirectory, error) {
ddSz := binary.Size(DataDirectory{})
if uint32(sz) != n*uint32(ddSz) {
return nil, fmt.Errorf("size of data directories(%d) is inconsistent with number of data directories(%d)", sz, n)
}
dd := make([]DataDirectory, n)
if err := binary.Read(r, binary.LittleEndian, dd); err != nil {
return nil, fmt.Errorf("failure to read data directories: %v", err)
}
return dd, nil
}
./github.com/canonical/go-efilib/internal/pe1.14/pe.go 0000664 0000000 0000000 00000010673 00000000000 020655 0 ustar 00 0000000 0000000 // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pe
type FileHeader struct {
Machine uint16
NumberOfSections uint16
TimeDateStamp uint32
PointerToSymbolTable uint32
NumberOfSymbols uint32
SizeOfOptionalHeader uint16
Characteristics uint16
}
type DataDirectory struct {
VirtualAddress uint32
Size uint32
}
type OptionalHeader32 struct {
Magic uint16
MajorLinkerVersion uint8
MinorLinkerVersion uint8
SizeOfCode uint32
SizeOfInitializedData uint32
SizeOfUninitializedData uint32
AddressOfEntryPoint uint32
BaseOfCode uint32
BaseOfData uint32
ImageBase uint32
SectionAlignment uint32
FileAlignment uint32
MajorOperatingSystemVersion uint16
MinorOperatingSystemVersion uint16
MajorImageVersion uint16
MinorImageVersion uint16
MajorSubsystemVersion uint16
MinorSubsystemVersion uint16
Win32VersionValue uint32
SizeOfImage uint32
SizeOfHeaders uint32
CheckSum uint32
Subsystem uint16
DllCharacteristics uint16
SizeOfStackReserve uint32
SizeOfStackCommit uint32
SizeOfHeapReserve uint32
SizeOfHeapCommit uint32
LoaderFlags uint32
NumberOfRvaAndSizes uint32
DataDirectory [16]DataDirectory
}
type OptionalHeader64 struct {
Magic uint16
MajorLinkerVersion uint8
MinorLinkerVersion uint8
SizeOfCode uint32
SizeOfInitializedData uint32
SizeOfUninitializedData uint32
AddressOfEntryPoint uint32
BaseOfCode uint32
ImageBase uint64
SectionAlignment uint32
FileAlignment uint32
MajorOperatingSystemVersion uint16
MinorOperatingSystemVersion uint16
MajorImageVersion uint16
MinorImageVersion uint16
MajorSubsystemVersion uint16
MinorSubsystemVersion uint16
Win32VersionValue uint32
SizeOfImage uint32
SizeOfHeaders uint32
CheckSum uint32
Subsystem uint16
DllCharacteristics uint16
SizeOfStackReserve uint64
SizeOfStackCommit uint64
SizeOfHeapReserve uint64
SizeOfHeapCommit uint64
LoaderFlags uint32
NumberOfRvaAndSizes uint32
DataDirectory [16]DataDirectory
}
const (
IMAGE_FILE_MACHINE_UNKNOWN = 0x0
IMAGE_FILE_MACHINE_AM33 = 0x1d3
IMAGE_FILE_MACHINE_AMD64 = 0x8664
IMAGE_FILE_MACHINE_ARM = 0x1c0
IMAGE_FILE_MACHINE_ARMNT = 0x1c4
IMAGE_FILE_MACHINE_ARM64 = 0xaa64
IMAGE_FILE_MACHINE_EBC = 0xebc
IMAGE_FILE_MACHINE_I386 = 0x14c
IMAGE_FILE_MACHINE_IA64 = 0x200
IMAGE_FILE_MACHINE_LOONGARCH32 = 0x6232
IMAGE_FILE_MACHINE_LOONGARCH64 = 0x6264
IMAGE_FILE_MACHINE_M32R = 0x9041
IMAGE_FILE_MACHINE_MIPS16 = 0x266
IMAGE_FILE_MACHINE_MIPSFPU = 0x366
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466
IMAGE_FILE_MACHINE_POWERPC = 0x1f0
IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1
IMAGE_FILE_MACHINE_R4000 = 0x166
IMAGE_FILE_MACHINE_RISCV32 = 0x5032
IMAGE_FILE_MACHINE_RISCV64 = 0x5064
IMAGE_FILE_MACHINE_RISCV128 = 0x5128
IMAGE_FILE_MACHINE_SH3 = 0x1a2
IMAGE_FILE_MACHINE_SH3DSP = 0x1a3
IMAGE_FILE_MACHINE_SH4 = 0x1a6
IMAGE_FILE_MACHINE_SH5 = 0x1a8
IMAGE_FILE_MACHINE_THUMB = 0x1c2
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
)
// IMAGE_DIRECTORY_ENTRY constants
const (
IMAGE_DIRECTORY_ENTRY_EXPORT = 0
IMAGE_DIRECTORY_ENTRY_IMPORT = 1
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3
IMAGE_DIRECTORY_ENTRY_SECURITY = 4
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5
IMAGE_DIRECTORY_ENTRY_DEBUG = 6
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8
IMAGE_DIRECTORY_ENTRY_TLS = 9
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11
IMAGE_DIRECTORY_ENTRY_IAT = 12
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14
)
./github.com/canonical/go-efilib/internal/pe1.14/section.go 0000664 0000000 0000000 00000005604 00000000000 021713 0 ustar 00 0000000 0000000 // Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pe
import (
"encoding/binary"
"fmt"
"io"
"strconv"
)
// SectionHeader32 represents real PE COFF section header.
type SectionHeader32 struct {
Name [8]uint8
VirtualSize uint32
VirtualAddress uint32
SizeOfRawData uint32
PointerToRawData uint32
PointerToRelocations uint32
PointerToLineNumbers uint32
NumberOfRelocations uint16
NumberOfLineNumbers uint16
Characteristics uint32
}
// fullName finds real name of section sh. Normally name is stored
// in sh.Name, but if it is longer then 8 characters, it is stored
// in COFF string table st instead.
func (sh *SectionHeader32) fullName(st StringTable) (string, error) {
if sh.Name[0] != '/' {
return cstring(sh.Name[:]), nil
}
i, err := strconv.Atoi(cstring(sh.Name[1:]))
if err != nil {
return "", err
}
return st.String(uint32(i))
}
// TODO(brainman): copy all IMAGE_REL_* consts from ldpe.go here
// Reloc represents a PE COFF relocation.
// Each section contains its own relocation list.
type Reloc struct {
VirtualAddress uint32
SymbolTableIndex uint32
Type uint16
}
func readRelocs(sh *SectionHeader, r io.ReadSeeker) ([]Reloc, error) {
if sh.NumberOfRelocations <= 0 {
return nil, nil
}
_, err := r.Seek(int64(sh.PointerToRelocations), seekStart)
if err != nil {
return nil, fmt.Errorf("fail to seek to %q section relocations: %v", sh.Name, err)
}
relocs := make([]Reloc, sh.NumberOfRelocations)
err = binary.Read(r, binary.LittleEndian, relocs)
if err != nil {
return nil, fmt.Errorf("fail to read section relocations: %v", err)
}
return relocs, nil
}
// SectionHeader is similar to SectionHeader32 with Name
// field replaced by Go string.
type SectionHeader struct {
Name string
VirtualSize uint32
VirtualAddress uint32
Size uint32
Offset uint32
PointerToRelocations uint32
PointerToLineNumbers uint32
NumberOfRelocations uint16
NumberOfLineNumbers uint16
Characteristics uint32
}
// Section provides access to PE COFF section.
type Section struct {
SectionHeader
Relocs []Reloc
// Embed ReaderAt for ReadAt method.
// Do not embed SectionReader directly
// to avoid having Read and Seek.
// If a client wants Read and Seek it must use
// Open() to avoid fighting over the seek offset
// with other clients.
io.ReaderAt
sr *io.SectionReader
}
// Data reads and returns the contents of the PE section s.
func (s *Section) Data() ([]byte, error) {
dat := make([]byte, s.sr.Size())
n, err := s.sr.ReadAt(dat, 0)
if n == len(dat) {
err = nil
}
return dat[0:n], err
}
// Open returns a new ReadSeeker reading the PE section s.
func (s *Section) Open() io.ReadSeeker {
return io.NewSectionReader(s.sr, 0, 1<<63-1)
}
./github.com/canonical/go-efilib/internal/pe1.14/string.go 0000664 0000000 0000000 00000003433 00000000000 021553 0 ustar 00 0000000 0000000 // Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pe
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
// cstring converts ASCII byte sequence b to string.
// It stops once it finds 0 or reaches end of b.
func cstring(b []byte) string {
i := bytes.IndexByte(b, 0)
if i == -1 {
i = len(b)
}
return string(b[:i])
}
// StringTable is a COFF string table.
type StringTable []byte
func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) {
// COFF string table is located right after COFF symbol table.
if fh.PointerToSymbolTable <= 0 {
return nil, nil
}
offset := fh.PointerToSymbolTable + COFFSymbolSize*fh.NumberOfSymbols
_, err := r.Seek(int64(offset), seekStart)
if err != nil {
return nil, fmt.Errorf("fail to seek to string table: %v", err)
}
var l uint32
err = binary.Read(r, binary.LittleEndian, &l)
if err != nil {
return nil, fmt.Errorf("fail to read string table length: %v", err)
}
// string table length includes itself
if l <= 4 {
return nil, nil
}
l -= 4
buf := make([]byte, l)
_, err = io.ReadFull(r, buf)
if err != nil {
return nil, fmt.Errorf("fail to read string table: %v", err)
}
return StringTable(buf), nil
}
// TODO(brainman): decide if start parameter should be int instead of uint32
// String extracts string from COFF string table st at offset start.
func (st StringTable) String(start uint32) (string, error) {
// start includes 4 bytes of string table length
if start < 4 {
return "", fmt.Errorf("offset %d is before the start of string table", start)
}
start -= 4
if int(start) > len(st) {
return "", fmt.Errorf("offset %d is beyond the end of string table", start)
}
return cstring(st[start:]), nil
}
./github.com/canonical/go-efilib/internal/pe1.14/symbol.go 0000664 0000000 0000000 00000004656 00000000000 021562 0 ustar 00 0000000 0000000 // Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pe
import (
"encoding/binary"
"fmt"
"io"
)
const COFFSymbolSize = 18
// COFFSymbol represents single COFF symbol table record.
type COFFSymbol struct {
Name [8]uint8
Value uint32
SectionNumber int16
Type uint16
StorageClass uint8
NumberOfAuxSymbols uint8
}
func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) {
if fh.PointerToSymbolTable == 0 {
return nil, nil
}
if fh.NumberOfSymbols <= 0 {
return nil, nil
}
_, err := r.Seek(int64(fh.PointerToSymbolTable), seekStart)
if err != nil {
return nil, fmt.Errorf("fail to seek to symbol table: %v", err)
}
syms := make([]COFFSymbol, fh.NumberOfSymbols)
err = binary.Read(r, binary.LittleEndian, syms)
if err != nil {
return nil, fmt.Errorf("fail to read symbol table: %v", err)
}
return syms, nil
}
// isSymNameOffset checks symbol name if it is encoded as offset into string table.
func isSymNameOffset(name [8]byte) (bool, uint32) {
if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 {
return true, binary.LittleEndian.Uint32(name[4:])
}
return false, 0
}
// FullName finds real name of symbol sym. Normally name is stored
// in sym.Name, but if it is longer then 8 characters, it is stored
// in COFF string table st instead.
func (sym *COFFSymbol) FullName(st StringTable) (string, error) {
if ok, offset := isSymNameOffset(sym.Name); ok {
return st.String(offset)
}
return cstring(sym.Name[:]), nil
}
func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) {
if len(allsyms) == 0 {
return nil, nil
}
syms := make([]*Symbol, 0)
aux := uint8(0)
for _, sym := range allsyms {
if aux > 0 {
aux--
continue
}
name, err := sym.FullName(st)
if err != nil {
return nil, err
}
aux = sym.NumberOfAuxSymbols
s := &Symbol{
Name: name,
Value: sym.Value,
SectionNumber: sym.SectionNumber,
Type: sym.Type,
StorageClass: sym.StorageClass,
}
syms = append(syms, s)
}
return syms, nil
}
// Symbol is similar to COFFSymbol with Name field replaced
// by Go string. Symbol also does not have NumberOfAuxSymbols.
type Symbol struct {
Name string
Value uint32
SectionNumber int16
Type uint16
StorageClass uint8
}
./github.com/canonical/go-efilib/internal/uefi/ 0000775 0000000 0000000 00000000000 00000000000 017733 5 ustar 00 0000000 0000000 ./github.com/canonical/go-efilib/internal/uefi/authvars.go 0000664 0000000 0000000 00000005135 00000000000 022123 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
import (
"encoding/binary"
"io"
"github.com/canonical/go-efilib/internal/ioerr"
)
const (
EFI_VARIABLE_AUTHENTICATION_3_CERT_ID_SHA256 = 1
EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE = 1
EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE = 2
)
type EFI_VARIABLE_AUTHENTICATION struct {
MonotonicCount uint64
AuthInfo WIN_CERTIFICATE_UEFI_GUID
}
func Read_EFI_VARIABLE_AUTHENTICATION(r io.Reader) (out *EFI_VARIABLE_AUTHENTICATION, err error) {
out = &EFI_VARIABLE_AUTHENTICATION{}
if err := binary.Read(r, binary.LittleEndian, &out.MonotonicCount); err != nil {
return nil, err
}
cert, err := Read_WIN_CERTIFICATE_UEFI_GUID(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out.AuthInfo = *cert
return out, nil
}
type EFI_VARIABLE_AUTHENTICATION_2 struct {
TimeStamp EFI_TIME
AuthInfo WIN_CERTIFICATE_UEFI_GUID
}
func Read_EFI_VARIABLE_AUTHENTICATION_2(r io.Reader) (out *EFI_VARIABLE_AUTHENTICATION_2, err error) {
out = &EFI_VARIABLE_AUTHENTICATION_2{}
if err := binary.Read(r, binary.LittleEndian, &out.TimeStamp); err != nil {
return nil, err
}
cert, err := Read_WIN_CERTIFICATE_UEFI_GUID(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out.AuthInfo = *cert
return out, nil
}
type EFI_VARIABLE_AUTHENTICATION_3 struct {
Version uint8
Type uint8
MetadataSize uint32
Flags uint32
}
type EFI_VARIABLE_AUTHENTICATION_3_CERT_ID struct {
Type uint8
IdSize uint32
Id []byte
}
func Read_EFI_VARIABLE_AUTHENTICATION_3_CERT_ID(r io.Reader) (out *EFI_VARIABLE_AUTHENTICATION_3_CERT_ID, err error) {
out = &EFI_VARIABLE_AUTHENTICATION_3_CERT_ID{}
if err := binary.Read(r, binary.LittleEndian, &out.Type); err != nil {
return nil, err
}
if err := binary.Read(r, binary.LittleEndian, &out.IdSize); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out.Id = make([]byte, out.IdSize)
if _, err := io.ReadFull(r, out.Id); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return out, nil
}
type EFI_VARIABLE_AUTHENTICATION_3_NONCE struct {
NonceSize uint32
Nonce []byte
}
func Read_EFI_VARIABLE_AUTHENTICATION_3_NONCE(r io.Reader) (out *EFI_VARIABLE_AUTHENTICATION_3_NONCE, err error) {
out = &EFI_VARIABLE_AUTHENTICATION_3_NONCE{}
if err := binary.Read(r, binary.LittleEndian, &out.NonceSize); err != nil {
return nil, err
}
out.Nonce = make([]byte, out.NonceSize)
if _, err := io.ReadFull(r, out.Nonce); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return out, nil
}
./github.com/canonical/go-efilib/internal/uefi/base.go 0000664 0000000 0000000 00000001161 00000000000 021173 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
import (
"encoding/binary"
)
type EFI_GUID [16]byte
func New_EFI_GUID(a uint32, b, c, d uint16, e [6]uint8) (out EFI_GUID) {
binary.LittleEndian.PutUint32(out[0:4], a)
binary.LittleEndian.PutUint16(out[4:6], b)
binary.LittleEndian.PutUint16(out[6:8], c)
binary.BigEndian.PutUint16(out[8:10], d)
copy(out[10:], e[:])
return
}
type EFI_LBA uint64
type EFI_TABLE_HEADER struct {
Signature uint64
Revision uint32
HeaderSize uint32
CRC uint32
Reserved uint32
}
./github.com/canonical/go-efilib/internal/uefi/db.go 0000664 0000000 0000000 00000006371 00000000000 020656 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
import (
"encoding/binary"
"errors"
"io"
"math"
"github.com/canonical/go-efilib/internal/ioerr"
)
const ESLHeaderSize = 28
type EFI_SIGNATURE_DATA struct {
SignatureOwner EFI_GUID
SignatureData []byte
}
func (d *EFI_SIGNATURE_DATA) Write(w io.Writer) error {
if _, err := w.Write(d.SignatureOwner[:]); err != nil {
return err
}
if _, err := w.Write(d.SignatureData); err != nil {
return err
}
return nil
}
type EFI_SIGNATURE_LIST struct {
SignatureType EFI_GUID
SignatureListSize uint32
SignatureHeaderSize uint32
SignatureSize uint32
SignatureHeader []byte
Signatures []EFI_SIGNATURE_DATA
}
func (l *EFI_SIGNATURE_LIST) Write(w io.Writer) error {
if _, err := w.Write(l.SignatureType[:]); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, l.SignatureListSize); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, l.SignatureHeaderSize); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, l.SignatureSize); err != nil {
return err
}
if _, err := w.Write(l.SignatureHeader); err != nil {
return err
}
for _, s := range l.Signatures {
if err := s.Write(w); err != nil {
return err
}
}
return nil
}
func Read_EFI_SIGNATURE_LIST(r io.Reader) (out *EFI_SIGNATURE_LIST, err error) {
out = &EFI_SIGNATURE_LIST{}
if err := binary.Read(r, binary.LittleEndian, &out.SignatureType); err != nil {
return nil, err
}
if err := binary.Read(r, binary.LittleEndian, &out.SignatureListSize); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
if err := binary.Read(r, binary.LittleEndian, &out.SignatureHeaderSize); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
if err := binary.Read(r, binary.LittleEndian, &out.SignatureSize); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
if out.SignatureHeaderSize > math.MaxUint32-ESLHeaderSize {
return nil, errors.New("signature header size too large")
}
if out.SignatureHeaderSize+ESLHeaderSize > out.SignatureListSize {
return nil, errors.New("inconsistent size fields: total signatures payload size underflows")
}
signaturesSize := out.SignatureListSize - out.SignatureHeaderSize - ESLHeaderSize
if out.SignatureSize < uint32(binary.Size(EFI_GUID{})) {
return nil, errors.New("invalid SignatureSize")
}
if signaturesSize%out.SignatureSize != 0 {
return nil, errors.New("inconsistent size fields: total signatures payload size not a multiple of the individual signature size")
}
numOfSignatures := int(signaturesSize / out.SignatureSize)
out.SignatureHeader = make([]byte, out.SignatureHeaderSize)
if _, err := io.ReadFull(r, out.SignatureHeader); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
for i := 0; i < numOfSignatures; i++ {
var s EFI_SIGNATURE_DATA
if _, err := io.ReadFull(r, s.SignatureOwner[:]); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
s.SignatureData = make([]byte, int(out.SignatureSize)-binary.Size(s.SignatureOwner))
if _, err := io.ReadFull(r, s.SignatureData); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out.Signatures = append(out.Signatures, s)
}
return out, nil
}
./github.com/canonical/go-efilib/internal/uefi/devicepath.go 0000664 0000000 0000000 00000013303 00000000000 022376 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
import (
"encoding/binary"
"io"
"github.com/canonical/go-efilib/internal/ioerr"
)
const (
NO_DISK_SIGNATURE = 0x00
SIGNATURE_TYPE_MBR = 0x01
SIGNATURE_TYPE_GUID = 0x02
HARDWARE_DEVICE_PATH = 0x01
ACPI_DEVICE_PATH = 0x02
MESSAGING_DEVICE_PATH = 0x03
MEDIA_DEVICE_PATH = 0x04
BBS_DEVICE_PATH = 0x05
END_DEVICE_PATH_TYPE = 0x7f
HW_PCI_DP = 0x01
HW_VENDOR_DP = 0x04
ACPI_DP = 0x01
ACPI_EXTENDED_DP = 0x02
MSG_ATAPI_DP = 0x01
MSG_SCSI_DP = 0x02
MSG_USB_DP = 0x05
MSG_USB_CLASS_DP = 0x0f
MSG_VENDOR_DP = 0x0a
MSG_USB_WWID_DP = 0x10
MSG_DEVICE_LOGICAL_UNIT_DP = 0x11
MSG_SATA_DP = 0x12
MSG_NVME_NAMESPACE_DP = 0x17
MEDIA_HARDDRIVE_DP = 0x01
MEDIA_CDROM_DP = 0x02
MEDIA_VENDOR_DP = 0x03
MEDIA_FILEPATH_DP = 0x04
MEDIA_PIWG_FW_FILE_DP = 0x06
MEDIA_PIWG_FW_VOL_DP = 0x07
MEDIA_RELATIVE_OFFSET_RANGE_DP = 0x08
END_ENTIRE_DEVICE_PATH_SUBTYPE = 0xff
)
type EFI_DEVICE_PATH_PROTOCOL struct {
Type uint8
SubType uint8
Length uint16
}
type PCI_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
Function uint8
Device uint8
}
type VENDOR_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
Guid EFI_GUID
}
type ACPI_HID_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
HID uint32
UID uint32
}
type ACPI_EXTENDED_HID_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
HID uint32
UID uint32
CID uint32
}
type ATAPI_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
PrimarySecondary uint8
SlaveMaster uint8
Lun uint16
}
type SCSI_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
Pun uint16
Lun uint16
}
type USB_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
ParentPortNumber uint8
InterfaceNumber uint8
}
type USB_CLASS_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
VendorId uint16
ProductId uint16
DeviceClass uint8
DeviceSubClass uint8
DeviceProtocol uint8
}
type USB_WWID_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
InterfaceNumber uint16
VendorId uint16
ProductId uint16
SerialNumber []uint16
}
func (p *USB_WWID_DEVICE_PATH) Write(w io.Writer) error {
if err := binary.Write(w, binary.LittleEndian, &p.Header); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, p.InterfaceNumber); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, p.VendorId); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, p.ProductId); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, p.SerialNumber); err != nil {
return err
}
return nil
}
func Read_USB_WWID_DEVICE_PATH(r io.Reader) (out *USB_WWID_DEVICE_PATH, err error) {
out = &USB_WWID_DEVICE_PATH{}
if err := binary.Read(r, binary.LittleEndian, &out.Header); err != nil {
return nil, err
}
if err := binary.Read(r, binary.LittleEndian, &out.InterfaceNumber); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
if err := binary.Read(r, binary.LittleEndian, &out.VendorId); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
if err := binary.Read(r, binary.LittleEndian, &out.ProductId); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out.SerialNumber = make([]uint16, (int(out.Header.Length)-binary.Size(out.Header)-binary.Size(out.InterfaceNumber)-binary.Size(out.VendorId)-binary.Size(out.ProductId))/2)
if err := binary.Read(r, binary.LittleEndian, out.SerialNumber); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return out, nil
}
type DEVICE_LOGICAL_UNIT_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
Lun uint8
}
type SATA_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
HBAPortNumber uint16
PortMultiplierPortNumber uint16
Lun uint16
}
type NVME_NAMESPACE_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
NamespaceId uint32
NamespaceUuid uint64
}
type HARDDRIVE_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
PartitionNumber uint32
PartitionStart uint64
PartitionSize uint64
Signature [16]uint8
MBRType uint8
SignatureType uint8
}
type CDROM_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
BootEntry uint32
PartitionStart uint64
PartitionSize uint64
}
type FILEPATH_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
PathName []uint16
}
func (p *FILEPATH_DEVICE_PATH) Write(w io.Writer) error {
if err := binary.Write(w, binary.LittleEndian, &p.Header); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, p.PathName); err != nil {
return err
}
return nil
}
func Read_FILEPATH_DEVICE_PATH(r io.Reader) (out *FILEPATH_DEVICE_PATH, err error) {
out = &FILEPATH_DEVICE_PATH{}
if err := binary.Read(r, binary.LittleEndian, &out.Header); err != nil {
return nil, err
}
out.PathName = make([]uint16, (int(out.Header.Length)-binary.Size(out.Header))/2)
if err := binary.Read(r, binary.LittleEndian, &out.PathName); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return out, nil
}
type MEDIA_FW_VOL_FILEPATH_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
FvFileName EFI_GUID
}
type MEDIA_FW_VOL_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
FvName EFI_GUID
}
type MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH struct {
Header EFI_DEVICE_PATH_PROTOCOL
Reserved uint32
StartingOffset uint64
EndingOffset uint64
}
./github.com/canonical/go-efilib/internal/uefi/gpt.go 0000664 0000000 0000000 00000003331 00000000000 021054 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
import (
"bytes"
"encoding/binary"
"errors"
"hash/crc32"
"io"
"github.com/canonical/go-efilib/internal/ioerr"
)
const EFI_PTAB_HEADER_ID uint64 = 0x5452415020494645
type EFI_PARTITION_ENTRY struct {
PartitionTypeGUID EFI_GUID
UniquePartitionGUID EFI_GUID
StartingLBA EFI_LBA
EndingLBA EFI_LBA
Attributes uint64
PartitionName [36]uint16
}
type EFI_PARTITION_TABLE_HEADER struct {
Hdr EFI_TABLE_HEADER
MyLBA EFI_LBA
AlternateLBA EFI_LBA
FirstUsableLBA EFI_LBA
LastUsableLBA EFI_LBA
DiskGUID EFI_GUID
PartitionEntryLBA EFI_LBA
NumberOfPartitionEntries uint32
SizeOfPartitionEntry uint32
PartitionEntryArrayCRC32 uint32
}
func Read_EFI_PARTITION_TABLE_HEADER(r io.Reader) (out *EFI_PARTITION_TABLE_HEADER, crc uint32, err error) {
var hdr EFI_TABLE_HEADER
if err := binary.Read(r, binary.LittleEndian, &hdr); err != nil {
return nil, 0, err
}
if hdr.HeaderSize < uint32(binary.Size(hdr)) {
return nil, 0, errors.New("invalid header size")
}
origCrc := hdr.CRC
hdr.CRC = 0
b := new(bytes.Buffer)
if err := binary.Write(b, binary.LittleEndian, &hdr); err != nil {
return nil, 0, err
}
if _, err := io.CopyN(b, r, int64(hdr.HeaderSize-uint32(binary.Size(hdr)))); err != nil {
return nil, 0, ioerr.EOFIsUnexpected(err)
}
crc = crc32.ChecksumIEEE(b.Bytes())
out = &EFI_PARTITION_TABLE_HEADER{}
if err := binary.Read(b, binary.LittleEndian, out); err != nil {
return nil, 0, err
}
out.Hdr.CRC = origCrc
return out, crc, nil
}
./github.com/canonical/go-efilib/internal/uefi/ids.go 0000664 0000000 0000000 00000005426 00000000000 021050 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
var (
EFI_HASH_ALGORITHM_SHA1_GUID = New_EFI_GUID(0x2ae9d80f, 0x3fb2, 0x4095, 0xb7b1, [...]uint8{0xe9, 0x31, 0x57, 0xb9, 0x46, 0xb6})
EFI_HASH_ALGORITHM_SHA256_GUID = New_EFI_GUID(0x51aa59de, 0xfdf2, 0x4ea3, 0xbc63, [...]uint8{0x87, 0x5f, 0xb7, 0x84, 0x2e, 0xe9})
EFI_HASH_ALGORITHM_SHA224_GUID = New_EFI_GUID(0x8df01a06, 0x9bd5, 0x4bf7, 0xb021, [...]uint8{0xdb, 0x4f, 0xd9, 0xcc, 0xf4, 0x5b})
EFI_HASH_ALGORITHM_SHA384_GUID = New_EFI_GUID(0xefa96432, 0xde33, 0x4dd2, 0xaee6, [...]uint8{0x32, 0x8c, 0x33, 0xdf, 0x77, 0x7a})
EFI_HASH_ALGORITHM_SHA512_GUID = New_EFI_GUID(0xcaa4381e, 0x750c, 0x4770, 0xb870, [...]uint8{0x7a, 0x23, 0xb4, 0xe4, 0x21, 0x30})
EFI_CERT_TYPE_RSA2048_SHA256_GUID = New_EFI_GUID(0xa7717414, 0xc616, 0x4977, 0x9420, [...]uint8{0x84, 0x47, 0x12, 0xa7, 0x35, 0xbf})
EFI_CERT_TYPE_PKCS7_GUID = New_EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8aa9, [...]uint8{0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7})
EFI_CERT_SHA1_GUID = New_EFI_GUID(0x826ca512, 0xcf10, 0x4ac9, 0xb187, [...]uint8{0xbe, 0x01, 0x49, 0x66, 0x31, 0xbd})
EFI_CERT_SHA256_GUID = New_EFI_GUID(0xc1c41626, 0x504c, 0x4092, 0xaca9, [...]uint8{0x41, 0xf9, 0x36, 0x93, 0x43, 0x28})
EFI_CERT_SHA224_GUID = New_EFI_GUID(0xb6e5233, 0xa65c, 0x44c9, 0x9407, [...]uint8{0xd9, 0xab, 0x83, 0xbf, 0xc8, 0xbd})
EFI_CERT_SHA384_GUID = New_EFI_GUID(0xff3e5307, 0x9fd0, 0x48c9, 0x85f1, [...]uint8{0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x01})
EFI_CERT_SHA512_GUID = New_EFI_GUID(0x093e0fae, 0xa6c4, 0x4f50, 0x9f1b, [...]uint8{0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a})
EFI_CERT_RSA2048_GUID = New_EFI_GUID(0x3c5766e8, 0x269c, 0x4e34, 0xaa14, [...]uint8{0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6})
EFI_CERT_RSA2048_SHA1_GUID = New_EFI_GUID(0x67f8444f, 0x8743, 0x48f1, 0xa328, [...]uint8{0x1e, 0xaa, 0xb8, 0x73, 0x60, 0x80})
EFI_CERT_RSA2048_SHA256_GUID = New_EFI_GUID(0xe2b36190, 0x879b, 0x4a3d, 0xad8d, [...]uint8{0xf2, 0xe7, 0xbb, 0xa3, 0x27, 0x84})
EFI_CERT_X509_GUID = New_EFI_GUID(0xa5c059a1, 0x94e4, 0x4aa7, 0x87b5, [...]uint8{0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72})
EFI_CERT_X509_SHA256_GUID = New_EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb420, [...]uint8{0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed})
EFI_CERT_X509_SHA384_GUID = New_EFI_GUID(0x7076876e, 0x80c2, 0x4ee6, 0xaad2, [...]uint8{0x28, 0xb3, 0x49, 0xa6, 0x86, 0x5b})
EFI_CERT_X509_SHA512_GUID = New_EFI_GUID(0x446dbf63, 0x2502, 0x4cda, 0xbcfa, [...]uint8{0x24, 0x65, 0xd2, 0xb0, 0xfe, 0x9d})
EFI_GLOBAL_VARIABLE = New_EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa0d, [...]uint8{0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c})
EFI_IMAGE_SECURITY_DATABASE_GUID = New_EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3bc, [...]uint8{0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f})
)
./github.com/canonical/go-efilib/internal/uefi/loadoption.go 0000664 0000000 0000000 00000003735 00000000000 022442 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
import (
"encoding/binary"
"io"
"io/ioutil"
"github.com/canonical/go-efilib/internal/ioerr"
)
const (
LOAD_OPTION_ACTIVE = 0x00000001
LOAD_OPTION_FORCE_RECONNECT = 0x00000002
LOAD_OPTION_HIDDEN = 0x00000008
LOAD_OPTION_CATEGORY = 0x00001f00
LOAD_OPTION_CATEGORY_BOOT = 0x00000000
LOAD_OPTION_CATEGORY_APP = 0x00000100
)
type EFI_LOAD_OPTION struct {
Attributes uint32
FilePathListLength uint16
Description []uint16
FilePathList []byte
OptionalData []byte
}
func (o *EFI_LOAD_OPTION) Write(w io.Writer) error {
if err := binary.Write(w, binary.LittleEndian, o.Attributes); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, o.FilePathListLength); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, o.Description); err != nil {
return err
}
if _, err := w.Write(o.FilePathList); err != nil {
return err
}
if _, err := w.Write(o.OptionalData); err != nil {
return err
}
return nil
}
func Read_EFI_LOAD_OPTION(r io.Reader) (out *EFI_LOAD_OPTION, err error) {
out = &EFI_LOAD_OPTION{}
if err := binary.Read(r, binary.LittleEndian, &out.Attributes); err != nil {
return nil, err
}
if err := binary.Read(r, binary.LittleEndian, &out.FilePathListLength); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
for i := 0; ; i++ {
var c uint16
if err := binary.Read(r, binary.LittleEndian, &c); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out.Description = append(out.Description, c)
if c == 0 {
break
}
}
out.FilePathList = make([]byte, out.FilePathListLength)
if _, err := io.ReadFull(r, out.FilePathList); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
optionalData, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
out.OptionalData = optionalData
return out, nil
}
./github.com/canonical/go-efilib/internal/uefi/time.go 0000664 0000000 0000000 00000001664 00000000000 021227 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
import (
"time"
)
type EFI_TIME struct {
Year uint16
Month uint8
Day uint8
Hour uint8
Minute uint8
Second uint8
Pad1 uint8
Nanosecond uint32
Timezone int16
Daylight uint8
Pad2 uint8
}
func (t *EFI_TIME) GoTime() time.Time {
return time.Date(int(t.Year), time.Month(t.Month), int(t.Day), int(t.Hour), int(t.Minute), int(t.Second), int(t.Nanosecond), time.FixedZone("", -int(t.Timezone)*60))
}
func New_EFI_TIME(t time.Time) *EFI_TIME {
_, offset := t.Zone()
return &EFI_TIME{
Year: uint16(t.Year()),
Month: uint8(t.Month()),
Day: uint8(t.Day()),
Hour: uint8(t.Hour()),
Minute: uint8(t.Minute()),
Second: uint8(t.Second()),
Nanosecond: uint32(t.Nanosecond()),
Timezone: -int16(offset / 60)}
}
./github.com/canonical/go-efilib/internal/uefi/vars.go 0000664 0000000 0000000 00000001177 00000000000 021243 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
const (
EFI_VARIABLE_NON_VOLATILE = 1 << 0
EFI_VARIABLE_BOOTSERVICE_ACCESS = 1 << 1
EFI_VARIABLE_RUNTIME_ACCESS = 1 << 2
EFI_VARIABLE_HARDWARE_ERROR_RECORD = 1 << 3
EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS = 1 << 4
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS = 1 << 5
EFI_VARIABLE_APPEND_WRITE = 1 << 6
EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS = 1 << 7
)
./github.com/canonical/go-efilib/internal/uefi/wincert.go 0000664 0000000 0000000 00000003007 00000000000 021735 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package uefi
import (
"encoding/binary"
"errors"
"io"
"github.com/canonical/go-efilib/internal/ioerr"
)
const (
WIN_CERT_TYPE_PKCS_SIGNED_DATA = 0x0002
WIN_CERT_TYPE_EFI_PKCS115 = 0x0ef0
WIN_CERT_TYPE_EFI_GUID = 0x0ef1
)
type WIN_CERTIFICATE struct {
Length uint32
Revision uint16
CertificateType uint16
}
type WIN_CERTIFICATE_EFI_PKCS1_15 struct {
Hdr WIN_CERTIFICATE
HashAlgorithm EFI_GUID
Signature []byte
}
type WIN_CERTIFICATE_UEFI_GUID struct {
Hdr WIN_CERTIFICATE
CertType EFI_GUID
CertData []byte
}
func Read_WIN_CERTIFICATE_UEFI_GUID(r io.Reader) (out *WIN_CERTIFICATE_UEFI_GUID, err error) {
out = &WIN_CERTIFICATE_UEFI_GUID{}
if err := binary.Read(r, binary.LittleEndian, &out.Hdr); err != nil {
return nil, err
}
if out.Hdr.Revision != 0x0200 {
return nil, errors.New("unexpected Hdr.Revision")
}
if out.Hdr.CertificateType != WIN_CERT_TYPE_EFI_GUID {
return nil, errors.New("unexpected Hdr.CertificateType")
}
if _, err := io.ReadFull(r, out.CertType[:]); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out.CertData = make([]byte, int(out.Hdr.Length)-binary.Size(out.Hdr)-binary.Size(out.CertType))
if _, err := io.ReadFull(r, out.CertData); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return out, nil
}
type WIN_CERTIFICATE_EFI_PKCS struct {
Hdr WIN_CERTIFICATE
CertData []byte
}
./github.com/canonical/go-efilib/internal/unix/ 0000775 0000000 0000000 00000000000 00000000000 017766 5 ustar 00 0000000 0000000 ./github.com/canonical/go-efilib/internal/unix/ioctl.go 0000664 0000000 0000000 00000001542 00000000000 021431 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package unix
import (
"syscall"
"unsafe"
)
func IoctlGetUint(fd int, req uint) (uint, error) {
var value uint
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(unsafe.Pointer(&value)))
if err != 0 {
return 0, err
}
return value, nil
}
func IoctlGetUint64(fd int, req uint) (uint64, error) {
var value uint64
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(unsafe.Pointer(&value)))
if err != 0 {
return 0, err
}
return value, nil
}
func IoctlSetPointerUint(fd int, req, value uint) error {
v := value
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(unsafe.Pointer(&v)))
if err != 0 {
return err
}
return nil
}
./github.com/canonical/go-efilib/linux/ 0000775 0000000 0000000 00000000000 00000000000 016326 5 ustar 00 0000000 0000000 ./github.com/canonical/go-efilib/linux/disk.go 0000664 0000000 0000000 00000004040 00000000000 017605 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"errors"
"math"
"os"
"syscall"
"golang.org/x/sys/unix"
"golang.org/x/xerrors"
"github.com/canonical/go-efilib"
internal_unix "github.com/canonical/go-efilib/internal/unix"
)
func getSectorSize(f *os.File) (int64, error) {
fi, err := f.Stat()
if err != nil {
return 0, err
}
if fi.Mode().IsRegular() {
return 512, nil
}
if fi.Mode()&os.ModeDevice == 0 {
return 0, errors.New("not a regular file or device")
}
sz, err := unix.IoctlGetInt(int(f.Fd()), unix.BLKSSZGET)
if err != nil {
return 0, err
}
return int64(sz), nil
}
func getDeviceSize(f *os.File) (int64, error) {
fi, err := f.Stat()
if err != nil {
return 0, err
}
if fi.Mode().IsRegular() {
return fi.Size(), nil
}
if fi.Mode()&os.ModeDevice == 0 {
return 0, errors.New("not a regular file or device")
}
sz, err := internal_unix.IoctlGetUint64(int(f.Fd()), unix.BLKGETSIZE64)
switch {
case err == syscall.ENOTTY:
n, err := internal_unix.IoctlGetUint(int(f.Fd()), unix.BLKGETSIZE)
if err != nil {
return 0, err
}
if int64(n) > int64(math.MaxInt64>>9) {
return 0, errors.New("overflow")
}
return int64(n << 9), nil
case err != nil:
return 0, err
case sz > math.MaxInt64:
return 0, errors.New("overflow")
default:
return int64(sz), nil
}
}
// NewHardDriveDevicePathNodeFromDevice constructs a HardDriveDevicePathNode for the
// specified partition on the device or file at the supplied path.
func NewHardDriveDevicePathNodeFromDevice(dev string, part int) (*efi.HardDriveDevicePathNode, error) {
f, err := osOpen(dev)
if err != nil {
return nil, err
}
defer f.Close()
sz, err := getDeviceSize(f)
if err != nil {
return nil, xerrors.Errorf("cannot determine device size: %w", err)
}
ssz, err := getSectorSize(f)
if err != nil {
return nil, xerrors.Errorf("cannot determine logical sector size: %w", err)
}
return efi.NewHardDriveDevicePathNodeFromDevice(f, sz, ssz, part)
}
./github.com/canonical/go-efilib/linux/dp_acpi.go 0000664 0000000 0000000 00000005506 00000000000 020262 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/canonical/go-efilib"
)
// acpiIdRE matches a ACPI or PNP ID, capturing the vendor and product.
var acpiIdRE = regexp.MustCompile(`^([[:upper:][:digit:]]{3,4})([[:xdigit:]]{4})$`)
// acpiModaliasRE matches a modalias for an ACPI node, capturing the CID.
var acpiModaliasRE = regexp.MustCompile(`^acpi:[[:alnum:]]+:([[:alnum:]]*)`)
func maybeUseSimpleACPIDevicePathNode(node *efi.ACPIExtendedDevicePathNode) efi.DevicePathNode {
if node.HIDStr != "" || node.UIDStr != "" || node.CIDStr != "" {
return node
}
if node.CID != 0 && node.CID != node.HID {
return node
}
return &efi.ACPIDevicePathNode{HID: node.HID, UID: node.UID}
}
func decodeACPIOrPNPId(str string) (efi.EISAID, string) {
m := acpiIdRE.FindStringSubmatch(str)
if len(m) == 0 {
return 0, str
}
vendor := m[1]
p, _ := hex.DecodeString(m[2])
product := binary.BigEndian.Uint16(p)
if len(vendor) != 3 {
return 0, fmt.Sprintf("%s%04x", vendor, product)
}
id, _ := efi.NewEISAID(vendor, product)
return id, ""
}
func newACPIExtendedDevicePathNode(path string) (*efi.ACPIExtendedDevicePathNode, error) {
node := new(efi.ACPIExtendedDevicePathNode)
hidBytes, err := ioutil.ReadFile(filepath.Join(path, "hid"))
if err != nil {
return nil, err
}
hid, hidStr := decodeACPIOrPNPId(strings.TrimSpace(string(hidBytes)))
node.HID = hid
node.HIDStr = hidStr
modalias, err := ioutil.ReadFile(filepath.Join(path, "modalias"))
switch {
case os.IsNotExist(err):
case err != nil:
return nil, err
default:
m := acpiModaliasRE.FindSubmatch(modalias)
if len(m) == 0 {
return nil, errors.New("invalid modalias")
}
if len(m[1]) > 0 {
cid, cidStr := decodeACPIOrPNPId(string(m[1]))
node.CID = cid
node.CIDStr = cidStr
}
}
uidBytes, err := ioutil.ReadFile(filepath.Join(path, "uid"))
switch {
case os.IsNotExist(err):
case err != nil:
return nil, err
default:
uidStr := strings.TrimSpace(string(uidBytes))
uid, err := strconv.ParseUint(uidStr, 10, 32)
if err != nil {
node.UIDStr = uidStr
} else {
node.UID = uint32(uid)
}
}
return node, nil
}
func handleACPIDevicePathNode(builder devicePathBuilder) error {
component := builder.next(1)
subsystem, err := filepath.EvalSymlinks(filepath.Join(builder.absPath(component), "subsystem"))
switch {
case os.IsNotExist(err):
return errSkipDevicePathNodeHandler
case err != nil:
return err
}
if subsystem != filepath.Join(sysfsPath, "bus", "acpi") {
return errSkipDevicePathNodeHandler
}
builder.advance(1)
return nil
}
func init() {
registerDevicePathNodeHandler("acpi", handleACPIDevicePathNode, 0)
}
./github.com/canonical/go-efilib/linux/dp_ata.go 0000664 0000000 0000000 00000003160 00000000000 020105 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"golang.org/x/xerrors"
)
// ataRE matches an ATA path component, capturing the ATA print ID.
var ataRE = regexp.MustCompile(`^ata([[:digit:]]+)$`)
type ataParams struct {
printId uint32
port uint32
*scsiParams
}
func handleATAPath(path string) (*ataParams, error) {
components := strings.Split(path, string(os.PathSeparator))
if len(components) < 6 {
return nil, errors.New("invalid path: insufficient components")
}
ata := components[len(components)-6]
m := ataRE.FindStringSubmatch(ata)
if len(m) == 0 {
return nil, fmt.Errorf("invalid path component: %s", ata)
}
scsiParams, err := handleSCSIPath(path)
if err != nil {
return nil, err
}
printId, err := strconv.ParseUint(m[1], 10, 32)
if err != nil {
return nil, xerrors.Errorf("invalid print ID: %w", err)
}
// Obtain the ATA port number local to this ATA controller. The kernel
// creates one ata%d device per port (see drivers/ata/libata-core.c:ata_host_register).
portBytes, err := ioutil.ReadFile(filepath.Join(path, "../../../../..", "ata_port", ata, "port_no"))
if err != nil {
return nil, xerrors.Errorf("cannot obtain port ID: %w", err)
}
port, err := strconv.ParseUint(strings.TrimSpace(string(portBytes)), 10, 16)
if err != nil {
return nil, xerrors.Errorf("invalid port ID: %w", err)
}
return &ataParams{
printId: uint32(printId),
port: uint32(port),
scsiParams: scsiParams}, nil
}
./github.com/canonical/go-efilib/linux/dp_hv.go 0000664 0000000 0000000 00000002550 00000000000 017757 0 ustar 00 0000000 0000000 // Copyright 2022 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"io/ioutil"
"path/filepath"
"github.com/canonical/go-efilib"
)
var (
hvVendorGuid = efi.MakeGUID(0x9b17e5a2, 0x0891, 0x42dd, 0xb653, [...]uint8{0x80, 0xb5, 0xc2, 0x28, 0x09, 0xba})
hvSCSIGuid = efi.MakeGUID(0xba6163d9, 0x04a1, 0x4d29, 0xb605, [...]uint8{0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f})
)
func handleHVDevicePathNode(builder devicePathBuilder) error {
component := builder.next(1)
deviceId, err := efi.DecodeGUIDString(component)
if err != nil {
return err
}
classIdStr, err := ioutil.ReadFile(filepath.Join(builder.absPath(component), "class_id"))
if err != nil {
return err
}
builder.advance(1)
classId, err := efi.DecodeGUIDString(string(classIdStr))
if err != nil {
return err
}
switch classId {
case hvSCSIGuid:
builder.setInterfaceType(interfaceTypeSCSI)
default:
return errUnsupportedDevice("unhandled device class: " + classId.String())
}
data := make([]byte, len(deviceId)+len(classId))
copy(data, classId[:])
copy(data[len(classId):], deviceId[:])
builder.append(&efi.VendorDevicePathNode{
Type: efi.HardwareDevicePath,
GUID: hvVendorGuid,
Data: data})
return nil
}
func init() {
registerDevicePathNodeHandler("hv", handleHVDevicePathNode, 0, interfaceTypeVMBus)
}
./github.com/canonical/go-efilib/linux/dp_ide.go 0000664 0000000 0000000 00000002547 00000000000 020111 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"errors"
"fmt"
"math"
"github.com/canonical/go-efilib"
)
func handleIDEDevicePathNode(builder devicePathBuilder) error {
if builder.numRemaining() < 6 {
return errors.New("invalid path: insufficient components")
}
params, err := handleATAPath(builder.absPath(builder.next(6)))
if err != nil {
return err
}
builder.advance(6)
// PATA has a maximum of 2 ports.
if params.port < 1 || params.port > 2 {
return fmt.Errorf("invalid port: %d", params.port)
}
// Each PATA device is represented in the SCSI layer by setting the
// target to the drive number, and the LUN as the LUN (see
// drivers/ata/libata-scsi.c:ata_scsi_scan_host).
// The channel is always 0 for PATA devices (no port multiplier).
if params.channel != 0 {
return errors.New("invalid SCSI channel")
}
if params.target > 1 {
return errors.New("invalid drive")
}
if params.lun > math.MaxUint16 {
return errors.New("invalid LUN")
}
builder.append(&efi.ATAPIDevicePathNode{
Controller: efi.ATAPIControllerRole(params.port - 1),
Drive: efi.ATAPIDriveRole(params.target),
LUN: uint16(params.lun)})
return nil
}
func init() {
registerDevicePathNodeHandler("ide", handleIDEDevicePathNode, 0, interfaceTypeIDE)
}
./github.com/canonical/go-efilib/linux/dp_nvme.go 0000664 0000000 0000000 00000003474 00000000000 020315 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"golang.org/x/xerrors"
"github.com/canonical/go-efilib"
)
// nvmeNSRe matches "nvme/nvme/nvmen", capturing ns_id
var nvmeNSRe = regexp.MustCompile(`^nvme\/nvme[[:digit:]]+\/nvme[[:digit:]]+n([[:digit:]]+)$`)
func handleNVMEDevicePathNode(builder devicePathBuilder) error {
if builder.numRemaining() < 3 {
return errors.New("invalid path: not enough components")
}
components := builder.next(3)
m := nvmeNSRe.FindStringSubmatch(components)
if len(m) == 0 {
return errors.New("invalid path")
}
builder.advance(3)
nsid, err := strconv.ParseUint(m[1], 10, 32)
if err != nil {
return xerrors.Errorf("cannot parse nsid: %w", err)
}
var euid [8]uint8
euidBuf, err := ioutil.ReadFile(filepath.Join(builder.absPath(components), "eui"))
if os.IsNotExist(err) {
euidBuf, err = ioutil.ReadFile(filepath.Join(builder.absPath(components), "device", "eui"))
}
switch {
case os.IsNotExist(err):
// Nothing to do
case err != nil:
return xerrors.Errorf("cannot determine euid: %w", err)
default:
n, err := fmt.Sscanf(string(euidBuf), "%02x %02x %02x %02x %02x %02x %02x %02x",
&euid[0], &euid[1], &euid[2], &euid[3], &euid[4], &euid[5], &euid[6], &euid[7])
if err != nil {
return xerrors.Errorf("cannot parse euid: %w", err)
}
if n != 8 {
return errors.New("invalid euid")
}
}
builder.append(&efi.NVMENamespaceDevicePathNode{
NamespaceID: uint32(nsid),
NamespaceUUID: uint64(binary.LittleEndian.Uint64(euid[:]))})
return nil
}
func init() {
registerDevicePathNodeHandler("nvme", handleNVMEDevicePathNode, 0, interfaceTypeNVME)
}
./github.com/canonical/go-efilib/linux/dp_pci.go 0000664 0000000 0000000 00000004026 00000000000 020115 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"regexp"
"strconv"
"golang.org/x/xerrors"
"github.com/canonical/go-efilib"
)
var classRE = regexp.MustCompile(`^0x([[:xdigit:]]+)$`)
// pciRE matches "nnnn:bb:dd:f" where "nnnn" is the domain, "bb" is the bus number,
// "dd" is the device number and "f" is the function. It captures the device and
// function.
var pciRE = regexp.MustCompile(`^[[:xdigit:]]{4}:[[:xdigit:]]{2}:([[:xdigit:]]{2})\.([[:digit:]]{1})$`)
func handlePCIDevicePathNode(builder devicePathBuilder) error {
component := builder.next(1)
m := pciRE.FindStringSubmatch(component)
if len(m) == 0 {
return fmt.Errorf("invalid component: %s", component)
}
devNum, _ := strconv.ParseUint(m[1], 16, 8)
fun, _ := strconv.ParseUint(m[2], 10, 8)
classBytes, err := ioutil.ReadFile(filepath.Join(builder.absPath(component), "class"))
if err != nil {
return xerrors.Errorf("cannot read device class: %w", err)
}
var class []byte
if n, err := fmt.Sscanf(string(classBytes), "0x%x", &class); err != nil || n != 1 {
return errors.New("cannot decode device class")
}
builder.advance(1)
switch {
case bytes.HasPrefix(class, []byte{0x01, 0x00}):
builder.setInterfaceType(interfaceTypeSCSI)
case bytes.HasPrefix(class, []byte{0x01, 0x01}):
builder.setInterfaceType(interfaceTypeIDE)
case bytes.HasPrefix(class, []byte{0x01, 0x06}):
builder.setInterfaceType(interfaceTypeSATA)
case bytes.HasPrefix(class, []byte{0x01, 0x08}):
builder.setInterfaceType(interfaceTypeNVME)
case bytes.HasPrefix(class, []byte{0x06, 0x04}):
builder.setInterfaceType(interfaceTypePCI)
default:
return errUnsupportedDevice("unhandled device class: " + string(classBytes))
}
builder.append(&efi.PCIDevicePathNode{
Function: uint8(fun),
Device: uint8(devNum)})
return nil
}
func init() {
registerDevicePathNodeHandler("pci", handlePCIDevicePathNode, 0, interfaceTypePCI)
}
./github.com/canonical/go-efilib/linux/dp_pci_root.go 0000664 0000000 0000000 00000002311 00000000000 021153 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"fmt"
"path/filepath"
"regexp"
)
// pcirootRE matches a pcixxxx.xx path component.
var pcirootRE = regexp.MustCompile(`^pci[[:xdigit:]]{4}:[[:xdigit:]]{2}$`)
func handlePCIRootDevicePathNode(builder devicePathBuilder) error {
component := builder.next(1)
if !pcirootRE.MatchString(component) {
return errSkipDevicePathNodeHandler
}
node, err := newACPIExtendedDevicePathNode(filepath.Join(builder.absPath(component), "firmware_node"))
if err != nil {
return err
}
if node.HID.Vendor() != "PNP" || (node.HID.Product() != 0x0a03 && node.HID.Product() != 0x0a08) {
return fmt.Errorf("unexpected hid: %v", node.HID)
}
node.HID = 0x0a0341d0
if node.CID != 0 && (node.CID.Vendor() != "PNP" || (node.CID.Product() != 0x0a03 && node.CID.Product() != 0x0a08)) {
return fmt.Errorf("unexpected cid: %v", node.CID)
}
builder.advance(1)
builder.setInterfaceType(interfaceTypePCI)
builder.append(maybeUseSimpleACPIDevicePathNode(node))
return nil
}
func init() {
registerDevicePathNodeHandler("pci-root", handlePCIRootDevicePathNode, prependHandler)
}
./github.com/canonical/go-efilib/linux/dp_sata.go 0000664 0000000 0000000 00000004100 00000000000 020263 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/canonical/go-efilib"
)
func handleSATADevicePathNode(builder devicePathBuilder) error {
if builder.numRemaining() < 6 {
return errors.New("invalid path: insufficient components")
}
params, err := handleATAPath(builder.absPath(builder.next(6)))
if err != nil {
return err
}
// Each SATA device is represented in the SCSI layer by setting the
// channel to the port multiplier port number and the LUN as the LUN (see
// drivers/ata/libata-scsi.c:ata_scsi_scan_host).
pmp := params.channel
if pmp > 0x7fff {
return errors.New("invalid PMP")
}
// The target is always zero for SATA devices, as each port only has
// a single device.
if params.target != 0 {
return errors.New("invalid SCSI target")
}
// We need to determine if the device is connected via a port
// multiplier because we have to set the PMP address to 0xffff
// if it isn't. Unfortunately, it is zero indexed so checking
// that it is zero isn't sufficient.
//
// The kernel will expose a single host link%d device if there
// is no port multiplier, or one of more PMP link%d.%d devices
// if there is a port multiplier attached (see
// drivers/ata/libata-pmp.c:sata_pmp_init_links and
// drivers/ata/libata-transport.c:ata_tlink_add).
_, err = os.Stat(filepath.Join(builder.next(1), fmt.Sprintf("link%d.%d", params.printId, pmp)))
switch {
case os.IsNotExist(err):
// No port multiplier is connected.
pmp = 0xffff
case err != nil:
return err
default:
// A port multiplier is connected.
}
builder.advance(6)
builder.append(&efi.SATADevicePathNode{
// The kernel provides a one-indexed number and the firmware is zero-indexed.
HBAPortNumber: uint16(params.port) - 1,
PortMultiplierPortNumber: uint16(pmp),
LUN: uint16(params.lun)})
return nil
}
func init() {
registerDevicePathNodeHandler("sata", handleSATADevicePathNode, 0, interfaceTypeSATA)
}
./github.com/canonical/go-efilib/linux/dp_scsi.go 0000664 0000000 0000000 00000004242 00000000000 020303 0 ustar 00 0000000 0000000 // Copyright 2022 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"errors"
"fmt"
"math"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/canonical/go-efilib"
"golang.org/x/xerrors"
)
// scsiRE matches a SCSI path, capturing the channel, target and LUN.
var scsiRE = regexp.MustCompile(`^host[[:digit:]]+\/target[[:digit:]]+\:[[:digit:]]+\:[[:digit:]]+\/[[:digit:]]+\:([[:digit:]]+)\:([[:digit:]]+)\:([[:digit:]]+)\/block\/s[dr][[:alpha:]]$`)
type scsiParams struct {
channel uint32
target uint32
lun uint64
}
func handleSCSIPath(path string) (*scsiParams, error) {
components := strings.Split(path, string(os.PathSeparator))
if len(components) < 5 {
return nil, errors.New("invalid path: insufficient components")
}
path = filepath.Join(components[len(components)-5:]...)
m := scsiRE.FindStringSubmatch(path)
if len(m) == 0 {
return nil, fmt.Errorf("invalid path components: %s", path)
}
channel, err := strconv.ParseUint(m[1], 10, 32)
if err != nil {
return nil, xerrors.Errorf("invalid channel: %w", err)
}
target, err := strconv.ParseUint(m[2], 10, 32)
if err != nil {
return nil, xerrors.Errorf("invalid target: %w", err)
}
lun, err := strconv.ParseUint(m[3], 10, 64)
if err != nil {
return nil, xerrors.Errorf("invalid lun: %w", err)
}
return &scsiParams{
channel: uint32(channel),
target: uint32(target),
lun: lun}, nil
}
func handleSCSIDevicePathNode(builder devicePathBuilder) error {
if builder.numRemaining() < 5 {
return errors.New("invalid path: insufficient components")
}
params, err := handleSCSIPath(builder.absPath(builder.next(5)))
if err != nil {
return err
}
builder.advance(5)
if params.channel != 0 {
return errors.New("invalid channel")
}
if params.target > math.MaxUint16 {
return errors.New("invalid target")
}
if params.lun > math.MaxUint16 {
return errors.New("invalid LUN")
}
builder.append(&efi.SCSIDevicePathNode{
PUN: uint16(params.target),
LUN: uint16(params.lun)})
return nil
}
func init() {
registerDevicePathNodeHandler("scsi", handleSCSIDevicePathNode, 0, interfaceTypeSCSI)
}
./github.com/canonical/go-efilib/linux/dp_virtio.go 0000664 0000000 0000000 00000001022 00000000000 020647 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"regexp"
)
var virtioRE = regexp.MustCompile(`^virtio[[:digit:]]`)
func handleVirtioDevicePathNode(builder devicePathBuilder) error {
if !virtioRE.MatchString(builder.next(1)) {
return errSkipDevicePathNodeHandler
}
builder.advance(1)
return nil
}
func init() {
registerDevicePathNodeHandler("virtio", handleVirtioDevicePathNode, prependHandler, interfaceTypeSCSI)
}
./github.com/canonical/go-efilib/linux/dp_virtual.go 0000664 0000000 0000000 00000000675 00000000000 021036 0 ustar 00 0000000 0000000 // Copyright 2022 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
func handleVirtualDevicePathNode(builder devicePathBuilder) error {
if builder.next(1) == "virtual" {
return errUnsupportedDevice("virtual devices are not supported")
}
return errSkipDevicePathNodeHandler
}
func init() {
registerDevicePathNodeHandler("virtual", handleVirtualDevicePathNode, 0)
}
./github.com/canonical/go-efilib/linux/dp_vmbus_root.go 0000664 0000000 0000000 00000002464 00000000000 021545 0 ustar 00 0000000 0000000 // Copyright 2022 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"fmt"
"regexp"
)
// vmbusrootRE matches a VMBUS:XX component.
var vmbusrootRE = regexp.MustCompile(`^VMBUS:[[:xdigit:]]{2}$`)
func handleVMBusRootDevicePathNode(builder devicePathBuilder) error {
component := builder.next(1)
if !vmbusrootRE.MatchString(component) {
return errSkipDevicePathNodeHandler
}
node, err := newACPIExtendedDevicePathNode(builder.absPath(component))
if err != nil {
return err
}
if node.HID != 0 || node.CID != 0 || node.HIDStr != "VMBUS" || node.CIDStr != "" {
return fmt.Errorf("unexpected node properties: %v", node)
}
// The hardware ID exposed by the kernel seems to be capitalized, but the
// one exposed from the firmware on an instance I've tested on isn't. Fix
// up here - I'm not sure if this is right (is it always "VMBus"?), but the
// device path does need to be an exact match for lookups because the firmware
// essentially just does a memcmp.
node.HIDStr = "VMBus"
builder.advance(1)
builder.setInterfaceType(interfaceTypeVMBus)
builder.append(maybeUseSimpleACPIDevicePathNode(node))
return nil
}
func init() {
registerDevicePathNodeHandler("vmbus-root", handleVMBusRootDevicePathNode, prependHandler)
}
./github.com/canonical/go-efilib/linux/filepath.go 0000664 0000000 0000000 00000030351 00000000000 020453 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"bufio"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"golang.org/x/sys/unix"
"golang.org/x/xerrors"
"github.com/canonical/go-efilib"
)
// FileDevicePathMode specifies the mode for NewFileDevicePath
type FileDevicePathMode int
const (
// FullPath indicates that only a full device path should be created.
FullPath FileDevicePathMode = iota
// ShortFormPathHD indicates that a short-form device path beginning
// with a HD() node should be created.
ShortFormPathHD
// ShortFormPathFile indicates that a short-form device path consisting
// of only the file path relative to the device should be created.
ShortFormPathFile
)
// ErrNoDevicePath is returned from NewFileDevicePath if the device in
// which a file is stored cannot be mapped to a device path with the
// specified mode.
type ErrNoDevicePath string
func (e ErrNoDevicePath) Error() string {
return "cannot map file path to a UEFI device path: " + string(e)
}
type interfaceType int
const (
interfaceTypeUnknown interfaceType = iota
interfaceTypePCI
interfaceTypeUSB
interfaceTypeSCSI
interfaceTypeIDE
interfaceTypeSATA
interfaceTypeNVME
interfaceTypeVMBus
)
const (
// prependHandler indicates that a handler wants to be tried
// before handlers registered without this flag. These handlers
// should use errSkipDevicePathNodeHandler on unhandled nodes.
prependHandler = 1 << 0
)
var (
// errSkipDevicePathNodeHandler is returned from a handler when it
// wants to defer handling to another handler.
errSkipDevicePathNodeHandler = errors.New("")
)
// errUnsupportedDevice is returned from a handler when it cannot
// determine the interface.
type errUnsupportedDevice string
func (e errUnsupportedDevice) Error() string {
return "unsupported device: " + string(e)
}
type devicePathNodeHandler func(devicePathBuilder) error
type registeredDpHandler struct {
name string
fn devicePathNodeHandler
}
var devicePathNodeHandlers = make(map[interfaceType][]registeredDpHandler)
func registerDevicePathNodeHandler(name string, fn devicePathNodeHandler, flags int, interfaces ...interfaceType) {
if len(interfaces) == 0 {
interfaces = []interfaceType{interfaceTypeUnknown}
}
for _, i := range interfaces {
if flags&prependHandler > 0 {
devicePathNodeHandlers[i] = append([]registeredDpHandler{{name, fn}}, devicePathNodeHandlers[i]...)
} else {
devicePathNodeHandlers[i] = append(devicePathNodeHandlers[i], registeredDpHandler{name, fn})
}
}
}
type devicePathBuilder interface {
// numRemaining returns the number of remaining sysfs components
// to process.
numRemaining() int
// next returns the next n sysfs components to process. -1 returns
// all remaining components.
next(n int) string
// absPath turns the supplied sysfs path components into an
// absolute path.
absPath(path string) string
// advance marks the specified number of sysfs components
// as handled and advances to the next ones.
advance(n int)
// interfaceType returns the type of the interface detected
// by the last handler.
interfaceType() interfaceType
// setInterfaceType allows a handler to set the detected interface
// type.
setInterfaceType(iface interfaceType)
// append allows a handler to append device path nodes to the current
// path.
append(nodes ...efi.DevicePathNode)
}
type devicePathBuilderImpl struct {
iface interfaceType
devPath efi.DevicePath
processed []string
remaining []string
}
func (b *devicePathBuilderImpl) numRemaining() int {
return len(b.remaining)
}
func (b *devicePathBuilderImpl) next(n int) string {
if n < 0 {
return filepath.Join(b.remaining...)
}
return filepath.Join(b.remaining[:n]...)
}
func (b *devicePathBuilderImpl) absPath(path string) string {
return filepath.Join(sysfsPath, "devices", filepath.Join(b.processed...), path)
}
func (b *devicePathBuilderImpl) advance(n int) {
b.processed = append(b.processed, b.remaining[:n]...)
b.remaining = b.remaining[n:]
}
func (b *devicePathBuilderImpl) interfaceType() interfaceType {
return b.iface
}
func (b *devicePathBuilderImpl) setInterfaceType(iface interfaceType) {
b.iface = iface
}
func (b *devicePathBuilderImpl) append(nodes ...efi.DevicePathNode) {
b.devPath = append(b.devPath, nodes...)
}
func (b *devicePathBuilderImpl) done() bool {
return len(b.remaining) == 0
}
func (b *devicePathBuilderImpl) processNextComponent() error {
nProcessed := len(b.processed)
remaining := b.remaining
iface := b.iface
handlers := devicePathNodeHandlers[b.iface]
if len(handlers) == 0 {
// There should always be at least one handler registered for an interface.
panic(fmt.Sprintf("no handlers registered for interface type %v", b.iface))
}
for _, handler := range handlers {
err := handler.fn(b)
if err != nil {
// Roll back changes
b.processed = b.processed[:nProcessed]
b.remaining = remaining
b.iface = iface
}
if err == errSkipDevicePathNodeHandler {
// Try the next handler.
continue
}
if err != nil {
return xerrors.Errorf("[handler %s]: %w", handler.name, err)
}
if iface != interfaceTypeUnknown && b.iface == interfaceTypeUnknown {
// The handler set the interface type back to unknown. Turn this
// in to a errUnsupportedDevice error.
return errUnsupportedDevice("[handler " + handler.name + "]: unrecognized interface")
}
return nil
}
// If we get here, then all handlers returned errSkipDevicePathNodeHandler.
if b.iface != interfaceTypeUnknown {
// If the interface has already been determined, require at least one
// handler to handle this node or return an error.
panic(fmt.Sprintf("all handlers skipped handling interface type %v", b.iface))
}
return errUnsupportedDevice("unhandled root node")
}
func newDevicePathBuilder(dev *dev) (*devicePathBuilderImpl, error) {
path, err := filepath.Rel(filepath.Join(sysfsPath, "devices"), dev.sysfsPath)
if err != nil {
return nil, err
}
return &devicePathBuilderImpl{remaining: strings.Split(path, string(os.PathSeparator))}, nil
}
type mountPoint struct {
dev uint64
root string
mountDir string
mountSource string
}
func scanBlockDeviceMounts() (mounts []*mountPoint, err error) {
f, err := os.Open(mountsPath)
if err != nil {
return nil, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) < 10 || len(fields) > 11 {
return nil, errors.New("invalid mount info: incorrect number of fields")
}
devStr := strings.Split(fields[2], ":")
if len(devStr) != 2 {
return nil, errors.New("invalid mount info: invalid device number")
}
devMajor, err := strconv.ParseUint(devStr[0], 10, 32)
if err != nil {
return nil, xerrors.Errorf("invalid mount info: invalid device number: %w", err)
}
devMinor, err := strconv.ParseUint(devStr[1], 10, 32)
if err != nil {
return nil, xerrors.Errorf("invalid mount info: invalid device number: %w", err)
}
var mountSource string
if len(fields) == 10 {
mountSource = fields[8]
} else {
mountSource = fields[9]
}
if !filepath.IsAbs(mountSource) {
continue
}
mounts = append(mounts, &mountPoint{
dev: unix.Mkdev(uint32(devMajor), uint32(devMinor)),
root: fields[3],
mountDir: fields[4],
mountSource: mountSource})
}
if scanner.Err() != nil {
return nil, xerrors.Errorf("cannot parse mount info: %w", err)
}
return mounts, nil
}
func getFileMountPoint(path string) (*mountPoint, error) {
mounts, err := scanBlockDeviceMounts()
if err != nil {
return nil, xerrors.Errorf("cannot obtain list of block device mounts: %w", err)
}
var candidate *mountPoint
for _, mount := range mounts {
if !strings.HasPrefix(path, mount.mountDir) {
continue
}
if candidate == nil {
candidate = mount
}
if len(mount.mountDir) > len(candidate.mountDir) {
candidate = mount
}
}
if candidate == nil {
return nil, errors.New("not found")
}
return candidate, nil
}
type dev struct {
sysfsPath string
devPath string
part int
}
type filePath struct {
dev
path string
}
func newFilePath(path string) (*filePath, error) {
path, err := filepathEvalSymlinks(path)
if err != nil {
return nil, xerrors.Errorf("cannot evaluate symbolic links: %w", err)
}
mount, err := getFileMountPoint(path)
if err != nil {
return nil, xerrors.Errorf("cannot obtain mount information for path: %w", err)
}
rel, err := filepath.Rel(mount.mountDir, path)
if err != nil {
return nil, err
}
out := &filePath{path: filepath.Join(mount.root, rel)}
childDev, err := filepath.EvalSymlinks(filepath.Join(sysfsPath, "dev/block", fmt.Sprintf("%d:%d", unix.Major(mount.dev), unix.Minor(mount.dev))))
if err != nil {
return nil, err
}
parentDev := filepath.Dir(childDev)
parentSubsystem, err := filepath.EvalSymlinks(filepath.Join(parentDev, "subsystem"))
switch {
case os.IsNotExist(err):
// No subsystem link, could be the block/ directory
case err != nil:
return nil, err
}
if parentSubsystem != filepath.Join(sysfsPath, "class", "block") {
// Parent device is not a block device
out.dev.sysfsPath = childDev
out.dev.devPath = filepath.Join("/dev", filepath.Base(childDev))
} else {
// Parent device is a block device, so this is a partitioned
// device.
out.dev.sysfsPath = parentDev
out.dev.devPath = filepath.Join("/dev", filepath.Base(parentDev))
b, err := ioutil.ReadFile(filepath.Join(childDev, "partition"))
if err != nil {
return nil, xerrors.Errorf("cannot obtain partition number for %s: %w", mount.dev, err)
}
part, err := strconv.Atoi(strings.TrimSpace(string(b)))
if err != nil {
return nil, xerrors.Errorf("cannot determine partition number for %s: %w", mount.dev, err)
}
out.dev.part = part
}
return out, nil
}
// NewFileDevicePath creates an EFI device path from the supplied filepath.
//
// If mode is FullPath, this will attempt to create a full device path which
// requires the use of sysfs. If the device in which the file is stored cannot be
// mapped to a device path, a ErrNoDevicePath error is returned. This could be
// because the device is not recognized by this package, or because the device
// genuinely cannot be mapped to a device path (eg, it is a device-mapper or loop
// device). In this case, one of the ShortForm modes can be used.
//
// If mode is ShortFormPathHD, this will attempt to create a short-form device
// path beginning with a HD() component. If the file is stored inside an
// unpartitioned device, a ErrNoDevicePath error will be returned. In this case,
// ShortFormPathFile can be used.
//
// When mode is ShortFormPathHD or FullPath and the file is stored inside a
// partitoned device, read access is required on the underlying block device
// in order to decode the partition table.
//
// If mode is ShortFormPathFile, this will attempt to create a short-form device
// path consisting only of the file path relative to the device.
//
// In all modes, read access to the file's directory is required.
func NewFileDevicePath(path string, mode FileDevicePathMode) (out efi.DevicePath, err error) {
fp, err := newFilePath(path)
if err != nil {
return nil, err
}
if mode == ShortFormPathHD && fp.part == 0 {
return nil, ErrNoDevicePath("file is not inside partitioned media - use linux.ShortFormPathFile")
}
builder, err := newDevicePathBuilder(&fp.dev)
if err != nil {
return nil, err
}
if mode == FullPath {
for !builder.done() {
var e errUnsupportedDevice
err := builder.processNextComponent()
switch {
case xerrors.As(err, &e):
return nil, ErrNoDevicePath("encountered an error when handling components " +
builder.next(-1) + " from device path " +
builder.absPath(builder.next(-1)) + ": " + err.Error())
case err != nil:
return nil, xerrors.Errorf("cannot process components %s from device path %s: %w",
builder.next(-1), builder.absPath(builder.next(-1)), err)
}
}
}
out = builder.devPath
if mode != ShortFormPathFile && fp.part > 0 {
node, err := NewHardDriveDevicePathNodeFromDevice(fp.devPath, fp.part)
if err != nil {
return nil, xerrors.Errorf("cannot construct hard drive device path node: %w", err)
}
out = append(out, node)
}
out = append(out, efi.NewFilePathDevicePathNode(fp.path))
return out, err
}
./github.com/canonical/go-efilib/linux/mockable.go 0000664 0000000 0000000 00000000502 00000000000 020427 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package linux
import (
"os"
"path/filepath"
)
var (
mountsPath = "/proc/self/mountinfo"
sysfsPath = "/sys"
filepathEvalSymlinks = filepath.EvalSymlinks
osOpen = os.Open
)
./github.com/canonical/go-efilib/loadoption.go 0000664 0000000 0000000 00000005242 00000000000 017671 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"bytes"
"errors"
"fmt"
"io"
"math"
"golang.org/x/xerrors"
"github.com/canonical/go-efilib/internal/uefi"
)
type LoadOptionAttributes uint32
func (a LoadOptionAttributes) Category() LoadOptionAttributes {
return a & LoadOptionAttributes(uefi.LOAD_OPTION_CATEGORY)
}
const (
LoadOptionActive LoadOptionAttributes = uefi.LOAD_OPTION_ACTIVE
LoadOptionForceReconnect LoadOptionAttributes = uefi.LOAD_OPTION_FORCE_RECONNECT
LoadOptionHidden LoadOptionAttributes = uefi.LOAD_OPTION_HIDDEN
LoadOptionCategoryBoot LoadOptionAttributes = uefi.LOAD_OPTION_CATEGORY_BOOT
LoadOptionCategoryApp LoadOptionAttributes = uefi.LOAD_OPTION_CATEGORY_APP
)
// LoadOption corresponds to the EFI_LOAD_OPTION type.
type LoadOption struct {
Attributes LoadOptionAttributes
Description string
FilePath DevicePath
OptionalData []byte
}
func (o *LoadOption) String() string {
return fmt.Sprintf("EFI_LOAD_OPTION{ Attributes: %d, Description: \"%s\", FilePath: %s, OptionalData: %x }",
o.Attributes, o.Description, o.FilePath, o.OptionalData)
}
// Bytes returns the serialized form of this load option.
func (o *LoadOption) Bytes() ([]byte, error) {
w := new(bytes.Buffer)
if err := o.Write(w); err != nil {
return nil, err
}
return w.Bytes(), nil
}
// Write serializes this load option to the supplied io.Writer.
func (o *LoadOption) Write(w io.Writer) error {
opt := uefi.EFI_LOAD_OPTION{
Attributes: uint32(o.Attributes),
Description: ConvertUTF8ToUCS2(o.Description + "\x00"),
OptionalData: o.OptionalData}
dp := new(bytes.Buffer)
if err := o.FilePath.Write(dp); err != nil {
return err
}
if dp.Len() > math.MaxUint16 {
return errors.New("FilePath too long")
}
opt.FilePathList = dp.Bytes()
opt.FilePathListLength = uint16(dp.Len())
return opt.Write(w)
}
// ReadLoadOption reads a LoadOption from the supplied io.Reader. Due to the
// way that EFI_LOAD_OPTION is defined, where there is no size encoded for the
// OptionalData field, this function will consume all of the bytes available
// from the supplied reader.
func ReadLoadOption(r io.Reader) (out *LoadOption, err error) {
opt, err := uefi.Read_EFI_LOAD_OPTION(r)
if err != nil {
return nil, err
}
out = &LoadOption{
Attributes: LoadOptionAttributes(opt.Attributes),
Description: ConvertUTF16ToUTF8(opt.Description),
OptionalData: opt.OptionalData}
dp, err := ReadDevicePath(bytes.NewReader(opt.FilePathList))
if err != nil {
return nil, xerrors.Errorf("cannot read device path: %w", err)
}
out.FilePath = dp
return out, nil
}
./github.com/canonical/go-efilib/mbr/ 0000775 0000000 0000000 00000000000 00000000000 015747 5 ustar 00 0000000 0000000 ./github.com/canonical/go-efilib/mbr/mbr.go 0000664 0000000 0000000 00000003020 00000000000 017051 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package mbr
import (
"encoding/binary"
"errors"
"io"
)
const mbrSignature = 0xaa55
var ErrInvalidSignature = errors.New("invalid master boot record signature")
// Address is a CHS address.
type Address [3]uint8
func (a Address) Head() uint8 {
return a[0]
}
func (a Address) Sector() uint8 {
return a[1] & 0x3f
}
func (a Address) Cylinder() uint16 {
c := uint16(a[2])
c |= uint16(a[1]&0xc0) << 2
return c
}
// PartitionEntry corresponds to a partition entry from a MBR.
type PartitionEntry struct {
BootIndicator uint8
StartAddress Address
Type uint8
EndAddress Address
StartingLBA uint32
NumberOfSectors uint32
}
// Record corresponds to a MBR.
type Record struct {
BootstrapCode [440]byte
UniqueSignature uint32
Partitions [4]PartitionEntry
}
type record struct {
BootstrapCode [440]byte
UniqueSignature uint32
Unknown [2]uint8
Partitions [4]PartitionEntry
Signature uint16
}
// ReadRecord reads a MBR from r. It returns ErrInvalidSignature if the
// MBR has an invalid signature.
func ReadRecord(r io.Reader) (*Record, error) {
var rec record
if err := binary.Read(r, binary.LittleEndian, &rec); err != nil {
return nil, err
}
if rec.Signature != mbrSignature {
return nil, ErrInvalidSignature
}
return &Record{BootstrapCode: rec.BootstrapCode,
UniqueSignature: rec.UniqueSignature,
Partitions: rec.Partitions}, nil
}
./github.com/canonical/go-efilib/mockable_linux.go 0000664 0000000 0000000 00000000431 00000000000 020510 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"os"
"golang.org/x/sys/unix"
)
var (
openVarFile = realOpenVarFile
removeVarFile = os.Remove
unixStatfs = unix.Statfs
)
./github.com/canonical/go-efilib/pe.go 0000664 0000000 0000000 00000014502 00000000000 016124 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"crypto"
"encoding/binary"
"errors"
"fmt"
"hash"
"io"
"sort"
"golang.org/x/xerrors"
"github.com/canonical/go-efilib/internal/ioerr"
"github.com/canonical/go-efilib/internal/pe1.14"
)
const (
certTableIndex = 4 // Index of the Certificate Table entry in the Data Directory of a PE image optional header
)
type eofIsUnexpectedReaderAt struct {
r io.ReaderAt
}
func (r *eofIsUnexpectedReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
n, err = r.r.ReadAt(p, off)
return n, ioerr.EOFIsUnexpected(err)
}
// ComputePeImageDigest computes the digest of the supplied PE image in accordance with the
// Authenticode specification, using the specified digest algorithm.
func ComputePeImageDigest(alg crypto.Hash, r io.ReaderAt, sz int64) ([]byte, error) {
var dosheader [96]byte
if n, err := r.ReadAt(dosheader[0:], 0); err != nil {
if n > 0 && err == io.EOF {
err = io.ErrUnexpectedEOF
}
return nil, err
}
var coffHeaderOffset int64
if dosheader[0] == 'M' && dosheader[1] == 'Z' {
signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
var sign [4]byte
r.ReadAt(sign[:], signoff)
if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
return nil, fmt.Errorf("invalid PE COFF file signature: %v", sign)
}
coffHeaderOffset = signoff + 4
}
p, err := pe.NewFile(r)
if err != nil {
return nil, xerrors.Errorf("cannot decode PE binary: %w", err)
}
var isPe32Plus bool
var sizeOfHeaders int64
var dd []pe.DataDirectory
switch oh := p.OptionalHeader.(type) {
case *pe.OptionalHeader32:
sizeOfHeaders = int64(oh.SizeOfHeaders)
dd = oh.DataDirectory[0:oh.NumberOfRvaAndSizes]
case *pe.OptionalHeader64:
isPe32Plus = true
sizeOfHeaders = int64(oh.SizeOfHeaders)
dd = oh.DataDirectory[0:oh.NumberOfRvaAndSizes]
default:
return nil, errors.New("PE binary doesn't contain an optional header")
}
// 1) Load the image header in to memory.
hr := io.NewSectionReader(&eofIsUnexpectedReaderAt{r}, 0, sizeOfHeaders)
// 2) Initialize a hash algorithm context.
h := alg.New()
// 3) Hash the image header from its base to immediately before the start of the checksum address in the optional header.
// This includes the DOS header, 4-byte PE signature, COFF header, and the first 64 bytes of the optional header.
b := make([]byte, int(coffHeaderOffset)+binary.Size(p.FileHeader)+64)
if _, err := io.ReadFull(hr, b); err != nil {
return nil, xerrors.Errorf("cannot read from image to start to checksum: %w", err)
}
h.Write(b)
// 4) Skip over the checksum, which is a 4-byte field.
hr.Seek(4, io.SeekCurrent)
var certTable *pe.DataDirectory
if len(dd) > certTableIndex {
// 5) Hash everything from the end of the checksum field to immediately before the start of the Certificate Table entry in the
// optional header data directory.
// This is 60 bytes for PE32 format binaries, or 76 bytes for PE32+ format binaries.
sz := 60
if isPe32Plus {
sz = 76
}
b = make([]byte, sz)
if _, err := io.ReadFull(hr, b); err != nil {
return nil, xerrors.Errorf("cannot read from checksum to certificate table data directory entry: %w", err)
}
h.Write(b)
// 6) Get the Attribute Certificate Table address and size from the Certificate Table entry.
certTable = &dd[certTableIndex]
}
// 7) Exclude the Certificate Table entry from the calculation and hash everything from the end of the Certificate Table entry
// to the end of image header, including the Section Table. The Certificate Table entry is 8 bytes long.
if certTable != nil {
hr.Seek(8, io.SeekCurrent)
}
chunkedHashAll := func(r io.Reader, h hash.Hash) error {
b := make([]byte, 4096)
for {
n, err := r.Read(b)
h.Write(b[:n])
if err == io.EOF {
return nil
}
if err != nil {
return err
}
}
}
if err := chunkedHashAll(hr, h); err != nil {
return nil, xerrors.Errorf("cannot hash remainder of headers and section table: %w", err)
}
// 8) Create a counter called sumOfBytesHashed, which is not part of the signature. Set this counter to the SizeOfHeaders field.
sumOfBytesHashed := sizeOfHeaders
// 9) Build a temporary table of pointers to all of the section headers in the image. Do not include any section headers in the
// table whose Size field is zero.
var sections []*pe.SectionHeader
for _, section := range p.Sections {
if section.Size == 0 {
continue
}
sections = append(sections, §ion.SectionHeader)
}
// 10) Using the Offset field in the referenced SectionHeader structure as a key, arrange the table's elements in ascending order.
// In other words, sort the section headers in ascending order according to the disk-file offset of the sections.
sort.Slice(sections, func(i, j int) bool { return sections[i].Offset < sections[j].Offset })
for _, section := range sections {
// 11) Walk through the sorted table, load the corresponding section into memory, and hash the entire section. Use the
// Size field in the SectionHeader structure to determine the amount of data to hash.
sr := io.NewSectionReader(&eofIsUnexpectedReaderAt{r}, int64(section.Offset), int64(section.Size))
if err := chunkedHashAll(sr, h); err != nil {
return nil, xerrors.Errorf("cannot hash section %s: %w", section.Name, err)
}
// 12) Add the section’s Size value to sumOfBytesHashed.
sumOfBytesHashed += int64(section.Size)
// 13) Repeat steps 11 and 12 for all of the sections in the sorted table.
}
// 14) Create a value called fileSize, which is not part of the signature. Set this value to the image’s file size. If fileSize is
// greater than sumOfBytesHashed, the file contains extra data that must be added to the hash. This data begins at the
// sumOfBytesHashed file offset, and its length is:
// fileSize – (certTable.Size + sumOfBytesHashed)
fileSize := sz
if fileSize > sumOfBytesHashed {
var certSize int64
if certTable != nil {
certSize = int64(certTable.Size)
}
if fileSize < (sumOfBytesHashed + certSize) {
return nil, errors.New("image too short")
}
sr := io.NewSectionReader(&eofIsUnexpectedReaderAt{r}, sumOfBytesHashed, fileSize-sumOfBytesHashed-certSize)
if err := chunkedHashAll(sr, h); err != nil {
return nil, xerrors.Errorf("cannot hash extra data: %w", err)
}
}
return h.Sum(nil), nil
}
./github.com/canonical/go-efilib/string.go 0000664 0000000 0000000 00000002613 00000000000 017026 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"unicode"
"unicode/utf16"
"unicode/utf8"
)
// ConvertUTF16ToUTF8 converts the supplied UTF-16 or UCS2 string
// to a UTF-8 string. If the supplied string is NULL-terminated,
// then the NULL termination is removed from the string.
func ConvertUTF16ToUTF8(in []uint16) string {
var u8 []byte
for _, r := range utf16.Decode(in) {
if r == 0 {
break
}
u8Char := make([]byte, utf8.RuneLen(r))
utf8.EncodeRune(u8Char, r)
u8 = append(u8, u8Char...)
}
return string(u8)
}
// ConvertUTF8ToUTF16 converts the supplied UTF-8 string to a
// UTF-16 string.
func ConvertUTF8ToUTF16(in string) []uint16 {
var unicodeStr []rune
for len(in) > 0 {
r, sz := utf8.DecodeRuneInString(in)
unicodeStr = append(unicodeStr, r)
in = in[sz:]
}
return utf16.Encode(unicodeStr)
}
// ConvertUTF8ToUCS2 converts the supplied UTF-8 string to a
// UCS2 string. Any code point outside of the Basic Multilingual
// Plane cannot be represented by UCS2 and is converted to the
// replacement character.
func ConvertUTF8ToUCS2(in string) []uint16 {
var unicodeStr []rune
for len(in) > 0 {
r, sz := utf8.DecodeRuneInString(in)
if r >= 0x10000 {
r = unicode.ReplacementChar
}
unicodeStr = append(unicodeStr, r)
in = in[sz:]
}
return utf16.Encode(unicodeStr)
}
./github.com/canonical/go-efilib/types.go 0000664 0000000 0000000 00000000341 00000000000 016660 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
type PhysicalAddress uint64
// LBA corresponds to the EFI_LBA type.
type LBA uint64
./github.com/canonical/go-efilib/vars.go 0000664 0000000 0000000 00000005554 00000000000 016502 0 ustar 00 0000000 0000000 // Copyright 2020-2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"errors"
"github.com/canonical/go-efilib/internal/uefi"
)
type VariableAttributes uint32
const (
AttributeNonVolatile VariableAttributes = uefi.EFI_VARIABLE_NON_VOLATILE
AttributeBootserviceAccess VariableAttributes = uefi.EFI_VARIABLE_BOOTSERVICE_ACCESS
AttributeRuntimeAccess VariableAttributes = uefi.EFI_VARIABLE_RUNTIME_ACCESS
AttributeHardwareErrorRecord VariableAttributes = uefi.EFI_VARIABLE_HARDWARE_ERROR_RECORD
AttributeAuthenticatedWriteAccess VariableAttributes = uefi.EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
AttributeTimeBasedAuthenticatedWriteAccess VariableAttributes = uefi.EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
AttributeAppendWrite VariableAttributes = uefi.EFI_VARIABLE_APPEND_WRITE
AttributeEnhancedAuthenticatedAccess VariableAttributes = uefi.EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS
)
var (
ErrVarsUnavailable = errors.New("no variable backend is available")
ErrVarNotExist = errors.New("variable does not exist")
ErrVarPermission = errors.New("permission denied")
)
// VariableDescriptor represents the identity of a variable.
type VariableDescriptor struct {
Name string
GUID GUID
}
type varsBackend interface {
Get(name string, guid GUID) (VariableAttributes, []byte, error)
Set(name string, guid GUID, attrs VariableAttributes, data []byte) error
List() ([]VariableDescriptor, error)
}
type nullVarsBackend struct{}
func (v nullVarsBackend) Get(name string, guid GUID) (VariableAttributes, []byte, error) {
return 0, nil, ErrVarsUnavailable
}
func (v nullVarsBackend) Set(name string, guid GUID, attrs VariableAttributes, data []byte) error {
return ErrVarsUnavailable
}
func (v nullVarsBackend) List() ([]VariableDescriptor, error) {
return nil, ErrVarsUnavailable
}
var vars varsBackend = nullVarsBackend{}
// ReadVariable returns the value and attributes of the EFI variable with the specified
// name and GUID.
func ReadVariable(name string, guid GUID) ([]byte, VariableAttributes, error) {
attrs, data, err := vars.Get(name, guid)
return data, attrs, err
}
// WriteVariable writes the supplied data value with the specified attributes to the
// EFI variable with the specified name and GUID.
//
// If the variable already exists, the specified attributes must match the existing
// attributes with the exception of AttributeAppendWrite.
//
// If the variable does not exist, it will be created.
func WriteVariable(name string, guid GUID, attrs VariableAttributes, data []byte) error {
return vars.Set(name, guid, attrs, data)
}
// ListVariables returns a list of variables that can be accessed.
func ListVariables() ([]VariableDescriptor, error) {
return vars.List()
}
./github.com/canonical/go-efilib/vars_linux.go 0000664 0000000 0000000 00000015270 00000000000 017715 0 ustar 00 0000000 0000000 // Copyright 2020-2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"syscall"
"golang.org/x/sys/unix"
"golang.org/x/xerrors"
internal_unix "github.com/canonical/go-efilib/internal/unix"
)
func efivarfsPath() string {
return "/sys/firmware/efi/efivars"
}
type varFile interface {
io.ReadWriteCloser
Readdir(n int) ([]os.FileInfo, error)
GetInodeFlags() (uint, error)
SetInodeFlags(flags uint) error
}
func makeVarFileMutable(f varFile) (restore func() error, err error) {
const immutableFlag = 0x00000010
flags, err := f.GetInodeFlags()
if err != nil {
return nil, err
}
if flags&immutableFlag == 0 {
// Nothing to do
return func() error { return nil }, nil
}
if err := f.SetInodeFlags(flags &^ immutableFlag); err != nil {
return nil, err
}
return func() error {
return f.SetInodeFlags(flags)
}, nil
}
type realVarFile struct {
*os.File
}
func (f *realVarFile) GetInodeFlags() (uint, error) {
flags, err := internal_unix.IoctlGetUint(int(f.Fd()), unix.FS_IOC_GETFLAGS)
if err != nil {
return 0, &os.PathError{Op: "ioctl", Path: f.Name(), Err: err}
}
return flags, nil
}
func (f *realVarFile) SetInodeFlags(flags uint) error {
if err := internal_unix.IoctlSetPointerUint(int(f.Fd()), unix.FS_IOC_SETFLAGS, flags); err != nil {
return &os.PathError{Op: "ioctl", Path: f.Name(), Err: err}
}
return nil
}
func realOpenVarFile(path string, flags int, perm os.FileMode) (varFile, error) {
f, err := os.OpenFile(path, flags, perm)
if err != nil {
return nil, err
}
return &realVarFile{f}, nil
}
var guidLength = len("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
func probeEfivarfs() bool {
var st unix.Statfs_t
if err := unixStatfs(efivarfsPath(), &st); err != nil {
return false
}
if uint(st.Type) != uint(unix.EFIVARFS_MAGIC) {
return false
}
return true
}
func maybeRetry(n int, fn func() (bool, error)) error {
for i := 1; ; i++ {
retry, err := fn()
switch {
case i > n:
return err
case !retry:
return err
case err == nil:
return nil
}
}
}
func processEfivarfsFileAccessError(err error) (retry bool, errOut error) {
if os.IsPermission(err) {
var se syscall.Errno
if !xerrors.As(err, &se) {
// This shouldn't happen, but just return ErrVarPermission
// in this case and don't retry.
return false, ErrVarPermission
}
if se == syscall.EACCES {
// open will fail with EACCES if we lack the privileges
// to write to the file.
// open will fail with EACCES if we lack the privileges
// to write to the parent directory in the case where we
// need to create a new file.
// unlink will fail with EACCES if we lack the privileges
// to write to the parent directory.
// Don't retry in these cases.
return false, ErrVarPermission
}
// open and unlink will fail with EPERM if the file exists but
// it is immutable. This might happen as a result of a race with
// another process that might have been writing to the variable
// or may have deleted and recreated it, making the underlying
// inode immutable again.
// Retry in this case.
return true, ErrVarPermission
}
// Don't retry for any other error.
return false, err
}
func writeEfivarfsFile(path string, attrs VariableAttributes, data []byte) (retry bool, err error) {
// Open for reading to make the inode mutable
r, err := openVarFile(path, os.O_RDONLY, 0)
switch {
case os.IsNotExist(err):
case os.IsPermission(err):
return false, ErrVarPermission
case err != nil:
return false, err
default:
defer r.Close()
restoreImmutable, err := makeVarFileMutable(r)
switch {
case os.IsPermission(err):
return false, ErrVarPermission
case err != nil:
return false, err
}
defer restoreImmutable()
}
if len(data) == 0 {
return processEfivarfsFileAccessError(removeVarFile(path))
}
flags := os.O_WRONLY | os.O_CREATE
if attrs&AttributeAppendWrite != 0 {
flags |= os.O_APPEND
}
w, err := openVarFile(path, flags, 0644)
if err != nil {
return processEfivarfsFileAccessError(err)
}
defer w.Close()
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, attrs)
buf.Write(data)
_, err = buf.WriteTo(w)
return false, err
}
type efivarfsVarsBackend struct{}
func (v efivarfsVarsBackend) Get(name string, guid GUID) (VariableAttributes, []byte, error) {
path := filepath.Join(efivarfsPath(), fmt.Sprintf("%s-%s", name, guid))
f, err := openVarFile(path, os.O_RDONLY, 0)
switch {
case os.IsNotExist(err):
return 0, nil, ErrVarNotExist
case os.IsPermission(err):
return 0, nil, ErrVarPermission
case err != nil:
return 0, nil, err
}
defer f.Close()
var attrs VariableAttributes
if err := binary.Read(f, binary.LittleEndian, &attrs); err != nil {
if err == io.EOF {
return 0, nil, ErrVarNotExist
}
return 0, nil, err
}
data, err := ioutil.ReadAll(f)
if err != nil {
return 0, nil, err
}
return attrs, data, nil
}
func (v efivarfsVarsBackend) Set(name string, guid GUID, attrs VariableAttributes, data []byte) error {
path := filepath.Join(efivarfsPath(), fmt.Sprintf("%s-%s", name, guid))
return maybeRetry(4, func() (bool, error) { return writeEfivarfsFile(path, attrs, data) })
}
func (v efivarfsVarsBackend) List() ([]VariableDescriptor, error) {
f, err := openVarFile(efivarfsPath(), os.O_RDONLY, 0)
switch {
case os.IsNotExist(err):
return nil, ErrVarsUnavailable
case os.IsPermission(err):
return nil, ErrVarPermission
case err != nil:
return nil, err
}
defer f.Close()
dirents, err := f.Readdir(-1)
if err != nil {
return nil, err
}
var entries []VariableDescriptor
for _, dirent := range dirents {
if !dirent.Mode().IsRegular() {
// Skip non-regular files
continue
}
if len(dirent.Name()) < guidLength+1 {
// Skip files with a basename that isn't long enough
// to contain a GUID and a hyphen
continue
}
if dirent.Name()[len(dirent.Name())-guidLength-1] != '-' {
// Skip files where the basename doesn't contain a
// hyphen between the name and GUID
continue
}
if dirent.Size() == 0 {
// Skip files with zero size. These are variables that
// have been deleted by writing an empty payload
continue
}
name := dirent.Name()[:len(dirent.Name())-guidLength-1]
guid, err := DecodeGUIDString(dirent.Name()[len(name)+1:])
if err != nil {
continue
}
entries = append(entries, VariableDescriptor{Name: name, GUID: guid})
}
sort.Slice(entries, func(i, j int) bool {
return fmt.Sprintf("%s-%v", entries[i].Name, entries[i].GUID) < fmt.Sprintf("%s-%v", entries[j].Name, entries[j].GUID)
})
return entries, nil
}
func init() {
if !probeEfivarfs() {
return
}
vars = efivarfsVarsBackend{}
}
./github.com/canonical/go-efilib/wincert.go 0000664 0000000 0000000 00000011053 00000000000 017171 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package efi
import (
"encoding/binary"
"errors"
"io"
"github.com/canonical/go-efilib/internal/ioerr"
"github.com/canonical/go-efilib/internal/uefi"
)
// WinCertificate is an interface type corresponding to implementations of WIN_CERTIFICATE.
type WinCertificate interface {
Write(w io.Writer) error // Encode this certificate to the supplied io.Writer
}
// WinCertificatePKCS1v15 corresponds to the WIN_CERTIFICATE_EFI_PKCS1_15 type.
type WinCertificatePKCS1v15 struct {
HashAlgorithm GUID
Signature []byte
}
func (c *WinCertificatePKCS1v15) Write(w io.Writer) error {
cert := uefi.WIN_CERTIFICATE_EFI_PKCS1_15{
HashAlgorithm: uefi.EFI_GUID(c.HashAlgorithm),
Signature: c.Signature}
cert.Hdr = uefi.WIN_CERTIFICATE{
Length: uint32(binary.Size(cert.Hdr) + binary.Size(cert.HashAlgorithm) + len(c.Signature)),
Revision: 0x0200,
CertificateType: uefi.WIN_CERT_TYPE_EFI_PKCS115}
return binary.Write(w, binary.LittleEndian, &cert)
}
// WinCertificateGUID corresponds to the WIN_CERTIFICATE_UEFI_GUID type.
type WinCertificateGUID struct {
Type GUID
Data []byte
}
func (c *WinCertificateGUID) Write(w io.Writer) error {
return binary.Write(w, binary.LittleEndian, c.toUefiType())
}
func (c *WinCertificateGUID) toUefiType() *uefi.WIN_CERTIFICATE_UEFI_GUID {
cert := &uefi.WIN_CERTIFICATE_UEFI_GUID{
CertType: uefi.EFI_GUID(c.Type),
CertData: c.Data}
cert.Hdr = uefi.WIN_CERTIFICATE{
Length: uint32(binary.Size(cert.Hdr) + binary.Size(cert.CertType) + len(c.Data)),
Revision: 0x0200,
CertificateType: uefi.WIN_CERT_TYPE_EFI_GUID}
return cert
}
func newWinCertificateGUID(cert *uefi.WIN_CERTIFICATE_UEFI_GUID) *WinCertificateGUID {
return &WinCertificateGUID{Type: GUID(cert.CertType), Data: cert.CertData}
}
// WinCertificateAuthenticode corresponds to an Authenticode signature.
type WinCertificateAuthenticode []byte
func (c WinCertificateAuthenticode) Write(w io.Writer) error {
cert := uefi.WIN_CERTIFICATE_EFI_PKCS{CertData: c}
cert.Hdr = uefi.WIN_CERTIFICATE{
Length: uint32(binary.Size(cert.Hdr) + len(c)),
Revision: 0x0200,
CertificateType: uefi.WIN_CERT_TYPE_PKCS_SIGNED_DATA}
return binary.Write(w, binary.LittleEndian, &cert)
}
// ReadWinCertificate decodes a signature (something that is confusingly represented by types with "certificate" in the name in both
// the UEFI and PE/COFF specifications) from the supplied io.Reader and returns a WinCertificate of the appropriate type. The type
// returned is dependent on the data, and will be one of *WinCertificateAuthenticode, *WinCertificatePKCS1_15 or *WinCertificateGUID.
func ReadWinCertificate(r io.Reader) (WinCertificate, error) {
var hdr uefi.WIN_CERTIFICATE
if err := binary.Read(r, binary.LittleEndian, &hdr); err != nil {
return nil, err
}
if hdr.Revision != 0x0200 {
return nil, errors.New("unexpected revision")
}
switch hdr.CertificateType {
case uefi.WIN_CERT_TYPE_PKCS_SIGNED_DATA:
cert := uefi.WIN_CERTIFICATE_EFI_PKCS{Hdr: hdr}
cert.CertData = make([]byte, int(cert.Hdr.Length)-binary.Size(cert.Hdr))
if _, err := io.ReadFull(r, cert.CertData); err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read WIN_CERTIFICATE_EFI_PKCS: %w", err)
}
return WinCertificateAuthenticode(cert.CertData), nil
case uefi.WIN_CERT_TYPE_EFI_PKCS115:
cert := uefi.WIN_CERTIFICATE_EFI_PKCS1_15{Hdr: hdr}
cert.Signature = make([]byte, int(cert.Hdr.Length)-binary.Size(cert.Hdr)-binary.Size(cert.HashAlgorithm))
if _, err := io.ReadFull(r, cert.HashAlgorithm[:]); err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read WIN_CERTIFICATE_EFI_PKCS1_15: %w", err)
}
if _, err := io.ReadFull(r, cert.Signature); err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read WIN_CERTIFICATE_EFI_PKCS1_15: %w", err)
}
return &WinCertificatePKCS1v15{HashAlgorithm: GUID(cert.HashAlgorithm), Signature: cert.Signature}, nil
case uefi.WIN_CERT_TYPE_EFI_GUID:
cert := uefi.WIN_CERTIFICATE_UEFI_GUID{Hdr: hdr}
cert.CertData = make([]byte, int(cert.Hdr.Length)-binary.Size(cert.Hdr)-binary.Size(cert.CertType))
if _, err := io.ReadFull(r, cert.CertType[:]); err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read WIN_CERTIFICATE_UEFI_GUID: %w", err)
}
if _, err := io.ReadFull(r, cert.CertData); err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read WIN_CERTIFICATE_UEFI_GUID: %w", err)
}
return newWinCertificateGUID(&cert), nil
default:
return nil, errors.New("unexpected type")
}
}
./github.com/canonical/go-sp800.108-kdf/ 0000775 0000000 0000000 00000000000 00000000000 015660 5 ustar 00 0000000 0000000 ./github.com/canonical/go-sp800.108-kdf/LICENSE 0000664 0000000 0000000 00000021501 00000000000 016664 0 ustar 00 0000000 0000000 All files in this repository are licensed as follows. If you contribute
to this repository, it is assumed that you license your contribution
under the same license unless you state otherwise.
All files Copyright (C) 2021 Canonical Ltd. unless otherwise specified in the file.
This software is licensed under the LGPLv3, included below.
As a special exception to the GNU Lesser General Public License version 3
("LGPL3"), the copyright holders of this Library give you permission to
convey to a third party a Combined Work that links statically or dynamically
to this Library without providing any Minimal Corresponding Source or
Minimal Application Code as set out in 4d or providing the installation
information set out in section 4e, provided that you comply with the other
provisions of LGPL3 and provided that you meet, for the Application the
terms and conditions of the license(s) which apply to the Application.
Except as stated in this special exception, the provisions of LGPL3 will
continue to comply in full to this Library. If you modify this Library, you
may apply this exception to your version of this Library, but you are not
obliged to do so. If you do not wish to do so, delete this exception
statement from your version. This exception does not (and cannot) modify any
license terms which apply to the Application, with which you must still
comply.
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
./github.com/canonical/go-sp800.108-kdf/kdf.go 0000664 0000000 0000000 00000007570 00000000000 016764 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
/*
Package kdf implements the key derivation functions described in NIST SP-800-108
(see https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf).
All 3 modes are implemented - counter, feedback and pipeline.
*/
package kdf
import (
"bytes"
"crypto"
"crypto/hmac"
"encoding/binary"
"hash"
)
// PRF represents a pseudorandom function, required by the key derivation functions.
type PRF interface {
// Len returns the length of this PRF.
Len() uint32
// Run computes bytes for the supplied seed and input value.
Run(s, x []byte) []byte
}
type hmacPRF crypto.Hash
func (p hmacPRF) Len() uint32 {
return uint32(crypto.Hash(p).Size())
}
func (p hmacPRF) Run(s, x []byte) []byte {
h := hmac.New(func() hash.Hash { return crypto.Hash(p).New() }, s)
h.Write(x)
return h.Sum(nil)
}
// NewHMACPRF creates a new HMAC based PRF using the supplied digest algorithm.
func NewHMACPRF(h crypto.Hash) PRF {
return hmacPRF(h)
}
func fixedBytes(label, context []byte, bitLength uint32) []byte {
var res bytes.Buffer
res.Write(label)
res.Write([]byte{0})
res.Write(context)
binary.Write(&res, binary.BigEndian, bitLength)
return res.Bytes()
}
func commonKDF(prfLen uint32, fixed []byte, bitLength uint32, fn func(uint32) []byte) []byte {
n := (bitLength + prfLen - 1) / prfLen
var res bytes.Buffer
for i := uint32(1); i <= n; i++ {
res.Write(fn(i))
}
return res.Bytes()[:(bitLength+7)/8]
}
func counterModeKeyInternal(prf PRF, key, fixed []byte, bitLength uint32) []byte {
return commonKDF(prf.Len(), fixed, bitLength, func(i uint32) []byte {
var x bytes.Buffer
binary.Write(&x, binary.BigEndian, i)
x.Write(fixed)
return prf.Run(key, x.Bytes())
})
}
// CounterModeKey derives a key of the specified length using the counter mode
// function described in NIST SP-800-108, using the supplied PRF, secret key and
// other input parameters.
func CounterModeKey(prf PRF, key, label, context []byte, bitLength uint32) []byte {
return counterModeKeyInternal(prf, key, fixedBytes(label, context, bitLength), bitLength)
}
func feedbackModeKeyInternal(prf PRF, key, fixed, iv []byte, bitLength uint32, useCounter bool) []byte {
k := iv
return commonKDF(prf.Len(), fixed, bitLength, func(i uint32) []byte {
var x bytes.Buffer
x.Write(k)
if useCounter {
binary.Write(&x, binary.BigEndian, i)
}
x.Write(fixed)
k = prf.Run(key, x.Bytes())
return k
})
}
// FeebackModeKey derives a key of the specified length using the feedback mode
// function described in NIST SP-800-108, using the supplied PRF, secret key and
// other input parameters.
//
// The useCounter argument specifies whether the iteration counter should be
// included as an input to the PRF.
func FeedbackModeKey(prf PRF, key, label, context, iv []byte, bitLength uint32, useCounter bool) []byte {
return feedbackModeKeyInternal(prf, key, fixedBytes(label, context, bitLength), iv, bitLength, useCounter)
}
func pipelineModeKeyInternal(prf PRF, key, fixed []byte, bitLength uint32, useCounter bool) []byte {
a := fixed
return commonKDF(prf.Len(), fixed, bitLength, func(i uint32) []byte {
a = prf.Run(key, a)
var x bytes.Buffer
x.Write(a)
if useCounter {
binary.Write(&x, binary.BigEndian, i)
}
x.Write(fixed)
return prf.Run(key, x.Bytes())
})
}
// PipelineModeKey derives a key of the specified length using the double-pipeline
// iteration mode function described in NIST SP-800-108, using the supplied PRF,
// secret key and other input parameters.
//
// The useCounter argument specifies whether the iteration counter should be
// included as an input to the PRF.
func PipelineModeKey(prf PRF, key, label, context []byte, bitLength uint32, useCounter bool) []byte {
return pipelineModeKeyInternal(prf, key, fixedBytes(label, context, bitLength), bitLength, useCounter)
}
./github.com/canonical/go-sp800.90a-drbg/ 0000775 0000000 0000000 00000000000 00000000000 016113 5 ustar 00 0000000 0000000 ./github.com/canonical/go-sp800.90a-drbg/.gitignore 0000664 0000000 0000000 00000000012 00000000000 020074 0 ustar 00 0000000 0000000 vendor/*/
./github.com/canonical/go-sp800.90a-drbg/LICENSE 0000664 0000000 0000000 00000021501 00000000000 017117 0 ustar 00 0000000 0000000 All files in this repository are licensed as follows. If you contribute
to this repository, it is assumed that you license your contribution
under the same license unless you state otherwise.
All files Copyright (C) 2021 Canonical Ltd. unless otherwise specified in the file.
This software is licensed under the LGPLv3, included below.
As a special exception to the GNU Lesser General Public License version 3
("LGPL3"), the copyright holders of this Library give you permission to
convey to a third party a Combined Work that links statically or dynamically
to this Library without providing any Minimal Corresponding Source or
Minimal Application Code as set out in 4d or providing the installation
information set out in section 4e, provided that you comply with the other
provisions of LGPL3 and provided that you meet, for the Application the
terms and conditions of the license(s) which apply to the Application.
Except as stated in this special exception, the provisions of LGPL3 will
continue to comply in full to this Library. If you modify this Library, you
may apply this exception to your version of this Library, but you are not
obliged to do so. If you do not wish to do so, delete this exception
statement from your version. This exception does not (and cannot) modify any
license terms which apply to the Application, with which you must still
comply.
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
./github.com/canonical/go-sp800.90a-drbg/ctr.go 0000664 0000000 0000000 00000023240 00000000000 017233 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package drbg
import (
"bytes"
"crypto/aes"
"encoding/binary"
"errors"
"fmt"
"io"
"math/big"
"golang.org/x/xerrors"
)
var (
dfKey = []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}
)
type blockCipher interface {
encrypt(key, data []byte) []byte
blockSize() int
}
type aesBlockCipherImpl struct{}
var aesBlockCipher = aesBlockCipherImpl{}
func (b aesBlockCipherImpl) encrypt(key, data []byte) (out []byte) {
c, err := aes.NewCipher(key)
if err != nil {
panic(fmt.Sprintf("cannot create cipher: %v", err))
}
out = make([]byte, len(data))
c.Encrypt(out, data)
return
}
func (b aesBlockCipherImpl) blockSize() int {
return aes.BlockSize
}
// bcc implements BCC, described in section 10.3.3 of SP800-90A.
func bcc(b blockCipher, key, data []byte) []byte {
if len(data)%b.blockSize() != 0 {
panic("data length must be a multiple of the block length")
}
// 1) chaining_value = 0(x outlen)
chainingValue := make([]byte, b.blockSize())
// 2) n = len (data)/outlen.
n := len(data) / b.blockSize()
// 3) Starting with the leftmost bits of data, split data into n blocks of
// outlen bits each, forming block[1] to block[n].
blocks := make([][]byte, n)
for i := 0; i < n; i++ {
blocks[i] = data[i*b.blockSize() : (i+1)*b.blockSize()]
}
inputBlock := make([]byte, b.blockSize())
// 4) For i = 1 to n do
for i := 0; i < n; i++ {
for j := 0; j < len(inputBlock); j++ {
// 4.1) input_block = chaining_value ⊕ block[i].
inputBlock[j] = chainingValue[j] ^ blocks[i][j]
}
// 4.2) chaining_value = Block_Encrypt (Key, input_block).
chainingValue = b.encrypt(key, inputBlock)
}
return chainingValue
}
// block_cipher_df implements Block_Cipher_df, described in section 10.3.2 of SP800-90A.
func block_cipher_df(b blockCipher, keyLen int, input []byte, requestedBytes int) []byte {
// 2) L = len (input_string)/8.
l := uint32(len(input))
// 3) N = number_of_bits_to_return/8.
n := uint32(requestedBytes)
// 4) S = L || N || input_string || 0x80.
var s bytes.Buffer
binary.Write(&s, binary.BigEndian, l)
binary.Write(&s, binary.BigEndian, n)
s.Write(input)
s.Write([]byte{0x80})
// 5) While (len (S) mod outlen) ≠ 0, do
// S = S || 0x00.
for s.Len()%b.blockSize() != 0 {
s.Write([]byte{0x00})
}
// 6) temp = the Null string.
var temp bytes.Buffer
// 7) i = 0.
i := uint32(0)
// 8) K = leftmost (0x00010203...1D1E1F, keylen).
k := dfKey[:keyLen]
iv := make([]byte, b.blockSize())
// 9) While len(temp) < keylen + outlen, do
for temp.Len() < (keyLen + b.blockSize()) {
// 9.1) IV = i || 0(x (outlen - len (i))).
binary.BigEndian.PutUint32(iv, i)
// 9.2) temp = temp || BCC (K, (IV || S)).
var data bytes.Buffer
data.Write(iv)
data.Write(s.Bytes())
temp.Write(bcc(b, k, data.Bytes()))
// 9.3) i = i + 1.
i += 1
}
// 10) K = leftmost (temp, keylen).
k = make([]byte, keyLen)
copy(k, temp.Bytes())
// 11) X = select (temp, keylen+1, keylen+outlen).
x := make([]byte, b.blockSize())
copy(x, temp.Bytes()[keyLen:])
// 12) temp = the Null string.
temp.Reset()
// 13) While len (temp) < number_of_bits_to_return, do
for temp.Len() < requestedBytes {
// 13.1) = Block_Encrypt (K, X).
x = b.encrypt(k, x)
// 13.2) temp = temp || X.
temp.Write(x)
}
// 14) requested_bits = leftmost (temp, number_of_bits_to_return).
return temp.Bytes()[:requestedBytes]
}
type ctrDRBG struct {
b blockCipher
v []byte
key []byte
reseedCounter uint64
}
func (d *ctrDRBG) keyLen() int {
return len(d.key)
}
func (d *ctrDRBG) blockSize() int {
return d.b.blockSize()
}
func (d *ctrDRBG) seedLength() int {
return d.keyLen() + d.b.blockSize()
}
// update implements CTR_DRBG_Update, described in section 10.2.1.2 of
// SP800-90A.
func (d *ctrDRBG) update(providedData []byte) {
if len(providedData) != d.seedLength() {
panic("provided data has the wrong length")
}
// 1) temp = Null
var temp bytes.Buffer
mod := twoExp(uint(d.blockSize() * 8))
v := new(big.Int)
// 2) While (len(temp) < seedLen) do
for temp.Len() < d.seedLength() {
// 2.1) V = (V+1) mod 2^blocklen
v.SetBytes(d.v)
v.Add(v, one)
v.Mod(v, mod)
d.v = zeroExtendBytes(v, d.blockSize())
// 2.2) output_block = Block_Encrypt (Key, V).
// 2.3) temp = temp || output_block.
temp.Write(d.b.encrypt(d.key, d.v))
}
// 3) temp = leftmost(temp, seedLen)
temp.Truncate(d.seedLength())
// 4) temp = temp ⊕ provided_data.
for i := 0; i < temp.Len(); i++ {
temp.Bytes()[i] ^= providedData[i]
}
// 5) Key = leftmost (temp, keylen).
d.key = temp.Bytes()[:d.keyLen()]
// 6) V = rightmost (temp, blocklen).
d.v = temp.Bytes()[d.keyLen():]
}
// instantiate implements CTR_DRBG_Instantiate_algorithm, described in section 10.2.1.3.2 of
// SP800-90A.
func (d *ctrDRBG) instantiate(entropyInput, nonce, personalization []byte, securityStrength int) {
var tmp bytes.Buffer
// 1) seed_material = entropy_input || nonce || personalization_string.
tmp.Write(entropyInput)
tmp.Write(nonce)
tmp.Write(personalization)
seedMaterial := tmp.Bytes()
// 2) seed_material = df (seed_material, seedlen).
seedMaterial = block_cipher_df(d.b, d.keyLen(), seedMaterial, d.seedLength())
// 3) Key = 0(x keylen) is done in NewCTR.
// 4) V = 0(x blocklen).
d.v = make([]byte, d.blockSize())
// 5) (Key, V) = CTR_DRBG_Update (seed_material, Key, V).
d.update(seedMaterial)
// 6) reseed_counter = 1.
d.reseedCounter = 1
}
// reseed implements CTR_DRBG_Reseed_algorithm, described in section 10.2.1.4.2 of
// SP800-90A.
func (d *ctrDRBG) reseed(entropyInput, additionalInput []byte) {
var tmp bytes.Buffer
// 1) seed_material = entropy_input || additional_input.
tmp.Write(entropyInput)
tmp.Write(additionalInput)
seedMaterial := tmp.Bytes()
// 2) seed_material = df (seed_material, seedlen).
seedMaterial = block_cipher_df(d.b, d.keyLen(), seedMaterial, d.seedLength())
// 3) (Key, V) = CTR_DRBG_Update (seed_material, Key, V).
d.update(seedMaterial)
// 4) reseed_counter = 1.
d.reseedCounter = 1
}
// generate implements CTR_DRBG_Generate_algorithm, described in section 10.2.1.5.2 of
// SP800-90A.
func (d *ctrDRBG) generate(additionalInput, data []byte) error {
// 1) If reseed_counter > reseed_interval, then return an indication that a
// reseed is required.
if d.reseedCounter > 1<<48 {
return ErrReseedRequired
}
// 2) If (additional_input ≠ Null), then
if len(additionalInput) > 0 {
// 2.1) additional_input = Block_Cipher_df (additional_input, seedlen).
additionalInput = block_cipher_df(d.b, d.keyLen(), additionalInput, d.seedLength())
// 2.2) (Key, V) = CTR_DRBG_Update (additional_input, Key, V).
d.update(additionalInput)
// Else additional_input = 0(x seedlen).
} else {
additionalInput = make([]byte, d.seedLength())
}
// 3) temp = Null.
var temp bytes.Buffer
mod := twoExp(uint(d.blockSize() * 8))
v := new(big.Int)
// 4) While (len (temp) < requested_number_of_bits) do:
for temp.Len() < len(data) {
// 4.1.2) V = (V+1) mod 2^blocklen.
v.SetBytes(d.v)
v.Add(v, one)
v.Mod(v, mod)
d.v = zeroExtendBytes(v, d.blockSize())
// 4.2) output_block = Block_Encrypt (Key, V).
outputBlock := d.b.encrypt(d.key, d.v)
// 4.3) temp = temp || output_block.
temp.Write(outputBlock)
}
// 5) returned_bits = leftmost (temp, requested_number_of_bits).
copy(data, temp.Bytes())
// 6) (Key, V) = CTR_DRBG_Update (additional_input, Key, V).
d.update(additionalInput)
// 7) reseed_counter = reseed_counter + 1.
d.reseedCounter += 1
return nil
}
// NewCTR creates a new block cipher based DRBG as specified in section 10.2 of SP-800-90A.
// The DRBG uses the AES block cipher.
//
// The optional personalization argument is combined with entropy input to derive the
// initial seed. This argument can be used to differentiate this instantiation from others.
//
// The optional entropySource argument allows the default entropy source (rand.Reader from
// the crypto/rand package) to be overridden. The supplied entropy source must be truly
// random.
func NewCTR(keyLen int, personalization []byte, entropySource io.Reader) (*DRBG, error) {
switch keyLen {
case 16, 24, 32:
default:
return nil, errors.New("invalid key size")
}
d := &DRBG{impl: &ctrDRBG{b: aesBlockCipher, key: make([]byte, keyLen)}}
if err := d.instantiate(personalization, entropySource, keyLen); err != nil {
return nil, xerrors.Errorf("cannot instantiate: %w", err)
}
return d, nil
}
// NewCTRWithExternalEntropy creates a new block cipher based DRBG as specified in
// section 10.2 of SP-800-90A. The DRBG uses the AES block cipher. The entropyInput and
// nonce arguments provide the initial entropy to seed the created DRBG.
//
// The optional personalization argument is combined with entropy input to derive the
// initial seed. This argument can be used to differentiate this instantiation from others.
//
// The optional entropySource argument provides the entropy source for future reseeding. If
// it is not supplied, then the DRBG can only be reseeded with externally supplied entropy.
// The supplied entropy source must be truly random.
func NewCTRWithExternalEntropy(keyLen int, entropyInput, nonce, personalization []byte, entropySource io.Reader) (*DRBG, error) {
switch keyLen {
case 16, 24, 32:
default:
return nil, errors.New("invalid key size")
}
d := &DRBG{impl: &ctrDRBG{b: aesBlockCipher, key: make([]byte, keyLen)}}
if err := d.instantiateWithExternalEntropy(entropyInput, nonce, personalization, entropySource, keyLen); err != nil {
return nil, err
}
return d, nil
}
./github.com/canonical/go-sp800.90a-drbg/drbg.go 0000664 0000000 0000000 00000020456 00000000000 017367 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
/*
Package drbg implements several DRBGs as recommended by NIST SP-800-90A (see
http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf).
The hash, HMAC and block cipher mode DRBGs are implemented.
DRBG instances are automatically reseeded once the current seed period
expires.
All DRBGs are instantiated with the maximum security strength associated
with the requested configuration. The security strength cannot be specified
via the API.
DRBGs are instantiated by default using the platform's default entropy source
(via the crypto/rand package). This entropy source can be overridden, but it
must provide truly random data in order to achieve the selected security
strength.
Note that prediction resistance is not implemented. Prediction resistance
requires that the supplied entropy source is non-deterministic.
*/
package drbg
import (
"crypto/rand"
"errors"
"io"
"math/big"
"golang.org/x/xerrors"
)
// ErrReseedRequired indicates that the DRBG must be reseeded before
// it can generate random bytes.
var ErrReseedRequired = errors.New("the DRGB must be reseeded")
var one = big.NewInt(1)
func twoExp(n uint) (out *big.Int) {
d := make([]byte, n/8+1)
d[0] = byte(1 << (n % 8))
out = new(big.Int)
out.SetBytes(d)
return
}
func zeroExtendBytes(x *big.Int, l int) (out []byte) {
out = make([]byte, l)
tmp := x.Bytes()
copy(out[len(out)-len(tmp):], tmp)
return
}
func selectSecurityStrength(requested int) int {
switch {
case requested <= 14:
return 14
case requested <= 16:
return 16
case requested <= 24:
return 24
default:
return 32
}
}
type drbgImpl interface {
instantiate(entropyInput, nonce, personalization []byte, securityStrength int)
reseed(entropyInput, additionalInput []byte)
generate(additionalInput, data []byte) error
}
// DRBG corresponds to an instantiated DRBG based on one of the mechanisms specified
// in SP-800-90A.
type DRBG struct {
entropySource io.Reader
securityStrength int
impl drbgImpl
}
// instantiate implements the steps described in section 9.1 of SP800-90A.
func (d *DRBG) instantiate(personalization []byte, entropySource io.Reader, securityStrength int) error {
// 3) If the length of the personalization_string > max_personalization_string_length,
// return (ERROR_FLAG, Invalid).
if int64(len(personalization)) > 1<<32 {
return errors.New("personalization too large")
}
d.entropySource = rand.Reader
if entropySource != nil {
d.entropySource = entropySource
}
// 4) Set security_strength to the lowest security strength greater than or equal
// to requested_instantiation_security_strength from the set {112, 128, 192, 256}
d.securityStrength = selectSecurityStrength(securityStrength)
// 6) (status, entropy_input) = Get_entropy_input (security_strength, min_length,
// max_length, prediction_resistance_request).
// 7) If (status ≠ SUCCESS), return (status, Invalid).
entropyInput := make([]byte, securityStrength)
if _, err := d.entropySource.Read(entropyInput); err != nil {
return xerrors.Errorf("cannot get entropy: %w", err)
}
// 8) Obtain a nonce.
nonce := make([]byte, securityStrength/2)
if _, err := d.entropySource.Read(nonce); err != nil {
return xerrors.Errorf("cannot get nonce: %w", err)
}
// 9) initial_working_state = Instantiate_algorithm (entropy_input, nonce,
// personalization_string, security_strength).
d.impl.instantiate(entropyInput, nonce, personalization, securityStrength)
return nil
}
func (d *DRBG) instantiateWithExternalEntropy(entropyInput, nonce, personalization []byte, entropySource io.Reader, securityStrength int) error {
if len(entropyInput) < securityStrength {
return errors.New("entropyInput too small")
}
if int64(len(entropyInput)) > 1<<32 {
return errors.New("entropyInput too large")
}
if int64(len(personalization)) > 1<<32 {
return errors.New("personalization too large")
}
d.entropySource = entropySource
d.securityStrength = selectSecurityStrength(securityStrength)
d.impl.instantiate(entropyInput, nonce, personalization, securityStrength)
return nil
}
// ReseedWithExternalEntropy will reseed the DRBG with the supplied entropy.
func (d *DRBG) ReseedWithExternalEntropy(entropyInput, additionalInput []byte) error {
if int64(len(additionalInput)) > 1<<32 {
return errors.New("additionalInput too large")
}
if len(entropyInput) < d.securityStrength {
return errors.New("entropyInput too small")
}
if int64(len(entropyInput)) > 1<<32 {
return errors.New("entropyInput too large")
}
d.impl.reseed(entropyInput, additionalInput)
return nil
}
// Reseed will reseed the DRBG with additional entropy using the entropy source
// it was initialized with.
func (d *DRBG) Reseed(additionalInput []byte) error {
// 3) If the length of the additional_input > max_additional_input_length,
// return (ERROR_FLAG).
if int64(len(additionalInput)) > 1<<32 {
return errors.New("additionalInput too large")
}
if d.entropySource == nil {
return errors.New("cannot reseed without external entropy")
}
// 4) (status, entropy_input) = Get_entropy_input (security_strength, min_length,
// max_length, prediction_resistance_request).
entropyInput := make([]byte, d.securityStrength)
if _, err := d.entropySource.Read(entropyInput); err != nil {
// 5) If (status ≠ SUCCESS), return (status).
return xerrors.Errorf("cannot get entropy: %w", err)
}
// 6) new_working_state = Reseed_algorithm (working_state, entropy_input,
// additional_input).
d.impl.reseed(entropyInput, additionalInput)
return nil
}
// Generate will fill the supplied data buffer with random bytes.
//
// If the DRBG needs to be reseeded before it can generate random bytes and it
// has been initialized with a source of entropy, the reseed operation will be
// performed automatically. If the DRBG hasn't been initialized with a source of
// entropy and it needs to be reseeded, ErrNeedsReseed will be returned.
//
// If the length of data is greater than 65536 bytes, an error will be returned.
func (d *DRBG) Generate(additionalInput, data []byte) error {
// 2) If requested_number_of_bits > max_number_of_bits_per_request, then
// return (ERROR_FLAG, Null).
if len(data) > 65536 {
return errors.New("too many bytes requested")
}
// 4) If the length of the additional_input > max_additional_input_length,
// then return (ERROR_FLAG, Null).
if int64(len(additionalInput)) > 1<<32 {
return errors.New("additionalInput too large")
}
// 6) Clear the reseed_required_flag.
reseedRequired := false
for {
// 7) If reseed_required_flag is set, or if prediction_resistance_request
// is set, then
if reseedRequired {
// 7.1) status = Reseed_function (state_handle, prediction_resistance_request,
// additional_input).
if err := d.Reseed(additionalInput); err != nil {
// 7.2) If (status ≠ SUCCESS), then return (status, Null).
return xerrors.Errorf("cannot reseed: %w", err)
}
// 7.4) additional_input = the Null string.
additionalInput = nil
// 7.5) Clear the reseed_required_flag.
reseedRequired = false
}
// 8) (status, pseudorandom_bits, new_working_state) = Generate_algorithm (
// working_state, requested_number_of_bits, additional_input).
err := d.impl.generate(additionalInput, data)
switch {
case err == ErrReseedRequired && d.entropySource != nil:
// 9) If status indicates that a reseed is required before the requested bits
// can be generated, then
// 9.1) Set the reseed_required_flag.
// 9.3) Go to step 7.
reseedRequired = true
case err == ErrReseedRequired:
return err
case err != nil:
return xerrors.Errorf("cannot generate random data: %w", err)
default:
return nil
}
}
}
// Read will read len(data) random bytes in to data.
//
// If the DRBG needs to be reseeded in order to generate all of the random bytes
// and it has been initialized with a source of entropy, the reseed operation will
// be performed automatically. If the DRBG hasn't been initialized with a source of
// entropy and it needs to be reseeded, ErrNeedsReseed will be returned.
func (d *DRBG) Read(data []byte) (int, error) {
total := 0
for len(data) > 0 {
b := data
if len(data) > 65536 {
b = data[:65536]
}
if err := d.Generate(nil, b); err != nil {
return total, err
}
total += len(b)
data = data[len(b):]
}
return total, nil
}
./github.com/canonical/go-sp800.90a-drbg/hash.go 0000664 0000000 0000000 00000016112 00000000000 017366 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package drbg
import (
"bytes"
"crypto"
"encoding/binary"
"fmt"
"io"
"math/big"
"golang.org/x/xerrors"
)
func seedLength(h crypto.Hash) (int, error) {
switch h {
case crypto.SHA1, crypto.SHA224, crypto.SHA256, crypto.SHA512_224, crypto.SHA512_256:
return 55, nil
case crypto.SHA384, crypto.SHA512:
return 111, nil
default:
return 0, fmt.Errorf("unsupported digest algorithm: %v", h)
}
}
// hashgen implements Hashgen, described in section 10.1.1.4 of
// SP800-90A.
func hashgen(alg crypto.Hash, v []byte, requestedBytes int) []byte {
// 1) m = requested_no_of_bits / outlen.
m := (requestedBytes + (alg.Size() - 1)) / alg.Size()
// 2) data = V.
data := v
// 3) W = the Null string.
var W bytes.Buffer
mod := twoExp(uint(len(v) * 8))
h := alg.New()
tmp := new(big.Int)
// 4) For i = 1 to m
for i := 1; i <= m; i++ {
// 4.1) w = Hash (data).
h.Reset()
h.Write(data)
w := h.Sum(nil)
// 4.2) W = W || w.
W.Write(w)
// 4.3) data = (data + 1) mod 2^seedlen.
tmp.SetBytes(data)
tmp.Add(tmp, one)
tmp.Mod(tmp, mod)
data = zeroExtendBytes(tmp, len(v))
}
// 5) returned_bits = leftmost (W, requested_no_of_bits).
return W.Bytes()[:requestedBytes]
}
// hash_df implements the Hash_df function described in section 10.3.1 of
// SP800-90A.
func hash_df(alg crypto.Hash, input []byte, requestedBytes int) []byte {
// 1) temp = the Null string.
var temp bytes.Buffer
// 2) len = no_of_bits_to_return / outlen.
n := (requestedBytes + (alg.Size() - 1)) / alg.Size()
if n > 0xff {
panic("invalid requested bytes")
}
// 3) counter = 0x01.
counter := uint8(1)
h := alg.New()
requestedBits := uint32(requestedBytes * 8)
// 4) For i = 1 to len do
for i := 1; i <= n; i++ {
// 4.1) temp = temp || Hash (counter || no_of_bits_to_return || input_string).
h.Reset()
h.Write([]byte{counter})
binary.Write(h, binary.BigEndian, requestedBits)
h.Write(input)
temp.Write(h.Sum(nil))
// 4.2) counter = counter + 1.
counter += 1
}
// 5) requested_bits = leftmost (temp, no_of_bits_to_return).
return temp.Bytes()[:requestedBytes]
}
type hashDRBG struct {
h crypto.Hash
v []byte
c []byte
reseedCounter uint64
}
func (d *hashDRBG) seedLen() int {
return len(d.v)
}
// instantiate implements Hash_DRBG_Instantiate_algorithm, described in section 10.1.1.2 of
// SP800-90A.
func (d *hashDRBG) instantiate(entropyInput, nonce, personalization []byte, securityStrength int) {
// 1) seed_material = entropy_input || nonce || personalization_string.
var seedMaterial bytes.Buffer
seedMaterial.Write(entropyInput)
seedMaterial.Write(nonce)
seedMaterial.Write(personalization)
// 2) seed = Hash_df (seed_material, seedlen).
seed := hash_df(d.h, seedMaterial.Bytes(), d.seedLen())
// 3) V = seed.
d.v = seed
// 4) C = Hash_df ((0x00 || V), seedlen).
d.c = hash_df(d.h, append([]byte{0x00}, d.v...), d.seedLen())
// 5) reseed_counter = 1.
d.reseedCounter = 1
}
// reseed implements Hash_DRBG_Reseed_algorithm, described in section 10.1.1.3 of
// SP800-90A.
func (d *hashDRBG) reseed(entropyInput, additionalInput []byte) {
// 1) seed_material = 0x01 || V || entropy_input || additional_input.
var seedMaterial bytes.Buffer
seedMaterial.Write([]byte{0x01})
seedMaterial.Write(d.v)
seedMaterial.Write(entropyInput)
seedMaterial.Write(additionalInput)
// 2) seed = Hash_df (seed_material, seedlen).
seed := hash_df(d.h, seedMaterial.Bytes(), d.seedLen())
// 3) V = seed.
d.v = seed
// 4) C = Hash_df ((0x00 || V), seedlen).
d.c = hash_df(d.h, append([]byte{0x00}, seed...), d.seedLen())
// 5) reseed_counter = 1.
d.reseedCounter = 1
}
// generate implements Hash_DRBG_Generate_algorithm, described in section 10.1.1.4 of
// SP800-90A.
func (d *hashDRBG) generate(additionalInput, data []byte) error {
// 1) If reseed_counter > reseed_interval, then return an indication that a reseed
// is required.
if d.reseedCounter > 1<<48 {
return ErrReseedRequired
}
mod := twoExp(uint(d.seedLen() * 8))
// 2) If (additional_input ≠ Null), then do
if len(additionalInput) > 0 {
// 2.1) w = Hash (0x02 || V || additional_input).
h := d.h.New()
h.Write([]byte{0x02})
h.Write(d.v)
h.Write(additionalInput)
w := new(big.Int).SetBytes(h.Sum(nil))
// 2.2) V = (V + w) mod 2^seedlen.
v := new(big.Int).SetBytes(d.v)
v.Add(v, w)
v.Mod(v, mod)
d.v = zeroExtendBytes(v, d.seedLen())
}
// 3) (returned_bits) = Hashgen (requested_number_of_bits, V).
returnedBytes := hashgen(d.h, d.v, len(data))
copy(data, returnedBytes)
// 4) H = Hash (0x03 || V).
hash := d.h.New()
hash.Write([]byte{0x03})
hash.Write(d.v)
h := hash.Sum(nil)
// 5) V = (V + H + C + reseed_counter) mod 2^seedlen.
v := new(big.Int).SetBytes(d.v)
v.Add(v, new(big.Int).SetBytes(h))
v.Add(v, new(big.Int).SetBytes(d.c))
v.Add(v, big.NewInt(int64(d.reseedCounter)))
v.Mod(v, mod)
d.v = zeroExtendBytes(v, d.seedLen())
// 6) reseed_counter = reseed_counter + 1.
d.reseedCounter += 1
return nil
}
// NewHash creates a new hash based DRBG as specified in section 10.1.1 of SP-800-90A.
// The DRBG uses the supplied hash algorithm.
//
// The optional personalization argument is combined with entropy input to derive the
// initial seed. This argument can be used to differentiate this instantiation from others.
//
// The optional entropySource argument allows the default entropy source (rand.Reader from
// the crypto/rand package) to be overridden. The supplied entropy source must be truly
// random.
func NewHash(h crypto.Hash, personalization []byte, entropySource io.Reader) (*DRBG, error) {
seedLen, err := seedLength(h)
if err != nil {
return nil, xerrors.Errorf("cannot compute seed length: %w", err)
}
d := &DRBG{impl: &hashDRBG{h: h, v: make([]byte, seedLen)}}
if err := d.instantiate(personalization, entropySource, h.Size()/2); err != nil {
return nil, xerrors.Errorf("cannot instantiate: %w", err)
}
return d, nil
}
// NewHashWithExternalEntropy creates a new hash based DRBG as specified in section
// 10.1.1 of SP-800-90A. The DRBG uses the supplied hash algorithm. The entropyInput and
// nonce arguments provide the initial entropy to seed the created DRBG.
//
// The optional personalization argument is combined with entropy input to derive the
// initial seed. This argument can be used to differentiate this instantiation from others.
//
// The optional entropySource argument provides the entropy source for future reseeding. If
// it is not supplied, then the DRBG can only be reseeded with externally supplied entropy.
// The supplied entropy source must be truly random.
func NewHashWithExternalEntropy(h crypto.Hash, entropyInput, nonce, personalization []byte, entropySource io.Reader) (*DRBG, error) {
seedLen, err := seedLength(h)
if err != nil {
return nil, xerrors.Errorf("cannot compute seed length: %w", err)
}
d := &DRBG{impl: &hashDRBG{h: h, v: make([]byte, seedLen)}}
if err := d.instantiateWithExternalEntropy(entropyInput, nonce, personalization, entropySource, h.Size()/2); err != nil {
return nil, err
}
return d, nil
}
./github.com/canonical/go-sp800.90a-drbg/hmac.go 0000664 0000000 0000000 00000012245 00000000000 017356 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package drbg
import (
"bytes"
"crypto"
"crypto/hmac"
"hash"
"io"
"golang.org/x/xerrors"
)
type hmacDRBG struct {
h crypto.Hash
v []byte
key []byte
reseedCounter uint64
}
// update implements HMAC_DRBG_Update, described in section 10.1.2.2 of
// SP800-90A.
func (d *hmacDRBG) update(providedData []byte) {
// 1) K = HMAC (K, V || 0x00 || provided_data).
h := hmac.New(func() hash.Hash { return d.h.New() }, d.key)
h.Write(d.v)
h.Write([]byte{0x00})
h.Write(providedData)
d.key = h.Sum(nil)
// 2) V = HMAC (K, V).
h = hmac.New(func() hash.Hash { return d.h.New() }, d.key)
h.Write(d.v)
d.v = h.Sum(nil)
// 3) If (provided_data = Null), then return K and V.
if len(providedData) == 0 {
return
}
// 4) K = HMAC (K, V || 0x01 || provided_data).
h = hmac.New(func() hash.Hash { return d.h.New() }, d.key)
h.Write(d.v)
h.Write([]byte{0x01})
h.Write(providedData)
d.key = h.Sum(nil)
// 5) V = HMAC (K, V).
h = hmac.New(func() hash.Hash { return d.h.New() }, d.key)
h.Write(d.v)
d.v = h.Sum(nil)
}
// instantiate implements HMAC_DRBG_Instantiate_algorithm, described in section 10.1.2.3 of
// SP800-90A.
func (d *hmacDRBG) instantiate(entropyInput, nonce, personalization []byte, securityStrength int) {
// 1) seed_material = entropy_input || nonce || personalization_string.
var seedMaterial bytes.Buffer
seedMaterial.Write(entropyInput)
seedMaterial.Write(nonce)
seedMaterial.Write(personalization)
// 2) Key = 0x00 00...00. Comment: outlen bits.
d.key = make([]byte, d.h.Size())
// 3) V = 0x01 01...01. Comment: outlen bits.
d.v = make([]byte, d.h.Size())
for i := range d.v {
d.v[i] = 0x01
}
// 4) (Key, V) = HMAC_DRBG_Update (seed_material, Key, V).
d.update(seedMaterial.Bytes())
// 5) reseed_counter = 1.
d.reseedCounter = 1
}
// reseed implements HMAC_DRBG_Reseed_algorithm, described in section 10.1.2.4 of
// SP800-90A.
func (d *hmacDRBG) reseed(entropyInput, additionalInput []byte) {
// 1) seed_material = entropy_input || additional_input.
var seedMaterial bytes.Buffer
seedMaterial.Write(entropyInput)
seedMaterial.Write(additionalInput)
// 2) (Key, V) = HMAC_DRBG_Update (seed_material, Key, V).
d.update(seedMaterial.Bytes())
// 3) reseed_counter = 1.
d.reseedCounter = 1
}
// generate implements HMAC_DRBG_Generate_algorithm, described in section 10.1.2.5 of
// SP800-90A.
func (d *hmacDRBG) generate(additionalInput, data []byte) error {
// 1) If reseed_counter > reseed_interval, then return an indication that a
// reseed is required.
if d.reseedCounter > 1<<48 {
return ErrReseedRequired
}
// 2) If additional_input ≠ Null, then
// (Key, V) = HMAC_DRBG_Update (additional_input, Key, V).
if len(additionalInput) > 0 {
d.update(additionalInput)
}
// 3) temp = Null.
var temp bytes.Buffer
h := hmac.New(func() hash.Hash { return d.h.New() }, d.key)
// 4) While (len (temp) < requested_number_of_bits) do:
for temp.Len() < len(data) {
// 4.1) V = HMAC (Key , V).
h.Reset()
h.Write(d.v)
d.v = h.Sum(nil)
// 4.2) temp = temp || V.
temp.Write(d.v)
}
// 5) returned_bits = leftmost (temp, requested_number_of_bits).
copy(data, temp.Bytes())
// 6) (Key, V) = HMAC_DRBG_Update (additional_input, Key, V).
d.update(additionalInput)
// 7) reseed_counter = reseed_counter + 1.
d.reseedCounter += 1
return nil
}
// NewHMAC creates a new HMAC based DRBG as specified in section 10.1.2 of SP-800-90A.
// The DRBG uses the supplied hash algorithm.
//
// The optional personalization argument is combined with entropy input to derive the
// initial seed. This argument can be used to differentiate this instantiation from others.
//
// The optional entropySource argument allows the default entropy source (rand.Reader from
// the crypto/rand package) to be overridden. The supplied entropy source must be truly
// random.
func NewHMAC(h crypto.Hash, personalization []byte, entropySource io.Reader) (*DRBG, error) {
d := &DRBG{impl: &hmacDRBG{h: h}}
if err := d.instantiate(personalization, entropySource, h.Size()/2); err != nil {
return nil, xerrors.Errorf("cannot instantiate: %w", err)
}
return d, nil
}
// NewHMACWithExternalEntropy creates a new hash based DRBG as specified in section
// 10.1.2 of SP-800-90A. The DRBG uses the supplied hash algorithm. The entropyInput and
// nonce arguments provide the initial entropy to seed the created DRBG.
//
// The optional personalization argument is combined with entropy input to derive the
// initial seed. This argument can be used to differentiate this instantiation from others.
//
// The optional entropySource argument provides the entropy source for future reseeding. If
// it is not supplied, then the DRBG can only be reseeded with externally supplied entropy.
// The supplied entropy source must be truly random.
func NewHMACWithExternalEntropy(h crypto.Hash, entropyInput, nonce, personalization []byte, entropySource io.Reader) (*DRBG, error) {
d := &DRBG{impl: &hmacDRBG{h: h}}
if err := d.instantiateWithExternalEntropy(entropyInput, nonce, personalization, entropySource, h.Size()/2); err != nil {
return nil, err
}
return d, nil
}
./github.com/canonical/go-tpm2/ 0000775 0000000 0000000 00000000000 00000000000 014617 5 ustar 00 0000000 0000000 ./github.com/canonical/go-tpm2/.gitignore 0000664 0000000 0000000 00000000036 00000000000 016606 0 ustar 00 0000000 0000000 go-tpm2.test
NVChip
vendor/*/
./github.com/canonical/go-tpm2/.travis.yml 0000664 0000000 0000000 00000000304 00000000000 016725 0 ustar 00 0000000 0000000 language: go
dist: xenial
install:
- sudo snap install core core18
- sudo snap install --edge tpm2-simulator-chrisccoulson
- ./get-deps
script:
- ./run-tests --with-mssim
- go vet ./...
./github.com/canonical/go-tpm2/LICENSE 0000664 0000000 0000000 00000021501 00000000000 015623 0 ustar 00 0000000 0000000 All files in this repository are licensed as follows. If you contribute
to this repository, it is assumed that you license your contribution
under the same license unless you state otherwise.
All files Copyright (C) 2019 Canonical Ltd. unless otherwise specified in the file.
This software is licensed under the LGPLv3, included below.
As a special exception to the GNU Lesser General Public License version 3
("LGPL3"), the copyright holders of this Library give you permission to
convey to a third party a Combined Work that links statically or dynamically
to this Library without providing any Minimal Corresponding Source or
Minimal Application Code as set out in 4d or providing the installation
information set out in section 4e, provided that you comply with the other
provisions of LGPL3 and provided that you meet, for the Application the
terms and conditions of the license(s) which apply to the Application.
Except as stated in this special exception, the provisions of LGPL3 will
continue to comply in full to this Library. If you modify this Library, you
may apply this exception to your version of this Library, but you are not
obliged to do so. If you do not wish to do so, delete this exception
statement from your version. This exception does not (and cannot) modify any
license terms which apply to the Application, with which you must still
comply.
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
./github.com/canonical/go-tpm2/README.md 0000664 0000000 0000000 00000004456 00000000000 016107 0 ustar 00 0000000 0000000 # go-tpm2
[](https://github.com/canonical/go-tpm2/actions?query=workflow%3ATests) [](https://godoc.org/github.com/canonical/go-tpm2)
This repository contains a go library for interacting with TPM 2.0 devices. Some currently supported features are:
- All authorization modes: cleartext password, HMAC session based and policy session based.
- All session configurations: salted or unsalted + bound or unbound.
- Session-based command and response parameter encryption using AES-CFB or XOR obfuscation.
- Session-based command auditing.
- Backends for Linux TPM character devices and TPM simulators implementing the Microsoft TPM 2.0 simulator interface.
The current support status for each command group is detailed below.
Command group | Support | Comment
--- | --- | ---
Start-up | Full |
Testing | Full |
Session Commands | Full |
Object Commands | Full |
Duplication Commands | Partial | TPM2_Duplicate and TPM2_Import are supported
Asymmetric Primitives | None |
Symmetric Primitives | None |
Random Number Generator | Full |
Hash/HMAC/Event Sequences | Full |
Attestation Commands | Full |
Ephemeral EC Keys | None |
Signing and Signature Verification | Full |
Command Audit | Full |
Integrity Collection (PCR) | Partial | TPM2_PCR_Extend, TPM2_PCR_Event, TPM2_PCR_Read and TPM2_PCR_Reset are supported
Enhanced Authorization (EA) Commands | Partial | All commands are supported except for TPM2_PolicyLocality, TPM2_PolicyPhysicalPresence, TPM2_PolicyTemplate and TPM2_PolicyAuthorizeNV
Hierarchy Commands | Partial | TPM2_CreatePrimary, TPM2_HierarchyControl, TPM2_Clear, TPM2_ClearControl and TPM2_HierarchyChangeAuth are supported
Dictionary Attack Functions | Full |
Miscellaneous Management Functions | None |
Field Upgrade | None |
Context Management | Full |
Clocks and Timers | Partial | TPM2_ReadClock is supported
Capability Commands | Full |
Non-Volatile Storage | Partial | All commands are supported except for TPM2_NV_Certify
Vendor Specific | None |
## Relevant links
- [TPM 2.0 Library Specification](https://trustedcomputinggroup.org/resource/tpm-library-specification/)
- [IBM's Software TPM 2.0](https://sourceforge.net/projects/ibmswtpm2/)
./github.com/canonical/go-tpm2/auth.go 0000664 0000000 0000000 00000021160 00000000000 016107 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"bytes"
"crypto/hmac"
"errors"
"fmt"
"hash"
)
type policyHMACType uint8
const (
policyHMACTypeNoAuth policyHMACType = iota
policyHMACTypeAuth
policyHMACTypePassword
policyHMACTypeMax = policyHMACTypePassword
)
type sessionParam struct {
session *sessionContext // The session instance used for this session parameter - will be nil for a password authorization
associatedContext ResourceContext // The resource associated with an authorization - can be nil
includeAuthValue bool // Whether the authorization value of associatedContext is included in the HMAC key
decryptNonce Nonce
encryptNonce Nonce
}
func (s *sessionParam) IsAuth() bool {
return s.associatedContext != nil
}
func (s *sessionParam) ComputeSessionHMACKey() []byte {
var key []byte
key = append(key, s.session.Data().SessionKey...)
if s.includeAuthValue {
key = append(key, s.associatedContext.(resourceContextPrivate).GetAuthValue()...)
}
return key
}
func (s *sessionParam) computeHMAC(pHash []byte, nonceNewer, nonceOlder, nonceDecrypt, nonceEncrypt Nonce, attrs SessionAttributes) ([]byte, bool) {
key := s.ComputeSessionHMACKey()
h := hmac.New(func() hash.Hash { return s.session.Data().HashAlg.NewHash() }, key)
h.Write(pHash)
h.Write(nonceNewer)
h.Write(nonceOlder)
h.Write(nonceDecrypt)
h.Write(nonceEncrypt)
h.Write([]byte{uint8(attrs)})
return h.Sum(nil), len(key) > 0
}
func (s *sessionParam) computeCommandHMAC(commandCode CommandCode, commandHandles []Name, cpBytes []byte) []byte {
data := s.session.Data()
cpHash := ComputeCpHash(data.HashAlg, commandCode, commandHandles, cpBytes)
h, _ := s.computeHMAC(cpHash, data.NonceCaller, data.NonceTPM, s.decryptNonce, s.encryptNonce, s.session.attrs.canonicalize())
return h
}
func (s *sessionParam) buildCommandSessionAuth(commandCode CommandCode, commandHandles []Name, cpBytes []byte) *AuthCommand {
data := s.session.Data()
var hmac []byte
if data.SessionType == SessionTypePolicy && data.PolicyHMACType == policyHMACTypePassword {
// Policy session that contains a TPM2_PolicyPassword assertion. The HMAC is just the authorization value
// of the resource being authorized.
if s.IsAuth() {
hmac = s.associatedContext.(resourceContextPrivate).GetAuthValue()
}
} else {
hmac = s.computeCommandHMAC(commandCode, commandHandles, cpBytes)
}
return &AuthCommand{
SessionHandle: s.session.Handle(),
Nonce: data.NonceCaller,
SessionAttributes: s.session.attrs.canonicalize(),
HMAC: hmac}
}
func (s *sessionParam) buildCommandPasswordAuth() *AuthCommand {
return &AuthCommand{SessionHandle: HandlePW, SessionAttributes: AttrContinueSession, HMAC: s.associatedContext.(resourceContextPrivate).GetAuthValue()}
}
func (s *sessionParam) buildCommandAuth(commandCode CommandCode, commandHandles []Name, cpBytes []byte) *AuthCommand {
if s.session == nil {
// Cleartext password session
return s.buildCommandPasswordAuth()
}
// HMAC or policy session
return s.buildCommandSessionAuth(commandCode, commandHandles, cpBytes)
}
func (s *sessionParam) computeResponseHMAC(resp AuthResponse, responseCode ResponseCode, commandCode CommandCode, rpBytes []byte) ([]byte, bool) {
data := s.session.Data()
rpHash := cryptComputeRpHash(data.HashAlg, responseCode, commandCode, rpBytes)
return s.computeHMAC(rpHash, data.NonceTPM, data.NonceCaller, nil, nil, resp.SessionAttributes)
}
func (s *sessionParam) processResponseAuth(resp AuthResponse, responseCode ResponseCode, commandCode CommandCode, rpBytes []byte) error {
if s.session == nil {
return nil
}
data := s.session.Data()
data.NonceTPM = resp.Nonce
data.IsAudit = resp.SessionAttributes&AttrAudit > 0
data.IsExclusive = resp.SessionAttributes&AttrAuditExclusive > 0
if data.SessionType == SessionTypePolicy && data.PolicyHMACType == policyHMACTypePassword {
if len(resp.HMAC) != 0 {
return errors.New("non-zero length HMAC for policy session with PolicyPassword assertion")
}
return nil
}
hmac, hmacRequired := s.computeResponseHMAC(resp, responseCode, commandCode, rpBytes)
if (hmacRequired || len(resp.HMAC) > 0) && !bytes.Equal(hmac, resp.HMAC) {
return errors.New("incorrect HMAC")
}
return nil
}
func computeBindName(name Name, auth Auth) Name {
if len(auth) > len(name) {
auth = auth[0:len(name)]
}
r := make(Name, len(name))
copy(r, name)
j := 0
for i := len(name) - len(auth); i < len(name); i++ {
r[i] ^= auth[j]
j++
}
return r
}
type sessionParams struct {
commandCode CommandCode
sessions []*sessionParam
}
func (p *sessionParams) findSessionWithAttr(attr SessionAttributes) (*sessionParam, int) {
for i, session := range p.sessions {
if session.session == nil {
continue
}
if session.session.attrs.canonicalize()&attr > 0 {
return session, i
}
}
return nil, 0
}
func (p *sessionParams) validateAndAppend(s *sessionParam) error {
if len(p.sessions) >= 3 {
return errors.New("too many session parameters")
}
if s.session != nil {
data := s.session.Data()
if data == nil {
return errors.New("invalid context for session: incomplete session can only be used in TPMContext.FlushContext")
}
switch data.SessionType {
case SessionTypeHMAC:
switch {
case !s.IsAuth():
// HMAC session not used for authorization
case !data.IsBound:
// A non-bound HMAC session used for authorization. Include the auth value of the associated
// ResourceContext in the HMAC key
s.includeAuthValue = true
default:
// A bound HMAC session used for authorization. Include the auth value of the associated
// ResourceContext only if it is not the bind entity.
bindName := computeBindName(s.associatedContext.Name(), s.associatedContext.(resourceContextPrivate).GetAuthValue())
s.includeAuthValue = !bytes.Equal(bindName, data.BoundEntity)
}
case SessionTypePolicy:
// A policy session that includes a TPM2_PolicyAuthValue assertion. Include the auth value of the associated
// ResourceContext.
switch {
case !s.IsAuth():
// This is actually an invalid case, but just let the TPM return the appropriate error
default:
s.includeAuthValue = data.PolicyHMACType == policyHMACTypeAuth
}
}
}
p.sessions = append(p.sessions, s)
return nil
}
func (p *sessionParams) validateAndAppendAuth(in ResourceContextWithSession) error {
sc, _ := in.Session.(*sessionContext)
associatedContext := in.Context
if associatedContext == nil {
associatedContext = makePermanentContext(HandleNull)
}
s := &sessionParam{associatedContext: associatedContext, session: sc}
return p.validateAndAppend(s)
}
func (p *sessionParams) validateAndAppendExtra(in []SessionContext) error {
for _, s := range in {
if s == nil {
continue
}
if err := p.validateAndAppend(&sessionParam{session: s.(*sessionContext)}); err != nil {
return err
}
}
return nil
}
func (p *sessionParams) computeCallerNonces() error {
for _, s := range p.sessions {
if s.session == nil {
continue
}
if err := cryptComputeNonce(s.session.Data().NonceCaller); err != nil {
return fmt.Errorf("cannot compute new caller nonce: %v", err)
}
}
return nil
}
func (p *sessionParams) buildCommandAuthArea(commandCode CommandCode, commandHandles []Name, cpBytes []byte) ([]AuthCommand, error) {
if err := p.computeCallerNonces(); err != nil {
return nil, fmt.Errorf("cannot compute caller nonces: %v", err)
}
if err := p.encryptCommandParameter(cpBytes); err != nil {
return nil, fmt.Errorf("cannot encrypt first command parameter: %v", err)
}
p.computeEncryptNonce()
p.commandCode = commandCode
var area []AuthCommand
for _, s := range p.sessions {
a := s.buildCommandAuth(commandCode, commandHandles, cpBytes)
area = append(area, *a)
}
return area, nil
}
func (p *sessionParams) invalidateSessionContexts(authResponses []AuthResponse) {
for i, resp := range authResponses {
session := p.sessions[i].session
if session == nil {
continue
}
if resp.SessionAttributes&AttrContinueSession != 0 {
continue
}
session.invalidate()
}
}
func (p *sessionParams) processResponseAuthArea(authResponses []AuthResponse, responseCode ResponseCode, rpBytes []byte) error {
defer p.invalidateSessionContexts(authResponses)
for i, resp := range authResponses {
if err := p.sessions[i].processResponseAuth(resp, responseCode, p.commandCode, rpBytes); err != nil {
return fmt.Errorf("encountered an error for session at index %d: %v", i, err)
}
}
if err := p.decryptResponseParameter(rpBytes); err != nil {
return fmt.Errorf("cannot decrypt first response parameter: %v", err)
}
return nil
}
./github.com/canonical/go-tpm2/cmds_attestation.go 0000664 0000000 0000000 00000035656 00000000000 020532 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"github.com/canonical/go-tpm2/mu"
)
// Section 18 - Attestation Commands
// Certify executes the TPM2_Certify command, which is used to prove that an object with a specific name is loaded in to the TPM.
// By producing an attestation, the TPM certifies that the object with a given name is loaded in to the TPM and consistent with a
// valid sensitive area.
//
// The objectContext parameter corresponds to the object for which to produce an attestation. The command requires authorization with
// the admin role for objectContext, with session based authorization provided via objectContextAuthSession.
//
// If signContext is not nil, the returned attestation will be signed by the key associated with it. This command requires
// authorization with the user auth role for signContext, with session based authorization provided via signContextAuthSession.
//
// If signContext is not nil and the object associated with signContext is not a signing key, a *TPMHandleError error with an error
// code of ErrorKey will be returned for handle index 2.
//
// If signContext is not nil and if the scheme of the key associated with signContext is AsymSchemeNull, then inScheme must be
// provided to specify a valid signing scheme for the key. If it isn't, a *TPMParameterError error with an error code of ErrorScheme
// will be returned for parameter index 2.
//
// If signContext is not nil and the scheme of the key associated with signContext is not AsymSchemeNull, then inScheme may be nil. If
// it is provided, then the specified scheme must match that of the signing key, else a *TPMParameterError error with an error code of
// ErrorScheme will be returned for parameter index 2.
//
// On successful, it returns an attestation structure detailing the name of the object associated with objectContext. If signContext
// is not nil, the attestation structure will be signed by the associated key and returned too.
func (t *TPMContext) Certify(objectContext, signContext ResourceContext, qualifyingData Data, inScheme *SigScheme, objectContextAuthSession, signContextAuthSession SessionContext, sessions ...SessionContext) (certifyInfo *Attest, signature *Signature, err error) {
if inScheme == nil {
inScheme = &SigScheme{Scheme: SigSchemeAlgNull}
}
if err := t.RunCommand(CommandCertify, sessions,
ResourceContextWithSession{Context: objectContext, Session: objectContextAuthSession}, ResourceContextWithSession{Context: signContext, Session: signContextAuthSession}, Delimiter,
qualifyingData, inScheme, Delimiter,
Delimiter,
mu.Sized(&certifyInfo), &signature); err != nil {
return nil, nil, err
}
return certifyInfo, signature, nil
}
// CertifyCreation executes the TPM2_CertifyCreation command, which is used to prove the association between the object represented
// by objectContext and its creation data represented by creationHash. It does this by computing a ticket from creationHash and the
// name of the object represented by objectContext and then verifying that it matches the provided creationTicket, which was provided
// by the TPM at object creation time.
//
// If signContext is not nil, the returned attestation will be signed by the key associated with it. This command requires
// authorization with the user auth role for signContext, with session based authorization provided via signContextAuthSession.
//
// If signContext is not nil and the object associated with signContext is not a signing key, a *TPMHandleError error with an error
// code of ErrorKey will be returned for handle index 1.
//
// If signContext is not nil and if the scheme of the key associated with signContext is AsymSchemeNull, then inScheme must be
// provided to specify a valid signing scheme for the key. If it isn't, a *TPMParameterError error with an error code of ErrorScheme
// will be returned for parameter index 3.
//
// If signContext is not nil and the scheme of the key associated with signContext is not AsymSchemeNull, then inScheme may be nil. If
// it is provided, then the specified scheme must match that of the signing key, else a *TPMParameterError error with an error code of
// ErrorScheme will be returned for parameter index 3.
//
// If creationTicket corresponds to an invalid ticket, a *TPMParameterError error with an error code of ErrorTicket will be returned
// for parameter index 4.
//
// If the digest generated for signing is greater than or has a larger size than the modulus of the key associated with signContext, a
// *TPMError with an error code of ErrorValue will be returned.
//
// If successful, it returns an attestation structure. If signContext is not nil, the attestation structure will be signed by the
// associated key and returned too.
func (t *TPMContext) CertifyCreation(signContext, objectContext ResourceContext, qualifyingData Data, creationHash Digest, inScheme *SigScheme, creationTicket *TkCreation, signContextAuthSession SessionContext, sessions ...SessionContext) (certifyInfo *Attest, signature *Signature, err error) {
if inScheme == nil {
inScheme = &SigScheme{Scheme: SigSchemeAlgNull}
}
if err := t.RunCommand(CommandCertifyCreation, sessions,
ResourceContextWithSession{Context: signContext, Session: signContextAuthSession}, objectContext, Delimiter,
qualifyingData, creationHash, inScheme, creationTicket, Delimiter,
Delimiter,
mu.Sized(&certifyInfo), &signature); err != nil {
return nil, nil, err
}
return certifyInfo, signature, nil
}
// Quote executes the TPM2_Quote command in order to quote a set of PCR values. The TPM will hash the set of PCRs specified by the
// pcrs parameter.
//
// If signContext is not nil, the returned attestation will be signed by the key associated with it. This command requires
// authorization with the user auth role for signContext, with session based authorization provided via signContextAuthSession.
//
// If signContext is not nil and the object associated with signContext is not a signing key, a *TPMHandleError error with an error
// code of ErrorKey will be returned for handle index 1.
//
// If signContext is not nil and if the scheme of the key associated with signContext is AsymSchemeNull, then inScheme must be
// provided to specify a valid signing scheme for the key. If it isn't, a *TPMParameterError error with an error code of ErrorScheme
// will be returned for parameter index 2.
//
// If signContext is not nil and the scheme of the key associated with signContext is not AsymSchemeNull, then inScheme may be nil. If
// it is provided, then the specified scheme must match that of the signing key, else a *TPMParameterError error with an error code of
// ErrorScheme will be returned for parameter index 2.
//
// On successful, it returns an attestation structure containing the hash of the PCRs selected by the pcrs parameter. If signContext
// is not nil, the attestation structure will be signed by the associated key and returned too.
func (t *TPMContext) Quote(signContext ResourceContext, qualifyingData Data, inScheme *SigScheme, pcrs PCRSelectionList, signContextAuthSession SessionContext, sessions ...SessionContext) (quoted *Attest, signature *Signature, err error) {
if inScheme == nil {
inScheme = &SigScheme{Scheme: SigSchemeAlgNull}
}
if err := t.RunCommand(CommandQuote, sessions,
ResourceContextWithSession{Context: signContext, Session: signContextAuthSession}, Delimiter,
qualifyingData, inScheme, pcrs, Delimiter,
Delimiter,
mu.Sized("ed), &signature); err != nil {
return nil, nil, err
}
return quoted, signature, nil
}
// GetSessionAuditDigest executes the TPM2_GetSessionAuditDigest to obtain the current digest of the audit session corresponding to
// sessionContext.
//
// The privacyAdminContext argument must be a ResourceContext that corresponds to HandleEndorsement. This command requires authorization
// with the user auth role for privacyAdminContext, with session based authorization provided via privacyAdminContextAuthSession.
//
// If signContext is not nil, the returned attestation will be signed by the key associated with it. This command requires
// authorization with the user auth role for signContext, with session based authorization provided via signContextAuthSession.
//
// If signContext is not nil and the object associated with signContext is not a signing key, a *TPMHandleError error with an error
// code of ErrorKey will be returned for handle index 2.
//
// If signContext is not nil and if the scheme of the key associated with signContext is AsymSchemeNull, then inScheme must be
// provided to specify a valid signing scheme for the key. If it isn't, a *TPMParameterError error with an error code of ErrorScheme
// will be returned for parameter index 2.
//
// If signContext is not nil and the scheme of the key associated with signContext is not AsymSchemeNull, then inScheme may be nil. If
// it is provided, then the specified scheme must match that of the signing key, else a *TPMParameterError error with an error code of
// ErrorScheme will be returned for parameter index 2.
//
// On success, it returns an attestation structure detailing the current audit digest for sessionContext. If signContext is not nil,
// the attestation structure will be signed by the associated key and returned too.
func (t *TPMContext) GetSessionAuditDigest(privacyAdminContext, signContext ResourceContext, sessionContext SessionContext, qualifyingData Data, inScheme *SigScheme, privacyAdminContextAuthSession, signContextAuthSession SessionContext, sessions ...SessionContext) (auditInfo *Attest, signature *Signature, err error) {
if inScheme == nil {
inScheme = &SigScheme{Scheme: SigSchemeAlgNull}
}
if err := t.RunCommand(CommandGetSessionAuditDigest, sessions,
ResourceContextWithSession{Context: privacyAdminContext, Session: privacyAdminContextAuthSession}, ResourceContextWithSession{Context: signContext, Session: signContextAuthSession}, sessionContext, Delimiter,
qualifyingData, inScheme, Delimiter,
Delimiter,
mu.Sized(&auditInfo), &signature); err != nil {
return nil, nil, err
}
return auditInfo, signature, nil
}
// GetCommandAuditDigest executes the TPM2_GetCommandAuditDigest command to obtain the current command audit digest, the current
// audit digest algorithm and a digest of the list of commands being audited.
//
// The privacyContext argument must be a ResourceContext corresponding to HandleEndorsement. This command requires authorization with
// the user auth role for privacyContext, with session based authorization provided via privacyContextAuthSession.
//
// If signContext is not nil, the returned attestation will be signed by the key associated with it. This command requires
// authorization with the user auth role for signContext, with session based authorization provided via provided via
// signContextAuthSession.
//
// If signContext is not nil and the object associated with signContext is not a signing key, a *TPMHandleError error with an error
// code of ErrorKey will be returned for handle index 2.
//
// If signContext is not nil and if the scheme of the key associated with signContext is AsymSchemeNull, then inScheme must be
// provided to specify a valid signing scheme for the key. If it isn't, a *TPMParameterError error with an error code of ErrorScheme
// will be returned for parameter index 2.
//
// If signContext is not nil and the scheme of the key associated with signContext is not AsymSchemeNull, then inScheme may be nil. If
// it is provided, then the specified scheme must match that of the signing key, else a *TPMParameterError error with an error code of
// ErrorScheme will be returned for parameter index 2.
//
// On success, it returns an attestation structure detailing the current command audit digest, digest algorithm and a digest of the
// list of commands being audited. If signContext is not nil, the attestation structure will be signed by the associated key and
// returned too.
func (t *TPMContext) GetCommandAuditDigest(privacyContext, signContext ResourceContext, qualifyingData Data, inScheme *SigScheme, privacyContextAuthSession, signContextAuthSession SessionContext, sessions ...SessionContext) (auditInfo *Attest, signature *Signature, err error) {
if inScheme == nil {
inScheme = &SigScheme{Scheme: SigSchemeAlgNull}
}
if err := t.RunCommand(CommandGetCommandAuditDigest, sessions,
ResourceContextWithSession{Context: privacyContext, Session: privacyContextAuthSession}, ResourceContextWithSession{Context: signContext, Session: signContextAuthSession}, Delimiter,
qualifyingData, inScheme, Delimiter,
Delimiter,
mu.Sized(&auditInfo), &signature); err != nil {
return nil, nil, err
}
return auditInfo, signature, nil
}
// GetTime executes the TPM2_GetTime command in order to obtain the current values of time and clock.
//
// The privacyAdminContext argument must be a ResourceContext that corresponds to HandleEndorsement. The command requires authorization
// with the user auth role for privacyAdminContext, with session based authorization provided via privacyAdminContextAuthSession.
//
// If signContext is not nil, the returned attestation will be signed by the key associated with it. This command requires
// authorization with the user auth role for signContext, with session based authorization provided via signContextAuthSession.
//
// If signContext is not nil and the object associated with signContext is not a signing key, a *TPMHandleError error with an error
// code of ErrorKey will be returned for handle index 2.
//
// If signContext is not nil and if the scheme of the key associated with signContext is AsymSchemeNull, then inScheme must be
// provided to specify a valid signing scheme for the key. If it isn't, a *TPMParameterError error with an error code of ErrorScheme
// will be returned for parameter index 2.
//
// If signContext is not nil and the scheme of the key associated with signContext is not AsymSchemeNull, then inScheme may be nil. If
// it is provided, then the specified scheme must match that of the signing key, else a *TPMParameterError error with an error code of
// ErrorScheme will be returned for parameter index 2.
//
// On success, it returns an attestation structure detailing the current values of time and clock. If signContext is not nil, the
// attestation structure will be signed by the associated key and returned too.
func (t *TPMContext) GetTime(privacyAdminContext, signContext ResourceContext, qualifyingData Data, inScheme *SigScheme, privacyAdminContextAuthSession, signContextAuthSession SessionContext, sessions ...SessionContext) (timeInfo *Attest, signature *Signature, err error) {
if inScheme == nil {
inScheme = &SigScheme{Scheme: SigSchemeAlgNull}
}
if err := t.RunCommand(CommandGetTime, sessions,
ResourceContextWithSession{Context: privacyAdminContext, Session: privacyAdminContextAuthSession}, ResourceContextWithSession{Context: signContext, Session: signContextAuthSession}, Delimiter,
qualifyingData, inScheme, Delimiter,
Delimiter,
mu.Sized(&timeInfo), &signature); err != nil {
return nil, nil, err
}
return timeInfo, signature, nil
}
./github.com/canonical/go-tpm2/cmds_cap.go 0000664 0000000 0000000 00000046232 00000000000 016726 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"encoding/binary"
"fmt"
)
// Section 30 - Capability Commands
// GetCapability executes the TPM2_GetCapability command, which returns various properties of the TPM and its current state. The
// capability parameter indicates the category of data to be returned. The property parameter indicates the first value of the
// selected category to be returned. The propertyCount parameter indicates the number of values to be returned.
//
// If no property in the TPM corresponds to the value of property, then the next property is returned.
//
// The underlying implementation of TPM2_GetCapability is not required to (or may not be able to) return all of the requested
// values in a single request. This function will re-execute the TPM2_GetCapability command until all of the requested properties
// have been returned. As a consequence, any SessionContext instances provided should have the AttrContinueSession attribute defined.
//
// If capability is CapabilityHandles and property does not correspond to a valid handle type, a *TPMParameterError error with
// an error code of ErrorHandle is returned for parameter index 2.
func (t *TPMContext) GetCapability(capability Capability, property, propertyCount uint32, sessions ...SessionContext) (capabilityData *CapabilityData, err error) {
capabilityData = &CapabilityData{Capability: capability, Data: &CapabilitiesU{}}
nextProperty := property
remaining := propertyCount
for {
var moreData bool
var data CapabilityData
if err := t.RunCommand(CommandGetCapability, sessions,
Delimiter,
capability, nextProperty, remaining, Delimiter,
Delimiter,
&moreData, &data); err != nil {
return nil, err
}
if data.Capability != capability {
return nil, &InvalidResponseError{CommandGetCapability,
fmt.Sprintf("TPM responded with data for the wrong capability (got %s)", data.Capability)}
}
var l int
var p uint32
switch data.Capability {
case CapabilityAlgs:
capabilityData.Data.Algorithms = append(capabilityData.Data.Algorithms, data.Data.Algorithms...)
l = len(data.Data.Algorithms)
if l > 0 {
p = uint32(data.Data.Algorithms[l-1].Alg)
}
case CapabilityHandles:
capabilityData.Data.Handles = append(capabilityData.Data.Handles, data.Data.Handles...)
l = len(data.Data.Handles)
if l > 0 {
p = uint32(data.Data.Handles[l-1])
}
case CapabilityCommands:
capabilityData.Data.Command = append(capabilityData.Data.Command, data.Data.Command...)
l = len(data.Data.Command)
if l > 0 {
p = uint32(data.Data.Command[l-1].CommandCode())
}
case CapabilityPPCommands:
capabilityData.Data.PPCommands = append(capabilityData.Data.PPCommands, data.Data.PPCommands...)
l = len(data.Data.PPCommands)
if l > 0 {
p = uint32(data.Data.PPCommands[l-1])
}
case CapabilityAuditCommands:
capabilityData.Data.AuditCommands = append(capabilityData.Data.AuditCommands, data.Data.AuditCommands...)
l = len(data.Data.AuditCommands)
if l > 0 {
p = uint32(data.Data.AuditCommands[l-1])
}
case CapabilityPCRs:
if moreData {
return nil, &InvalidResponseError{CommandGetCapability,
fmt.Sprintf("TPM did not respond with all requested properties for capability %s", data.Capability)}
}
return &data, nil
case CapabilityTPMProperties:
capabilityData.Data.TPMProperties = append(capabilityData.Data.TPMProperties, data.Data.TPMProperties...)
l = len(data.Data.TPMProperties)
if l > 0 {
p = uint32(data.Data.TPMProperties[l-1].Property)
}
case CapabilityPCRProperties:
capabilityData.Data.PCRProperties = append(capabilityData.Data.PCRProperties, data.Data.PCRProperties...)
l = len(data.Data.PCRProperties)
if l > 0 {
p = uint32(data.Data.PCRProperties[l-1].Tag)
}
case CapabilityECCCurves:
capabilityData.Data.ECCCurves = append(capabilityData.Data.ECCCurves, data.Data.ECCCurves...)
l = len(data.Data.ECCCurves)
if l > 0 {
p = uint32(data.Data.ECCCurves[l-1])
}
case CapabilityAuthPolicies:
capabilityData.Data.AuthPolicies = append(capabilityData.Data.AuthPolicies, data.Data.AuthPolicies...)
l = len(data.Data.AuthPolicies)
if l > 0 {
p = uint32(data.Data.AuthPolicies[l-1].Handle)
}
}
nextProperty += p + 1
remaining -= uint32(l)
if !moreData || remaining <= 0 {
break
}
}
return capabilityData, nil
}
// GetCapabilityAlgs is a convenience function for TPMContext.GetCapability, and
// returns properties of the algorithms supported by the TPM. The first parameter
// indicates the first algorithm for which to return properties. If this algorithm
// isn't supported, then the properties of the next supported algorithm are returned
// instead. The propertyCount parameter indicates the number of algorithms for which
// to return properties.
func (t *TPMContext) GetCapabilityAlgs(first AlgorithmId, propertyCount uint32, sessions ...SessionContext) (algs AlgorithmPropertyList, err error) {
data, err := t.GetCapability(CapabilityAlgs, uint32(first), propertyCount, sessions...)
if err != nil {
return nil, err
}
return data.Data.Algorithms, nil
}
// GetCapabilityAlg is a convenience function for TPMContext.GetCapability that
// returns the properties of the specified algorithm if it is supported by the TPM.
// If it isn't supported, an error is returned.
func (t *TPMContext) GetCapabilityAlg(alg AlgorithmId, sessions ...SessionContext) (AlgorithmProperty, error) {
algs, err := t.GetCapabilityAlgs(alg, 1, sessions...)
if err != nil {
return AlgorithmProperty{}, err
}
if len(algs) == 0 || algs[0].Alg != alg {
return AlgorithmProperty{}, fmt.Errorf("algorithm %v does not exist", alg)
}
return algs[0], nil
}
// IsAlgorithmSupported is a convenience function for TPMContext.GetCapability that
// determines if the specified algorithm is supported by the TPM. Note that this will
// indicate that the algorithm is unsupported if the TPM returns an error.
func (t *TPMContext) IsAlgorithmSupported(alg AlgorithmId, sessions ...SessionContext) bool {
if _, err := t.GetCapabilityAlg(alg, sessions...); err != nil {
return false
}
return true
}
// GetCapabilityCommands is a convenience function for TPMContext.GetCapability, and
// returns attributes of the commands supported by the TPM. The first parameter indicates
// the first command for which to return attributes. If this command isn't supported,
// then the attributes of the next supported command are returned instead. The propertyCount
// parameter indicates the number of commands for which to return attributes.
func (t *TPMContext) GetCapabilityCommands(first CommandCode, propertyCount uint32, sessions ...SessionContext) (commands CommandAttributesList, err error) {
data, err := t.GetCapability(CapabilityCommands, uint32(first), propertyCount, sessions...)
if err != nil {
return nil, err
}
return data.Data.Command, nil
}
// GetCapabilityCommand is a convenience function for TPMContext.GetCapability that
// returns the attributes of the specified command if it is supported by the TPM. If
// it isn't supported, an error is returned.
func (t *TPMContext) GetCapabilityCommand(code CommandCode, sessions ...SessionContext) (CommandAttributes, error) {
commands, err := t.GetCapabilityCommands(code, 1, sessions...)
if err != nil {
return 0, err
}
if len(commands) == 0 || commands[0].CommandCode() != code {
return 0, fmt.Errorf("command %v does not exist", code)
}
return commands[0], nil
}
// IsCommandSupported is a convenience function for TPMContext.GetCapability that
// determines if the specified command is supported by the TPM. Note that this will
// indicate that the command is unsupported if the TPM returns an error.
func (t *TPMContext) IsCommandSupported(code CommandCode, sessions ...SessionContext) bool {
if _, err := t.GetCapabilityCommand(code, sessions...); err != nil {
return false
}
return true
}
// GetCapabilityPPCommands is a convenience function for TPMContext.GetCapability, and
// returns a list of commands that require physical presence for platform authorization.
// The first parameter indicates the command code at which the returned list should start.
// The propertyCount parameter indicates the maximum number of command codes to return.
func (t *TPMContext) GetCapabilityPPCommands(first CommandCode, propertyCount uint32, sessions ...SessionContext) (ppCommands CommandCodeList, err error) {
data, err := t.GetCapability(CapabilityPPCommands, uint32(first), propertyCount, sessions...)
if err != nil {
return nil, err
}
return data.Data.PPCommands, nil
}
// GetCapabilityAuditCommands is a convenience function for TPMContext.GetCapability, and
// returns a list of commands that are currently set for command audit. The first parameter
// indicates the command code at which the returned list should start. The propertyCount
// parameter indicates the maximum number of command codes to return.
func (t *TPMContext) GetCapabilityAuditCommands(first CommandCode, propertyCount uint32, sessions ...SessionContext) (auditCommands CommandCodeList, err error) {
data, err := t.GetCapability(CapabilityAuditCommands, uint32(first), propertyCount, sessions...)
if err != nil {
return nil, err
}
return data.Data.AuditCommands, nil
}
// GetCapabilityHandles is a convenience function for TPMContext.GetCapability, and returns
// a list of handles of resources on the TPM. The firstHandle parameter indicates the type of
// handles to be returned (represented by the most-significant byte), and also the handle at
// which the list should start. The propertyCount parameter indicates the maximum number of
// handles to return.
func (t *TPMContext) GetCapabilityHandles(firstHandle Handle, propertyCount uint32, sessions ...SessionContext) (handles HandleList, err error) {
data, err := t.GetCapability(CapabilityHandles, uint32(firstHandle), propertyCount, sessions...)
if err != nil {
return nil, err
}
return data.Data.Handles, nil
}
// DoesHandleExist is a convenience function for TPMContext.GetCapability that determines
// if a resource with the specified handle exists on the TPM. This will indicate that the
// resource does not exist if the TPM returns an error.
func (t *TPMContext) DoesHandleExist(handle Handle, sessions ...SessionContext) bool {
handles, err := t.GetCapabilityHandles(handle, 1, sessions...)
if err != nil {
return false
}
if len(handles) == 0 || handles[0] != handle {
return false
}
return true
}
// GetCapabilityPCRs is a convenience function for TPMContext.GetCapability, and returns the
// current allocation of PCRs on the TPM.
func (t *TPMContext) GetCapabilityPCRs(sessions ...SessionContext) (pcrs PCRSelectionList, err error) {
data, err := t.GetCapability(CapabilityPCRs, 0, CapabilityMaxProperties, sessions...)
if err != nil {
return nil, err
}
return data.Data.AssignedPCR, nil
}
// GetCapabilityTPMProperties is a convenience function for TPMContext.GetCapability, and
// returns the values of properties of the TPM. The first parameter indicates the first
// property for which to return a value. If the property does not exist, then the value of
// the next available property is returned. The propertyCount parameter indicates the number
// of properties for which to return values.
func (t *TPMContext) GetCapabilityTPMProperties(first Property, propertyCount uint32, sessions ...SessionContext) (tpmProperties TaggedTPMPropertyList, err error) {
data, err := t.GetCapability(CapabilityTPMProperties, uint32(first), propertyCount, sessions...)
if err != nil {
return nil, err
}
return data.Data.TPMProperties, nil
}
// GetCapabilityTPMProperty is a convenience function for TPMContext.GetCapability that returns
// the value of the specified property if it exists. If it doesn't exist, an error is returned.
func (t *TPMContext) GetCapabilityTPMProperty(property Property, sessions ...SessionContext) (uint32, error) {
props, err := t.GetCapabilityTPMProperties(property, 1, sessions...)
if err != nil {
return 0, err
}
if len(props) == 0 || props[0].Property != property {
return 0, fmt.Errorf("property %v does not exist", property)
}
return props[0].Value, nil
}
// GetManufacturer is a convenience function for TPMContext.GetCapability that returns the ID of
// the TPM manufacturer.
func (t *TPMContext) GetManufacturer(sessions ...SessionContext) (manufacturer TPMManufacturer, err error) {
m, err := t.GetCapabilityTPMProperty(PropertyManufacturer, sessions...)
if err != nil {
return 0, err
}
return TPMManufacturer(m), nil
}
// GetInputBuffer is a convenience function for TPMContext.GetCapability that returns the value of
// the PropertyInputBuffer property, which indicates the maximum size of arguments of the MaxBuffer
// type in bytes. The size is TPM implementation specific, but required to be at least 1024 bytes.
func (t *TPMContext) GetInputBuffer(sessions ...SessionContext) int {
n, err := t.GetCapabilityTPMProperty(PropertyInputBuffer, sessions...)
if err != nil {
return 1024
}
return int(n)
}
// GetMaxDigest is a convenience function for TPMContext.GetCapability that returns the value of
// the PropertyMaxDigest property, which indicates the size of the largest digest algorithm
// supported by the TPM in bytes.
func (t *TPMContext) GetMaxDigest(sessions ...SessionContext) (int, error) {
n, err := t.GetCapabilityTPMProperty(PropertyMaxDigest, sessions...)
if err != nil {
return 0, err
}
return int(n), nil
}
// GetMaxData is a convenience function for TPMContext.GetCapability that returns the maximum
// size of arguments of the Data type supported by the TPM in bytes.
func (t *TPMContext) GetMaxData(sessions ...SessionContext) (int, error) {
n, err := t.GetMaxDigest(sessions...)
if err != nil {
return 0, err
}
return n + binary.Size(AlgorithmId(0)), nil
}
// GetNVBufferMax is a convenience function for TPMContext.GetCapability that returns the value
// of the PropertyNVBufferMax property, which indicates the maximum buffer size supported by
// the TPM in bytes for TPMContext.NVReadRaw and TPMContext.NVWriteRaw.
func (t *TPMContext) GetNVBufferMax(sessions ...SessionContext) (int, error) {
n, err := t.GetCapabilityTPMProperty(PropertyNVBufferMax, sessions...)
if err != nil {
return 0, err
}
return int(n), nil
}
// GetNVIndexMax is a convenience function for TPMContext.GetCapability that returns
// the value of the PropertyNVIndexMax property, which indicates the maximum size of a
// single NV index.
func (t *TPMContext) GetNVIndexMax(sessions ...SessionContext) (int, error) {
n, err := t.GetCapabilityTPMProperty(PropertyNVIndexMax, sessions...)
if err != nil {
return 0, err
}
return int(n), nil
}
// GetCapabilityPCRProperties is a convenience function for TPMContext.GetCapability, and returns
// the values of PCR properties. The first parameter indicates the first property for which to
// return a value. If the property does not exist, then the value of the next available property
// is returned. The propertyCount parameter indicates the number of properties for which to
// return values. Each returned property value is a list of PCR indexes associated with a property.
func (t *TPMContext) GetCapabilityPCRProperties(first PropertyPCR, propertyCount uint32, sessions ...SessionContext) (pcrProperties TaggedPCRPropertyList, err error) {
data, err := t.GetCapability(CapabilityPCRProperties, uint32(first), propertyCount, sessions...)
if err != nil {
return nil, err
}
return data.Data.PCRProperties, nil
}
// GetCapabilityECCCurves is a convenience function for TPMContext.GetCapability, and returns a
// list of ECC curves supported by the TPM.
func (t *TPMContext) GetCapabilityECCCurves(sessions ...SessionContext) (eccCurves ECCCurveList, err error) {
data, err := t.GetCapability(CapabilityECCCurves, uint32(ECCCurveFirst), CapabilityMaxProperties, sessions...)
if err != nil {
return nil, err
}
return data.Data.ECCCurves, nil
}
// IsECCCurveSupported is a convenience function for TPMContext.GetCapability that determines
// if the specified curve is supported. This will indicate that the specified curve is unsupported
// if the TPM returns an error.
func (t *TPMContext) IsECCCurveSupported(curve ECCCurve, sessions ...SessionContext) bool {
curves, err := t.GetCapabilityECCCurves(sessions...)
if err != nil {
return false
}
for _, supported := range curves {
if supported == curve {
return true
}
}
return false
}
// GetCapabilityAuthPolicies is a convenience function for TPMContext.GetCapability, and returns
// auth policy digests associated with permanent handles. The first parameter indicates the first
// handle for which to return an auth policy. If the handle doesn't exist, then the auth policy
// for the next available handle is returned. The propertyCount parameter indicates the number
// of permanent handles for which to return an auth policy.
func (t *TPMContext) GetCapabilityAuthPolicies(first Handle, propertyCount uint32, sessions ...SessionContext) (authPolicies TaggedPolicyList, err error) {
data, err := t.GetCapability(CapabilityAuthPolicies, uint32(first), propertyCount, sessions...)
if err != nil {
return nil, err
}
return data.Data.AuthPolicies, nil
}
// IsTPM2 determines whether this TPMContext is connected to a TPM2 device. It does this by
// attempting to execute a TPM2_GetCapability command, and verifying that the response packet
// has the expected tag.
//
// On success, this will return true if TPMContext is connected to a TPM2 device, or false if
// it is connected to a TPM1.2 device. It will return false if communication with the device
// fails of if the response is badly formed.
func (t *TPMContext) IsTPM2() (isTpm2 bool) {
_, err := t.GetCapabilityTPMProperties(PropertyTotalCommands, 0)
if e, ok := err.(*TPMError); ok && e.Code == ErrorBadTag {
return false
}
return true
}
// TestParms executes the TPM2_TestParms command to check if the specified combination of
// algorithm parameters is supported.
func (t *TPMContext) TestParms(parameters *PublicParams, sessions ...SessionContext) error {
return t.RunCommand(CommandTestParms, sessions, Delimiter, parameters)
}
// IsRSAKeySizeSupporters is a convenience function around TestParms that determines whether
// the specified RSA key size is supported.
func (t *TPMContext) IsRSAKeySizeSupported(keyBits uint16, sessions ...SessionContext) bool {
params := PublicParams{
Type: ObjectTypeRSA,
Parameters: &PublicParamsU{
RSADetail: &RSAParams{
Symmetric: SymDefObject{Algorithm: SymObjectAlgorithmNull},
Scheme: RSAScheme{Scheme: RSASchemeNull},
KeyBits: keyBits,
Exponent: 0}}}
if err := t.TestParms(¶ms, sessions...); err != nil {
return false
}
return true
}
// IsSymmetricAlgorithmSupported is a convenience function around TestParms that determines whether
// the specified symmetric algorithm and key size combination is supported.
func (t *TPMContext) IsSymmetricAlgorithmSupported(algorithm SymObjectAlgorithmId, keyBits uint16, sessions ...SessionContext) bool {
params := PublicParams{
Type: ObjectTypeSymCipher,
Parameters: &PublicParamsU{
SymDetail: &SymCipherParams{
Sym: SymDefObject{
Algorithm: algorithm,
KeyBits: &SymKeyBitsU{Sym: keyBits},
Mode: &SymModeU{Sym: SymModeCFB}}}}}
if err := t.TestParms(¶ms, sessions...); err != nil {
return false
}
return true
}
./github.com/canonical/go-tpm2/cmds_clock.go 0000664 0000000 0000000 00000001505 00000000000 017250 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 29 - Clocks and Timers
// ReadClock executes the TPM2_ReadClock command. On succesful completion, it will return a TimeInfo struct that contains the current
// value of time, clock, reset and restart counts.
func (t *TPMContext) ReadClock(sessions ...SessionContext) (currentTime *TimeInfo, err error) {
if err := t.RunCommand(CommandReadClock, sessions,
Delimiter,
Delimiter,
Delimiter,
¤tTime); err != nil {
return nil, err
}
return currentTime, nil
}
// func (t *TPMContext) ClockSet(auth Handle, newTime uint64, authAuth interface{}) error {
// }
// func (t *TPMContext) ClockRateAdjust(auth Handle, rateAdjust ClockAdjust, authAuth interface{}) error {
// }
./github.com/canonical/go-tpm2/cmds_command_audit.go 0000664 0000000 0000000 00000003117 00000000000 020762 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 21 - Command Audit
// SetCommandCodeAuditStatus executes the TPM2_SetCommandCodeAuditStatus command to allow the privacy administrator or platform to
// change the audit status of a command, or change the digest algorithm used for command auditing (but not both at the same time).
//
// The auth parameter should be a ResourceContext corresponding to either HandlePlatform or HandleOwner. This command requires
// authorization of auth with the user auth role, with session based authorization provided via authAuthSession.
//
// The auditAlg argument specifies the digest algorithm for command auditing. The setList argument is used to specify which commands
// should be added to the list of commands to be audited. The clearList argument is used to specify which commands should be removed
// from the list of commands to be audited.
//
// If auditAlg is not HashAlgorithmNull or the current audit digest algorith, and the length of setList or clearList is greater than
// zero, a *TPMParameterError error with an error code of ErrorValue will be returned for parameter index 1.
func (t *TPMContext) SetCommandCodeAuditStatus(auth ResourceContext, auditAlg HashAlgorithmId, setList, clearList CommandCodeList, authAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandSetCommandCodeAuditStatus, sessions,
ResourceContextWithSession{Context: auth, Session: authAuthSession}, Delimiter,
auditAlg, setList, clearList)
}
./github.com/canonical/go-tpm2/cmds_context.go 0000664 0000000 0000000 00000023746 00000000000 017654 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 28 - Context Management
import (
"errors"
"fmt"
"github.com/canonical/go-tpm2/mu"
"golang.org/x/xerrors"
)
// ContextSave executes the TPM2_ContextSave command on the handle referenced by saveContext, in order to save the context associated
// with that handle outside of the TPM. The TPM encrypts and integrity protects the context with a key derived from the hierarchy
// proof. If saveContext does not correspond to a transient object or a session, then it will return an error.
//
// On successful completion, it returns a Context instance that can be passed to TPMContext.ContextLoad. Note that this function
// wraps the context data returned from the TPM with some host-side state associated with the resource, so that it can be restored
// fully in TPMContext.ContextLoad. If saveContext corresponds to a session, the host-side state that is added to the returned context
// blob includes the session key.
//
// If saveContext corresponds to a session, then TPM2_ContextSave also removes resources associated with the session from the TPM
// (it becomes a saved session rather than a loaded session). In this case, saveContext is marked as not loaded and can only be used
// as an argument to TPMContext.FlushContext.
//
// If saveContext corresponds to a session and no more contexts can be saved, a *TPMError error will be returned with an error code
// of ErrorTooManyContexts. If a context ID cannot be assigned for the session, a *TPMWarning error with a warning code of
// WarningContextGap will be returned.
func (t *TPMContext) ContextSave(saveContext HandleContext) (context *Context, err error) {
switch c := saveContext.(type) {
case *handleContext:
if c.Type == handleContextTypePartial {
return nil, makeInvalidArgError("saveContext", "unusable partial HandleContext")
}
}
if err := t.RunCommand(CommandContextSave, nil,
saveContext, Delimiter,
Delimiter,
Delimiter,
&context); err != nil {
return nil, err
}
blob := mu.MustMarshalToBytes(saveContext.SerializeToBytes(), context.Blob)
context.Blob = blob
switch c := saveContext.(type) {
case *sessionContext:
c.handleContext.Data.Session = nil
if t.exclusiveSession == c {
t.exclusiveSession = nil
}
}
return context, nil
}
// ContextLoad executes the TPM2_ContextLoad command with the supplied Context, in order to restore a context previously saved from
// TPMContext.ContextSave.
//
// If the size field of the integrity HMAC in the context blob is greater than the size of the largest digest algorithm, a *TPMError
// with an error code of ErrorSize is returned. If the context blob is shorter than the size indicated for the integrity HMAC, a
// *TPMError with an error code of ErrorInsufficient is returned.
//
// If the size of the context's integrity HMAC does not match the context integrity digest algorithm for the TPM, or the context
// blob is too short, a *TPMParameterError error with an error code of ErrorSize will be returned. If the integrity HMAC check fails,
// a *TPMParameterError with an error code of ErrorIntegrity will be returned.
//
// If the hierarchy that the context is part of is disabled, a *TPMParameterError error with an error code of ErrorHierarchy will be
// returned.
//
// If the context corresponds to a session but the handle doesn't reference a saved session or the sequence number is invalid, a
// *TPMParameterError error with an error code of ErrorHandle will be returned.
//
// If the context corresponds to a session and no more sessions can be created until the oldest session is context loaded, and context
// doesn't correspond to the oldest session, a *TPMWarning error with a warning code of WarningContextGap will be returned.
//
// If there are no more slots available for objects or loaded sessions, a *TPMWarning error with a warning code of either
// WarningSessionMemory or WarningObjectMemory will be returned.
//
// On successful completion, it returns a HandleContext which corresponds to the resource loaded in to the TPM. If the context
// corresponds to an object, this will be a new ResourceContext. If context corresponds to a session, then this will be a new
// SessionContext.
func (t *TPMContext) ContextLoad(context *Context) (loadedContext HandleContext, err error) {
if context == nil {
return nil, makeInvalidArgError("context", "nil value")
}
var contextData []byte
var blob ContextData
if _, err := mu.UnmarshalFromBytes(context.Blob, &contextData, &blob); err != nil {
return nil, xerrors.Errorf("cannot unmarshal context blob: %w", err)
}
hc, _, err := CreateHandleContextFromBytes(contextData)
if err != nil {
return nil, xerrors.Errorf("cannot unmarshal handle context: %w", err)
}
switch hc.Handle().Type() {
case HandleTypeHMACSession, HandleTypePolicySession:
if hc.Handle() != context.SavedHandle {
return nil, errors.New("host and TPM context blobs have inconsistent handles")
}
case HandleTypeTransient:
default:
return nil, errors.New("unexpected context type")
}
tpmContext := Context{
Sequence: context.Sequence,
SavedHandle: context.SavedHandle,
Hierarchy: context.Hierarchy,
Blob: blob}
var loadedHandle Handle
if err := t.RunCommand(CommandContextLoad, nil,
Delimiter,
tpmContext, Delimiter,
&loadedHandle); err != nil {
return nil, err
}
switch hc.Handle().Type() {
case HandleTypeTransient:
if loadedHandle.Type() != HandleTypeTransient {
return nil, &InvalidResponseError{CommandContextLoad, fmt.Sprintf("handle %v returned from TPM is the wrong type", loadedHandle)}
}
hc.(*objectContext).H = loadedHandle
case HandleTypeHMACSession, HandleTypePolicySession:
if loadedHandle != context.SavedHandle {
return nil, &InvalidResponseError{CommandContextLoad, fmt.Sprintf("handle %v returned from TPM is incorrect", loadedHandle)}
}
hc.(*sessionContext).Data().IsExclusive = false
default:
panic("not reached")
}
return hc, nil
}
// FlushContext executes the TPM2_FlushContext command on the handle referenced by flushContext, in order to flush resources
// associated with it from the TPM. If flushContext does not correspond to a transient object or a session, then it will return
// with an error.
//
// On successful completion, flushContext is invalidated. If flushContext corresponded to a session, then it will no longer be
// possible to restore that session with TPMContext.ContextLoad, even if it was previously saved with TPMContext.ContextSave.
func (t *TPMContext) FlushContext(flushContext HandleContext) error {
if flushContext == nil {
return makeInvalidArgError("flushContext", "nil value")
}
if err := t.RunCommand(CommandFlushContext, nil,
Delimiter,
flushContext.Handle()); err != nil {
return err
}
flushContext.(handleContextPrivate).invalidate()
return nil
}
// EvictControl executes the TPM2_EvictControl command on the handle referenced by object. To persist a transient object,
// object should correspond to the transient object and persistentHandle should specify the persistent handle to which the
// resource associated with object should be persisted. To evict a persistent object, object should correspond to the
// persistent object and persistentHandle should be the handle associated with that resource.
//
// The auth parameter should be a ResourceContext that corresponds to a hierarchy - it should be HandlePlatform for objects within
// the platform hierarchy, or HandleOwner for objects within the storage or endorsement hierarchies. If auth is a ResourceContext
// corresponding to HandlePlatform but object corresponds to an object outside of the platform hierarchy, or auth is a ResourceContext
// corresponding to HandleOwner but object corresponds to an object inside of the platform hierarchy, a *TPMHandleError error with
// an error code of ErrorHierarchy will be returned for handle index 2. The auth handle requires authorization with the user auth
// role, with session based authorization provided via authAuthSession.
//
// If object corresponds to a transient object that only has a public part loaded, or which has the AttrStClear attribute set,
// then a *TPMHandleError error with an error code of ErrorAttributes will be returned for handle index 2.
//
// If object corresponds to a persistent object and persistentHandle is not the handle for that object, a *TPMHandleError error
// with an error code of ErrorHandle will be returned for handle index 2.
//
// If object corresponds to a transient object and persistentHandle is not in the correct range determined by the value of
// auth, a *TPMParameterError error with an error code of ErrorRange will be returned.
//
// If there is insuffient space to persist a transient object, a *TPMError error with an error code of ErrorNVSpace will be returned.
// If a persistent object already exists at the specified handle, a *TPMError error with an error code of ErrorNVDefined will be
// returned.
//
// On successful completion of persisting a transient object, it returns a ResourceContext that corresponds to the persistent object.
// On successful completion of evicting a persistent object, it returns a nil ResourceContext, and object will be invalidated.
func (t *TPMContext) EvictControl(auth, object ResourceContext, persistentHandle Handle, authAuthSession SessionContext, sessions ...SessionContext) (ResourceContext, error) {
if object == nil {
return nil, makeInvalidArgError("object", "nil value")
}
var public *Public
if object.Handle() != persistentHandle {
if err := mu.CopyValue(&public, object.(*objectContext).GetPublic()); err != nil {
return nil, fmt.Errorf("cannot copy public area of object: %v", err)
}
}
if err := t.RunCommand(CommandEvictControl, sessions,
ResourceContextWithSession{Context: auth, Session: authAuthSession}, object, Delimiter,
persistentHandle); err != nil {
return nil, err
}
if object.Handle() == persistentHandle {
object.(handleContextPrivate).invalidate()
return nil, nil
}
return makeObjectContext(persistentHandle, object.Name(), public), nil
}
./github.com/canonical/go-tpm2/cmds_da.go 0000664 0000000 0000000 00000004761 00000000000 016550 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 25 - Dictionary Attack Functions
// DictionaryAttackLockReset executes the TPM2_DictionaryAttackLockReset command to cancel the effect of a TPM lockout. The lockContext
// parameter must always be a ResourceContext corresponding to HandleLockout. The command requires authorization with the user auth role for
// lockContext, with session based authorization provided via lockContextAuthSession.
//
// On successful completion, the lockout counter will be reset to zero.
func (t *TPMContext) DictionaryAttackLockReset(lockContext ResourceContext, lockContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandDictionaryAttackLockReset, sessions,
ResourceContextWithSession{Context: lockContext, Session: lockContextAuthSession})
}
// DictionaryAttackParameters executes the TPM2_DictionaryAttackParameters command to change the dictionary attack lockout settings.
// The newMaxTries parameter sets the maximum value of the lockout counter before the TPM enters lockout mode. If it is set to zero,
// then the TPM will enter lockout mode and the use of dictionary attack protected entities will be disabled. The newRecoveryTime
// parameter specifies the amount of time in seconds it takes for the lockout counter to decrement by one. If it is set to zero, then
// dictionary attack protection is disabled. The lockoutRecovery parameter specifies the amount of time in seconds that the lockout
// hierarchy authorization cannot be used after an authorization failure. If it is set to zero, then the lockout hierarchy can be used
// again after a TPM reset, restart or resume. The newRecoveryTime and lockoutRecovery parameters are measured against powered on time
// rather than clock time.
//
// The lockContext parameter must be a ResourceContext corresponding to HandleLockout. The command requires authorization with the user
// auth role for lockContext, with session based authorization provided via lockContextAuthSession.
func (t *TPMContext) DictionaryAttackParameters(lockContext ResourceContext, newMaxTries, newRecoveryTime, lockoutRecovery uint32, lockContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandDictionaryAttackParameters, sessions,
ResourceContextWithSession{Context: lockContext, Session: lockContextAuthSession}, Delimiter,
newMaxTries, newRecoveryTime, lockoutRecovery)
}
./github.com/canonical/go-tpm2/cmds_duplication.go 0000664 0000000 0000000 00000022513 00000000000 020472 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"github.com/canonical/go-tpm2/mu"
)
// Section 13 - Duplication Commands
// Duplicate executes the TPM2_Duplicate command in order to duplicate the object associated with objectContext so that it may be
// used in a different hierarchy. The new parent is specified by the newParentContext argument, which may correspond to an object
// on the same or a different TPM, or may be nil for no parent.
//
// This command requires authorization for objectContext with the duplication role, with the session provided via
// objectContextAuthSession.
//
// If symmetricAlg is provided, it defines the symmetric algorithm used for the inner duplication wrapper (see section 23.3 -
// "Protected Storage Hierarchy - Duplication" of Part 1 of the Trusted Platform Module Library specification). If symmetricAlg
// is provided and symmetricAlg.Algorithm is not SymObjectAlgorithmNull, a symmetric key for the inner duplication wrapper may be
// provided via encryptionKeyIn.
//
// If the object associated with objectContext has the AttrFixedParent atttribute set, a *TPMHandleError error with an error code
// of ErrorAttributes will be returned for handle index 1.
//
// If the object associated with objectContext has a name algorithm of HashAlgorithmNull, a *TPMHandleError error with an error code
// of ErrorType will be returned for handle index 1.
//
// If newParentContext is provided and it does not correspond to a storage parent, a *TPMHandleError error with an error code of
// ErrorType will be returned for handle index 2.
//
// If the object associated with objectContext has the AttrEncryptedDuplication attribute set and no symmetricAlg is provided or
// symmetricAlg.Algorithm is SymObjectAlgorithmNull, a *TPMParameterError error with an error code of ErrorSymmetric will be returned
// for parameter index 2.
//
// If the object associated with objectContext has the AttrEncryptedDuplication attribute set and newParentContext is not provided,
// a *TPMHandleError error with an error code of ErrorHierarchy will be returned for handle index 2.
//
// If the length of encryptionKeyIn is not consistent with symmetricAlg, *TPMParameterError error with an error code of ErrorSize
// will be returned for parameter index 1.
//
// If newParentContext corresponds to an ECC key and the public point of the key is not on the curve specified by the key, a *TPMError
// error with an error code of ErrorKey will be returned.
//
// On success, the function returns a randomly generated symmetric key as Data for the inner duplication wrapper if symmetricAlg was
// provided, symmetricAlg.Algorithm was not SymObjectAlgorithmNull and encryptionKeyIn was not provided. It also returns the sensitive
// area associated with objectContext protected with an inner duplication wrapper (if specified by symmetricAlg) and an outer
// duplication wrapper (if newParentContext was provided). If newParentContext was provided, the seed used to generate the symmetric
// key and the HMAC key for the outer duplication wrapper is encrypted using the methods defined by newParentContext and returned as
// an EncryptedSecret.
func (t *TPMContext) Duplicate(objectContext, newParentContext ResourceContext, encryptionKeyIn Data, symmetricAlg *SymDefObject, objectContextAuthSession SessionContext, sessions ...SessionContext) (encryptionKeyOut Data, duplicate Private, outSymSeed EncryptedSecret, err error) {
if symmetricAlg == nil {
symmetricAlg = &SymDefObject{Algorithm: SymObjectAlgorithmNull}
}
if err := t.RunCommand(CommandDuplicate, sessions,
ResourceContextWithSession{Context: objectContext, Session: objectContextAuthSession}, newParentContext, Delimiter,
encryptionKeyIn, symmetricAlg, Delimiter,
Delimiter,
&encryptionKeyOut, &duplicate, &outSymSeed); err != nil {
return nil, nil, nil, err
}
return encryptionKeyOut, duplicate, outSymSeed, nil
}
// func (t *TPMContext) Rewrap(oldParent, newParent HandleContext, inDuplicate Private, name Name, inSymSeed EncryptedSecret, oldParentAuth interface{}, sessions ...SessionContext) (Private, EncryptedSecret, error) {
// }
// Import executes the TPM2_Import command in order to encrypt the sensitive area of the object associated with the objectPublic and
// duplicate arguments with the symmetric algorithm of the storage parent associated with parentContext, so that it can be loaded and
// used in the new hierarchy. If the object to be imported has an inner duplication wrapper (see section 23.3 - "Protected Storage
// Hierarchy - Duplication" of Part 1 of the Trusted Platform Module Library specification), then symmetricAlg must be provided with
// the algorithm of the inner duplication wrapper, and encryptionKey must be provided with the symmetric key for the inner duplication
// wrapper. If the object to be imported has an outer duplication wrapper, the seed used to generate the symmetric key and HMAC key
// for the outer duplication wrapper, encrypted using the methods defined by parentContext, must be provided via the inSymSeed
// argument.
//
// This command requires authorization with the user auth role for parentContext, with session based authorization provided via
// parentContextAuthSession.
//
// If objectPublic has the AttrFixedTPM or AttrFixedParent attributes set, a *TPMParameterError error with an error code of
// ErrorAttributes will be returned for parameter index 2.
//
// If parentContext is not associated with a storage parent, a *TPMHandleError error with an error code of ErrorType will be returned.
//
// If the length of encryptionKey is not consistent with symmetricAlg, a *TPMParameterError error with an error code of ErrorSize
// will be returned for parameter index 1.
//
// If symmetricAlg is not provided or symmetricAlg.Algorithm is SymObjectAlgorithmNull and objectPublic has the
// AttrEncryptedDuplication attribute set, a *TPMParameterError error with an error code of ErrorAttributes will be returned for
// parameter index 1.
//
// If the length of inSymSeed is not zero and the object associated with parentContext is not an asymmetric key, a *TPMHandleError
// error with an error code of ErrorType will be returned.
//
// If parentContext is associated with a RSA key and the size of inSymSeed does not match the size of the key's public modulus, a
// *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 4.
//
// If parentContext is associated with a RSA key and the plaintext size of inSymSeed is larger than the name algorithm, a
// *TPMParameterError error with an error code of ErrorValue will be returned for parameter index 4.
//
// If parentContext is associated with a ECC key and inSymSeed does not contain enough data to unmarshal a ECC point, a
// *TPMParameterError error with an error code of ErrorInsufficient will be returned for parameter index 4.
//
// If parentContext is associated with a ECC key and the ECC point in inSymSeed is not on the curve specified by the parent key, a
// *TPMParameterError error with an error code of ErrorECCPoint will be returned for parameter index 4.
//
// If parentContext is associated with a ECC key and multiplication of the ECC point in inSymSeed results in a point at infinity, a
// *TPMParameterError error with an error code of ErrorNoResult will be returned for parameter index 4.
//
// If the name of the object associated with objectPublic cannot be computed, a *TPMParameterError error with an error code of
// ErrorHash will be returned for parameter index 2.
//
// If the object has an outer duplication wrapper and the integrity value of duplicate cannot be unmarshalled correctly, a
// *TPMParameterError error with an error code of either ErrorSize or ErrorInsufficient will be returned for parameter index 3.
// If the integrity check fails, a *TPMParameterError error with an error code of ErrorIntegrity will be returned for parameter
// index 3.
//
// If the object has an inner duplication wrapper and the integrity value of duplicate cannot be unmarshalled correctly after
// decrypting the inner wrapper, a *TPMParameterError error with an error code of either ErrorSize or ErrorInsufficient will be
// returned for parameter index 3. If the integrity check fails, a *TPMParameterError error with an error code of ErrorIntegrity
// will be returned for parameter index 3.
//
// If, after removing the duplication wrappers, the sensitive area does not unmarshal correctly, a *TPMParameterError error with
// an error code of either ErrorSize or ErrorInsufficient will be returned for parameter index 3.
//
// On success, a new private area encrypted with the symmetric algorithm defined by the object associated with parentContext is
// returned.
func (t *TPMContext) Import(parentContext ResourceContext, encryptionKey Data, objectPublic *Public, duplicate Private, inSymSeed EncryptedSecret, symmetricAlg *SymDefObject, parentContextAuthSession SessionContext, sessions ...SessionContext) (outPrivate Private, err error) {
if symmetricAlg == nil {
symmetricAlg = &SymDefObject{Algorithm: SymObjectAlgorithmNull}
}
if err := t.RunCommand(CommandImport, sessions,
ResourceContextWithSession{Context: parentContext, Session: parentContextAuthSession}, Delimiter,
encryptionKey, mu.Sized(objectPublic), duplicate, inSymSeed, symmetricAlg, Delimiter,
Delimiter,
&outPrivate); err != nil {
return nil, err
}
return outPrivate, nil
}
./github.com/canonical/go-tpm2/cmds_ea.go 0000664 0000000 0000000 00000070453 00000000000 016552 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 23 - Enhanced Authorization (EA) Commands
// PolicySigned executes the TPM2_PolicySigned command to include a signed authorization in a policy. This is a combined assertion
// that binds a policy to the signing key associated with authContext.
//
// An authorizing entity signs a digest of authorization qualifiers with the key associated with authContext. The digest is computed as:
// digest := H(nonceTPM||expiration||cpHashA||policyRef)
// ... where H is the digest algorithm associated with the auth parameter. Where there are no restrictions, the digest is computed
// from 4 zero bytes, which corresponds to an expiration time of zero. The authorization qualifiers must match the arguments passed
// to this command. The signature is provided via the auth parameter.
//
// If includeNonceTPM is set to true, this function includes the most recently received TPM nonce value for the session associated
// with policySession in the command. In this case, the nonce value must be included in the digest that is signed by the authorizing
// entity.
//
// The cpHashA parameter allows the session to be bound to a specific command and set of command parameters by providing a command
// parameter digest. Command parameter digests can be computed using ComputeCpHash, using the digest algorithm for the session. If
// provided, the cpHashA value must be included in the digest that is signed by the authorizing entity.
//
// If policySession does not correspond to a trial session, a *TPMError error with an error code of ErrorCpHash will be returned if
// the session context already has a command parameter digest, name digest or template digest recorded on it and cpHashA does not
// match it.
//
// If policySession does not correspond to a trial session and the length of cpHashA does not match the digest algorithm for the
// session, a *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 2.
//
// If the expiration parameter is not 0, it sets a timeout based on the absolute value of expiration in seconds since the start of
// the session by which the authorization will expire. If the session associated with policySession is not a trial session and
// expiration corresponds to a time in the past, or the TPM's time epoch has changed since the session was started, a
// *TPMParameterError error with an error code of ErrorExpired will be returned for parameter index 4.
//
// If the session associated with policySession is not a trial session and the signing scheme or digest algorithm associated with
// the auth parameter is not supported by the TPM, a *TPMParameterError error with an error code of ErrorScheme will be returned for
// parameter index 5.
//
// If the session associated with policySession is not a trial session, the signature will be validated against a digest computed from
// the provided arguments, using the key associated with authContext. If the signature is invalid, a *TPMParameterError error with an
// error code of ErrorSignature will be returned for parameter index 5.
//
// On successful completion, the policy digest of the session associated with policySession will be extended to include the name of
// authContext and the value of policyRef. If provided, the value of cpHashA will be recorded on the session context to restrict the
// session's usage. If expiration is non-zero, the expiration time of the session context will be updated unless it already has an
// expiration time that is earlier. If expiration is less than zero, a timeout value and corresponding *TkAuth ticket will be
// returned if policySession does not correspond to a trial session.
func (t *TPMContext) PolicySigned(authContext ResourceContext, policySession SessionContext, includeNonceTPM bool, cpHashA Digest, policyRef Nonce, expiration int32, auth *Signature, sessions ...SessionContext) (timeout Timeout, policyTicket *TkAuth, err error) {
var nonceTPM Nonce
if includeNonceTPM {
nonceTPM = policySession.NonceTPM()
}
if err := t.RunCommand(CommandPolicySigned, sessions,
authContext, policySession, Delimiter,
nonceTPM, cpHashA, policyRef, expiration, auth, Delimiter,
Delimiter,
&timeout, &policyTicket); err != nil {
return nil, nil, err
}
return timeout, policyTicket, nil
}
// PolicySecret executes the TPM2_PolicySecret command to include a secret-based authorization to the policy session associated
// with policySession, and is a combined assertion. The command requires authorization with the user auth role for authContext, with
// session based authorization provided via authContextAuthSession. If authContextAuthSession corresponds a policy session, and that
// session does not include a TPM2_PolicyPassword or TPM2_PolicyAuthValue assertion, a *TPMSessionError error with an error code of
// ErrorMode will be returned for session index 1.
//
// The cpHashA parameter allows the session to be bound to a specific command and set of command parameters by providing a command
// parameter digest. Command parameter digests can be computed using ComputeCpHash, using the digest algorithm for the session. If
// provided, the cpHashA value must be included in the digest that is signed by the authorizing entity.
//
// If policySession does not correspond to a trial session, a *TPMError error with an error code of ErrorCpHash will be returned if
// the session context already has a command parameter digest, name digest or template digest recorded on it and cpHashA does not
// match it.
//
// If policySession does not correspond to a trial session and the length of cpHashA does not match the digest algorithm for the
// session, a *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 2.
//
// If the expiration parameter is not 0, it sets a timeout based on the absolute value of expiration in seconds since the start of
// the session by which the authorization will expire. If the session associated with policySession is not a trial session and
// expiration corresponds to a time in the past, or the TPM's time epoch has changed since the session was started, a
// *TPMParameterError error with an error code of ErrorExpired will be returned for parameter index 4.
//
// On successful completion, knowledge of the authorization value associated with authContext is proven. The policy digest of the
// session associated with olicySession will be extended to include the name of authContext and the value of policyRef. If provided,
// the value of cpHashA will be recorded on the session context to restrict the session's usage. If expiration is non-zero, the
// expiration time of the session context will be updated unless it already has an expiration time that is earlier. If expiration is
// less than zero, a timeout value and corresponding *TkAuth ticket will be returned if policySession does not correspond to a trial
// session.
func (t *TPMContext) PolicySecret(authContext ResourceContext, policySession SessionContext, cpHashA Digest, policyRef Nonce, expiration int32, authContextAuthSession SessionContext, sessions ...SessionContext) (timeout Timeout, policyTicket *TkAuth, err error) {
if err := t.RunCommand(CommandPolicySecret, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, policySession, Delimiter,
policySession.NonceTPM(), cpHashA, policyRef, expiration, Delimiter,
Delimiter,
&timeout, &policyTicket); err != nil {
return nil, nil, err
}
return timeout, policyTicket, nil
}
// PolicyTicket executes the TPM2_PolicyTicket command, and behaves similarly to TPMContext.PolicySigned with the exception that it
// takes an authorization ticket rather than a signed authorization. The ticket parameter represents a valid authorization with an
// expiration time, and will have been returned from a previous call to TPMContext.PolicySigned or TPMContext.PolicySecret when called
// with an expiration time of less than zero.
//
// If policySession corresponds to a trial session, a *TPMHandleError error with an error code of ErrorAttributes will be returned.
//
// If the size of timeout is not the expected size, a *TPMParameterError with an error code of ErrorSize will be returned for
// parameter index 1.
//
// A *TPMError error with an error code of ErrorCpHash will be returned if the session context already has a command parameter digest,
// name digest or template digest recorded on it and cpHashA does not match it.
//
// The cpHashA and policyRef arguments must match the values passed to the command that originally produced the ticket. If the command
// that produced the ticket was TPMContext.PolicySecret, authName must correspond to the name of the entity of which knowledge of the
// authorization value was proven. If the command that produced the ticket was TPMContext.PolicySigned, authName must correspond to
// the name of the key that produced the signed authorization.
//
// If the ticket is invalid, a *TPMParameterError error with an error code of ErrorTicket will be returned for parameter index 5. If
// the ticket corresponds to an authorization that has expired, a *TPMParameterError error with an error code of ErrorExpired will
// be returned for parameter index 1.
//
// On successful verification of the ticket, the policy digest of the session context associated with policySession will be extended
// with the same values that the command that produced the ticket would extend it with. If provided, the value of cpHashA will be
// recorded on the session context to restrict the session's usage. The expiration time of the session context will be updated with
// the value of timeout, unless it already has an expiration time that is earlier.
func (t *TPMContext) PolicyTicket(policySession SessionContext, timeout Timeout, cpHashA Digest, policyRef Nonce, authName Name, ticket *TkAuth, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyTicket, sessions,
policySession, Delimiter,
timeout, cpHashA, policyRef, authName, ticket)
}
// PolicyOR executes the TPM2_PolicyOR command to allow a policy to be satisfied by different sets of conditions, and is an immediate
// assertion. If policySession does not correspond to a trial session, it determines if the current policy digest of the session
// context associated with policySession is contained in the list of digests specified via pHashList. If it is not, then a
// *TPMParameterError error with an error code of ErrorValue is returned without making any changes to the session context.
//
// On successful completion, the policy digest of the session context associated with policySession is cleared, and then extended to
// include the concatenation of all of the digests contained in pHashList.
func (t *TPMContext) PolicyOR(policySession SessionContext, pHashList DigestList, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyOR, sessions,
policySession, Delimiter,
pHashList)
}
// PolicyPCR executes the TPM2_PolicyPCR command to gate a policy based on the values of the PCRs selected via the pcrs parameter. If
// no digest has been specified via the pcrDigest parameter, then it is a deferred assertion and the policy digest of the session
// context associated with policySession will be extended to include the value of the PCR selection and a digest computed from the
// selected PCR contents.
//
// If pcrDigest is provided, then it is a combined assertion. If policySession does not correspond to a trial session, the digest
// computed from the selected PCRs will be compared to the value of pcrDigest and a *TPMParameterError error with an error code of
// ErrorValue will be returned for parameter index 1 if they don't match, without making any changes to the session context. If
// policySession corresponds to a trial session, the digest computed from the selected PCRs is not compared to the value of pcrDigest;
// instead, the policy digest of the session is extended to include the value of the PCR selection and the value of pcrDigest.
//
// If the PCR contents have changed since the last time this command was executed for this session, a *TPMError error will be returned
// with an error code of ErrorPCRChanged.
func (t *TPMContext) PolicyPCR(policySession SessionContext, pcrDigest Digest, pcrs PCRSelectionList, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyPCR, sessions,
policySession, Delimiter,
pcrDigest, pcrs)
}
// func (t *TPMContext) PolicyLocality(policySession HandleContext, locality Locality, sessions ...SessionContext) error {
// }
// PolicyNV executes the TPM2_PolicyNV command to gate a policy based on the contents of the NV index associated with nvIndex, and is
// an immediate assertion. The caller specifies a value to be used for the comparison via the operandB argument, an offset from the
// start of the NV index data from which to start the comparison via the offset argument, and a comparison operator via the operation
// argument.
//
// The command requires authorization to read the NV index, defined by the state of the AttrNVPPRead, AttrNVOwnerRead, AttrNVAuthRead
// and AttrNVPolicyRead attributes. The handle used for authorization is specified via authContext. If the NV index has the
// AttrNVPPRead attribute, authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerRead attribute,
// authorization can be satisfied with HandleOwner. If the NV index has the AttrNVAuthRead or AttrNVPolicyRead attribute,
// authorization can be satisfied with nvIndex. The command requires authorization with the user auth role for authContext, with
// session based authorization provided via authContextAuthSession. If the resource associated with authContext is not permitted to
// authorize this access and policySession does not correspond to a trial session, a *TPMError error with an error code of
// ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthRead attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyRead attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index associated with nvIndex has the AttrNVReadLocked attribute set and policySession does not correspond to a trial
// session, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the index associated with nvIndex has not been initialized (ie, the AttrNVWritten attribute is not set) and policySession does
// not correspond to a trial session, a *TPMError with an error code of ErrorNVUninitialized will be returned.
//
// If the session associated with policySession is not a trial session and offset is outside of the bounds of the NV index, a
// *TPMParameterError error with an error code of ErrorValue is returned for paramter index 2.
//
// If the session associated with policySession is not a trial session and the size of operandB in combination with the value of
// offset would result in a read outside of the bounds of the NV index, a *TPMParameterError error with an error code of ErrorSize
// is returned for paramter index 1.
//
// If the comparison fails and policySession does not correspond to a trial session, a *TPMError error will be returned with an error
// code of ErrorPolicy.
//
// On successful completion, the policy digest of the session context associated with policySession is extended to include the values
// of operandB, offset, operation and the name of nvIndex.
func (t *TPMContext) PolicyNV(authContext, nvIndex ResourceContext, policySession SessionContext, operandB Operand, offset uint16, operation ArithmeticOp, authContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyNV, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, nvIndex, policySession, Delimiter,
operandB, offset, operation)
}
// PolicyCounterTimer executes the TPM2_PolicyCounterTimer command to gate a policy based on the contents of the TimeInfo structure,
// and is an immediate assertion. The caller specifies a value to be used for the comparison via the operandB argument, an offset from
// the start of the TimeInfo structure from which to start the comparison via the offset argument, and a comparison operator via the
// operation argument.
//
// If the comparison fails and policySession does not correspond to a trial session, a *TPMError error will be returned with an error
// code of ErrorPolicy.
//
// On successful completion, the policy digest of the session context associated with policySession is extended to include the values
// of operandB, offset and operation.
func (t *TPMContext) PolicyCounterTimer(policySession SessionContext, operandB Operand, offset uint16, operation ArithmeticOp, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyCounterTimer, sessions,
policySession, Delimiter,
operandB, offset, operation)
}
// PolicyCommandCode executes the TPM2_PolicyCommandCode command to indicate that an authorization policy should be limited to a
// specific command. Ths is a deferred assertion.
//
// If the command code is not implemented, a *TPMParameterError error with an error code of ErrorPolicyCC will be returned. If
// the session associated with policySession has already been limited to a different command code, a *TPMParameterError error with
// an error code of ErrorValue will be returned.
//
// On successful completion, the policy digest of the session context associated with policySession will be extended to
// include the value of the specified command code, and the command code will be recorded on the session context to limit usage of
// the session.
func (t *TPMContext) PolicyCommandCode(policySession SessionContext, code CommandCode, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyCommandCode, sessions,
policySession, Delimiter,
code)
}
// func (t *TPMContext) PolicyPhysicalPresence(policySession HandleContext, sessions ...SessionContext) error {
// }
// PolicyCpHash executes the TPM2_PolicyCpHash command to bind a policy to a specific command and set of command parameters. This is
// a deferred assertion.
//
// TPMContext.PolicySigned, TPMContext.PolicySecret and TPMContext.PolicyTicket allow an authorizing entity to execute an arbitrary
// command as the cpHashA parameter is not included in the session's policy digest. TPMContext.PolicyCommandCode allows the policy
// to be limited to a specific command. This command allows the policy to be limited further to a specific command set of command
// parameters.
//
// Command parameter digests can be computed using ComputeCpHash, using the digest algorithm for the session.
//
// If the size of cpHashA is inconsistent with the digest algorithm for the session, a *TPMParameterError error with an error code
// of ErrorSize will be returned.
//
// If the session associated with policySession already has a command parameter digest, name digest or template digest defined, a
// *TPMError error with an error code of ErrorCpHash will be returned if cpHashA does not match the digest already recorded on the
// session context.
//
// On successful completion, the policy digest of the session context associated with policySession will be extended to include the
// value of cpHashA, and the value of cpHashA will be recorded on the session context to limit usage of the session to the specific
// command and set of command parameters.
func (t *TPMContext) PolicyCpHash(policySession SessionContext, cpHashA Digest, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyCpHash, sessions, policySession, Delimiter, cpHashA)
}
// PolicyNameHash executes the TPM2_PolicyNameHash command to bind a policy to a specific set of TPM entities, without being bound
// to the parameters of the command. This is a deferred assertion.
//
// If the size of nameHash is inconsistent with the digest algorithm for the session, a *TPMParameterError error with an error code
// of ErrorSize will be returned.
//
// If the session associated with policySession already has a name digest, command parameter digest or template digest defined, a
// *TPMError error with an error code of ErrorCpHash will be returned.
//
// On successful completion, the policy digest of the session context associated with policySession will be extended to include the
// value of nameHash, and the value of nameHash will be recorded on the session context to limit usage of the session to the specific
// set of TPM entities.
func (t *TPMContext) PolicyNameHash(policySession SessionContext, nameHash Digest, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyNameHash, sessions, policySession, Delimiter, nameHash)
}
// PolicyDuplicationSelect executes the TPM2_PolicyDuplicationSelect command to allow the policy to be restricted to duplication
// and to allow duplication to a specific new parent. The objectName argument corresponds to the name of the object to be duplicated.
// The newParentName argument corresponds to the name of the new parent object. This is a deferred assertion.
//
// If the session associated with policySession already has a command parameter digest, name digest or template digest defined, a
// *TPMError error with an error code of ErrorCpHash will be returned.
//
// If the session associated with policySession has already been limited to a specific command code, a *TPMError error with an error
// code of ErrorCommandCode will be returned.
//
// On successful completion, the policy digest of the session context associated with policySession will be extended to include the
// value of newParentName and includeObject. If includeObject is true, the policy digest of the session will be extended to also
// include the value of objectName. A digest of objectName and newParentName will be recorded as the name hash on the session context
// to limit usage of the session to those entities, and the CommandDuplicate command code will be recorded to limit usage of the
// session to TPMContext.Duplicate.
func (t *TPMContext) PolicyDuplicationSelect(policySession SessionContext, objectName, newParentName Name, includeObject bool, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyDuplicationSelect, sessions,
policySession, Delimiter,
objectName, newParentName, includeObject)
}
// PolicyAuthorize executes the TPM2_PolicyAuthorize command, which allows policies to change. This is an immediate assertion. The
// command allows an authorizing entity to sign a new policy that can be used in an existing policy. The authorizing party signs a
// digest that is computed as follows:
// digest := H(approvedPolicy||policyRef)
// ... where H is the name algorithm of the key used to sign the digest.
//
// The signature is then verified by TPMContext.VerifySignature, which provides a ticket that is used by this function.
//
// If the name algorithm of the signing key is not supported, a *TPMParameterError error with an error code of ErrorHash will be
// returned for parameter index 3.
//
// If the length of keySign does not match the length of the name algorithm, a *TPMParameterError error with an error code of
// ErrorSize will be returned for parameter index 3.
//
// If policySession is not associated with a trial session, the current digest of the session associated with policySession will be
// compared with approvedPolicy. If they don't match, then a *TPMParameterError error with an error code of ErrorValue will be
// returned for parameter index 1.
//
// If policySession is not associated with a trial session and checkTicket is invalid, a *TPMParameterError error with an error
// code of ErrorValue will be returned for parameter index 4.
//
// On successful completion, the policy digest of the session context associated with policySession is cleared, and then extended to
// include the value of keySign and policyRef.
func (t *TPMContext) PolicyAuthorize(policySession SessionContext, approvedPolicy Digest, policyRef Nonce, keySign Name, checkTicket *TkVerified, sessions ...SessionContext) error {
if checkTicket == nil {
checkTicket = &TkVerified{Tag: TagVerified, Hierarchy: HandleNull}
}
return t.RunCommand(CommandPolicyAuthorize, sessions,
policySession, Delimiter,
approvedPolicy, policyRef, keySign, checkTicket)
}
// PolicyAuthValue executes the TPM2_PolicyAuthValue command to bind the policy to the authorization value of the entity on which the
// authorization is used. This is a deferred assertion. On successful completion, the policy digest of the session context associated
// with policySession will be extended to record that this assertion has been executed, and a flag will be set on the session context
// to indicate that the authorization value of the entity on which the authorization is used must be included in the key for computing
// the command HMAC when the session is used.
//
// When using policySession in a subsequent authorization, the authorization value of the entity being authorized must be provided by
// calling ResourceContext.SetAuthValue.
func (t *TPMContext) PolicyAuthValue(policySession SessionContext, sessions ...SessionContext) error {
sessionData := policySession.(*sessionContext).Data()
if sessionData == nil {
return makeInvalidArgError("policySession", "incomplete session can only be used in TPMContext.FlushContext")
}
if err := t.RunCommand(CommandPolicyAuthValue, sessions, policySession); err != nil {
return err
}
sessionData.PolicyHMACType = policyHMACTypeAuth
return nil
}
// PolicyPassword executes the TPM2_PolicyPassword command to bind the policy to the authorization value of the entity on which the
// authorization is used. This is a deferred assertion. On successful completion, the policy digest of the session context associated
// with policySession will be extended to record that this assertion has been executed, and a flag will be set on the session context
// to indicate that the authorization value of the entity on which the authorization is used must be included in cleartext in the
// command authorization when the session is used.
//
// When using policySession in a subsequent authorization, the authorization value of the entity being authorized must be provided by
// calling ResourceContext.SetAuthValue.
func (t *TPMContext) PolicyPassword(policySession SessionContext, sessions ...SessionContext) error {
sessionData := policySession.(*sessionContext).Data()
if sessionData == nil {
return makeInvalidArgError("policySession", "incomplete session can only be used in TPMContext.FlushContext")
}
if err := t.RunCommand(CommandPolicyPassword, sessions, policySession); err != nil {
return err
}
sessionData.PolicyHMACType = policyHMACTypePassword
return nil
}
// PolicyGetDigest executes the TPM2_PolicyGetDigest command to return the current policy digest of the session context associated
// with policySession.
func (t *TPMContext) PolicyGetDigest(policySession SessionContext, sessions ...SessionContext) (policyDigest Digest, err error) {
if err := t.RunCommand(CommandPolicyGetDigest, sessions,
policySession, Delimiter,
Delimiter,
Delimiter,
&policyDigest); err != nil {
return nil, err
}
return policyDigest, nil
}
// PolicyNvWritten executes the TPM2_PolicyNvWritten command to bind a policy to the value of the AttrNVWritten attribute of the
// NV index being authorized, and is a deferred assertion.
//
// If this command has been executed previously in this session, and the value of writtenSet doesn't match the value provided
// previously, a *TPMParameterError error with an error code of ErrorValue will be returned.
//
// On successful completion, the policy digest of the session associated with policySession will be extended to include the value of
// writtenSet. A flag will be set on the session context so that the value of the AttrNVWritten attribute of the NV index being
// authorized will be compared to writtenSet when the session is used.
func (t *TPMContext) PolicyNvWritten(policySession SessionContext, writtenSet bool, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyNvWritten, sessions, policySession, Delimiter, writtenSet)
}
// func (t *TPMContext) PolicyTemplate(policySession HandleContext, templateHash Digest, sessions ...SessionContext) error {
// }
// func (t *TPMContext) PolicyAuthorizeNV(authContext, nvIndex, policySession HandleContext, authContextAuth interface{}, sessions ...SessionContext) error {
// }
./github.com/canonical/go-tpm2/cmds_hashhmac.go 0000664 0000000 0000000 00000030430 00000000000 017730 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 17 - Hash/HMAC/Event Sequences
// HMACStart executes the TPM2_HMAC_Start command to begin a HMAC sequence. The context argument corresponds to a loaded HMAC
// key. This command requires authorization with the user auth role for context, with session based authorization provided via
// contextAuthSession. The command creates a new HMAC sequence object on the TPM. The auth argument defines the authorization value
// for the newly created sequence object, which is required for subsequent use of it.
//
// If context does not correspond to an object with the type ObjectTypeKeyedHash, a *TPMHandleError error with an error code of
// ErrorType will be returned.
//
// If context corresponds to an object with the AttrRestricted attribute set, a *TPMHandleError error with an error code of
// ErrorAttributes will be returned.
//
// If context does not correspond to a signing key, a *TPMHandleError error with an error code of ErrorKey will be returned.
//
// The hashAlg argument specifies the HMAC algorithm. If the default scheme of the key associated with context is KeyedHashSchemeNull,
// then hashAlg must not be HashAlgorithmNull. If the default scheme of the key associated with context is not KeyedHashSchemeNull,
// then hashAlg must either be HashAlgorithmNull or must match the key's default scheme, else a *TPMParameterError error with an error
// code of ErrorValue will be returned for parameter index 2.
//
// On success, a ResourceContext corresponding to the newly created HMAC sequence object will be returned. It will not be necessary
// to call ResourceContext.SetAuthValue on it - this function sets the correct authorization value so that it can be used in
// subsequent commands that require knowledge of the authorization value.
func (t *TPMContext) HMACStart(context ResourceContext, auth Auth, hashAlg HashAlgorithmId, contextAuthSession SessionContext, sessions ...SessionContext) (sequenceContext ResourceContext, err error) {
var sequenceHandle Handle
if err := t.RunCommand(CommandHMACStart, sessions,
ResourceContextWithSession{Context: context, Session: contextAuthSession}, Delimiter,
auth, hashAlg, Delimiter,
&sequenceHandle); err != nil {
return nil, err
}
rc := makeObjectContext(sequenceHandle, nil, nil)
rc.authValue = make([]byte, len(auth))
copy(rc.authValue, auth)
return rc, nil
}
// HashSequenceStart executes the TPM2_HashSequenceStart command to begin a hash or event sequence. The command creates a new
// sequence object on the TPM. The auth argument defines the authorization value for the newly created sequence object, which is
// required for subsequent use of it.
//
// If hashAlg is HashAlgorithmNull, this function will return a ResourceContext corresponding to a newly created event sequence
// object. If hashAlg is not HashAlgorithmNull, this function will return a ResourceContext corresponding to a newly created hash
// sequence object. It will not be necessary to call ResourceContext.SetAuthValue on it - this function sets the correct authorization
// value so that it can be used in subsequent commands that require knowledge of the authorization value.
func (t *TPMContext) HashSequenceStart(auth Auth, hashAlg HashAlgorithmId, sessions ...SessionContext) (sequenceContext ResourceContext, err error) {
var sequenceHandle Handle
if err := t.RunCommand(CommandHashSequenceStart, sessions,
Delimiter,
auth, hashAlg, Delimiter,
&sequenceHandle); err != nil {
return nil, err
}
rc := makeObjectContext(sequenceHandle, nil, nil)
rc.authValue = make([]byte, len(auth))
copy(rc.authValue, auth)
return rc, nil
}
// SequenceUpdate executes the TPM2_SequenceUpdate command to add data to the HMAC, hash or event sequence associated with
// sequenceContext. This command requires authorization with the user auth role for sequenceContext, with session based authorization
// provided via sequenceContextAuthSession.
//
// If sequenceContext does not correspond to a sequence object, then a *TPMHandleError error with an error code of ErrorMode will be
// returned.
//
// If sequenceContext corresponds to a hash sequence and the hash sequence is intended to produce a digest that will be signed with
// a restricted signing key, the first block of data added to this sequence must be 4 bytes and not the value of TPMGeneratedValue.
func (t *TPMContext) SequenceUpdate(sequenceContext ResourceContext, buffer MaxBuffer, sequenceContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandSequenceUpdate, sessions,
ResourceContextWithSession{Context: sequenceContext, Session: sequenceContextAuthSession}, Delimiter,
buffer)
}
// SequenceComplete executes the TPM2_SequenceComplete command to add the last part of the data the HMAC or hash sequence associated
// with sequenceContext, and returns the result. This command requires authorization with the user auth role for sequenceContext, with
// session based authorization provided via sequenceContextAuthSession.
//
// If sequenceContext does not correspond to a HMAC or hash sequence object, then a *TPMHandleError error with an error code of
// ErrorMode will be returned.
//
// If sequenceContext corresponds to a hash sequence and the hash sequence is intended to produce a digest that will be signed with
// a restricted signing key, the first block of data added to this sequence must be 4 bytes and not the value of TPMGeneratedValue.
// If the returned digest is safe to sign with a restricted signing key, then a ticket that can be passed to TPMContext.Sign will be
// returned. In this case, the hierarchy argument is used to specify the hierarchy for the ticket.
//
// On success, the sequence object associated with sequenceContext will be evicted, and sequenceContext will become invalid.
func (t *TPMContext) SequenceComplete(sequenceContext ResourceContext, buffer MaxBuffer, hierarchy Handle, sequenceContextAuthSession SessionContext, sessions ...SessionContext) (result Digest, validation *TkHashcheck, err error) {
if err := t.RunCommand(CommandSequenceComplete, sessions,
ResourceContextWithSession{Context: sequenceContext, Session: sequenceContextAuthSession}, Delimiter,
buffer, hierarchy, Delimiter,
Delimiter,
&result, &validation); err != nil {
return nil, nil, err
}
if validation.Hierarchy == HandleNull && len(validation.Digest) == 0 {
validation = nil
}
sequenceContext.(handleContextPrivate).invalidate()
return result, validation, nil
}
// EventSequenceComplete executes the TPM2_EventSequenceComplete command to add the last part of the data to the event sequence
// associated with sequenceContext, and return the result. This command requires authorization with the user auth role for
// sequenceContext, with session based authorization provided via sequenceContextAuthSession.
//
// If pcrContext is not nil, the result will be extended to the corresponding PCR in the same manner as TPMContext.PCRExtend.
// Authorization with the user auth role is required for pcrContext, with session based authorization provided via
// pcrContextAuthSession.
//
// If sequenceContext does not correspond to an event sequence object, then a *TPMHandleError error with an error code of ErrorMode
// will be returned for handle index 2.
//
// If pcrContext is not nil and the corresponding PCR can not be extended from the current locality, a *TPMError error with an
// error code of ErrorLocality will be returned.
//
// On success, the sequence object associated with sequenceContext will be evicted, and sequenceContext will become invalid.
func (t *TPMContext) EventSequenceComplete(pcrContext, sequenceContext ResourceContext, buffer MaxBuffer, pcrContextAuthSession, sequenceContextAuthSession SessionContext, sessions ...SessionContext) (results TaggedHashList, err error) {
if err := t.RunCommand(CommandEventSequenceComplete, sessions,
ResourceContextWithSession{Context: pcrContext, Session: pcrContextAuthSession}, ResourceContextWithSession{Context: sequenceContext, Session: sequenceContextAuthSession}, Delimiter,
buffer, Delimiter,
Delimiter,
&results); err != nil {
return nil, err
}
sequenceContext.(handleContextPrivate).invalidate()
return results, nil
}
// SequenceExecute executes a hash or HMAC sequence to completion and returns the result by adding the provided data to the sequence
// with a number of TPM2_SequenceUpdate commands appropriate for the size of buffer, and executing a final TPM2_SequenceComplete
// command. This command requires authorization with the user auth role for sequenceContext, with session based authorization provided
// via sequenceContextAuthSession. As this function executes multiple commands, any SessionContext instances provided should have the
// AttrContinueSession attribute defined.
//
// If sequenceContext does not correspond to a hash or HMAC sequence object, then a *TPMHandleError error with an error code of
// ErrorMode will be returned.
//
// If sequenceContext corresponds to a hash sequence and the hash sequence is intended to produce a digest that will be signed with
// a restricted signing key, the first block of data added to this sequence must be 4 bytes and not the value of TPMGeneratedValue.
// If the returned digest is safe to sign with a restricted signing key, then a ticket that can be passed to TPMContext.Sign will be
// returned. In this case, the hierarchy argument is used to specify the hierarchy for the ticket.
//
// On success, the sequence object associated with sequenceContext will be evicted, and sequenceContext will become invalid.
func (t *TPMContext) SequenceExecute(sequenceContext ResourceContext, buffer []byte, hierarchy Handle, sequenceContextAuthSession SessionContext, sessions ...SessionContext) (result Digest, validation *TkHashcheck, err error) {
if err := t.initPropertiesIfNeeded(); err != nil {
return nil, nil, err
}
total := 0
for len(buffer)-total > t.maxBufferSize {
b := buffer[total:]
b = b[:t.maxBufferSize]
if err := t.SequenceUpdate(sequenceContext, b, sequenceContextAuthSession, sessions...); err != nil {
return nil, nil, err
}
total += len(b)
}
return t.SequenceComplete(sequenceContext, buffer[total:], hierarchy, sequenceContextAuthSession, sessions...)
}
// EventSequenceExecute executes an event sequence to completion and returns the result by adding the provided data to the sequence
// with a number of TPM2_SequenceUpdate commands appropriate for the size of buffer, and executing a final TPM2_EventSequenceComplete
// command. This command requires authorization with the user auth role for sequenceContext, with session based authorization provided
// via sequenceContextAuthSession.
//
// If pcrContext is not nil, the result will be extended to the corresponding PCR in the same manner as TPMContext.PCRExtend.
// Authorization with the user auth role is required for pcrContext, with session based authorization provided via
// pcrContextAuthSession.
//
// As this function executes multiple commands, any SessionContext instances provided should have the AttrContinueSession attribute
// defined.
//
// If sequenceContext does not correspond to an event sequence object, then a *TPMHandleError error with an error code of ErrorMode
// will be returned for handle index 1 if the command is CommandSequenceUpdate, or handle index 2 if the command is
// CommandEventSequenceComplete.
//
// If pcrContext is not nil and the corresponding PCR can not be extended from the current locality, a *TPMError error with an
// error code of ErrorLocality will be returned.
//
// On success, the sequence object associated with sequenceContext will be evicted, and sequenceContext will become invalid.
func (t *TPMContext) EventSequenceExecute(pcrContext, sequenceContext ResourceContext, buffer []byte, pcrContextAuthSession, sequenceContextAuthSession SessionContext, sessions ...SessionContext) (results TaggedHashList, err error) {
if err := t.initPropertiesIfNeeded(); err != nil {
return nil, err
}
total := 0
for len(buffer)-total > t.maxBufferSize {
b := buffer[total:]
b = b[:t.maxBufferSize]
if err := t.SequenceUpdate(sequenceContext, b, sequenceContextAuthSession, sessions...); err != nil {
return nil, err
}
total += len(b)
}
return t.EventSequenceComplete(pcrContext, sequenceContext, buffer[total:], pcrContextAuthSession, sequenceContextAuthSession, sessions...)
}
./github.com/canonical/go-tpm2/cmds_hierarchy.go 0000664 0000000 0000000 00000036163 00000000000 020143 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 24 - Hierarchy Commands
import (
"fmt"
"github.com/canonical/go-tpm2/mu"
)
// CreatePrimary executes the TPM2_CreatePrimary command to create a new primary object in the hierarchy corresponding to
// primaryObject.
//
// The primaryObject parameter should correspond to a hierarchy. The command requires authorization with the user auth role for
// primaryObject, with session based authorization provided via primaryObjectAuthSession.
//
// A template for the object is provided via the inPublic parameter. The Type field of inPublic defines the algorithm for the object.
// The NameAlg field defines the digest algorithm for computing the name of the object. The Attrs field defines the attributes of
// the object. The AuthPolicy field allows an authorization policy to be defined for the new object.
//
// Data that will form part of the sensitive area of the object can be provided via inSensitive, which is optional.
//
// If the Attrs field of inPublic does not have the AttrSensitiveDataOrigin attribute set, then the sensitive data in the created
// object is initialized with the data provided via the Data field of inSensitive.
//
// If the Attrs field of inPublic has the AttrSensitiveDataOrigin attribute set and Type is ObjectTypeSymCipher, then the sensitive
// data in the created object is initialized with a TPM generated key. The size of this key is determined by the value of the Params
// field of inPublic. If Type is ObjectTypeKeyedHash, then the sensitive data in the created object is initialized with a TPM
// generated value that is the same size as the name algorithm selected by the NameAlg field of inPublic.
//
// If the Type field of inPublic is ObjectTypeRSA or ObjectTypeECC, then the sensitive data in the created object is initialized with
// a TPM generated private key. The size of this is determined by the value of the Params field of inPublic.
//
// If the Type field of inPublic is ObjectTypeKeyedHash and the Attrs field has AttrSensitiveDataOrigin, AttrSign and AttrDecrypt all
// clear, then the created object is a sealed data object.
//
// If the Attrs field of inPublic has the AttrRestricted and AttrDecrypt attributes set, and the Type field is not
// ObjectTypeKeyedHash, then the newly created object will be a storage parent.
//
// If the Attrs field of inPublic has the AttrRestricted and AttrDecrypt attributes set, and the Type field is ObjectTypeKeyedHash,
// then the newly created object will be a derivation parent.
//
// The authorization value for the created object is initialized to the value of the UserAuth field of inSensitive.
//
// If there are no available slots for new objects on the TPM, a *TPMWarning error with a warning code of WarningObjectMemory will
// be returned.
//
// If the attributes in the Attrs field of inPublic are inconsistent or inappropriate for the usage, a *TPMParameterError error with
// an error code of ErrorAttributes will be returned for parameter index 2.
//
// If the NameAlg field of inPublic is HashAlgorithmNull, then a *TPMParameterError error with an error code of ErrorHash will be
// returned for parameter index 2.
//
// If an authorization policy is defined via the AuthPolicy field of inPublic then the length of the digest must match the name
// algorithm selected via the NameAlg field, else a *TPMParameterError error with an error code of ErrorSize is returned for parameter
// index 2.
//
// If the scheme in the Params field of inPublic is inappropriate for the usage, a *TPMParameterError errow with an error code of
// ErrorScheme will be returned for parameter index 2.
//
// If the digest algorithm specified by the scheme in the Params field of inPublic is inappropriate for the usage, a
// *TPMParameterError error with an error code of ErrorHash will be returned for parameter index 2.
//
// If the Type field of inPublic is not ObjectTypeKeyedHash, a *TPMParameterError error with an error code of ErrorSymmetric will be
// returned for parameter index 2 if the symmetric algorithm specified in the Params field of inPublic is inappropriate for the
// usage.
//
// If the Type field of inPublic is ObjectTypeECC and the KDF scheme specified in the Params field of inPublic is not
// KDFAlgorithmNull, a *TPMParameterError error with an error code of ErrorKDF will be returned for parameter index 2.
//
// If the length of the UserAuth field of inSensitive is longer than the name algorithm selected by the NameAlg field of inPublic, a
// *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeRSA and the Params field specifies an unsupported exponent, a *TPMError with an error
// code of ErrorRange will be returned. If the specified key size is an unsupported value, a *TPMError with an error code of
// ErrorValue will be returned.
//
// If the Type field of inPublic is ObjectTypeSymCipher and the key size is an unsupported value, a *TPMError with an error code of
// ErrorKeySize will be returned. If the AttrSensitiveDataOrigin attribute is not set and the length of the Data field of inSensitive
// does not match the key size specified in the Params field of inPublic, a *TPMError with an error code of ErrorKeySize will be
// returned.
//
// If the Type field of inPublic is ObjectTypeKeyedHash and the AttrSensitiveDataOrigin attribute is not set, a *TPMError with an
// error code of ErrorSize will be returned if the length of the Data field of inSensitive is longer than permitted for the digest
// algorithm selected by the specified scheme.
//
// On success, a ResourceContext instance will be returned that corresponds to the newly created object on the TPM. It will not be
// necessary to call ResourceContext.SetAuthValue on it - this function sets the correct authorization value so that it can be used in
// subsequent commands that require knowledge of the authorization value. If the Type field of inPublic is ObjectTypeKeyedHash or
// ObjectTypeSymCipher, then the returned *Public object will have a Unique field that is the digest of the sensitive data and the
// value of the object's seed in the sensitive area, computed using the object's name algorithm. If the Type field of inPublic is
// ObjectTypeECC or ObjectTypeRSA, then the returned *Public object will have a Unique field containing details about the public part
// of the key, computed from the private part of the key.
//
// The returned *CreationData will contain a digest computed from the values of PCRs selected by the creationPCR parameter at creation
// time in the PCRDigest field. It will also contain the provided outsideInfo in the OutsideInfo field. The returned *TkCreation
// ticket can be used to prove the association between the created object and the returned *CreationData via the
// TPMContext.CertifyCreation method.
func (t *TPMContext) CreatePrimary(primaryObject ResourceContext, inSensitive *SensitiveCreate, inPublic *Public, outsideInfo Data, creationPCR PCRSelectionList, primaryObjectAuthSession SessionContext, sessions ...SessionContext) (objectContext ResourceContext, outPublic *Public, creationData *CreationData, creationHash Digest, creationTicket *TkCreation, err error) {
if inSensitive == nil {
inSensitive = &SensitiveCreate{}
}
var objectHandle Handle
var name Name
if err := t.RunCommand(CommandCreatePrimary, sessions,
ResourceContextWithSession{Context: primaryObject, Session: primaryObjectAuthSession}, Delimiter,
mu.Sized(inSensitive), mu.Sized(inPublic), outsideInfo, creationPCR, Delimiter,
&objectHandle, Delimiter,
mu.Sized(&outPublic), mu.Sized(&creationData), &creationHash, &creationTicket, &name); err != nil {
return nil, nil, nil, nil, nil, err
}
if objectHandle.Type() != HandleTypeTransient {
return nil, nil, nil, nil, nil, &InvalidResponseError{CommandCreatePrimary,
fmt.Sprintf("handle 0x%08x returned from TPM is the wrong type", objectHandle)}
}
if outPublic == nil || !outPublic.compareName(name) {
return nil, nil, nil, nil, nil, &InvalidResponseError{CommandCreatePrimary,
"name and public area returned from TPM are not consistent"}
}
var public *Public
if err := mu.CopyValue(&public, outPublic); err != nil {
return nil, nil, nil, nil, nil, &InvalidResponseError{CommandCreatePrimary,
fmt.Sprintf("cannot copy returned public area from TPM: %v", err)}
}
rc := makeObjectContext(objectHandle, name, public)
rc.authValue = make([]byte, len(inSensitive.UserAuth))
copy(rc.authValue, inSensitive.UserAuth)
return rc, outPublic, creationData, creationHash, creationTicket, nil
}
// HierarchyControl executes the TPM2_HierarchyControl command in order to enable or disable the hierarchy associated with the
// enable argument. If state is true, the hierarchy associated with the enable argument will be enabled. If state is false, the
// hierarchy associated with the enable argument will be disabled. This command requires authorization with the user auth role
// for authContext, with session based authorization provided via authContextAuthSession.
//
// If enable is HandlePlatform and state is false, then this will disable use of the platform hierarchy. In this case, authContext
// must correspond to HandlePlatform.
//
// If enable is HandlePlatformNV and state is false, then this will disable the use of NV indices with the AttrNVPlatformCreate
// attribute set, indicating that they were created by the platform owner. In this case, authContext must correspond to
// HandlePlatform.
//
// If enable is HandleOwner and state is false, then this will disable the use of the storage hierarchy and any NV indices with
// the AttrNVPlatformCreate attribute clear. In this case, authContext must correspond to HandleOwner or HandlePlatform.
//
// If enable is HandleEndorsement and state is false, then this will disable the use of the endorsment hierarchy. In this case,
// authContext must correspond to HandleEndorsement or HandlePlatform.
//
// When a hierarchy is disabled, persistent objects associated with it become unavailable, and transient objects associated with it
// are flushed from the TPM.
//
// If state is true, then authContext must correspond to HandlePlatform. Note that the platform hierarchy can't be re-enabled by
// this command.
func (t *TPMContext) HierarchyControl(authContext ResourceContext, enable Handle, state bool, authContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandHierarchyControl, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, Delimiter,
enable, state)
}
// Clear executes the TPM2_Clear command to remove all context associated with the current owner. The command requires knowledge of
// the authorization value for either the platform or lockout hierarchy. The hierarchy is specified by passing a ResourceContext
// corresponding to either HandlePlatform or HandleLockout to authContext. The command requires authorization with the user auth
// role for authContext, with session based authorization provided via authContextAuthSession.
//
// On successful completion, all NV indices and objects associated with the current owner will have been evicted and subsequent use of
// ResourceContext instances associated with these resources will fail. The authorization values of the storage, endorsement and
// lockout hierarchies will have been cleared. It isn't necessary to update the corresponding ResourceContext instances for these
// by calling ResourceContext.SetAuthValue in order to use them in subsequent commands that require knowledge of the authorization
// value for those permanent resources.
//
// If the TPM2_Clear command has been disabled, a *TPMError error will be returned with an error code of ErrorDisabled.
func (t *TPMContext) Clear(authContext ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommandWithResponseCallback(CommandClear, sessions,
func() {
// Clear auth values for the owner, endorsement and lockout hierarchies. If the supplied session is not
// bound to authContext, the TPM will response with a HMAC generated with a key derived from the empty
// auth value.
for _, h := range []Handle{HandleOwner, HandleEndorsement, HandleLockout} {
if rc, exists := t.permanentResources[h]; exists {
rc.SetAuthValue(nil)
}
}
},
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession})
}
// ClearControl executes the TPM2_ClearControl command to enable or disable execution of the TPM2_Clear command (via the
// TPMContext.Clear function).
//
// If disable is true, then this command will disable the execution of TPM2_Clear. In this case, the command requires knowledge of
// the authorization value for the platform or lockout hierarchy. The hierarchy is specified via the authContext parameter by
// passing a ResourceContext corresponding to either HandlePlatform or HandleLockout.
//
// If disable is false, then this command will enable execution of TPM2_Clear. In this case, the command requires knowledge of the
// authorization value for the platform hierarchy, and authContext must be a ResourceContext corresponding to HandlePlatform. If
// authContext is a HandleContext corresponding to HandleOwner, a *TPMError error with an error code of ErrorAuthFail will be
// returned.
//
// The command requires the authorization with the user auth role for authContext, with session based authorization provided via
// authContextAuthSession.
func (t *TPMContext) ClearControl(authContext ResourceContext, disable bool, authContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandClearControl, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, Delimiter,
disable)
}
// HierarchyChangeAuth executes the TPM2_HierarchyChangeAuth command to change the authorization value for the hierarchy associated
// with the authContext parameter. The command requires authorization with the user auth role for authContext, with session based
// authorization provided via authContextAuthSession.
//
// If the value of newAuth is longer than the context integrity digest algorithm for the TPM, a *TPMParameterError error with an
// error code of ErrorSize will be returned.
//
// On successful completion, the authorization value of the hierarchy associated with authContext will be set to the value of
// newAuth, and authContext will be updated to reflect this - it isn't necessary to update authContext with
// ResourceContext.SetAuthValue in order to use it in subsequent commands that require knowledge of the authorization value for the
// resource.
func (t *TPMContext) HierarchyChangeAuth(authContext ResourceContext, newAuth Auth, authContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommandWithResponseCallback(CommandHierarchyChangeAuth, sessions,
func() {
// If the HMAC key for this command includes the auth value for authHandle, the TPM will respond with a HMAC generated with a key
// that includes newAuth instead.
authContext.SetAuthValue(newAuth)
},
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, Delimiter, newAuth)
}
./github.com/canonical/go-tpm2/cmds_nv.go 0000664 0000000 0000000 00000134666 00000000000 016617 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 31 - Non-volatile Storage
import (
"encoding/binary"
"errors"
"fmt"
"github.com/canonical/go-tpm2/mu"
)
// NVDefineSpace executes the TPM2_NV_DefineSpace command to reserve space to hold the data associated with a NV index described by
// the publicInfo parameter. The Index field of publicInfo defines the handle at which the index should be reserved. The NameAlg
// field defines the digest algorithm for computing the name of the NV index. The Attrs field is used to describe attributes for
// the index, as well as its type. An authorization policy for the index can be defined using the AuthPolicy field of publicInfo.
// The Size field defines the size of the index.
//
// The auth parameter specifies an authorization value for the NV index.
//
// The authContext parameter specifies the hierarchy used for authorization, and should correspond to HandlePlatform or HandleOwner.
// The command requires authorization with the user auth role for the specified hierarchy, with session based authorization provided
// via authContextAuthSession.
//
// If the Attrs field of publicInfo has AttrNVPolicyDelete set but TPM2_NV_UndefineSpaceSpecial isn't supported, or the Attrs field
// defines a type that is unsupported, a *TPMParameterError error with an error code of ErrorAttributes will be returned for parameter
// index 2.
//
// If the AuthPolicy field of publicInfo defines an authorization policy digest then the digest length must match the size of the
// name algorithm defined by the NameAlg field of publicInfo, else a *TPMParameterError error with an error code of ErrorSize will
// be returned for parameter index 2.
//
// If the length of auth is greater than the name algorithm selected by the NameAlg field of the publicInfo parameter, a
// *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 1.
//
// If authContext corresponds to HandlePlatform but the AttrPhEnableNV attribute is clear, a *TPMHandleError error with an error code
// of ErrorHierarchy will be returned.
//
// If the type indicated by the Attrs field of publicInfo isn't supported by the TPM, a *TPMParameterError error with an error code of
// ErrorAttributes will be returned for parameter index 2.
//
// If the type defined by publicInfo is NVTypeCounter, NVTypeBits, NVTypePinPass or NVTypePinFail, the Size field of publicInfo must
// be 8. If the type defined by publicInfo is NVTypeExtend, the Size field of publicInfo must match the size of the name algorithm
// defined by the NameAlg field. If the size is unexpected, or the size for an index of type NVTypeOrdinary is too large, a
// *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 2.
//
// If the type defined by publicInfo is NVTypeCounter, then the Attrs field must not have the AttrNVClearStClear attribute set, else
// a *TPMParameterError error with an error code of ErrorAttributes will be returned for parameter index 2.
//
// If the type defined by publicInfo is NVTypePinFail, then the Attrs field must have the AttrNVNoDA attribute set. If the type is
// either NVTypePinPass or NVTypePinFail, then the Attrs field must have the AttrNVAuthWrite, AttrNVGlobalLock and AttrNVWriteDefine
// attributes clear, else a *TPMParameterError error with an error code of ErrorAttributes will be returned for parameter index 2.
//
// If the Attrs field of publicInfo has either AttrNVWriteLocked, AttrNVReadLocked or AttrNVWritten set, a *TPMParameterError error
// with an error code of ErrorAttributes will be returned for parameter index 2.
//
// The Attrs field of publicInfo must have one of either AttrNVPPWrite, AttrNVOwnerWrite, AttrNVAuthWrite or AttrNVPolicyWrite set,
// and must also have one of either AttrNVPPRead, AttrNVOwnerRead, AttrNVAuthRead or AttrNVPolicyRead set. If there is no way to read
// or write an index, a *TPMParameterError error with an error code of ErrorAttributes will be returned for parameter index 2.
//
// If the Attrs field of publicInfo has AttrNVClearStClear set, a *TPMParameterError error with an error code of ErrorAttributes will
// be returned for parameter index 2 if AttrNVWriteDefine is set.
//
// If authContext corresponds to HandlePlatform, then the Attrs field of publicInfo must have the AttrNVPlatformCreate attribute set.
// If authContext corresponds to HandleOwner, then the AttrNVPlatformCreate attributes must be clear, else a *TPMHandleError error
// with an error code of ErrorAttributes will be returned.
//
// If the Attrs field of publicInfo has the AttrNVPolicyDelete attribute set, then HandlePlatform must be used for authorization via
// authContext, else a *TPMParameterError error with an error code of ErrorAttributes will be returned for parameter index 2.
//
// If an index is already defined at the location specified by the Index field of publicInfo, a *TPMError error with an error code of
// ErrorNVDefined will be returned.
//
// If there is insufficient space for the index, a *TPMError error with an error code of ErrorNVSpace will be returned.
//
// On successful completion, the NV index will be defined and a ResourceContext corresponding to the new NV index will be returned.
// It will not be necessary to call ResourceContext.SetAuthValue on the returned ResourceContext - this function sets the correct
// authorization value so that it can be used in subsequent commands that require knowledge of it.
func (t *TPMContext) NVDefineSpace(authContext ResourceContext, auth Auth, publicInfo *NVPublic, authContextAuthSession SessionContext, sessions ...SessionContext) (ResourceContext, error) {
if publicInfo == nil {
return nil, makeInvalidArgError("publicInfo", "nil value")
}
name, err := publicInfo.Name()
if err != nil {
return nil, fmt.Errorf("cannot compute name from public info: %v", err)
}
if err := t.RunCommand(CommandNVDefineSpace, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, Delimiter,
auth, mu.Sized(publicInfo)); err != nil {
return nil, err
}
var public *NVPublic
// publicInfo already marshalled correctly, so this can't fail.
mu.MustCopyValue(&public, publicInfo)
rc := makeNVIndexContext(name, public)
rc.authValue = make([]byte, len(auth))
copy(rc.authValue, auth)
return rc, nil
}
// NVUndefineSpace executes the TPM2_NV_UndefineSpace command to remove the NV index associated with nvIndex, and free the resources
// used by it. If the index has the AttrNVPolicyDelete attribute set, then a *TPMHandleError error with an error code of
// ErrorAttributes will be returned for handle index 2.
//
// The authContext parameter specifies the hierarchy used for authorization and should correspond to either HandlePlatform or
// HandleOwner. The command requires authorization with the user auth role for the specified hierarchy, with session based
// authorization provided via authContextAuthSession.
//
// If authContext corresponds to HandleOwner and the NV index has the AttrNVPlatformCreate attribute set, then a *TPMError error with
// an error code of ErrorNVAuthorization will be returned.
//
// On successful completion, nvIndex will be invalidated.
func (t *TPMContext) NVUndefineSpace(authContext, nvIndex ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) error {
if err := t.RunCommand(CommandNVUndefineSpace, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, nvIndex); err != nil {
return err
}
nvIndex.(handleContextPrivate).invalidate()
return nil
}
// NVUndefineSpaceSpecial executes the TPM2_NV_UndefineSpaceSpecial command to remove the NV index associated with nvIndex, and free
// the resources used by it. If the NV index does not have the AttrNVPolicyDelete attribute set, then a *TPMHandleError error with an
// error code of ErrorAttributes will be returned for handle index 1.
//
// The platform parameter must correspond to HandlePlatform. The command requires authorization with the user auth role for the
// platform hierarchy, with session based authorization provided via platformAuthSession. The command requires authorization with the
// admin role for nvIndex, with the session provided via nvIndexAuthSession.
//
// On successful completion, nvIndex will be invalidated.
func (t *TPMContext) NVUndefineSpaceSpecial(nvIndex, platform ResourceContext, nvIndexAuthSession, platformAuthSession SessionContext, sessions ...SessionContext) error {
if err := t.RunCommandWithResponseCallback(CommandNVUndefineSpaceSpecial, sessions,
func() {
// If the HMAC key for this command includes the authorization value for nvIndex (eg, because the PolicyAuthValue assertion was
// executed), the TPM will respond with a HMAC generated with a key based on an empty auth value.
nvIndex.SetAuthValue(nil)
},
ResourceContextWithSession{Context: nvIndex, Session: nvIndexAuthSession},
ResourceContextWithSession{Context: platform, Session: platformAuthSession}); err != nil {
return err
}
nvIndex.(handleContextPrivate).invalidate()
return nil
}
// NVReadPublic executes the TPM2_NV_ReadPublic command to read the public area of the NV index associated with nvIndex.
func (t *TPMContext) NVReadPublic(nvIndex HandleContext, sessions ...SessionContext) (nvPublic *NVPublic, nvName Name, err error) {
if err := t.RunCommand(CommandNVReadPublic, sessions,
nvIndex, Delimiter,
Delimiter,
Delimiter,
mu.Sized(&nvPublic), &nvName); err != nil {
return nil, nil, err
}
return nvPublic, nvName, nil
}
// NVWriteRaw executes the TPM2_NV_Write command to write data to the NV index associated with nvIndex, at the specified offset.
//
// The command requires authorization, defined by the state of the AttrNVPPWrite, AttrNVOwnerWrite, AttrNVAuthWrite and
// AttrNVPolicyWrite attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPWrite
// attribute, authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerWrite attribute, authorization
// can be satisfied with HandleOwner. If the NV index has the AttrNVAuthWrite or AttrNVPolicyWrite attribute, authorization can be
// satisfied with nvIndex. The command requires authorization with the user auth role for authContext, with session based
// authorization provided via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this
// access, a *TPMError error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthWrite attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyWrite attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has the AttrNVWriteLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the type of the index is NVTypeCounter, NVTypeBits or NVTypeExtend, a *TPMError error with an error code fo ErrorAttributes
// will be returned.
//
// If the value of offset is outside of the bounds of the index, a *TPMParameterError error with an error code of ErrorValue will be
// returned for parameter index 2.
//
// If the length of the data and the specified offset would result in a write outside of the bounds of the index, or if the index
// has the AttrNVWriteAll attribute set and the size of the data doesn't match the size of the index, a *TPMError error with an error
// code of ErrorNVRange will be returned.
//
// On successful completion, the AttrNVWritten flag will be set if this is the first time that the index has been written to.
func (t *TPMContext) NVWriteRaw(authContext, nvIndex ResourceContext, data MaxNVBuffer, offset uint16, authContextAuthSession SessionContext, sessions ...SessionContext) error {
if err := t.RunCommand(CommandNVWrite, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, nvIndex, Delimiter,
data, offset); err != nil {
return err
}
nvIndex.(*nvIndexContext).SetAttr(AttrNVWritten)
return nil
}
// NVWrite executes the TPM2_NV_Write command to write data to the NV index associated with nvIndex, at the specified offset.
//
// The command requires authorization, defined by the state of the AttrNVPPWrite, AttrNVOwnerWrite, AttrNVAuthWrite and
// AttrNVPolicyWrite attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPWrite
// attribute, authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerWrite attribute, authorization
// can be satisfied with HandleOwner. If the NV index has the AttrNVAuthWrite or AttrNVPolicyWrite attribute, authorization can be
// satisfied with nvIndex. The command requires authorization with the user auth role for authContext, with session based
// authorization provided via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this
// access, a *TPMError error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthWrite attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyWrite attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If data is too large to be written in a single command, this function will re-execute the TPM2_NV_Write command until all data is
// written. In this case, any SessionContext instances provided must have the AttrContinueSession attribute defined and
// authContextAuthSession must not be a policy session.
//
// If the index has the AttrNVWriteLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the type of the index is NVTypeCounter, NVTypeBits or NVTypeExtend, a *TPMError error with an error code fo ErrorAttributes
// will be returned.
//
// If the value of offset is outside of the bounds of the index, a *TPMParameterError error with an error code of ErrorValue will be
// returned for parameter index 2.
//
// If the length of the data and the specified offset would result in a write outside of the bounds of the index, or if the index
// has the AttrNVWriteAll attribute set and the size of the data doesn't match the size of the index, a *TPMError error with an error
// code of ErrorNVRange will be returned.
//
// On successful completion, the AttrNVWritten flag will be set if this is the first time that the index has been written to.
func (t *TPMContext) NVWrite(authContext, nvIndex ResourceContext, data []byte, offset uint16, authContextAuthSession SessionContext, sessions ...SessionContext) error {
if err := t.initPropertiesIfNeeded(); err != nil {
return err
}
if len(data) > t.maxNVBufferSize {
if authContextAuthSession != nil {
sessionPrivate := authContextAuthSession.(*sessionContext)
if sessionPrivate.attrs&AttrContinueSession == 0 {
return makeInvalidArgError("authContextAuthSession",
fmt.Sprintf("the AttrContinueSession attribute is required for authorization sessions for writes larger than %d bytes", t.maxNVBufferSize))
}
sessionData := sessionPrivate.Data()
if sessionData == nil {
return makeInvalidArgError("authContextAuthSession", "unusable session context")
}
if sessionData.SessionType == SessionTypePolicy {
return makeInvalidArgError("authContextAuthSession",
fmt.Sprintf("a policy authorization session cannot be used for writes larger than %d bytes", t.maxNVBufferSize))
}
}
for i, s := range sessions {
if s.(*sessionContext).attrs&AttrContinueSession == 0 {
return makeInvalidArgError("sessions",
fmt.Sprintf("the AttrContinueSession attribute is required for session at index %d for writes larger than %d bytes", i, t.maxNVBufferSize))
}
}
}
total := 0
for {
d := data[total:]
if len(d) > t.maxNVBufferSize {
d = d[:t.maxNVBufferSize]
}
if err := t.NVWriteRaw(authContext, nvIndex, d, offset+uint16(total), authContextAuthSession, sessions...); err != nil {
return err
}
total += len(d)
if len(data)-total == 0 {
break
}
}
return nil
}
// NVSetPinCounterParams is a convenience function for NVWrite for updating the contents of the NV pin pass or NV pin fail index associated
// with nvIndex. If the type of nvIndex is not NVTypePinPass of NVTypePinFail, an error will be returned.
//
// The command requires authorization, defined by the state of the AttrNVPPWrite, AttrNVOwnerWrite, AttrNVAuthWrite and
// AttrNVPolicyWrite attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPWrite
// attribute, authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerWrite attribute, authorization
// can be satisfied with HandleOwner. If the NV index has the AttrNVAuthWrite or AttrNVPolicyWrite attribute, authorization can be
// satisfied with nvIndex. The command requires authorization with the user auth role for authContext, with session based
// authorization provided via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this
// access, a *TPMError error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthWrite attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyWrite attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has the AttrNVWriteLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// On successful completion, the AttrNVWritten flag will be set if this is the first time that the index has been written to.
func (t *TPMContext) NVSetPinCounterParams(authContext, nvIndex ResourceContext, params *NVPinCounterParams, authContextAuthSession SessionContext, sessions ...SessionContext) error {
context, isNv := nvIndex.(*nvIndexContext)
if !isNv {
return errors.New("nvIndex does not correspond to a NV index")
}
if context.Attrs().Type() != NVTypePinPass && context.Attrs().Type() != NVTypePinFail {
return errors.New("nvIndex does not correspond to a PIN pass or PIN fail index")
}
data := mu.MustMarshalToBytes(params)
return t.NVWrite(authContext, nvIndex, data, 0, authContextAuthSession, sessions...)
}
// NVIncrement executes the TPM2_NV_Increment command to increment the counter associated with nvIndex.
//
// The command requires authorization, defined by the state of the AttrNVPPWrite, AttrNVOwnerWrite, AttrNVAuthWrite and
// AttrNVPolicyWrite attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPWrite
// attribute, authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerWrite attribute, authorization
// can be satisfied with HandleOwner. If the NV index has the AttrNVAuthWrite or AttrNVPolicyWrite attribute, authorization can be
// satisfied with nvIndex. The command requires authorization with the user auth role for authContext, with session based
// authorization provided via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this
// access, a *TPMError error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthWrite attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyWrite attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has the AttrNVWriteLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the type of the index is not NVTypeCounter, a *TPMHandleError error with an error code of ErrorAttributes will be returned for
// handle index 2.
//
// On successful completion, the AttrNVWritten flag will be set if this is the first time that the index has been written to.
func (t *TPMContext) NVIncrement(authContext, nvIndex ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) error {
if err := t.RunCommand(CommandNVIncrement, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, nvIndex); err != nil {
return err
}
nvIndex.(*nvIndexContext).SetAttr(AttrNVWritten)
return nil
}
// NVExtend executes the TPM2_NV_Extend command to extend data to the NV index associated with nvIndex, using the index's name
// algorithm.
//
// The command requires authorization, defined by the state of the AttrNVPPWrite, AttrNVOwnerWrite, AttrNVAuthWrite and
// AttrNVPolicyWrite attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPWrite
// attribute, authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerWrite attribute, authorization
// can be satisfied with HandleOwner. If the NV index has the AttrNVAuthWrite or AttrNVPolicyWrite attribute, authorization can be
// satisfied with nvIndex. The command requires authorization with the user auth role for authContext, with session based
// authorization provided via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this
// access, a *TPMError error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthWrite attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyWrite attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has the AttrNVWriteLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the type of the index is not NVTypeExtend, a *TPMHandleError error with an error code of ErrorAttributes will be returned for
// handle index 2.
//
// On successful completion, the AttrNVWritten flag will be set if this is the first time that the index has been written to.
func (t *TPMContext) NVExtend(authContext, nvIndex ResourceContext, data MaxNVBuffer, authContextAuthSession SessionContext, sessions ...SessionContext) error {
if err := t.RunCommand(CommandNVExtend, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, nvIndex, Delimiter,
data); err != nil {
return err
}
nvIndex.(*nvIndexContext).SetAttr(AttrNVWritten)
return nil
}
// NVSetBits executes the TPM2_NV_SetBits command to OR the value of bits with the contents of the NV index associated with nvIndex.
//
// The command requires authorization, defined by the state of the AttrNVPPWrite, AttrNVOwnerWrite, AttrNVAuthWrite and
// AttrNVPolicyWrite attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPWrite
// attribute, authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerWrite attribute, authorization
// can be satisfied with HandleOwner. If the NV index has the AttrNVAuthWrite or AttrNVPolicyWrite attribute, authorization can be
// satisfied with nvIndex. The command requires authorization with the user auth role for authContext, with session based
// authorization provided via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this
// access, a *TPMError error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthWrite attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyWrite attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has the AttrNVWriteLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the type of the index is not NVTypeBits, a *TPMHandleError error with an error code of ErrorAttributes will be returned for
// handle index 2.
//
// On successful completion, the AttrNVWritten flag will be set if this is the first time that the index has been written to.
func (t *TPMContext) NVSetBits(authContext, nvIndex ResourceContext, bits uint64, authContextAuthSession SessionContext, sessions ...SessionContext) error {
if err := t.RunCommand(CommandNVSetBits, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, nvIndex, Delimiter,
bits); err != nil {
return err
}
nvIndex.(*nvIndexContext).SetAttr(AttrNVWritten)
return nil
}
// NVWriteLock executes the TPM2_NV_WriteLock command to inhibit further writes to the NV index associated with nvIndex.
//
// The command requires authorization, defined by the state of the AttrNVPPWrite, AttrNVOwnerWrite, AttrNVAuthWrite and
// AttrNVPolicyWrite attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPWrite
// attribute, authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerWrite attribute, authorization
// can be satisfied with HandleOwner. If the NV index has the AttrNVAuthWrite or AttrNVPolicyWrite attribute, authorization can be
// satisfied with nvIndex. The command requires authorization with the user auth role for authContext, with session based
// authorization provided via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this
// command, a *TPMError error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthWrite attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyWrite attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has neither the AttrNVWriteDefine or AttrNVWriteStClear attributes set, then a *TPMHandleError error with an error
// code of ErrorAttributes will be returned for handle index 2.
//
// On successful completion, the AttrNVWriteLocked attribute will be set. It will be cleared again (and writes will be reenabled) on
// the next TPM reset or TPM restart unless the index has the AttrNVWriteDefine attribute set and AttrNVWritten attribute is set.
func (t *TPMContext) NVWriteLock(authContext, nvIndex ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) error {
if err := t.RunCommand(CommandNVWriteLock, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, nvIndex); err != nil {
return err
}
nvIndex.(*nvIndexContext).SetAttr(AttrNVWriteLocked)
return nil
}
// NVGlobalWriteLock executes the TPM2_NV_GlobalWriteLock command to inhibit further writes for all NV indexes that have the
// AttrNVGlobalLock attribute set.
//
// The authContext parameter specifies a hierarchy, and should correspond to either HandlePlatform or HandleOwner. The command
// requires the user auth role for authContext, with session based authorization provided via authContextAuthSession.
//
// On successful completion, the AttrNVWriteLocked attribute will be set for all NV indexes that have the AttrNVGlobalLock attribute
// set. If an index also has the AttrNVWriteDefine attribute set, this will permanently inhibit further writes unless AttrNVWritten
// is clear. ResourceContext instances associated with NV indices that are updated as a consequence of this function will no longer
// be able to be used because the name will be incorrect.
func (t *TPMContext) NVGlobalWriteLock(authContext ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandNVGlobalWriteLock, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession})
}
// NVReadRaw executes the TPM2_NV_Read command to read the contents of the NV index associated with nvIndex. The amount of data to read,
// and the offset within the index are defined by the size and offset parameters.
//
// The command requires authorization, defined by the state of the AttrNVPPRead, AttrNVOwnerRead, AttrNVAuthRead and AttrNVPolicyRead
// attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPRead attribute,
// authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerRead attribute, authorization can be
// satisfied with HandleOwner. If the NV index has the AttrNVAuthRead or AttrNVPolicyRead attribute, authorization can be satisfied
// with nvIndex. The command requires authorization with the user auth role for authContext, with session based authorization provided
// via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this access, a *TPMError
// error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthRead attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyRead attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has the AttrNVReadLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the index has not been initialized (ie, the AttrNVWritten attribute is not set), a *TPMError error with an error code of
// ErrorNVUninitialized will be returned.
//
// If the value of size is too large, a *TPMParameterError error with an error code of ErrorValue will be returned for parameter
// index 1.
//
// If the value of offset falls outside of the bounds of the index, a *TPMParameterError error with an error code of ErrorValue will
// be returned for parameter index 2.
//
// If the data selection falls outside of the bounds of the index, a *TPMError error with an error code of ErrorNVRange will be
// returned.
//
// On successful completion, the requested data will be returned.
func (t *TPMContext) NVReadRaw(authContext, nvIndex ResourceContext, size, offset uint16, authContextAuthSession SessionContext, sessions ...SessionContext) (data MaxNVBuffer, err error) {
if err := t.RunCommand(CommandNVRead, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, nvIndex, Delimiter,
size, offset, Delimiter,
Delimiter,
&data); err != nil {
return nil, err
}
return data, nil
}
// NVRead executes the TPM2_NV_Read command to read the contents of the NV index associated with nvIndex. The amount of data to read,
// and the offset within the index are defined by the size and offset parameters.
//
// The command requires authorization, defined by the state of the AttrNVPPRead, AttrNVOwnerRead, AttrNVAuthRead and AttrNVPolicyRead
// attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPRead attribute,
// authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerRead attribute, authorization can be
// satisfied with HandleOwner. If the NV index has the AttrNVAuthRead or AttrNVPolicyRead attribute, authorization can be satisfied
// with nvIndex. The command requires authorization with the user auth role for authContext, with session based authorization provided
// via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this access, a *TPMError
// error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthRead attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyRead attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the requested data can not be read in a single command, this function will re-execute the TPM2_NV_Read command until all data
// is read. In this case, any SessionContext instances provided should have the AttrContinueSession attribute defined and
// authContextAuth should not correspond to a policy session.
//
// If the index has the AttrNVReadLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the index has not been initialized (ie, the AttrNVWritten attribute is not set), a *TPMError error with an error code of
// ErrorNVUninitialized will be returned.
//
// If the value of size is too large, a *TPMParameterError error with an error code of ErrorValue will be returned for parameter
// index 1.
//
// If the value of offset falls outside of the bounds of the index, a *TPMParameterError error with an error code of ErrorValue will
// be returned for parameter index 2.
//
// If the data selection falls outside of the bounds of the index, a *TPMError error with an error code of ErrorNVRange will be
// returned.
//
// On successful completion, the requested data will be returned.
func (t *TPMContext) NVRead(authContext, nvIndex ResourceContext, size, offset uint16, authContextAuthSession SessionContext, sessions ...SessionContext) (data []byte, err error) {
if err := t.initPropertiesIfNeeded(); err != nil {
return nil, err
}
data = make([]byte, size)
total := 0
remaining := size
for {
sz := remaining
if remaining > uint16(t.maxNVBufferSize) {
sz = uint16(t.maxNVBufferSize)
}
tmpData, err := t.NVReadRaw(authContext, nvIndex, sz, offset+uint16(total), authContextAuthSession, sessions...)
if err != nil {
return nil, err
}
copy(data[total:], tmpData)
total += int(sz)
remaining -= sz
if remaining == 0 {
break
}
}
return data, nil
}
func (t *TPMContext) nvReadUint64(authContext, nvIndex ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) (uint64, error) {
data, err := t.NVRead(authContext, nvIndex, 8, 0, authContextAuthSession, sessions...)
if err != nil {
return 0, err
}
if len(data) != binary.Size(uint64(0)) {
return 0, &InvalidResponseError{CommandNVRead, fmt.Sprintf("unexpected number of bytes returned (got %d)", len(data))}
}
return binary.BigEndian.Uint64(data), nil
}
// NVReadBits is a convenience function for NVRead for reading the contents of the NV bit field index associated with nvIndex. If
// the type of nvIndex is not NVTypeBits, an error will be returned.
//
// The command requires authorization, defined by the state of the AttrNVPPRead, AttrNVOwnerRead, AttrNVAuthRead and AttrNVPolicyRead
// attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPRead attribute,
// authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerRead attribute, authorization can be
// satisfied with HandleOwner. If the NV index has the AttrNVAuthRead or AttrNVPolicyRead attribute, authorization can be satisfied
// with nvIndex. The command requires authorization with the user auth role for authContext, with session based authorization provided
// via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this access, a *TPMError
// error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthRead attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyRead attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has the AttrNVReadLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the index has not been initialized (ie, the AttrNVWritten attribute is not set), a *TPMError error with an error code of
// ErrorNVUninitialized will be returned.
//
// On successful completion, the current bitfield value will be returned.
func (t *TPMContext) NVReadBits(authContext, nvIndex ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) (uint64, error) {
context, isNv := nvIndex.(*nvIndexContext)
if !isNv {
return 0, errors.New("nvIndex does not correspond to a NV index")
}
if context.Attrs().Type() != NVTypeBits {
return 0, errors.New("nvIndex does not correspond to a bit field")
}
return t.nvReadUint64(authContext, nvIndex, authContextAuthSession, sessions...)
}
// NVReadCounter is a convenience function for NVRead for reading the contents of the NV counter index associated with nvIndex. If the
// type of nvIndex is not NVTypeCounter, an error will be returned.
//
// The command requires authorization, defined by the state of the AttrNVPPRead, AttrNVOwnerRead, AttrNVAuthRead and AttrNVPolicyRead
// attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPRead attribute,
// authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerRead attribute, authorization can be
// satisfied with HandleOwner. If the NV index has the AttrNVAuthRead or AttrNVPolicyRead attribute, authorization can be satisfied
// with nvIndex. The command requires authorization with the user auth role for authContext, with session based authorization provided
// via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this access, a *TPMError
// error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthRead attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyRead attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has the AttrNVReadLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the index has not been initialized (ie, the AttrNVWritten attribute is not set), a *TPMError error with an error code of
// ErrorNVUninitialized will be returned.
//
// On successful completion, the current counter value will be returned.
func (t *TPMContext) NVReadCounter(authContext, nvIndex ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) (uint64, error) {
context, isNv := nvIndex.(*nvIndexContext)
if !isNv {
return 0, errors.New("nvIndex does not correspond to a NV index")
}
if context.Attrs().Type() != NVTypeCounter {
return 0, errors.New("nvIndex does not correspond to a counter")
}
return t.nvReadUint64(authContext, nvIndex, authContextAuthSession, sessions...)
}
// NVReadPinCounterParams is a convenienc function for NVRead for reading the contents of the NV pin pass or NV pin fail index associated
// with nvIndex. If the type of nvIndex is not NVTypePinPass of NVTypePinFail, an error will be returned.
//
// The command requires authorization, defined by the state of the AttrNVPPRead, AttrNVOwnerRead, AttrNVAuthRead and AttrNVPolicyRead
// attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPRead attribute,
// authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerRead attribute, authorization can be
// satisfied with HandleOwner. If the NV index has the AttrNVAuthRead or AttrNVPolicyRead attribute, authorization can be satisfied
// with nvIndex. The command requires authorization with the user auth role for authContext, with session based authorization provided
// via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this access, a *TPMError
// error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthRead attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyRead attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index has the AttrNVReadLocked attribute set, a *TPMError error with an error code of ErrorNVLocked will be returned.
//
// If the index has not been initialized (ie, the AttrNVWritten attribute is not set), a *TPMError error with an error code of
// ErrorNVUninitialized will be returned.
//
// On successful completion, the current PIN count and limit will be returned.
func (t *TPMContext) NVReadPinCounterParams(authContext, nvIndex ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) (*NVPinCounterParams, error) {
context, isNv := nvIndex.(*nvIndexContext)
if !isNv {
return nil, errors.New("nvIndex does not correspond to a NV index")
}
if context.Attrs().Type() != NVTypePinPass && context.Attrs().Type() != NVTypePinFail {
return nil, errors.New("nvIndex does not correspond to a PIN pass or PIN fail index")
}
data, err := t.NVRead(authContext, nvIndex, 8, 0, authContextAuthSession, sessions...)
if err != nil {
return nil, err
}
var res NVPinCounterParams
if _, err := mu.UnmarshalFromBytes(data, &res); err != nil {
return nil, &InvalidResponseError{CommandNVRead, fmt.Sprintf("cannot unmarshal response bytes: %v", err)}
}
return &res, nil
}
// NVReadLock executes the TPM2_NV_ReadLock command to inhibit further reads of the NV index associated with nvIndex.
//
// The command requires authorization, defined by the state of the AttrNVPPRead, AttrNVOwnerRead, AttrNVAuthRead and AttrNVPolicyRead
// attributes. The handle used for authorization is specified via authContext. If the NV index has the AttrNVPPRead attribute,
// authorization can be satisfied with HandlePlatform. If the NV index has the AttrNVOwnerRead attribute, authorization can be
// satisfied with HandleOwner. If the NV index has the AttrNVAuthRead or AttrNVPolicyRead attribute, authorization can be satisfied
// with nvIndex. The command requires authorization with the user auth role for authContext, with session based authorization provided
// via authContextAuthSession. If the resource associated with authContext is not permitted to authorize this access, a *TPMError
// error with an error code of ErrorNVAuthorization will be returned.
//
// If nvIndex is being used for authorization and the AttrNVAuthRead attribute is defined, the authorization can be satisfied by
// demonstrating knowledge of the authorization value, either via cleartext or HMAC authorization. If nvIndex is being used for
// authorization and the AttrNVPolicyRead attribute is defined, the authorization can be satisfied using a policy session with a
// digest that matches the authorization policy for the index.
//
// If the index doesn't have the AttrNVReadStClear attribute set, then a *TPMHandleError error with an error code of ErrorAttributes
// will be returned for handle index 2.
//
// On successful completion, the AttrNVReadLocked attribute will be set. It will be cleared again (and reads will be reenabled) on
// the next TPM reset or TPM restart.
func (t *TPMContext) NVReadLock(authContext, nvIndex ResourceContext, authContextAuthSession SessionContext, sessions ...SessionContext) error {
if err := t.RunCommand(CommandNVReadLock, sessions,
ResourceContextWithSession{Context: authContext, Session: authContextAuthSession}, nvIndex); err != nil {
return err
}
nvIndex.(*nvIndexContext).SetAttr(AttrNVReadLocked)
return nil
}
// NVChangeAuth executes the TPM2_NV_ChangeAuth command to change the authorization value for the NV index associated with nvIndex,
// setting it to the new value defined by newAuth. The command requires the admin auth role for nvIndex, with the session provided
// via nvIndexAuthSession.
//
// If the size of newAuth is greater than the name algorithm for the index, a *TPMParameterError error with an error code of ErrorSize
// will be returned.
//
// On successful completion, the authorization value of the NV index associated with nvIndex will be set to the value of newAuth,
// and nvIndex will be updated to reflect this - it isn't necessary to update nvIndex with ResourceContext.SetAuthValue in order to
// use it in authorization roles that require knowledge of the authorization value for the index.
func (t *TPMContext) NVChangeAuth(nvIndex ResourceContext, newAuth Auth, nvIndexAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommandWithResponseCallback(CommandNVChangeAuth, sessions,
func() {
// If the session is not bound to nvIndex, the TPM will respond with a HMAC generated with a key derived from newAuth. If the
// session is bound, the TPM will respond with a HMAC generated from the original key
nvIndex.SetAuthValue(newAuth)
},
ResourceContextWithSession{Context: nvIndex, Session: nvIndexAuthSession}, Delimiter,
newAuth)
}
// func (t *TPMContext) NVCertify(signContext, authContext, nvIndex HandleContext, qualifyingData Data,
// inScheme *SigScheme, size, offset uint16, signContextAuth, authContextAuth interface{},
// sessions ...SessionContext) (*Attest, *Signature, error) {
// }
./github.com/canonical/go-tpm2/cmds_object.go 0000664 0000000 0000000 00000117621 00000000000 017432 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 12 - Object Commands
import (
"fmt"
"github.com/canonical/go-tpm2/mu"
)
// Create executes the TPM2_Create command to create a new ordinary object as a child of the storage parent associated with
// parentContext.
//
// The command requires authorization with the user auth role for parentContext, with session based authorization provided via
// parentContextAuthSession.
//
// A template for the object is provided via the inPublic parameter. The Type field of inPublic defines the algorithm for the object.
// The NameAlg field defines the digest algorithm for computing the name of the object. The Attrs field defines the attributes of
// the object. The AuthPolicy field allows an authorization policy to be defined for the new object.
//
// Data that will form part of the sensitive area of the object can be provided via inSensitive, which is optional.
//
// If the Attrs field of inPublic does not have the AttrSensitiveDataOrigin attribute set, then the sensitive data in the created
// object is initialized with the data provided via the Data field of inSensitive.
//
// If the Attrs field of inPublic has the AttrSensitiveDataOrigin attribute set and Type is ObjectTypeSymCipher, then the sensitive
// data in the created object is initialized with a TPM generated key. The size of this key is determined by the value of the Params
// field of inPublic. If Type is ObjectTypeKeyedHash, then the sensitive data in the created object is initialized with a TPM
// generated value that is the same size as the name algorithm selected by the NameAlg field of inPublic.
//
// If the Type field of inPublic is ObjectTypeRSA or ObjectTypeECC, then the sensitive data in the created object is initialized with
// a TPM generated private key. The size of this is determined by the value of the Params field of inPublic.
//
// If the Type field of inPublic is ObjectTypeKeyedHash and the Attrs field has AttrSensitiveDataOrigin, AttrSign and AttrDecrypt all
// clear, then the created object is a sealed data object.
//
// If the Attrs field of inPublic has the AttrRestricted and AttrDecrypt attributes set, and the Type field is not
// ObjectTypeKeyedHash, then the newly created object will be a storage parent.
//
// If the Attrs field of inPublic has the AttrRestricted and AttrDecrypt attributes set, and the Type field is ObjectTypeKeyedHash,
// then the newly created object will be a derivation parent.
//
// The authorization value for the created object is initialized to the value of the UserAuth field of inSensitive.
//
// If the object associated with parentContext is not a valid storage parent object, a *TPMHandleError error with an error code of
// ErrorType will be returned for handle index 1.
//
// If there are no available slots for new objects on the TPM, a *TPMWarning error with a warning code of WarningObjectMemory will
// be returned.
//
// If the Attrs field of inPublic as the AttrSensitiveDataOrigin attribute set and the Data field of inSensitive has a non-zero size,
// or the AttrSensitiveDataOrigin attribute is clear and the Data field of inSensitive has a zero size, a *TPMParameterError error
// with an error code of ErrorAttributes will be returned for parameter index 1.
//
// If the attributes in the Attrs field of inPublic are inconsistent or inappropriate for the usage, a *TPMParameterError error with
// an error code of ErrorAttributes will be returned for parameter index 2.
//
// If the NameAlg field of inPublic is HashAlgorithmNull, then a *TPMParameterError error with an error code of ErrorHash will be
// returned for parameter index 2.
//
// If an authorization policy is defined via the AuthPolicy field of inPublic then the length of the digest must match the name
// algorithm selected via the NameAlg field, else a *TPMParameterError error with an error code of ErrorSize is returned for parameter
// index 2.
//
// If the scheme in the Params field of inPublic is inappropriate for the usage, a *TPMParameterError errow with an error code of
// ErrorScheme will be returned for parameter index 2.
//
// If the digest algorithm specified by the scheme in the Params field of inPublic is inappropriate for the usage, a
// *TPMParameterError error with an error code of ErrorHash will be returned for parameter index 2.
//
// If the Type field of inPublic is not ObjectTypeKeyedHash, a *TPMParameterError error with an error code of ErrorSymmetric will be
// returned for parameter index 2 if the symmetric algorithm specified in the Params field of inPublic is inappropriate for the
// usage.
//
// If the Type field of inPublic is ObjectTypeECC and the KDF scheme specified in the Params field of inPublic is not
// KDFAlgorithmNull, a *TPMParameterError error with an error code of ErrorKDF will be returned for parameter index 2.
//
// If the Type field of inPublic is not ObjectTypeKeyedHash and the AttrRestricted, AttrFixedParent and AttrDecrypt attributes of
// Attrs are set, a *TPMParameterError error with an error code of ErrorHash will be returned for parameter index 2 if the NameAlg
// field of inPublic does not select the same name algorithm as the parent object. A *TPMParameterError error with an error code
// of ErrorSymmetric will be returned for parameter index 2 if the symmetric algorithm specified in the Params field of inPublic
// does not match the symmetric algorithm of the parent object.
//
// If the length of the UserAuth field of inSensitive is longer than the name algorithm selected by the NameAlg field of inPublic, a
// *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeRSA and the Params field specifies an unsupported exponent, a *TPMError with an error
// code of ErrorRange will be returned. If the specified key size is an unsupported value, a *TPMError with an error code of
// ErrorValue will be returned.
//
// If the Type field of inPublic is ObjectTypeSymCipher and the key size is an unsupported value, a *TPMError with an error code of
// ErrorKeySize will be returned. If the AttrSensitiveDataOrigin attribute is not set and the length of the Data field of inSensitive
// does not match the key size specified in the Params field of inPublic, a *TPMError with an error code of ErrorKeySize will be
// returned.
//
// If the Type field of inPublic is ObjectTypeKeyedHash and the AttrSensitiveDataOrigin attribute is not set, a *TPMError with an
// error code of ErrorSize will be returned if the length of the Data field of inSensitive is longer than permitted for the digest
// algorithm selected by the specified scheme.
//
// On success, the private and public parts of the newly created object will be returned. The newly created object will not exist on
// the TPM. If the Type field of inPublic is ObjectTypeKeyedHash or ObjectTypeSymCipher, then the returned *Public object will have a
// Unique field that is the digest of the sensitive data and the value of the object's seed in the sensitive area, computed using the
// object's name algorithm. If the Type field of inPublic is ObjectTypeECC or ObjectTypeRSA, then the returned *Public object will
// have a Unique field containing details about the public part of the key, computed from the private part of the key.
//
// The returned *CreationData will contain a digest computed from the values of PCRs selected by the creationPCR parameter at creation
// time in the PCRDigest field. It will also contain the provided outsideInfo in the OutsideInfo field. The returned *TkCreation ticket
// can be used to prove the association between the created object and the returned *CreationData via the TPMContext.CertifyCreation
// method.
func (t *TPMContext) Create(parentContext ResourceContext, inSensitive *SensitiveCreate, inPublic *Public, outsideInfo Data, creationPCR PCRSelectionList, parentContextAuthSession SessionContext, sessions ...SessionContext) (outPrivate Private, outPublic *Public, creationData *CreationData, creationHash Digest, creationTicket *TkCreation, err error) {
if inSensitive == nil {
inSensitive = &SensitiveCreate{}
}
if err := t.RunCommand(CommandCreate, sessions,
ResourceContextWithSession{Context: parentContext, Session: parentContextAuthSession}, Delimiter,
mu.Sized(inSensitive), mu.Sized(inPublic), outsideInfo, creationPCR, Delimiter,
Delimiter,
&outPrivate, mu.Sized(&outPublic), mu.Sized(&creationData), &creationHash, &creationTicket); err != nil {
return nil, nil, nil, nil, nil, err
}
return outPrivate, outPublic, creationData, creationHash, creationTicket, nil
}
// Load executes the TPM2_Load command in order to load both the public and private parts of an object in to the TPM.
//
// The parentContext parameter corresponds to the parent key. The command requires authorization with the user auth role for
// parentContext, with session based authorization provided via parentContextAuthSession.
//
// The object to load is specified by providing the inPrivate and inPublic arguments.
//
// If there are no available slots for new objects on the TPM, a *TPMWarning error with a warning code of WarningObjectMemory will
// be returned.
//
// If inPrivate is empty, a *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 1.
//
// If parentContext does not correspond to a storage parent, a *TPMHandleError error with an error code of ErrorType will be returned.
//
// If the name algorithm associated with inPublic is invalid, a *TPMParameterError error with an error code of ErrorHash will be
// returned for parameter index 2.
//
// If the integrity value or IV for inPrivate cannot be unmarshalled correctly, a *TPMParameterError error with an error code of
// either ErrorSize or ErrorInsufficient will be returned for parameter index 1. If the integrity check of inPrivate fails, a
// *TPMParameterError error with an error code of ErrorIntegrity will be returned for parameter index 1. If the size of the IV
// for inPrivate doesn't match the block size for the encryption algorithm, a *TPMParameterError error with an error code of
// ErrorValue will be returned for parameter index 1.
//
// TPM2_Load performs many of the same validations of the public attributes as TPM2_Create, and may return similar error codes as
// *TPMParameterError for parameter index 2.
//
// If the object associated with parentContext has the AttrFixedTPM attribute clear, some additional validation of the decrypted
// sensitive data is performed as detailed below.
//
// If the Type field of inPublic does not match the type specified in the sensitive data, a *TPMParameterError error with an error
// code of ErrorType is returned for parameter index 1. If the authorization value in the sensitive area is larger than the name
// algorithm, a *TPMParameterError error with an error code of ErrorSize is returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeRSA and the size of the modulus in the Unique field is inconsistent with the size
// specified in the Params field, a *TPMParameterError error with an error code of ErrorKey will be returned for parameter index 2.
// If the value of the exponent in the Params field is invalid, a *TPMParameterError error with an error code of ErrorValue will
// be returned for parameter index 2. If the size of private key in the sensitive area is not the correct size, a *TPMParameterError
// error with an error code of ErrorKeySize will be returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeECC and the private key in the sensitive area is invalid, a *TPMParameterError error
// with an error code of ErrorKeySize will be returned for parameter index 1. If the public point specified in the Unique field of
// inPublic does not belong to the private key, a *TPMError with an error code of ErrorBinding will be returned.
//
// If the Type field of inPublic is ObjectTypeSymCipher and the size of the symmetric key in the sensitive area is inconsistent with
// the symmetric algorithm specified in the Params field of inPublic, a *TPMParameterError error with an error code of ErrorKeySize
// will be returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeKeyedHash and the size of the sensitive data is larger than permitted for the digest
// algorithm selected by the scheme defined in the Params field of inPublic, a *TPMParameterError error with an error code of
// ErrorKeySize will be returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeSymCipher or ObjectTypeKeyedHash and the size of seed value in the sensitive area does
// not match the name algorithm, a *TPMError error with an error code of ErrorKeySize will be returned. If the digest in the Unique
// field of inPublic is inconsistent with the value of the sensitive data and the seed value, a *TPMError with an error code of
// ErrorBinding will be returned.
//
// If the loaded object is a storage parent and the size of the seed value in the sensitive area isn't sufficient for the selected
// name algorithm, a *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 1.
//
// On success, a ResourceContext corresponding to the newly loaded transient object will be returned. If subsequent use of the
// returned ResourceContext requires knowledge of the authorization value of the corresponding TPM resource, this should be provided
// by calling ResourceContext.SetAuthValue.
func (t *TPMContext) Load(parentContext ResourceContext, inPrivate Private, inPublic *Public, parentContextAuthSession SessionContext, sessions ...SessionContext) (objectContext ResourceContext, err error) {
var objectHandle Handle
var name Name
if err := t.RunCommand(CommandLoad, sessions,
ResourceContextWithSession{Context: parentContext, Session: parentContextAuthSession}, Delimiter,
inPrivate, mu.Sized(inPublic), Delimiter,
&objectHandle, Delimiter,
&name); err != nil {
return nil, err
}
if objectHandle.Type() != HandleTypeTransient {
return nil, &InvalidResponseError{CommandLoad, fmt.Sprintf("handle 0x%08x returned from TPM is the wrong type", objectHandle)}
}
if inPublic == nil || !inPublic.compareName(name) {
return nil, &InvalidResponseError{CommandLoad, "name returned from TPM not consistent with loaded public area"}
}
var public *Public
// inPublic already marshalled successfully, so this can't fail.
mu.MustCopyValue(&public, inPublic)
return makeObjectContext(objectHandle, name, public), nil
}
// LoadExternal executes the TPM2_LoadExternal command in order to load an object that is not a protected object in to the TPM.
// The object is specified by providing the inPrivate and inPublic arguments, although inPrivate is optional. If only the public
// part is to be loaded, the hierarchy parameter must specify a hierarchy to associate the loaded object with so that tickets can
// be created properly. If both the public and private parts are to be loaded, then hierarchy should be HandleNull.
//
// If there are no available slots for new objects on the TPM, a *TPMWarning error with a warning code of WarningObjectMemory will
// be returned.
//
// If the hierarchy specified by the hierarchy parameter is disabled, a *TPMParameterError error with an error code of ErrorHierarchy
// will be returned for parameter index 3.
//
// If inPrivate is provided and hierarchy is not HandleNull, a *TPMParameterError error with an error code of ErrorHierarchy will be
// returned for parameter index 3.
//
// If inPrivate is provided and the Attrs field of inPublic has either AttrFixedTPM, AttrFixedParent or AttrRestricted attribute set,
// a *TPMParameterError error with an error code of ErrorAttributes will be returned for parameter index 2.
//
// TPM2_LoadExternal performs many of the same validations of the public attributes as TPM2_Create, and may return similar error
// codes as *TPMParameterError for parameter index 2.
//
// If inPrivate is provided and the Type field of inPublic does not match the type specified in the sensitive data, a
// *TPMParameterError error with an error code of ErrorType is returned for parameter index 1. If the authorization value in the
// sensitive area is larger than the name algorithm, a *TPMParameterError error with an error code of ErrorSize is returned for
// parameter index 1.
//
// If the Type field of inPublic is ObjectTypeRSA and the size of the modulus in the Unique field is inconsistent with the size
// specified in the Params field, a *TPMParameterError error with an error code of ErrorKey will be returned for parameter index 2.
// If the value of the exponent in the Params field is invalid, a *TPMParameterError error with an error code of ErrorValue will
// be returned for parameter index 2. If inPrivate is provided and the size of private key in the sensitive area is not the correct
// size, a *TPMParameterError error with an error code of ErrorKeySize will be returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeECC, inPrivate is provided and the private key in the sensitive area is invalid, a
// *TPMParameterError error with an error code of ErrorKeySize will be returned for parameter index 1. If the public point specified
// in the Unique field of inPublic does not belong to the private key, a *TPMError with an error code of ErrorBinding will be
// returned.
//
// If the Type field of inPublic is ObjectTypeECC, inPrivate is not provided and the size of the public key in the Unique field of
// inPublic is inconsistent with the value of the Params field of inPublic, a *TPMParameterError error with an error code of ErrorKey
// is returned for parameter index 2. If the public point is not on the curve specified in the Params field of inPublic, a
// *TPMParameterError error with an error code of ErrorECCPoint will be returned for parameter index 2.
//
// If the Type field of inPublic is ObjectTypeSymCipher, inPrivate is provided and the size of the symmetric key in the sensitive area
// is inconsistent with the symmetric algorithm specified in the Params field of inPublic, a *TPMParameterError error with an error
// code of ErrorKeySize will be returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeKeyedHash, inPrivate is provided and the size of the sensitive data is larger than
// permitted for the digest algorithm selected by the scheme defined in the Params field of inPublic, a *TPMParameterError error
// with an error code of ErrorKeySize will be returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeSymCipher or ObjectTypeKeyedHash and inPrivate has not been provided, a
// *TPMParameterError error with an error code of ErrorKey will be returned for parameter index 2 if the size of the digest in the
// Unique field of inPublic does not match the selected name algorithm.
//
// If the Type field of inPublic is ObjectTypeSymCipher or ObjectTypeKeyedHash, inPrivate has been provided and the size of seed value
// in the sensitive area does not match the name algorithm, a *TPMError error with an error code of ErrorKeySize will be returned.
// If the digest in the Unique field of inPublic is inconsistent with the value of the sensitive data and the seed value, a
// *TPMError with an error code of ErrorBinding will be returned.
//
// On success, a ResourceContext corresponding to the newly loaded transient object will be returned. If inPrivate has been provided,
// it will not be necessary to call ResourceContext.SetAuthValue on it - this function sets the correct authorization value so that it
// can be used in subsequent commands that require knowledge of the authorization value.
func (t *TPMContext) LoadExternal(inPrivate *Sensitive, inPublic *Public, hierarchy Handle, sessions ...SessionContext) (objectContext ResourceContext, err error) {
var objectHandle Handle
var name Name
if err := t.RunCommand(CommandLoadExternal, sessions,
Delimiter,
mu.Sized(inPrivate), mu.Sized(inPublic), hierarchy, Delimiter,
&objectHandle, Delimiter,
&name); err != nil {
return nil, err
}
if objectHandle.Type() != HandleTypeTransient {
return nil, &InvalidResponseError{CommandLoadExternal,
fmt.Sprintf("handle 0x%08x returned from TPM is the wrong type", objectHandle)}
}
if inPublic == nil || !inPublic.compareName(name) {
return nil, &InvalidResponseError{CommandLoadExternal, "name returned from TPM not consistent with loaded public area"}
}
var public *Public
// inPublic already marshalled succesfully, so this can't fail.
mu.MustCopyValue(&public, inPublic)
rc := makeObjectContext(objectHandle, name, public)
if inPrivate != nil {
rc.authValue = make([]byte, len(inPrivate.AuthValue))
copy(rc.authValue, inPrivate.AuthValue)
}
return rc, nil
}
// ReadPublic executes the TPM2_ReadPublic command to read the public area of the object associated with objectContext.
//
// If objectContext corresponds to a sequence object, a *TPMError with an error code of ErrorSequence will be returned.
//
// On success, the public part of the object is returned, along with the object's name and qualified name.
func (t *TPMContext) ReadPublic(objectContext HandleContext, sessions ...SessionContext) (outPublic *Public, name Name, qualifiedName Name, err error) {
if err := t.RunCommand(CommandReadPublic, sessions,
objectContext, Delimiter,
Delimiter,
Delimiter,
mu.Sized(&outPublic), &name, &qualifiedName); err != nil {
return nil, nil, nil, err
}
return outPublic, name, qualifiedName, nil
}
// ActivateCredential executes the TPM2_ActivateCredential command to associate a certificate with the object associated with
// activateContext.
//
// The activateContext parameter corresponds to an object to which credentialBlob is to be associated. It would typically be an
// attestation key, and the issusing certificate authority would have validated that this object has the expected properties of an
// attestation key (it is a restricted, non-duplicable signing key) before issuing the credential. Authorization with the admin role
// is required for activateContext, with session based authorization provided via activateContextAuthSession.
//
// The credentialBlob is an encrypted and integrity protected credential issued by a certificate authority. It is encrypted with a key
// derived from a seed generated by the certificate authority, and the name of the object associated with activateContext. It is
// integrity protected by prepending a HMAC of the encrypted data and the name of the object associated with activateContext, using
// the same seed as the HMAC key.
//
// The keyContext parameter corresponds to an asymmetric restricted decrypt that was used to encrypt the seed value, which is provided
// via the secret parameter in encrypted form. It is typically an endorsement key, and the issuing certificate authority would have
// verified that it is a valid endorsement key by verifying the associated endorsement certificate. Authorization with the user auth
// role is required for keyContext, with session based authorization provided via keyContextAuthSession.
//
// If keyContext does not correspond to an asymmetric restricted decrypt key, a *TPMHandleError error with an error code of ErrorType
// is returned for handle index 2.
//
// If recovering the seed from secret fails, a *TPMParameterError error with an error code of ErrorScheme, ErrorValue, ErrorSize or
// ErrorECCPoint may be returned for parameter index 2.
//
// If the integrity value of IV for credentialBlob cannot be unmarshalled correctly or any other errors occur during unmarshalling
// of credentialBlob, a *TPMParameterError error with an error code of either ErrorSize or ErrorInsufficient will be returned for
// parameter index 1. If the integrity check of credentialBlob fails, a *TPMParameterError error with an error code of ErrorIntegrity
// will be returned for parameter index 1. If the size of the IV for credentialBlob doesn't match the block size for the encryption
// algorithm, a *TPMParameterError error with an error code of ErrorValue will be returned for parameter index 1.
//
// On success, the decrypted credential is returned. This is typically used to decrypt a certificate associated with activateContext,
// which was issued by a certificate authority.
func (t *TPMContext) ActivateCredential(activateContext, keyContext ResourceContext, credentialBlob IDObjectRaw, secret EncryptedSecret, activateContextAuthSession, keyContextAuthSession SessionContext, sessions ...SessionContext) (certInfo Digest, err error) {
if err := t.RunCommand(CommandActivateCredential, sessions,
ResourceContextWithSession{Context: activateContext, Session: activateContextAuthSession}, ResourceContextWithSession{Context: keyContext, Session: keyContextAuthSession}, Delimiter,
credentialBlob, secret, Delimiter,
Delimiter,
&certInfo); err != nil {
return nil, err
}
return certInfo, nil
}
// MakeCredential executes the TPM2_MakeCredential command to allow the TPM to perform the actions of a certificate authority, in
// order to create an activation credential.
//
// The object associated with context must be the public part of a storage key, which would typically be the endorsement key of the
// TPM from which the request originates. The certificate authority would normally be in receipt of the TPM manufacturer issued
// endorsement certificate corresponding to this key and would have validated this. The certificate is an assertion from the
// manufacturer that the key is a valid endorsement key (a restricted, non-duplicable decrypt key) that is resident on a genuine TPM.
//
// The credential parameter is the activation credential, which would typically be used to protect the generated certificate. The
// objectName parameter is the name of object for which a certificate is requested. The public part of this object would normally be
// validated by the certificate authority to ensure that it has the properties expected of an attestation key (it is a restricted,
// non-duplicable signing key).
//
// If context does not correspond to an asymmetric restricted decrypt key, a *TPMHandleError error with an error code of ErrorType is
// returned.
//
// If the size of credential is larger than the name algorithm associated with context, a *TPMParameterError error with an error code
// of ErrorSize will be returned for parameter index 1.
//
// If the algorithm of the object associated with context is ObjectTypeECC, a *TPMError with an error code of ErrorKey will be returned
// if the ECC key is invalid. If the algorithm of the object associated with context is ObjectTypeRSA, a *TPMError with an error code
// of ErrorScheme will be returned if the padding scheme is invalid or not supported.
//
// On success, the encrypted activation credential is returned as IDObjectRaw. The activation credential is encrypted with a key
// derived from a randomly generated seed and objectName, and an integrity HMAC of the encrypted credential and objectName is
// prepended using a HMAC key derived from the same seed. The seed is encrypted using the public key associated with context, and
// returned as EncryptedSecret.
//
// The certificate authority would typically protect the certificate it generates with the unencrypted credential, and then return the
// protected certificate, the encrypted credential blob and the encrypted seed to the requesting party. The seed and credential values
// can only be recovered on the TPM associated with the endorsement certificate that the requesting party provided if the object
// associated with objectName is resident on it.
func (t *TPMContext) MakeCredential(context ResourceContext, credential Digest, objectName Name, sessions ...SessionContext) (credentialBlob IDObjectRaw, secret EncryptedSecret, err error) {
if err := t.RunCommand(CommandMakeCredential, sessions,
context, Delimiter,
credential, objectName, Delimiter,
Delimiter,
&credentialBlob, &secret); err != nil {
return nil, nil, err
}
return credentialBlob, secret, nil
}
// Unseal executes the TPM2_Unseal command to decrypt the sealed data object associated with itemContext and retrieve its sensitive
// data. The command requires authorization with the user auth role for itemContext, with session based authorization provided via
// itemContextAuthSession.
//
// If the type of object associated with itemContext is not ObjectTypeKeyedHash, a *TPMHandleError error with an error code of
// ErrorType will be returned. If the object associated with itemContext has either the AttrDecrypt, AttrSign or AttrRestricted
// attributes set, a *TPMHandlerError error with an error code of ErrorAttributes will be returned.
//
// On success, the object's sensitive data is returned in decrypted form.
func (t *TPMContext) Unseal(itemContext ResourceContext, itemContextAuthSession SessionContext, sessions ...SessionContext) (outData SensitiveData, err error) {
if err := t.RunCommand(CommandUnseal, sessions,
ResourceContextWithSession{Context: itemContext, Session: itemContextAuthSession}, Delimiter,
Delimiter,
Delimiter,
&outData); err != nil {
return nil, err
}
return outData, nil
}
// ObjectChangeAuth executes the TPM2_ObjectChangeAuth to change the authorization value of the object associated with objectContext.
// This command requires authorization with the admin role for objectContext, with sessio based authorization provided via
// objectContextAuthSession.
//
// The new authorization value is provided via newAuth. The parentContext parameter must correspond to the parent object for
// objectContext. No authorization is required for parentContext.
//
// If the object associated with objectContext is a sequence object, a *TPMHandleError error with an error code of ErrorType will
// be returned for handle index 1.
//
// If the length of newAuth is longer than the name algorithm for objectContext, a *TPMParameterError error with an error code of
// ErrorSize will be returned.
//
// If the object associated with parentContext is not the parent object of objectContext, a *TPMHandleError error with an error code
// of ErrorType will be returned for handle index 2.
//
// On success, this returns a new private area for the object associated with objectContext. This function does not make any changes
// to the version of the object that is currently loaded in to the TPM.
func (t *TPMContext) ObjectChangeAuth(objectContext, parentContext ResourceContext, newAuth Auth, objectContextAuthSession SessionContext, sessions ...SessionContext) (outPrivate Private, err error) {
if err := t.RunCommand(CommandObjectChangeAuth, sessions,
ResourceContextWithSession{Context: objectContext, Session: objectContextAuthSession}, parentContext, Delimiter,
newAuth, Delimiter,
Delimiter,
&outPrivate); err != nil {
return nil, err
}
return outPrivate, nil
}
// CreateLoaded executes the TPM2_CreateLoaded command to create a new primary, ordinary or derived object. To create a new primary
// object, parentContext should correspond to a hierarchy. To create a new ordinary object, parentContext should correspond to a
// storage parent. To create a new derived object, parentContext should correspond to a derivation parent.
//
// The command requires authorization with the user auth role for parentContext, with session based authorization provided via
// parentContextAuthSession.
//
// A template for the object is provided via the inPublic parameter. The Type field of inPublic defines the algorithm for the object.
// The NameAlg field defines the digest algorithm for computing the name of the object. The Attrs field defines the attributes of
// the object. The AuthPolicy field allows an authorization policy to be defined for the new object.
//
// Data that will form part of the sensitive area of the object can be provided via inSensitive, which is optional.
//
// If parentContext does not correspond to a derivation parent and the Attrs field of inPublic does not have the
// AttrSensitiveDataOrigin attribute set, then the sensitive data in the created object is initialized with the data provided via the
// Data field of inSensitive.
//
// If the Attrs field of inPublic has the AttrSensitiveDataOrigin attribute set and Type is ObjectTypeSymCipher, then the sensitive
// data in the created object is initialized with a TPM generated key. The size of this key is determined by the value of the Params
// field of inPublic. If Type is ObjectTypeKeyedHash, then the sensitive data in the created object is initialized with a TPM
// generated value that is the same size as the name algorithm selected by the NameAlg field of inPublic.
//
// If the Type field of inPublic is ObjectTypeRSA then the sensitive data in the created object is initialized with a TPM generated
// private key. The size of this is determined by the value of the Params field of inPublic.
//
// If the Type field of inPublic is ObjectTypeECC and parentContext does not correspond to a derivation parent, then the sensitive
// data in the created object is initialized with a TPM generated private key. The size of this is determined by the value of the
// Params field of inPublic.
//
// If parentContext corresponds to a derivation parent, the sensitive data in the created object is initialized with a value derived
// from the parent object's private seed, and the derivation values specified in either the Unique field of inPublic or the Data
// field of inSensitive.
//
// If the Type field of inPublic is ObjectTypeKeyedHash, the Attrs field has AttrSensitiveDataOrigin, AttrSign and AttrDecrypt all
// clear, then the created object is a sealed data object.
//
// If the Attrs field of inPublic has the AttrRestricted and AttrDecrypt attributes set, and the Type field is not
// ObjectTypeKeyedHash, then the newly created object will be a storage parent.
//
// If the Attrs field of inPublic has the AttrRestricted and AttrDecrypt attributes set, and the Type field is ObjectTypeKeyedHash,
// then the newly created object will be a derivation parent.
//
// The authorization value for the created object is initialized to the value of the UserAuth field of inSensitive.
//
// If parentContext corresponds to an object and it isn't a valid storage parent or derivation parent, *TPMHandleError error with an
// error code of ErrorType will be returned for handle index 1.
//
// If there are no available slots for new objects on the TPM, a *TPMWarning error with a warning code of WarningObjectMemory will
// be returned.
//
// If the attributes in the Attrs field of inPublic are inconsistent or inappropriate for the usage, a *TPMParameterError error with
// an error code of ErrorAttributes will be returned for parameter index 2.
//
// If the NameAlg field of inPublic is HashAlgorithmNull, then a *TPMParameterError error with an error code of ErrorHash will be
// returned for parameter index 2.
//
// If an authorization policy is defined via the AuthPolicy field of inPublic then the length of the digest must match the name
// algorithm selected via the NameAlg field, else a *TPMParameterError error with an error code of ErrorSize is returned for parameter
// index 2.
//
// If the scheme in the Params field of inPublic is inappropriate for the usage, a *TPMParameterError errow with an error code of
// ErrorScheme will be returned for parameter index 2.
//
// If the digest algorithm specified by the scheme in the Params field of inPublic is inappropriate for the usage, a
// *TPMParameterError error with an error code of ErrorHash will be returned for parameter index 2.
//
// If the Type field of inPublic is not ObjectTypeKeyedHash, a *TPMParameterError error with an error code of ErrorSymmetric will be
// returned for parameter index 2 if the symmetric algorithm specified in the Params field of inPublic is inappropriate for the
// usage.
//
// If the Type field of inPublic is ObjectTypeECC and the KDF scheme specified in the Params field of inPublic is not
// KDFAlgorithmNull, a *TPMParameterError error with an error code of ErrorKDF will be returned for parameter index 2.
//
// If the Type field of inPublic is not ObjectTypeKeyedHash and the AttrRestricted, AttrFixedParent and AttrDecrypt attributes of
// Attrs are set, a *TPMParameterError error with an error code of ErrorHash will be returned for parameter index 2 if the NameAlg
// field of inPublic does not select the same name algorithm as the parent object. A *TPMParameterError error with an error code
// of ErrorSymmetric will be returned for parameter index 2 if the symmetric algorithm specified in the Params field of inPublic
// does not match the symmetric algorithm of the parent object.
//
// If the length of the UserAuth field of inSensitive is longer than the name algorithm selected by the NameAlg field of inPublic, a
// *TPMParameterError error with an error code of ErrorSize will be returned for parameter index 1.
//
// If the Type field of inPublic is ObjectTypeRSA and the Params field specifies an unsupported exponent, a *TPMError with an error
// code of ErrorRange will be returned. If the specified key size is an unsupported value, a *TPMError with an error code of
// ErrorValue will be returned.
//
// If the Type field of inPublic is ObjectTypeSymCipher and the key size is an unsupported value, a *TPMError with an error code of
// ErrorKeySize will be returned. If the AttrSensitiveDataOrigin attribute is not set and the length of the Data field of inSensitive
// does not match the key size specified in the Params field of inPublic, a *TPMError with an error code of ErrorKeySize will be
// returned.
//
// If the Type field of inPublic is ObjectTypeKeyedHash and the AttrSensitiveDataOrigin attribute is not set, a *TPMError with an
// error code of ErrorSize will be returned if the length of the Data field of inSensitive is longer than permitted for the digest
// algorithm selected by the specified scheme.
//
// On success, a ResourceContext instance will be returned that corresponds to the newly created object on the TPM, along with the
// private and public parts. It will not be necessary to call ResourceContext.SetAuthValue on the returned ResourceContext - this
// function sets the correct authorization value so that it can be used in subsequent commands that require knowledge of the
// authorization value. If the Type field of inPublic is ObjectTypeKeyedHash or ObjectTypeSymCipher, then the returned *Public object
// will have a Unique field that is the digest of the sensitive data and the value of the object's seed in the sensitive area,
// computed using the object's name algorithm. If the Type field of inPublic is ObjectTypeECC or ObjectTypeRSA, then the returned
// *Public object will have a Unique field containing details about the public part of the key, computed from the private part of the
// key.
func (t *TPMContext) CreateLoaded(parentContext ResourceContext, inSensitive *SensitiveCreate, inPublic PublicTemplate, parentContextAuthSession SessionContext, sessions ...SessionContext) (objectContext ResourceContext, outPrivate Private, outPublic *Public, err error) {
if inSensitive == nil {
inSensitive = &SensitiveCreate{}
}
if inPublic == nil {
return nil, nil, nil, makeInvalidArgError("inPublic", "nil value")
}
inTemplate, err := inPublic.ToTemplate()
if err != nil {
return nil, nil, nil, fmt.Errorf("cannot marshal public template: %v", err)
}
var objectHandle Handle
var name Name
if err := t.RunCommand(CommandCreateLoaded, sessions,
ResourceContextWithSession{Context: parentContext, Session: parentContextAuthSession}, Delimiter,
mu.Sized(inSensitive), inTemplate, Delimiter,
&objectHandle, Delimiter,
&outPrivate, mu.Sized(&outPublic), &name); err != nil {
return nil, nil, nil, err
}
if objectHandle.Type() != HandleTypeTransient {
return nil, nil, nil, &InvalidResponseError{CommandCreateLoaded,
fmt.Sprintf("handle 0x%08x returned from TPM is the wrong type", objectHandle)}
}
if outPublic == nil || !outPublic.compareName(name) {
return nil, nil, nil, &InvalidResponseError{CommandCreateLoaded, "name and public area returned from TPM are not consistent"}
}
var public *Public
if err := mu.CopyValue(&public, outPublic); err != nil {
return nil, nil, nil, &InvalidResponseError{CommandCreateLoaded, fmt.Sprintf("cannot copy returned public area from TPM: %v", err)}
}
rc := makeObjectContext(objectHandle, name, public)
rc.authValue = make([]byte, len(inSensitive.UserAuth))
copy(rc.authValue, inSensitive.UserAuth)
return rc, outPrivate, outPublic, nil
}
./github.com/canonical/go-tpm2/cmds_pcr.go 0000664 0000000 0000000 00000011577 00000000000 016753 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 22 - Integrity Collection (PCR)
// PCRExtend executes the TPM2_PCR_Extend command to extend the PCR associated with the pcrContext parameter with the tagged digests
// provided via the digests argument. If will iterate over the digests and extend the PCR with each one for the PCR bank associated
// with the algorithm for each digest.
//
// If pcrContext is nil, this function will do nothing. The command requires authorization with the user auth role for pcrContext,
// with session based authorization provided via pcrContextAuthSession.
//
// If the PCR associated with pcrContext can not be extended from the current locality, a *TPMError error with an error code of
// ErrorLocality will be returned.
func (t *TPMContext) PCRExtend(pcrContext ResourceContext, digests TaggedHashList, pcrContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandPCRExtend, sessions,
ResourceContextWithSession{Context: pcrContext, Session: pcrContextAuthSession}, Delimiter,
digests)
}
// PCREvent executes the TPM2_PCR_Event command to extend the PCR associated with the pcrContext parameter with a digest of the
// provided eventData, hashed with the algorithm for each supported PCR bank.
//
// If pcrContext is nil, this function will do nothing. The command requires authorization with the user auth role for pcrContext,
// with session based authorization provided via pcrContextAuthSession.
//
// If the PCR associated with pcrContext can not be extended from the current locality, a *TPMError error with an error code of
// ErrorLocality will be returned.
//
// On success, this function will return a list of tagged digests that the PCR associated with pcrContext was extended with.
func (t *TPMContext) PCREvent(pcrContext ResourceContext, eventData Event, pcrContextAuthSession SessionContext, sessions ...SessionContext) (digests TaggedHashList, err error) {
if err := t.RunCommand(CommandPCREvent, sessions,
ResourceContextWithSession{Context: pcrContext, Session: pcrContextAuthSession}, Delimiter,
eventData, Delimiter,
Delimiter,
&digests); err != nil {
return nil, err
}
return digests, nil
}
// PCRRead executes the TPM2_PCR_Read command to return the values of the PCRs defined in the pcrSelectionIn parameter. The
// underlying command may not be able to read all of the specified PCRs in a single transaction, so this function will
// re-execute the TPM2_PCR_Read command until all requested values have been read. As a consequence, any SessionContext instances
// provided should have the AttrContinueSession attribute defined.
//
// On success, the current value of pcrUpdateCounter is returned, as well as the requested PCR values.
func (t *TPMContext) PCRRead(pcrSelectionIn PCRSelectionList, sessions ...SessionContext) (pcrUpdateCounter uint32, pcrValues PCRValues, err error) {
var remaining PCRSelectionList
for _, s := range pcrSelectionIn {
c := PCRSelection{Hash: s.Hash, Select: make([]int, len(s.Select))}
copy(c.Select, s.Select)
remaining = append(remaining, c)
}
pcrValues = make(PCRValues)
for i := 0; ; i++ {
var updateCounter uint32
var pcrSelectionOut PCRSelectionList
var values DigestList
if err := t.RunCommand(CommandPCRRead, sessions,
Delimiter,
remaining, Delimiter,
Delimiter,
&updateCounter, &pcrSelectionOut, &values); err != nil {
return 0, nil, err
}
if i == 0 {
pcrUpdateCounter = updateCounter
} else if updateCounter != pcrUpdateCounter {
return 0, nil, &InvalidResponseError{CommandPCRRead, "PCR update counter changed between commands"}
} else if len(values) == 0 && pcrSelectionOut.IsEmpty() {
return 0, nil, makeInvalidArgError("pcrSelectionIn", "unimplemented PCRs specified")
}
if n, err := pcrValues.SetValuesFromListAndSelection(pcrSelectionOut, values); err != nil {
return 0, nil, &InvalidResponseError{CommandPCRRead, err.Error()}
} else if n != len(values) {
return 0, nil, &InvalidResponseError{CommandPCRRead, "too many digests"}
}
remaining = remaining.Remove(pcrSelectionOut)
if remaining.IsEmpty() {
break
}
}
return pcrUpdateCounter, pcrValues, nil
}
// PCRReset executes the TPM2_PCR_Reset command to reset the PCR associated with pcrContext in all banks. This command requires
// authorization with the user auth role for pcrContext, with session based authorization provided via pcrContextAuthSession.
//
// If the PCR associated with pcrContext can not be reset from the current locality, a *TPMError error with an error code of
// ErrorLocality will be returned.
func (t *TPMContext) PCRReset(pcrContext ResourceContext, pcrContextAuthSession SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandPCRReset, sessions, ResourceContextWithSession{Context: pcrContext, Session: pcrContextAuthSession})
}
./github.com/canonical/go-tpm2/cmds_rng.go 0000664 0000000 0000000 00000002470 00000000000 016745 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 16 - Random Number Generator
// GetRandom executes the TPM2_GetRandom command to return the next bytesRequested number of bytes from the TPM's
// random number generator. If the requested bytes cannot be read in a single command, this function will reexecute
// the TPM2_GetRandom command until all requested bytes have been read.
func (t *TPMContext) GetRandom(bytesRequested uint16, sessions ...SessionContext) (randomBytes []byte, err error) {
if err := t.initPropertiesIfNeeded(); err != nil {
return nil, err
}
randomBytes = make([]byte, bytesRequested)
total := 0
remaining := bytesRequested
for {
sz := remaining
if sz > uint16(t.maxDigestSize) {
sz = uint16(t.maxDigestSize)
}
var tmpBytes Digest
if err := t.RunCommand(CommandGetRandom, sessions,
Delimiter,
sz, Delimiter,
Delimiter,
&tmpBytes); err != nil {
return nil, err
}
copy(randomBytes[total:], tmpBytes)
total += int(sz)
remaining -= sz
if remaining == 0 {
break
}
}
return randomBytes, nil
}
func (t *TPMContext) StirRandom(inData SensitiveData, sessions ...SessionContext) error {
return t.RunCommand(CommandStirRandom, sessions, Delimiter, inData)
}
./github.com/canonical/go-tpm2/cmds_session.go 0000664 0000000 0000000 00000017334 00000000000 017647 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 11 - Session Commands
import (
"fmt"
"github.com/canonical/go-tpm2/internal"
)
// StartAuthSession executes the TPM2_StartAuthSession command to start an authorization session. On successful completion, it will
// return a SessionContext that corresponds to the new session.
//
// The type of session is defined by the sessionType parameter. If sessionType is SessionTypeHMAC or SessionTypePolicy, then the
// created session may be used for authorization. If sessionType is SessionTypeTrial, then the created session can only be used for
// computing an authorization policy digest.
//
// The authHash parameter defines the algorithm used for computing command and response parameter digests, command and response
// HMACs, and derivation of the session key and symmetric keys for parameter encryption where used. The size of the digest algorithm
// is used to determine the nonce size used for the session.
//
// If tpmKey is provided, it must correspond to an asymmetric decrypt key in the TPM. In this case, a random salt value will
// contribute to the session key derivation, and the salt will be encrypted using the method specified by tpmKey before being sent to
// the TPM. If tpmKey is provided but does not correspond to an asymmetric key, a *TPMHandleError error with an error code of ErrorKey
// will be returned for handle index 1. If tpmKey is provided but corresponds to an object with only its public part loaded, a
// *TPMHandleError error with an error code of ErrorHandle will be returned for handle index 1. If tpmKey is provided but does not
// correspond to a decrypt key, a *TPMHandleError error with an error code of ErrorAttributes will be returned for handle index 1.
//
// If tpmkey is provided but decryption of the salt fails on the TPM, a *TPMParameterError error with an error code of ErrorValue or
// ErrorKey may be returned for parameter index 2.
//
// If bind is specified, then the auhorization value for the corresponding resource must be known, by calling
// ResourceContext.SetAuthValue on bind before calling this function - the authorization value will contribute to the session key
// derivation. The created session will be bound to the resource associated with bind, unless the authorization value of that resource
// is subsequently changed. If bind corresponds to a transient object and only the public part of the object is loaded, or if bind
// corresponds to a NV index with a type of NVTypePinPass or NVTypePinFail, a *TPMHandleError error with an error code of ErrorHandle
// will be returned for handle index 2.
//
// If a session key is computed, this will be used (along with the authorization value of resources that the session is being used
// for authorization of if the session is not bound to them) to derive a HMAC key for generating command and response HMACs. If both
// tpmKey and bind are nil, no session key is created.
//
// If symmetric is provided, it defines the symmetric algorithm to use if the session is subsequently used for session based command
// or response parameter encryption. Session based parameter encryption allows the first command and/or response parameter for a
// command to be encrypted between the TPM and host CPU for supported parameter types (go types that correspond to TPM2B prefixed
// types). If symmetric is provided and corresponds to a symmetric block cipher (ie, the Algorithm field is not SymAlgorithmXOR) then
// the value of symmetric.Mode.Sym() must be SymModeCFB, else a *TPMParameterError error with an error code of ErrorMode is returned
// for parameter index 4.
//
// If a SessionContext instance with the AttrCommandEncrypt attribute set is provided in the variable length sessions parameter, then
// the initial caller nonce will be encrypted as this is the first command parameter, despite not being exposed via this API. If a
// SessionContext instance with the AttrResponseEncrypt attribute set is provided, then the initial TPM nonce will be encrypted in the
// response.
//
// If sessionType is SessionTypeHMAC and the session is subsequently used for authorization of a resource to which the session is not
// bound, the authorization value of that resource must be known as it is used to derive the key for computing command and response
// HMACs.
//
// If no more sessions can be created without first context loading the oldest saved session, then a *TPMWarning error with a warning
// code of WarningContextGap will be returned. If there are no more slots available for loaded sessions, a *TPMWarning error with a
// warning code of WarningSessionMemory will be returned. If there are no more session handles available, a *TPMwarning error with
// a warning code of WarningSessionHandles will be returned.
func (t *TPMContext) StartAuthSession(tpmKey, bind ResourceContext, sessionType SessionType, symmetric *SymDef, authHash HashAlgorithmId, sessions ...SessionContext) (sessionContext SessionContext, err error) {
if symmetric == nil {
symmetric = &SymDef{Algorithm: SymAlgorithmNull}
}
if !authHash.Available() {
return nil, makeInvalidArgError("authHash", fmt.Sprintf("unsupported digest algorithm or algorithm not linked in to binary (%v)", authHash))
}
digestSize := authHash.Size()
var salt []byte
var encryptedSalt EncryptedSecret
tpmKeyHandle := HandleNull
if tpmKey != nil {
object, isObject := tpmKey.(*objectContext)
if !isObject {
return nil, makeInvalidArgError("tpmKey", "resource context is not an object")
}
tpmKeyHandle = tpmKey.Handle()
var err error
encryptedSalt, salt, err = CryptSecretEncrypt(object.GetPublic(), []byte(SecretKey))
if err != nil {
return nil, fmt.Errorf("cannot compute encrypted salt: %v", err)
}
}
var authValue []byte
bindHandle := HandleNull
if bind != nil {
bindHandle = bind.Handle()
authValue = bind.(resourceContextPrivate).GetAuthValue()
}
var isBound bool = false
var boundEntity Name
if bindHandle != HandleNull && sessionType == SessionTypeHMAC {
boundEntity = computeBindName(bind.Name(), authValue)
isBound = true
}
nonceCaller := make([]byte, digestSize)
if err := cryptComputeNonce(nonceCaller); err != nil {
return nil, fmt.Errorf("cannot compute initial nonceCaller: %v", err)
}
var sessionHandle Handle
var nonceTPM Nonce
if err := t.RunCommand(CommandStartAuthSession, sessions,
tpmKey, bind, Delimiter,
Nonce(nonceCaller), encryptedSalt, sessionType, symmetric, authHash, Delimiter,
&sessionHandle, Delimiter,
&nonceTPM); err != nil {
return nil, err
}
switch sessionHandle.Type() {
case HandleTypeHMACSession, HandleTypePolicySession:
default:
return nil, &InvalidResponseError{CommandStartAuthSession,
fmt.Sprintf("handle 0x%08x returned from TPM is the wrong type", sessionHandle)}
}
data := &sessionContextData{
HashAlg: authHash,
SessionType: sessionType,
PolicyHMACType: policyHMACTypeNoAuth,
IsBound: isBound,
BoundEntity: boundEntity,
NonceCaller: nonceCaller,
NonceTPM: nonceTPM,
Symmetric: symmetric}
if tpmKeyHandle != HandleNull || bindHandle != HandleNull {
key := make([]byte, len(authValue)+len(salt))
copy(key, authValue)
copy(key[len(authValue):], salt)
data.SessionKey = internal.KDFa(authHash.GetHash(), key, []byte(SessionKey), []byte(nonceTPM), nonceCaller, digestSize*8)
}
return makeSessionContext(sessionHandle, data), nil
}
// PolicyRestart executes the TPM2_PolicyRestart command on the policy session associated with sessionContext, to reset the policy
// authorization session to its initial state.
func (t *TPMContext) PolicyRestart(sessionContext SessionContext, sessions ...SessionContext) error {
return t.RunCommand(CommandPolicyRestart, sessions, sessionContext)
}
./github.com/canonical/go-tpm2/cmds_signature.go 0000664 0000000 0000000 00000007622 00000000000 020164 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Secion 20 - Signing and Signature Verification
// VerifySignature executes the TPM2_VerifySignature command to validate the provided signature against a message with the provided
// digest, using the key associated with keyContext. If keyContext corresponds to an object that isn't a signing key, a
// *TPMHandleError error with an error code of ErrorAttributes will be returned.
//
// If the signature is invalid, a *TPMParameterError error with an error code of ErrorSignature will be returned for parameter index
// 2. If the signature references an unsupported signature scheme, a *TPMParameterError error with an error code of ErrorScheme will
// be returned for parameter index 2.
//
// If keyContext corresponds to a HMAC key but only the public part is loaded, a *TPMParameterError error with an error code of
// ErrorHandle will be returned for parameter index 2.
//
// On success, a valid TkVerified structure will be returned.
func (t *TPMContext) VerifySignature(keyContext ResourceContext, digest Digest, signature *Signature, sessions ...SessionContext) (validation *TkVerified, err error) {
if err := t.RunCommand(CommandVerifySignature, sessions,
keyContext, Delimiter,
digest, signature, Delimiter,
Delimiter,
&validation); err != nil {
return nil, err
}
return validation, nil
}
// Sign executes the TPM2_Sign command to sign the provided digest with the key associated with keyContext. The function requires
// authorization with the user auth role for keyContext, with session based authorization provided via keyContextAuthSession.
//
// If the object associated with keyContext is not a signing key, a *TPMHandleError error with an error code of ErrorKey will be
// returned.
//
// If the scheme of the key associated with keyContext is AsymSchemeNull, then inScheme must be provided to specify a valid signing
// scheme for the key. If it isn't, a *TPMParameterError error with an error code of ErrorScheme will be returned for parameter index
// 2.
//
// If the scheme of the key associated with keyContext is not AsymSchemeNull, then inScheme may be nil. If it is provided, then the
// specified scheme must match that of the signing key, else a *TPMParameterError error with an error code of ErrorScheme will be
// returned for parameter index 2.
//
// If the chosen scheme is unsupported, a *TPMError error with an error code of ErrorScheme will be returned.
//
// If the length of digest does not match the size of the digest associated with the selected signing scheme, a *TPMParameterError
// error with an error code of ErrorSize will be returned for parameter index 1.
//
// If the key associated with keyContext has the AttrRestricted attribute, then the validation parameter must be provided as proof
// that the supplied digest was created by the TPM. If the key associated with keyContext does not have the AttrRestricted attribute,
// then validation may be nil. If validation is not nil and doesn't correspond to a valid ticket, or it is nil and the key associated
// with keyContext has the AttrRestricted attribute set, a *TPMParameterError error with an error code of ErrorTicket will be returned
// for parameter index 3.
func (t *TPMContext) Sign(keyContext ResourceContext, digest Digest, inScheme *SigScheme, validation *TkHashcheck, keyContextAuthSession SessionContext, sessions ...SessionContext) (signature *Signature, err error) {
if inScheme == nil {
inScheme = &SigScheme{Scheme: SigSchemeAlgNull}
}
if validation == nil {
validation = &TkHashcheck{Tag: TagHashcheck, Hierarchy: HandleNull}
}
if err := t.RunCommand(CommandSign, sessions,
ResourceContextWithSession{Context: keyContext, Session: keyContextAuthSession}, Delimiter,
digest, inScheme, validation, Delimiter,
Delimiter,
&signature); err != nil {
return nil, err
}
return signature, nil
}
./github.com/canonical/go-tpm2/cmds_startup.go 0000664 0000000 0000000 00000004705 00000000000 017664 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 10 - Testing
// Startup executes the TPM2_Startup command with the specified StartupType. If this isn't preceded by _TPM_Init then it will return
// a *TPMError error with an error code of ErrorInitialize. The shutdown and startup sequence determines how the TPM responds to this
// call:
// * A call with startupType == StartupClear preceded by a call to TPMContext.Shutdown with shutdownType == StartupClear or without
// a preceding call to TPMContext.Shutdown will cause a TPM reset.
// * A call with startupType == StartupClear preceded by a call to TPMContext.Shutdown with shutdownType == StartupState will cause
// a TPM restart.
// * A call with startupType == StartupState preceded by a call to TPMContext.Shutdown with shutdownType == StartupState will cause
// a TPM resume.
// * A call with startupType == StartupState that isn't preceded by a call to TPMContext.Shutdown with shutdownType == StartupState
// will fail with a *TPMParameterError error with an error code of ErrorValue.
//
// If called with startupType == StartupState, a *TPMError error with an error code of ErrorNVUninitialized will be returned if the
// saved state cannot be recovered. In this case, the function must be called with startupType == StartupClear.
//
// Subsequent use of HandleContext instances corresponding to entities that are evicted as a consequence of this function will no
// longer work.
func (t *TPMContext) Startup(startupType StartupType) error {
return t.RunCommand(CommandStartup, nil, Delimiter, startupType)
}
// Shutdown executes the TPM2_Shutdown command with the specified StartupType, and is used to prepare the TPM for a power cycle.
// Calling this with shutdownType == StartupClear prepares the TPM for a TPM reset. Calling it with shutdownType == StartupState
// prepares the TPM for either a TPM restart or TPM resume, depending on how TPMContext.Startup is called. Some commands executed
// after TPMContext.Shutdown but before a power cycle will nullify the effect of this function.
//
// If a PCR bank has been reconfigured and shutdownType == StartupState, a *TPMParameterError error with an error code of
// ErrorType will be returned.
func (t *TPMContext) Shutdown(shutdownType StartupType, sessions ...SessionContext) error {
return t.RunCommand(CommandShutdown, sessions, Delimiter, shutdownType)
}
./github.com/canonical/go-tpm2/cmds_testing.go 0000664 0000000 0000000 00000001667 00000000000 017643 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// Section 9 - Start-up
func (t *TPMContext) SelfTest(fullTest bool, sessions ...SessionContext) error {
return t.RunCommand(CommandSelfTest, sessions, Delimiter, fullTest)
}
func (t *TPMContext) IncrementalSelfTest(toTest AlgorithmList, sessions ...SessionContext) (AlgorithmList, error) {
var toDoList AlgorithmList
if err := t.RunCommand(CommandIncrementalSelfTest, sessions,
Delimiter,
toTest, Delimiter,
Delimiter,
&toDoList); err != nil {
return nil, err
}
return toDoList, nil
}
func (t *TPMContext) GetTestResult(sessions ...SessionContext) (outData MaxBuffer, testResult ResponseCode, err error) {
if err := t.RunCommand(CommandGetTestResult, sessions, Delimiter, Delimiter, Delimiter, &outData, &testResult); err != nil {
return nil, 0, err
}
return outData, testResult, nil
}
./github.com/canonical/go-tpm2/command.go 0000664 0000000 0000000 00000015474 00000000000 016577 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"github.com/canonical/go-tpm2/mu"
"golang.org/x/xerrors"
)
const (
maxResponseSize int = 4096
)
// CommandHeader is the header for a TPM command.
type CommandHeader struct {
Tag StructTag
CommandSize uint32
CommandCode CommandCode
}
// CommandPacket corresponds to a complete command packet including header and payload.
type CommandPacket []byte
// GetCommandCode returns the command code contained within this packet.
func (p CommandPacket) GetCommandCode() (CommandCode, error) {
var header CommandHeader
if _, err := mu.UnmarshalFromBytes(p, &header); err != nil {
return 0, xerrors.Errorf("cannot unmarshal header: %w", err)
}
return header.CommandCode, nil
}
// Unmarshal unmarshals this command packet, returning the handles, auth area and
// parameters. The parameters will still be in the TPM wire format. The number of command
// handles associated with the command must be supplied by the caller.
func (p CommandPacket) Unmarshal(numHandles int) (handles HandleList, authArea []AuthCommand, parameters []byte, err error) {
buf := bytes.NewReader(p)
var header CommandHeader
if _, err := mu.UnmarshalFromReader(buf, &header); err != nil {
return nil, nil, nil, xerrors.Errorf("cannot unmarshal header: %w", err)
}
if header.CommandSize != uint32(len(p)) {
return nil, nil, nil, fmt.Errorf("invalid commandSize value (got %d, packet length %d)", header.CommandSize, len(p))
}
for i := 0; i < numHandles; i++ {
var handle Handle
if _, err := mu.UnmarshalFromReader(buf, &handle); err != nil {
return nil, nil, nil, xerrors.Errorf("cannot unmarshal handles: %w", err)
}
handles = append(handles, handle)
}
switch header.Tag {
case TagSessions:
var authSize uint32
if _, err := mu.UnmarshalFromReader(buf, &authSize); err != nil {
return nil, nil, nil, xerrors.Errorf("cannot unmarshal auth area size: %w", err)
}
r := &io.LimitedReader{R: buf, N: int64(authSize)}
for r.N > 0 {
if len(authArea) >= 3 {
return nil, nil, nil, fmt.Errorf("%d trailing byte(s) in auth area", r.N)
}
var auth AuthCommand
if _, err := mu.UnmarshalFromReader(r, &auth); err != nil {
return nil, nil, nil, xerrors.Errorf("cannot unmarshal auth: %w", err)
}
authArea = append(authArea, auth)
}
case TagNoSessions:
default:
return nil, nil, nil, fmt.Errorf("invalid tag: %v", header.Tag)
}
parameters, err = ioutil.ReadAll(buf)
if err != nil {
return nil, nil, nil, xerrors.Errorf("cannot read parameters: %w", err)
}
return handles, authArea, parameters, nil
}
// MarshalCommandPacket serializes a complete TPM packet from the provided arguments. The
// parameters argument must already be serialized to the TPM wire format.
func MarshalCommandPacket(command CommandCode, handles HandleList, authArea []AuthCommand, parameters []byte) CommandPacket {
header := CommandHeader{CommandCode: command}
var payload []byte
hBytes := new(bytes.Buffer)
for _, h := range handles {
mu.MustMarshalToWriter(hBytes, h)
}
switch {
case len(authArea) > 0:
header.Tag = TagSessions
aBytes := new(bytes.Buffer)
for _, auth := range authArea {
mu.MustMarshalToWriter(aBytes, auth)
}
payload = mu.MustMarshalToBytes(mu.RawBytes(hBytes.Bytes()), uint32(aBytes.Len()), mu.RawBytes(aBytes.Bytes()), mu.RawBytes(parameters))
case len(authArea) == 0:
header.Tag = TagNoSessions
payload = mu.MustMarshalToBytes(mu.RawBytes(hBytes.Bytes()), mu.RawBytes(parameters))
}
header.CommandSize = uint32(binary.Size(header) + len(payload))
return mu.MustMarshalToBytes(header, mu.RawBytes(payload))
}
// ResponseHeader is the header for the TPM's response to a command.
type ResponseHeader struct {
Tag StructTag
ResponseSize uint32
ResponseCode ResponseCode
}
// ResponsePacket corresponds to a complete response packet including header and payload.
type ResponsePacket []byte
// Unmarshal deserializes the response packet and returns the response code, handle, parameters
// and auth area. The parameters will still be in the TPM wire format. The caller supplies a
// pointer to which the response handle will be written. The pointer must be supplied if the
// command returns a handle, and must be nil if the command does not return a handle, else
// the response will be incorrectly unmarshalled.
func (p ResponsePacket) Unmarshal(handle *Handle) (rc ResponseCode, parameters []byte, authArea []AuthResponse, err error) {
if len(p) > maxResponseSize {
return 0, nil, nil, fmt.Errorf("packet too large (%d bytes)", len(p))
}
buf := bytes.NewReader(p)
var header ResponseHeader
if _, err := mu.UnmarshalFromReader(buf, &header); err != nil {
return 0, nil, nil, xerrors.Errorf("cannot unmarshal header: %w", err)
}
if header.ResponseSize != uint32(buf.Size()) {
return 0, nil, nil, fmt.Errorf("invalid responseSize value (got %d, packet length %d)", header.ResponseSize, len(p))
}
if header.ResponseCode != ResponseSuccess && buf.Len() != 0 {
return header.ResponseCode, nil, nil, fmt.Errorf("%d trailing byte(s) in unsuccessful response", buf.Len())
}
switch header.Tag {
case TagRspCommand:
if header.ResponseCode != ResponseBadTag {
return 0, nil, nil, fmt.Errorf("unexpected TPM1.2 response code 0x%08x", header.ResponseCode)
}
case TagSessions:
if header.ResponseCode != ResponseSuccess {
return 0, nil, nil, fmt.Errorf("unexpcted response code 0x%08x for TPM_ST_SESSIONS response", header.ResponseCode)
}
fallthrough
case TagNoSessions:
if header.ResponseCode == ResponseSuccess && handle != nil {
if _, err := mu.UnmarshalFromReader(buf, handle); err != nil {
return 0, nil, nil, xerrors.Errorf("cannot unmarshal handle: %w", err)
}
}
default:
return 0, nil, nil, fmt.Errorf("invalid tag: %v", header.Tag)
}
switch header.Tag {
case TagRspCommand:
case TagSessions:
var parameterSize uint32
if _, err := mu.UnmarshalFromReader(buf, ¶meterSize); err != nil {
return 0, nil, nil, xerrors.Errorf("cannot unmarshal parameterSize: %w", err)
}
parameters = make([]byte, parameterSize)
if _, err := io.ReadFull(buf, parameters); err != nil {
return 0, nil, nil, xerrors.Errorf("cannot read parameters: %w", err)
}
for buf.Len() > 0 {
if len(authArea) >= 3 {
return 0, nil, nil, fmt.Errorf("%d trailing byte(s)", buf.Len())
}
var auth AuthResponse
if _, err := mu.UnmarshalFromReader(buf, &auth); err != nil {
return 0, nil, nil, xerrors.Errorf("cannot unmarshal auth: %w", err)
}
authArea = append(authArea, auth)
}
case TagNoSessions:
parameters, err = ioutil.ReadAll(buf)
if err != nil {
return 0, nil, nil, xerrors.Errorf("cannot read parameters: %w", err)
}
}
return header.ResponseCode, parameters, authArea, nil
}
./github.com/canonical/go-tpm2/constants.go 0000664 0000000 0000000 00000002132 00000000000 017160 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"math"
)
const (
DefaultRSAExponent = 65537
)
const (
CapabilityMaxProperties uint32 = math.MaxUint32
)
const (
// CFBKey is used as the label for the symmetric key derivation used
// in parameter encryption.
CFBKey = "CFB"
// DuplicateString is used as the label for secret sharing used by
// object duplication.
DuplicateString = "DUPLICATE"
// IdentityKey is used as the label for secret sharing used by
// when issuing and using credentials.
IdentityKey = "IDENTITY"
// IntegrityKey is used as the label for the HMAC key derivation
// used for outer wrappers.
IntegrityKey = "INTEGRITY"
// SecretKey is used as the label for secret sharing used by
// TPM2_StartAuthSession.
SecretKey = "SECRET"
// SessionKey is used as the label for the session key derivation.
SessionKey = "ATH"
// StorageKey is used as the label for the symmetric key derivation
// used for encrypting and decrypting outer wrappers.
StorageKey = "STORAGE"
)
./github.com/canonical/go-tpm2/crypto.go 0000664 0000000 0000000 00000013650 00000000000 016473 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/binary"
"errors"
"fmt"
"math/big"
"github.com/canonical/go-tpm2/internal"
"github.com/canonical/go-tpm2/mu"
"golang.org/x/xerrors"
)
type NewCipherFunc func([]byte) (cipher.Block, error)
var (
eccCurves = map[ECCCurve]elliptic.Curve{
ECCCurveNIST_P224: elliptic.P224(),
ECCCurveNIST_P256: elliptic.P256(),
ECCCurveNIST_P384: elliptic.P384(),
ECCCurveNIST_P521: elliptic.P521(),
}
symmetricAlgs = map[SymAlgorithmId]NewCipherFunc{
SymAlgorithmAES: aes.NewCipher,
}
)
// RegisterCipher allows a go block cipher implementation to be registered for the
// specified algorithm, so binaries don't need to link against every implementation.
func RegisterCipher(alg SymAlgorithmId, fn NewCipherFunc) {
symmetricAlgs[alg] = fn
}
func eccCurveToGoCurve(curve ECCCurve) elliptic.Curve {
switch curve {
case ECCCurveNIST_P224:
return elliptic.P224()
case ECCCurveNIST_P256:
return elliptic.P256()
case ECCCurveNIST_P384:
return elliptic.P384()
case ECCCurveNIST_P521:
return elliptic.P521()
}
return nil
}
// ComputeCpHash computes a command parameter digest from the specified command code, the supplied
// handles (identified by their names) and parameter buffer using the specified digest algorithm.
//
// The result of this is useful for extended authorization commands that bind an authorization to
// a command and set of command parameters, such as TPMContext.PolicySigned, TPMContext.PolicySecret,
// TPMContext.PolicyTicket and TPMContext.PolicyCpHash.
//
// This will panic if alg is not available.
func ComputeCpHash(alg HashAlgorithmId, command CommandCode, handles []Name, parameters []byte) Digest {
hash := alg.NewHash()
binary.Write(hash, binary.BigEndian, command)
for _, name := range handles {
hash.Write([]byte(name))
}
hash.Write(parameters)
return hash.Sum(nil)
}
func cryptComputeRpHash(hashAlg HashAlgorithmId, responseCode ResponseCode, commandCode CommandCode, rpBytes []byte) []byte {
hash := hashAlg.NewHash()
binary.Write(hash, binary.BigEndian, responseCode)
binary.Write(hash, binary.BigEndian, commandCode)
hash.Write(rpBytes)
return hash.Sum(nil)
}
func cryptComputeNonce(nonce []byte) error {
_, err := rand.Read(nonce)
return err
}
// CryptSymmetricEncrypt performs in place symmetric encryption of the supplied
// data with the specified algorithm using CFB mode.
func CryptSymmetricEncrypt(alg SymAlgorithmId, key, iv, data []byte) error {
switch alg {
case SymAlgorithmXOR, SymAlgorithmNull:
return errors.New("unsupported symmetric algorithm")
default:
c, err := alg.NewCipher(key)
if err != nil {
return xerrors.Errorf("cannot create cipher: %w", err)
}
// The TPM uses CFB cipher mode for all secret sharing
s := cipher.NewCFBEncrypter(c, iv)
s.XORKeyStream(data, data)
return nil
}
}
// CryptSymmetricDecrypt performs in place symmetric decryption of the supplied
// data with the specified algorithm using CFB mode.
func CryptSymmetricDecrypt(alg SymAlgorithmId, key, iv, data []byte) error {
switch alg {
case SymAlgorithmXOR, SymAlgorithmNull:
return errors.New("unsupported symmetric algorithm")
default:
c, err := alg.NewCipher(key)
if err != nil {
return xerrors.Errorf("cannot create cipher: %w", err)
}
// The TPM uses CFB cipher mode for all secret sharing
s := cipher.NewCFBDecrypter(c, iv)
s.XORKeyStream(data, data)
return nil
}
}
func zeroExtendBytes(x *big.Int, l int) (out []byte) {
out = make([]byte, l)
tmp := x.Bytes()
copy(out[len(out)-len(tmp):], tmp)
return
}
// CryptSecretEncrypt creates a secret value and its associated secret structure using
// the asymmetric algorithm defined by public. This is useful for sharing secrets with
// the TPM via the TPMContext.Import and TPMContext.ActivateCredential functions.
//
// It is also used internally by TPMContext.StartAuthSession.
func CryptSecretEncrypt(public *Public, label []byte) (EncryptedSecret, []byte, error) {
if !public.NameAlg.Available() {
return nil, nil, fmt.Errorf("nameAlg %v is not available", public.NameAlg)
}
digestSize := public.NameAlg.Size()
switch public.Type {
case ObjectTypeRSA:
if public.Params.RSADetail.Scheme.Scheme != RSASchemeNull &&
public.Params.RSADetail.Scheme.Scheme != RSASchemeOAEP {
return nil, nil, errors.New("unsupported RSA scheme")
}
pub := public.Public().(*rsa.PublicKey)
secret := make([]byte, digestSize)
if _, err := rand.Read(secret); err != nil {
return nil, nil, fmt.Errorf("cannot read random bytes for secret: %v", err)
}
h := public.NameAlg.NewHash()
label0 := make([]byte, len(label)+1)
copy(label0, label)
encryptedSecret, err := rsa.EncryptOAEP(h, rand.Reader, pub, secret, label0)
return encryptedSecret, secret, err
case ObjectTypeECC:
pub := public.Public().(*ecdsa.PublicKey)
if pub.Curve == nil {
return nil, nil, fmt.Errorf("unsupported curve: %v", public.Params.ECCDetail.CurveID.GoCurve())
}
if !pub.Curve.IsOnCurve(pub.X, pub.Y) {
return nil, nil, fmt.Errorf("public key is not on curve")
}
ephPriv, ephX, ephY, err := elliptic.GenerateKey(pub.Curve, rand.Reader)
if err != nil {
return nil, nil, fmt.Errorf("cannot generate ephemeral ECC key: %v", err)
}
sz := pub.Curve.Params().BitSize / 8
encryptedSecret, err := mu.MarshalToBytes(&ECCPoint{
X: zeroExtendBytes(ephX, sz),
Y: zeroExtendBytes(ephY, sz)})
if err != nil {
panic(fmt.Sprintf("failed to marshal secret: %v", err))
}
mulX, _ := pub.Curve.ScalarMult(pub.X, pub.Y, ephPriv)
secret := internal.KDFe(public.NameAlg.GetHash(),
zeroExtendBytes(mulX, sz), label, zeroExtendBytes(ephX, sz),
zeroExtendBytes(pub.X, sz), digestSize*8)
return encryptedSecret, secret, nil
default:
return nil, nil, fmt.Errorf("unsupported key type %v", public.Type)
}
}
./github.com/canonical/go-tpm2/doc.go 0000664 0000000 0000000 00000007117 00000000000 015721 0 ustar 00 0000000 0000000 /*
Package tpm2 implements an API for communicating with TPM 2.0 devices.
This documentation refers to TPM commands and types that are described in more detail in the TPM 2.0 Library Specification, which can
be found at https://trustedcomputinggroup.org/resource/tpm-library-specification/. Knowledge of this specification is assumed in this
documentation.
Communication with Linux TPM character devices and TPM simulators implementing the Microsoft TPM2 simulator interface is supported.
The core type by which consumers of this package communicate with a TPM is TPMContext.
Quick start
In order to create a new TPMContext that can be used to communicate with a Linux TPM character device:
tcti, err := linux.OpenDevice("/dev/tpm0")
if err != nil {
return err
}
tpm := tpm2.NewTPMContext(tcti)
In order to create and persist a new storage primary key:
tcti, err := linux.OpenDevice("/dev/tpm0")
if err != nil {
return err
}
tpm := tpm2.NewTPMContext(tcti)
template = tpm2.Public{
Type: tpm2.ObjectTypeRSA,
NameAlg: tpm2.HashAlgorithmSHA256,
Attrs: tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth | tpm2.AttrNoDA | tpm2.AttrRestricted | tpm2.AttrDecrypt,
Params: &tpm2.PublicParamsU{
RSADetail: &tpm2.RSAParams{
Symmetric: tpm2.SymDefObject{
Algorithm: tpm2.SymObjectAlgorithmAES,
KeyBits: &tpm2.SymKeyBitsU{Sym: 128},
Mode: &tpm2.SymModeU{Sym: tpm2.SymModeCFB}},
Scheme: tpm2.RSAScheme{Scheme: tpm2.RSASchemeNull},
KeyBits: 2048,
Exponent: 0}},
Unique: &tpm2.PublicIDU{RSA: make(tpm2.PublicKeyRSA, 256)}}
context, _, _, _, _, err := tpm.CreatePrimary(tpm.OwnerHandleContext(), nil, &template, nil, nil, nil)
if err != nil {
return err
}
persistentContext, err := tpm.EvictControl(tpm.OwnerHandleContext(), context, tpm2.Handle(0x81000001), nil)
if err != nil {
return err
}
// persistentContext is a ResourceContext corresponding to the new persistent storage primary key.
In order to evict a persistent object:
tcti, err := linux.OpenDevice("/dev/tpm0")
if err != nil {
return err
}
tpm := tpm2.NewTPMContext(tcti)
context, err := tpm.CreateResourceContextFromTPM(tpm2.Handle(0x81000001))
if err != nil {
return err
}
if _, err := tpm.EvictControl(tpm.OwnerHandleContext(), context, context.Handle(), nil); err != nil {
return err
}
// The resource associated with context is now unavailable.
Authorization types
Some TPM resources require authorization in order to use them in some commands. There are 3 main types of authorization supported by
this package:
* Cleartext password: A cleartext authorization value is sent to the TPM by calling ResourceContext.SetAuthValue and supplying the
ResourceContext to a function requiring authorization. Authorization succeeds if the correct value is sent.
* HMAC session: Knowledge of an authorization value is demonstrated by calling ResourceContext.SetAuthValue and supplying the ResourceContext
to a function requiring authorization, along with a session with the type SessionTypeHMAC. Authorization succeeds if the computed HMAC
matches that expected by the TPM.
* Policy session: A ResourceContext is supplied to a function requiring authorization along with a session with the type
SessionTypePolicy, containing a record of and the result of a sequence of assertions. Authorization succeeds if the conditions
required by the resource's authorization policy are satisfied.
The type of authorizations permitted for a resource is dependent on the authorization role (user, admin or duplication), the type of
resource and the resource's attributes.
*/
package tpm2
./github.com/canonical/go-tpm2/errors.go 0000664 0000000 0000000 00000075604 00000000000 016476 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"bytes"
"fmt"
"golang.org/x/xerrors"
)
const (
// AnyCommandCode is used to match any command code when using IsTPMError,
// IsTPMHandleError, IsTPMParameterError, IsTPMSessionError and IsTPMWarning.
AnyCommandCode CommandCode = 0xc0000000
// AnyErrorCode is used to match any error code when using IsTPMError,
// IsTPMHandleError, IsTPMParameterError and IsTPMSessionError.
AnyErrorCode ErrorCode = 0xff
// AnyHandle is used to match any handle when using IsResourceUnavailableError.
AnyHandle Handle = 0xffffffff
// AnyHandleIndex is used to match any handle when using IsTPMHandleError.
AnyHandleIndex int = -1
// AnyParameterIndex is used to match any parameter when using IsTPMParameterError.
AnyParameterIndex int = -1
// AnySessionIndex is used to match any session when using IsTPMSessionError.
AnySessionIndex int = -1
// AnyWarningCode is used to match any warning code when using IsTPMWarning.
AnyWarningCode WarningCode = 0xff
)
// ResourceUnavailableError is returned from TPMContext.CreateResourceContextFromTPM if
// it is called with a handle that does not correspond to a resource that is available
// on the TPM. This could be because the resource doesn't exist on the TPM, or it lives within
// a hierarchy that is disabled.
type ResourceUnavailableError struct {
Handle Handle
}
func (e ResourceUnavailableError) Error() string {
return fmt.Sprintf("a resource at handle 0x%08x is not available on the TPM", e.Handle)
}
func (e ResourceUnavailableError) Is(target error) bool {
t, ok := target.(ResourceUnavailableError)
if !ok {
return false
}
return t.Handle == AnyHandle || t.Handle == e.Handle
}
// InvalidResponseError is returned from any TPMContext method that executes a TPM command
// if the TPM's response is invalid. An invalid response could be one that is shorter than
// the response header, one with an invalid responseSize field, a payload size that doesn't
// match what the responseSize field indicates, a payload that unmarshals incorrectly or an
// invalid response authorization.
//
// Any sessions used in the command that caused this error should be considered invalid.
//
// If any function that executes a command which allocates objects on the TPM returns this
// error, it is possible that these objects were allocated and now exist on the TPM without
// a corresponding HandleContext being created or any knowledge of the handle of the object
// created.
//
// If any function that executes a command which removes objects from the TPM returns this
// error, it is possible that these objects were removed from the TPM. Any associated
// HandleContexts should be considered stale after this error.
type InvalidResponseError struct {
Command CommandCode
msg string
}
func (e *InvalidResponseError) Error() string {
return fmt.Sprintf("TPM returned an invalid response for command %s: %v", e.Command, e.msg)
}
// TctiError is returned from any TPMContext method if the underlying TCTI returns an error.
type TctiError struct {
Op string // The operation that caused the error
err error
}
func (e *TctiError) Error() string {
return fmt.Sprintf("cannot complete %s operation on TCTI: %v", e.Op, e.err)
}
func (e *TctiError) Unwrap() error {
return e.err
}
// TPM1Error is returned from DecodeResponseCode and any TPMContext method that executes a
// command on the TPM if the TPM response code indicates an error from a TPM 1.2 device.
type TPM1Error struct {
Command CommandCode // Command code associated with this error
Code ResponseCode // Response code
}
func (e *TPM1Error) Error() string {
return fmt.Sprintf("TPM returned a 1.2 error whilst executing command %s: 0x%08x", e.Command, e.Code)
}
// TPMVendorError is returned from DecodeResponseCode and and TPMContext method that executes
// a command on the TPM if the TPM response code indicates a vendor-specific error.
type TPMVendorError struct {
Command CommandCode // Command code associated with this error
Code ResponseCode // Response code
}
func (e *TPMVendorError) Error() string {
return fmt.Sprintf("TPM returned a vendor defined error whilst executing command %s: 0x%08x", e.Command, e.Code)
}
// WarningCode represents a response from the TPM that is not necessarily an
// error. It represents TCG defined format 0 errors that are warnings
// (represented by response codes 0x900 to 0x97f).
type WarningCode uint8
const (
WarningContextGap WarningCode = 0x01 // TPM_RC_CONTEXT_GAP
WarningObjectMemory WarningCode = 0x02 // TPM_RC_OBJECT_MEMORY
WarningSessionMemory WarningCode = 0x03 // TPM_RC_SESSION_MEMORY
WarningMemory WarningCode = 0x04 // TPM_RC_MEMORY
WarningSessionHandles WarningCode = 0x05 // TPM_RC_SESSION_HANDLES
WarningObjectHandles WarningCode = 0x06 // TPM_RC_OBJECT_HANDLES
// WarningLocality corresponds to TPM_RC_LOCALITY and is returned for a command if a policy session is used for authorization and the
// session includes a TPM2_PolicyLocality assertion, but the command isn't executed with the authorized locality.
WarningLocality WarningCode = 0x07
// WarningYielded corresponds to TPM_RC_YIELDED and is returned for any command that is suspended as a hint that the command can be
// retried. This is handled automatically by all methods on TPMContext that execute commands via TPMContext.RunCommand by
// resubmitting the command.
WarningYielded WarningCode = 0x08
// WarningCanceled corresponds to TPM_RC_CANCELED and is returned for any command that is canceled before being able to complete.
WarningCanceled WarningCode = 0x09
WarningTesting WarningCode = 0x0a // TPM_RC_TESTING
WarningReferenceH0 WarningCode = 0x10 // TPM_RC_REFERENCE_H0
WarningReferenceH1 WarningCode = 0x11 // TPM_RC_REFERENCE_H1
WarningReferenceH2 WarningCode = 0x12 // TPM_RC_REFERENCE_H2
WarningReferenceH3 WarningCode = 0x13 // TPM_RC_REFERENCE_H3
WarningReferenceH4 WarningCode = 0x14 // TPM_RC_REFERENCE_H4
WarningReferenceH5 WarningCode = 0x15 // TPM_RC_REFERENCE_H5
WarningReferenceH6 WarningCode = 0x16 // TPM_RC_REFERENCE_H6
WarningReferenceS0 WarningCode = 0x18 // TPM_RC_REFERENCE_S0
WarningReferenceS1 WarningCode = 0x19 // TPM_RC_REFERENCE_S1
WarningReferenceS2 WarningCode = 0x1a // TPM_RC_REFERENCE_S2
WarningReferenceS3 WarningCode = 0x1b // TPM_RC_REFERENCE_S3
WarningReferenceS4 WarningCode = 0x1c // TPM_RC_REFERENCE_S4
WarningReferenceS5 WarningCode = 0x1d // TPM_RC_REFERENCE_S5
WarningReferenceS6 WarningCode = 0x1e // TPM_RC_REFERENCE_S6
// WarningNVRate corresponds to TPM_RC_NV_RATE and is returned for any command that requires NV access if NV access is currently
// rate limited to prevent the NV memory from wearing out.
WarningNVRate WarningCode = 0x20
// WarningLockout corresponds to TPM_RC_LOCKOUT and is returned for any command that requires authorization for an entity that is
// subject to dictionary attack protection, and the TPM is in dictionary attack lockout mode.
WarningLockout WarningCode = 0x21
// WarningRetry corresponds to TPM_RC_RETRY and is returned for any command if the TPM was not able to start the command. This is
// handled automatically by all methods on TPMContext that execute commands via TPMContext.RunCommand by resubmitting the command.
WarningRetry WarningCode = 0x22
// WarningNVUnavailable corresponds to TPM_RC_NV_UNAVAILABLE and is returned for any command that requires NV access but NV memory
// is currently not available.
WarningNVUnavailable WarningCode = 0x23
)
// TPMWarning is returned from DecodeResponseCode and any TPMContext method that executes
// a command on the TPM if the TPM response code indicates a condition that is not necessarily
// an error.
type TPMWarning struct {
Command CommandCode // Command code associated with this error
Code WarningCode // Warning code
}
func (e *TPMWarning) ResponseCode() ResponseCode {
return responseCodeS | responseCodeV | (ResponseCode(e.Code) & responseCodeE0)
}
func (e *TPMWarning) Error() string {
var builder bytes.Buffer
fmt.Fprintf(&builder, "TPM returned a warning whilst executing command %s: %s", e.Command, e.Code)
if desc, hasDesc := warningCodeDescriptions[e.Code]; hasDesc {
fmt.Fprintf(&builder, " (%s)", desc)
}
return builder.String()
}
func (e *TPMWarning) Is(target error) bool {
t, ok := target.(*TPMWarning)
if !ok {
return false
}
return (t.Code == AnyWarningCode || t.Code == e.Code) && (t.Command == AnyCommandCode || t.Command == e.Command)
}
// ErrorCode represents an error code from the TPM. This type represents
// TCG defined format 0 errors with the exception of warnings (represented
// by response codes 0x100 to 0x17f), and format 1 errors (represented by
// response codes with bit 7 set). Format 0 error numbers are 7 bits wide
// and are represented by codes 0x00 to 0x7f. Format 1 errors numbers are
// 6 bits wide and are represented by codes 0x80 to 0xbf.
type ErrorCode uint8
const (
// ErrorInitialize corresponds to TPM_RC_INITIALIZE and is returned for any command executed between a _TPM_Init event and a
// TPM2_Startup command.
ErrorInitialize ErrorCode = 0x00
// ErrorFailure corresponds to TPM_RC_FAILURE and is returned for any command if the TPM is in failure mode.
ErrorFailure ErrorCode = 0x01
ErrorSequence ErrorCode = 0x03 // TPM_RC_SEQUENCE
ErrorDisabled ErrorCode = 0x20 // TPM_RC_DISABLED
ErrorExclusive ErrorCode = 0x21 // TPM_RC_EXCLUSIVE
// ErrorAuthType corresponds to TPM_RC_AUTH_TYPE and is returned for a command where an authorization is required and the
// authorization type is expected to be a policy session, but another authorization type has been provided.
ErrorAuthType ErrorCode = 0x24
// ErrorAuthMissing corresponds to TPM_RC_AUTH_MISSING and is returned for a command that accepts a HandleContext or Handle
// argument that requires authorization, but no authorization session has been provided in the command payload.
ErrorAuthMissing ErrorCode = 0x25
ErrorPolicy ErrorCode = 0x26 // TPM_RC_POLICY
ErrorPCR ErrorCode = 0x27 // TPM_RC_PCR
// ErrorPCRChanged corresponds to TPM_RC_PCR_CHANGED and is returned for a command where a policy session is used for authorization
// and the PCR contents have been updated since the last time that they were checked in the session with a TPM2_PolicyPCR assertion.
ErrorPCRChanged ErrorCode = 0x28
// ErrorUpgrade corresponds to TPM_RC_UPGRADE and is returned for any command that isn't TPM2_FieldUpgradeData if the TPM is in
// field upgrade mode.
ErrorUpgrade ErrorCode = 0x2d
ErrorTooManyContexts ErrorCode = 0x2e // TPM_RC_TOO_MANY_CONTEXTS
// ErrorAuthUnavailable corresponds to TPM_RC_AUTH_UNAVAILABLE and is returned for a command where the provided authorization
// requires the use of the authorization value for an entity, but the authorization value cannot be used. For example, if the entity
// is an object and the command requires the user auth role but the object does not have the AttrUserWithAuth attribute.
ErrorAuthUnavailable ErrorCode = 0x2f
// ErrorReboot corresponds to TPM_RC_REBOOT and is returned for any command if the TPM requires a _TPM_Init event before it will
// execute any more commands.
ErrorReboot ErrorCode = 0x30
ErrorUnbalanced ErrorCode = 0x31 // TPM_RC_UNBALANCED
// ErrorCommandSize corresponds to TPM_RC_COMMAND_SIZE and indicates that the value of the commandSize field in the command header
// does not match the size of the command packet transmitted to the TPM.
ErrorCommandSize ErrorCode = 0x42
// ErrorCommandCode corresponds to TPM_RC_COMMAND_CODE and is returned for any command that is not implemented by the TPM.
ErrorCommandCode ErrorCode = 0x43
ErrorAuthsize ErrorCode = 0x44 // TPM_RC_AUTHSIZE
// ErrorAuthContext corresponds to TPM_RC_AUTH_CONTEXT and is returned for any command that does not accept any sessions if
// sessions have been provided in the command payload.
ErrorAuthContext ErrorCode = 0x45
ErrorNVRange ErrorCode = 0x46 // TPM_RC_NV_RANGE
ErrorNVSize ErrorCode = 0x47 // TPM_RC_NV_SIZE
ErrorNVLocked ErrorCode = 0x48 // TPM_RC_NV_LOCKED
ErrorNVAuthorization ErrorCode = 0x49 // TPM_RC_NV_AUTHORIZATION
ErrorNVUninitialized ErrorCode = 0x4a // TPM_RC_NV_UNINITIALIZED
ErrorNVSpace ErrorCode = 0x4b // TPM_RC_NV_SPACE
ErrorNVDefined ErrorCode = 0x4c // TPM_RC_NV_DEFINED
ErrorBadContext ErrorCode = 0x50 // TPM_RC_BAD_CONTEXT
ErrorCpHash ErrorCode = 0x51 // TPM_RC_CPHASH
ErrorParent ErrorCode = 0x52 // TPM_RC_PARENT
ErrorNeedsTest ErrorCode = 0x53 // TPM_RC_NEEDS_TEST
// ErrorNoResult corresponds to TPM_RC_NO_RESULT and is returned for any command if the TPM cannot process a request due to an
// unspecified problem.
ErrorNoResult ErrorCode = 0x54
ErrorSensitive ErrorCode = 0x55 // TPM_RC_SENSITIVE
errorCode1Start ErrorCode = 0x80
ErrorAsymmetric ErrorCode = errorCode1Start + 0x01 // TPM_RC_ASYMMETRIC
// ErrorAttributes corresponds to TPM_RC_ATTRIBUTES and is returned as a *TPMSessionError for a command in the following
// circumstances:
// * More than one SessionContext instance with the AttrCommandEncrypt attribute has been provided.
// * More than one SessionContext instance with the AttrResponseEncrypt attribute has been provided.
// * A SessionContext instance referencing a trial session has been provided for authorization.
ErrorAttributes ErrorCode = errorCode1Start + 0x02
// ErrorHash corresponds to TPM_RC_HASH and is returned as a *TPMParameterError error for any command that accepts a AlgorithmId
// parameter that corresponds to the TPMI_ALG_HASH interface type if the parameter value is not a valid digest algorithm.
ErrorHash ErrorCode = errorCode1Start + 0x03
// ErrorValue corresponds to TPM_RC_VALUE and is returned as a *TPMParameterError or *TPMHandleError for any command where an
// argument value is incorrect or out of range for the command.
ErrorValue ErrorCode = errorCode1Start + 0x04 // TPM_RC_VALUE
// ErrorHierarchy corresponds to TPM_RC_HIERARCHY and is returned as a *TPMHandleError error for any command that accepts a
// HandleContext or Handle argument if that argument corresponds to a hierarchy on the TPM that has been disabled.
ErrorHierarchy ErrorCode = errorCode1Start + 0x05
ErrorKeySize ErrorCode = errorCode1Start + 0x07 // TPM_RC_KEY_SIZE
ErrorMGF ErrorCode = errorCode1Start + 0x08 // TPM_RC_MGF
// ErrorMode corresponds to TPM_RC_MODE and is returned as a *TPMParameterError error for any command that accepts a AlgorithmId
// parameter that corresponds to the TPMI_ALG_SYM_MODE interface type if the parameter value is not a valid symmetric mode.
ErrorMode ErrorCode = errorCode1Start + 0x09
// ErrorType corresponds to TPM_RC_TYPE and is returned as a *TPMParameterError error for any command that accepts a AlgorithmId
// parameter that corresponds to the TPMI_ALG_PUBLIC interface type if the parameter value is not a valid public type.
ErrorType ErrorCode = errorCode1Start + 0x0a
ErrorHandle ErrorCode = errorCode1Start + 0x0b // TPM_RC_HANDLE
// ErrorKDF corresponds to TPM_RC_KDF and is returned as a *TPMParameterError error for any command that accepts a AlgorithmId
// parameter that corresponds to the TPMI_ALG_KDF interface type if the parameter value is not a valid key derivation function.
ErrorKDF ErrorCode = errorCode1Start + 0x0c
ErrorRange ErrorCode = errorCode1Start + 0x0d // TPM_RC_RANGE
// ErrorAuthFail corresponds to TPM_RC_AUTH_FAIL and is returned as a *TPMSessionError error for a command if an authorization
// check fails. The dictionary attack counter is incremented when this error is returned.
ErrorAuthFail ErrorCode = errorCode1Start + 0x0e
// ErrorNonce corresponds to TPM_RC_NONCE and is returned as a *TPMSessionError error for any command where a password authorization
// has been provided and the authorization session in the command payload contains a non-zero sized nonce field.
ErrorNonce ErrorCode = errorCode1Start + 0x0f
// ErrorPP corresponds to TPM_RC_PP and is returned as a *TPMSessionError for a command in the following circumstances:
// * Authorization of the platform hierarchy is provided and the command requires an assertion of physical presence that hasn't been
// provided.
// * Authorization is provided with a policy session that includes the TPM2_PolicyPhysicalPresence assertion, and an assertion of
// physical presence hasn't been provided.
ErrorPP ErrorCode = errorCode1Start + 0x10
// ErrorScheme corresponds to TPM_RC_SCHEME and is returned as a *TPMParameterError error for any command that accepts a AlgorithmId
// parameter that corresponds to the TPMI_ALG_SIG_SCHEME or TPMI_ALG_ECC_SCHEME interface types if the parameter value is not a valid
// signature or ECC key exchange scheme.
ErrorScheme ErrorCode = errorCode1Start + 0x12
// ErrorSize corresponds to TPM_RC_SIZE and is returned for a command in the following circumstances:
// * As a *TPMParameterError if the command accepts a parameter type corresponding to TPM2B or TPML prefixed types and the size or
// length field has an invalid value.
// * As a *TPMHandleError with an unspecified handle if the TPM's parameter unmarshalling doesn't consume all of the bytes in the
// input buffer.
// * As a *TPMHandleError with an unspecified handle if the size field of the command's authorization area is an invalid value.
// * As a *TPMSessionError if the authorization area for a command payload contains more than 3 sessions.
ErrorSize ErrorCode = errorCode1Start + 0x15
// ErrorSymmetric corresponds to TPM_RC_SYMMETRIC and is returned for a command in the following circumstances:
// * As a *TPMParameterError if the command accepts a AlgorithmId parameter that corresponds to the TPMI_ALG_SYM interface type
// and the parameter value is not a valid symmetric algorithm.
// * As a *TPMSessionError if a SessionContext instance is provided with the AttrCommandEncrypt attribute set but the session has no
// symmetric algorithm.
// * As a *TPMSessionError if a SessionContext instance is provided with the AttrResponseEncrypt attribute set but the session has no
// symmetric algorithm.
ErrorSymmetric ErrorCode = errorCode1Start + 0x16
// ErrorTag corresponds to TPM_RC_TAG and is returned as a *TPMParameterError error for a command that accepts a StructTag parameter
// if the parameter value is not the correct value.
ErrorTag ErrorCode = errorCode1Start + 0x17
// ErrorSelector corresponds to TPM_RC_SELECTOR and is returned as a *TPMParameterError error for a command that accepts a parameter
// type corresponding to a TPMU prefixed type if the value of the selector field in the surrounding TPMT prefixed type is incorrect.
ErrorSelector ErrorCode = errorCode1Start + 0x18
// ErrorInsufficient corresponds to TPM_RC_INSUFFICIENT and is returned as a *TPMParameterError for a command if there is
// insufficient data in the TPM's input buffer to complete unmarshalling of the command parameters.
ErrorInsufficient ErrorCode = errorCode1Start + 0x1a
ErrorSignature ErrorCode = errorCode1Start + 0x1b // TPM_RC_SIGNATURE
ErrorKey ErrorCode = errorCode1Start + 0x1c // TPM_RC_KEY
// ErrorPolicyFail corresponds to TPM_RC_POLICY_FAIL and is returned as a *TPMSessionError error for a command in the following
// circumstances:
// * A policy session is used for authorization and the policy session digest does not match the authorization policy digest for
// the entity being authorized.
// * A policy session is used for authorization and the digest algorithm of the session does not match the name algorithm of the
// entity being authorized.
// * A policy session is used for authorization but the authorization is for the admin or DUP role and the policy session does not
// include a TPM2_PolicyCommandCode assertion.
// * A policy session is used for authorization and the policy session includes a TPM2_PolicyNvWritten assertion but the entity
// being authorized is not a NV index.
// * A policy session is used for authorization, the policy session includes the TPM2_PolicyNvWritten assertion, but the NV index
// being authorized does not have the AttrNVWritten attribute set.
ErrorPolicyFail ErrorCode = errorCode1Start + 0x1d
ErrorIntegrity ErrorCode = errorCode1Start + 0x1f // TPM_RC_INTEGRITY
ErrorTicket ErrorCode = errorCode1Start + 0x20 // TPM_RC_TICKET
// ErroReservedBits corresponds to TPM_RC_RESERVED_BITS and is returned as a *TPMParameterError error for a command that accepts
// a parameter type corresponding to a TPMA prefixed type if the parameter value has reserved bits set.
ErrorReservedBits ErrorCode = errorCode1Start + 0x21
// ErrorBadAuth corresponds to TPM_RC_BAD_AUTH and is returned as a *TPMSessionError error for a command if an authorization
// check fails and the authorized entity is excempt from dictionary attack protections.
ErrorBadAuth ErrorCode = errorCode1Start + 0x22
// ErrorExpired corresponds to TPM_RC_EXPIRED and is returned as a *TPMSessionError error for a command if a policy session is used
// for authorization, and the session has expired.
ErrorExpired ErrorCode = errorCode1Start + 0x23
// ErrorPolicyCC corresponds to TPM_RC_POLICY_CC and is returned as a *TPMSessionError error for a command if a policy session is
// used for authorization, the session includes a TPM2_PolicyCommandCode assertion, but the command code doesn't match the command
// for which the authorization is being used for.
ErrorPolicyCC ErrorCode = errorCode1Start + 0x24
ErrorBinding ErrorCode = errorCode1Start + 0x25 // TPM_RC_BINDING
// ErrorCurve corresponds to TPM_RC_CURVE and is returned as a *TPMParameterError for a command that accepts a ECCCurve parameter
// if the parameter value is incorrect.
ErrorCurve ErrorCode = errorCode1Start + 0x26
ErrorECCPoint ErrorCode = errorCode1Start + 0x27 // TPM_RC_ECC_POINT
// ErrorBadTag corresponds to TPM_RC_BAD_TAG and is returned from any TPM command if the command tag is invalid.
// This will be the error when trying to execute a TPM2 command on a TPM1.2 device.
ErrorBadTag ErrorCode = 0xde
)
// TPMError is returned from DecodeResponseCode and any TPMContext method that
// executes a command on the TPM if the TPM response code indicates an error that
// is not associated with a handle, parameter or session.
type TPMError struct {
Command CommandCode // Command code associated with this error
Code ErrorCode // Error code
}
func (e *TPMError) ResponseCode() ResponseCode {
switch {
case e.Code == ErrorBadTag:
return ResponseBadTag
case e.Code >= 0x80:
return responseCodeF | (ResponseCode(e.Code) & responseCodeE1)
default:
return responseCodeV | (ResponseCode(e.Code) & responseCodeE0)
}
}
func (e *TPMError) Error() string {
var builder bytes.Buffer
fmt.Fprintf(&builder, "TPM returned an error whilst executing command %s: %s", e.Command, e.Code)
if desc, hasDesc := errorCodeDescriptions[e.Code]; hasDesc {
fmt.Fprintf(&builder, " (%s)", desc)
}
return builder.String()
}
func (e *TPMError) Is(target error) bool {
t, ok := target.(*TPMError)
if !ok {
return false
}
return (t.Code == AnyErrorCode || t.Code == e.Code) && (t.Command == AnyCommandCode || t.Command == e.Command)
}
// TPMParameterError is returned from DecodeResponseCode and any TPMContext method
// that executes a command on the TPM if the TPM response code indicates an error
// that is associated with a command parameter. It wraps a *TPMError.
type TPMParameterError struct {
*TPMError
Index int // Index of the parameter associated with this error in the command parameter area, starting from 1
}
func (e *TPMParameterError) ResponseCode() ResponseCode {
return (ResponseCode(uint8(e.Index)&responseCodeIndex) << responseCodeIndexShift) | responseCodeF | responseCodeP | (ResponseCode(e.Code) & responseCodeE0)
}
func (e *TPMParameterError) Error() string {
var builder bytes.Buffer
fmt.Fprintf(&builder, "TPM returned an error for parameter %d whilst executing command %s: %s", e.Index, e.Command, e.Code)
if desc, hasDesc := errorCodeDescriptions[e.Code]; hasDesc {
fmt.Fprintf(&builder, " (%s)", desc)
}
return builder.String()
}
func (e *TPMParameterError) Is(target error) bool {
t, ok := target.(*TPMParameterError)
if !ok {
return false
}
return e.TPMError.Is(t.TPMError) && (t.Index == AnyParameterIndex || t.Index == e.Index)
}
func (e *TPMParameterError) Unwrap() error {
return e.TPMError
}
// TPMSessionError is returned from DecodeResponseCode and any TPMContext method
// that executes a command on the TPM if the TPM response code indicates an error
// that is associated with a session. It wraps a *TPMError.
type TPMSessionError struct {
*TPMError
Index int // Index of the session associated with this error in the authorization area, starting from 1
}
const (
responseCodeHandleIndex uint8 = 0x7
responseCodeIsSession uint8 = 0x8
)
func (e *TPMSessionError) ResponseCode() ResponseCode {
return (ResponseCode(responseCodeIsSession|(uint8(e.Index)&responseCodeHandleIndex)) << responseCodeIndexShift) | responseCodeF | (ResponseCode(e.Code) & responseCodeE0)
}
func (e *TPMSessionError) Error() string {
var builder bytes.Buffer
fmt.Fprintf(&builder, "TPM returned an error for session %d whilst executing command %s: %s", e.Index, e.Command, e.Code)
if desc, hasDesc := errorCodeDescriptions[e.Code]; hasDesc {
fmt.Fprintf(&builder, " (%s)", desc)
}
return builder.String()
}
func (e *TPMSessionError) Is(target error) bool {
t, ok := target.(*TPMSessionError)
if !ok {
return false
}
return e.TPMError.Is(t.TPMError) && (t.Index == AnySessionIndex || t.Index == e.Index)
}
func (e *TPMSessionError) Unwrap() error {
return e.TPMError
}
// TPMHandleError is returned from DecodeResponseCode and any TPMContext method that
// executes a command on the TPM if the TPM response code indicates an error that is
// associated with a command handle. It wraps a *TPMError.
type TPMHandleError struct {
*TPMError
// Index is the index of the handle associated with this error in the command handle area, starting from 1. An index of 0 corresponds
// to an unspecified handle
Index int
}
func (e *TPMHandleError) ResponseCode() ResponseCode {
return (ResponseCode(uint8(e.Index)&responseCodeHandleIndex) << responseCodeIndexShift) | responseCodeF | (ResponseCode(e.Code) & responseCodeE0)
}
func (e *TPMHandleError) Error() string {
var builder bytes.Buffer
fmt.Fprintf(&builder, "TPM returned an error for handle %d whilst executing command %s: %s", e.Index, e.Command, e.Code)
if desc, hasDesc := errorCodeDescriptions[e.Code]; hasDesc {
fmt.Fprintf(&builder, " (%s)", desc)
}
return builder.String()
}
func (e *TPMHandleError) Is(target error) bool {
t, ok := target.(*TPMHandleError)
if !ok {
return false
}
return e.TPMError.Is(t.TPMError) && (t.Index == AnyHandleIndex || t.Index == e.Index)
}
func (e *TPMHandleError) Unwrap() error {
return e.TPMError
}
// IsResourceUnavailableError indicates whether an error is a ResourceUnavailableError with
// the specified handle. To test for any handle, use AnyHandle.
func IsResourceUnavailableError(err error, handle Handle) bool {
return xerrors.Is(err, ResourceUnavailableError{Handle: handle})
}
// IsTPMError indicates whether the error or any error within its chain is a *TPMError with
// the specified ErrorCode and CommandCode. To test for any error code, use AnyErrorCode. To
// test for any command code, use AnyCommandCode.
func IsTPMError(err error, code ErrorCode, command CommandCode) bool {
return xerrors.Is(err, &TPMError{Command: command, Code: code})
}
// IsTPMHandleError indicates whether the error or any error within its chain is a
// *TPMHandleError with the specified ErrorCode, CommandCode and handle index. To test for
// any error code, use AnyErrorCode. To test for any command code, use AnyCommandCode. To
// test for any handle index, use AnyHandleIndex.
func IsTPMHandleError(err error, code ErrorCode, command CommandCode, handle int) bool {
return xerrors.Is(err, &TPMHandleError{TPMError: &TPMError{Command: command, Code: code}, Index: handle})
}
// IsTPMParameterError indicates whether the error or any error within its chain is a
// *TPMParameterError with the specified ErrorCode, CommandCode and parameter index. To test
// for any error code, use AnyErrorCode. To test for any command code, use AnyCommandCode.
// To test for any parameter index, use AnyParameterIndex.
func IsTPMParameterError(err error, code ErrorCode, command CommandCode, param int) bool {
return xerrors.Is(err, &TPMParameterError{TPMError: &TPMError{Command: command, Code: code}, Index: param})
}
// IsTPMSessionError indicates whether the error or any error within its chain is a
// *TPMSessionError with the specified ErrorCode, CommandCode and session index. To test for any
// error code, use AnyErrorCode. To test for any command code, use AnyCommandCode. To test for
// any session index, use AnySessionIndex.
func IsTPMSessionError(err error, code ErrorCode, command CommandCode, session int) bool {
return xerrors.Is(err, &TPMSessionError{TPMError: &TPMError{Command: command, Code: code}, Index: session})
}
// IsTPMWarning indicates whether the error or any error within its chain is a *TPMWarning with the
// specified WarningCode and CommandCode. To test for any warning code, use AnyWarningCode. To test
// for any command code, use AnyCommandCode.
func IsTPMWarning(err error, code WarningCode, command CommandCode) bool {
return xerrors.Is(err, &TPMWarning{Command: command, Code: code})
}
type InvalidResponseCodeError ResponseCode
func (e InvalidResponseCodeError) Error() string {
return fmt.Sprintf("invalid response code 0x%08x", ResponseCode(e))
}
// DecodeResponseCode decodes the ResponseCode provided via resp. If the specified response code is
// Success, it returns no error, else it returns an error that is appropriate for the response code.
// The command code is used for adding context to the returned error.
//
// If the response code is invalid, an InvalidResponseCodeError error will be returned.
func DecodeResponseCode(command CommandCode, resp ResponseCode) error {
switch {
case resp == ResponseSuccess:
return nil
case resp == ResponseBadTag:
return &TPMError{Command: command, Code: ErrorBadTag}
case resp.F():
// Format-one error codes
err := &TPMError{Command: command, Code: ErrorCode(resp.E()) + errorCode1Start}
switch {
case resp.P():
// Associated with a parameter
return &TPMParameterError{TPMError: err, Index: int(resp.N())}
case resp.N()&0x8 != 0:
// Associated with a session
return &TPMSessionError{TPMError: err, Index: int(resp.N() & 0x7)}
case resp.N() != 0:
// Associated with a handle
return &TPMHandleError{TPMError: err, Index: int(resp.N())}
default:
// Not associated with a specific parameter, session or handle
return err
}
default:
// Format-zero error codes
switch {
case !resp.V():
// A TPM1.2 error
return InvalidResponseCodeError(resp)
case resp.T():
// An error defined by the TPM vendor
return &TPMVendorError{Command: command, Code: resp}
case resp.S():
// A warning
return &TPMWarning{Command: command, Code: WarningCode(resp.E())}
default:
return &TPMError{Command: command, Code: ErrorCode(resp.E())}
}
}
}
./github.com/canonical/go-tpm2/internal/ 0000775 0000000 0000000 00000000000 00000000000 016433 5 ustar 00 0000000 0000000 ./github.com/canonical/go-tpm2/internal/crypto.go 0000664 0000000 0000000 00000003041 00000000000 020300 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package internal
import (
"bytes"
"crypto"
"encoding/binary"
"github.com/canonical/go-sp800.108-kdf"
)
func KDFa(hashAlg crypto.Hash, key, label, contextU, contextV []byte, sizeInBits int) []byte {
context := make([]byte, len(contextU)+len(contextV))
copy(context, contextU)
copy(context[len(contextU):], contextV)
return kdf.CounterModeKey(kdf.NewHMACPRF(hashAlg), key, label, context, uint32(sizeInBits))
}
func KDFe(hashAlg crypto.Hash, z, label, partyUInfo, partyVInfo []byte, sizeInBits int) []byte {
digestSize := hashAlg.Size()
counter := 0
var res bytes.Buffer
for bytes := (sizeInBits + 7) / 8; bytes > 0; bytes -= digestSize {
if bytes < digestSize {
digestSize = bytes
}
counter++
h := hashAlg.New()
binary.Write(h, binary.BigEndian, uint32(counter))
h.Write(z)
h.Write(label)
h.Write([]byte{0})
h.Write(partyUInfo)
h.Write(partyVInfo)
res.Write(h.Sum(nil)[0:digestSize])
}
outKey := res.Bytes()
if sizeInBits%8 != 0 {
outKey[0] &= ((1 << uint(sizeInBits%8)) - 1)
}
return outKey
}
func XORObfuscation(hashAlg crypto.Hash, key []byte, contextU, contextV, data []byte) {
context := make([]byte, len(contextU)+len(contextV))
copy(context, contextU)
copy(context[len(contextU):], contextV)
dataSize := len(data)
mask := kdf.CounterModeKey(kdf.NewHMACPRF(hashAlg), key, []byte("XOR"), context, uint32(dataSize*8))
for i := 0; i < dataSize; i++ {
data[i] ^= mask[i]
}
}
./github.com/canonical/go-tpm2/linux/ 0000775 0000000 0000000 00000000000 00000000000 015756 5 ustar 00 0000000 0000000 ./github.com/canonical/go-tpm2/linux/linux.go 0000664 0000000 0000000 00000004366 00000000000 017455 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
/*
Package linux provides an interface for communicating with TPMs using a Linux TPM character device
*/
package linux
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"golang.org/x/sys/unix"
"golang.org/x/xerrors"
"github.com/canonical/go-tpm2"
)
const (
maxCommandSize int = 4096
)
// TctiDevice represents a connection to a Linux TPM character device.
type TctiDevice struct {
f *os.File
buf *bytes.Reader
}
func (d *TctiDevice) readMoreData() error {
fds := []unix.PollFd{unix.PollFd{Fd: int32(d.f.Fd()), Events: unix.POLLIN}}
_, err := unix.Ppoll(fds, nil, nil)
if err != nil {
return xerrors.Errorf("polling device failed: %w", err)
}
if fds[0].Events != fds[0].Revents {
return fmt.Errorf("invalid poll events returned: %d", fds[0].Revents)
}
buf := make([]byte, maxCommandSize)
n, err := d.f.Read(buf)
if err != nil {
return xerrors.Errorf("reading from device failed: %w", err)
}
d.buf = bytes.NewReader(buf[:n])
return nil
}
func (d *TctiDevice) Read(data []byte) (int, error) {
if d.buf == nil {
if err := d.readMoreData(); err != nil {
return 0, err
}
}
n, err := d.buf.Read(data)
if err == io.EOF {
d.buf = nil
}
return n, err
}
func (d *TctiDevice) Write(data []byte) (int, error) {
return d.f.Write(data)
}
func (d *TctiDevice) Close() error {
return d.f.Close()
}
func (d *TctiDevice) SetLocality(locality uint8) error {
return errors.New("not implemented")
}
func (d *TctiDevice) MakeSticky(handle tpm2.Handle, sticky bool) error {
return errors.New("not implemented")
}
// OpenDevice attempts to open a connection to the Linux TPM character device at
// the specified path. If successful, it returns a new TctiDevice instance which
// can be passed to tpm2.NewTPMContext. Failure to open the TPM character device
// will result in a *os.PathError being returned.
func OpenDevice(path string) (*TctiDevice, error) {
f, err := os.OpenFile(path, os.O_RDWR, 0)
if err != nil {
return nil, err
}
s, err := f.Stat()
if err != nil {
return nil, err
}
if s.Mode()&os.ModeDevice == 0 {
return nil, fmt.Errorf("unsupported file mode %v", s.Mode())
}
return &TctiDevice{f: f}, nil
}
./github.com/canonical/go-tpm2/mu/ 0000775 0000000 0000000 00000000000 00000000000 015240 5 ustar 00 0000000 0000000 ./github.com/canonical/go-tpm2/mu/doc.go 0000664 0000000 0000000 00000005065 00000000000 016342 0 ustar 00 0000000 0000000 /*
Package mu provides helpers to marshalling to and unmarshalling from the TPM wire format.
Go types are marshalled to and from the TPM wire format according to the following rules:
* UINT8 <-> uint8
* BYTE <-> byte
* INT8 <-> int8
* BOOL <-> bool
* UINT16 <-> uint16
* INT16 <-> int16
* UINT32 <-> uint32
* INT32 <-> int32
* UINT64 <-> uint64
* INT64 <-> int64
* TPM2B prefixed types (sized buffers with a 2-byte size field) fall in to 2 categories:
* Byte buffer <-> []byte, or any type with an identical underlying type.
* Sized structure <-> struct referenced from a pointer field in another structure, where the field has the `tpm2:"sized"`
tag. A zero sized struct is represented as a nil pointer.
* TPMA prefixed types (attributes) <-> whichever go type corresponds to the underlying TPM type (UINT8, UINT16, or UINT32).
* TPM_ALG_ID (algorithm enum) <-> tpm2.AlgorithmId
* TPML prefixed types (lists with a 4-byte length field) <-> slice of whichever go type corresponds to the underlying TPM type.
* TPMS prefixed types (structures) <-> struct
* TPMT prefixed types (structures with a tag field used as a union selector) <-> struct
* TPMU prefixed types (unions) <-> struct which implements the Union interface. These must be contained in another structure
and referenced from a pointer field. The first field of the enclosing structure is used as the selector value, although this
can be overridden by using the `tpm2:"selector:"` tag.
TPMI prefixed types (interface types) are generally not explicitly supported. These are used by the TPM for type checking during
unmarshalling, but this package doesn't distinguish between TPMI prefixed types with the same underlying type.
The marshalling code parses the "tpm2" tag on struct fields, the value of which is a comma separated list of options. These options are:
* selector: - used on fields that are pointers to structs that implement the Union interface to specify the field
used as the selector value. The default behaviour without this option is to use the first field as the selector.
* sized - used on fields that are pointers to indicate that it should be marshalled and unmarshalled as a sized value.
A nil pointer represents a zero-sized value. This is used to implement sized structures.
* raw - used on slice fields to indicate that it should be marshalled and unmarshalled without a length (if it represents a list)
or size (if it represents a sized buffer) field. The slice must be pre-allocated to the correct length by the caller during
unmarshalling.
*/
package mu
./github.com/canonical/go-tpm2/mu/mu.go 0000664 0000000 0000000 00000072437 00000000000 016225 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package mu
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"os"
"reflect"
"runtime"
"strings"
"golang.org/x/xerrors"
)
var (
customMuType reflect.Type = reflect.TypeOf((*customMuIface)(nil)).Elem()
emptyIfaceType reflect.Type = reflect.TypeOf((*interface{})(nil)).Elem()
unionType reflect.Type = reflect.TypeOf((*Union)(nil)).Elem()
nilValueType reflect.Type = reflect.TypeOf(NilUnionValue)
rawBytesType reflect.Type = reflect.TypeOf(RawBytes(nil))
)
// InvalidSelectorError may be returned as a wrapped error from UnmarshalFromBytes or UnmarshalFromReader when a union type indicates
// that a selector value is invalid.
type InvalidSelectorError struct {
Selector reflect.Value
}
func (e *InvalidSelectorError) Error() string {
return fmt.Sprintf("invalid selector value: %v", e.Selector)
}
type customMuIface interface {
CustomMarshaller
CustomUnmarshaller
}
// CustomMarshaller is implemented by types that require custom marshalling behaviour because they are non-standard and not
// directly supported by the marshalling code. This interface should be implemented by types with a value receiver if you want
// to be able to pass it directly by value to MarshalToBytes or MarshalToWriter. Implementations must also implement the
// CustomUnmarshaller interface.
//
// If the custom implementation makes a recursive call in the MarshalToWriter, it should propagate errors from
// this without wrapping. This allows the full context of the error to be surfaced from the originating call.
type CustomMarshaller interface {
Marshal(w io.Writer) error
}
// CustomUnmarshaller is implemented by types that require custom unmarshalling behaviour because they are non-standard and not
// directly supported by the marshalling code. This interface must be implemented by types with a pointer receiver, and types
// must also implement the CustomMarshaller interface.
//
// If the custom implementation makes a recursive call in the UnmarshalFromReader, it should propagate errors from
// this without wrapping. This allows the full context of the error to be surfaced from the originating call.
type CustomUnmarshaller interface {
Unmarshal(r Reader) error
}
type empty struct{}
// NilUnionValue is a special value, the type of which should be returned from implementations of Union.Select to indicate
// that a union contains no data for a particular selector value.
var NilUnionValue empty
// RawBytes is a special byte slice type which is marshalled and unmarshalled without a size field. The slice must be pre-allocated to
// the correct length by the caller during unmarshalling.
type RawBytes []byte
func Raw(val interface{}) interface{} {
return &struct {
Value interface{} `tpm2:"raw"`
}{val}
}
// Sized converts the supplied value to a sized value.
//
// To marshal a sized value, the supplied value must be a pointer to the actual
// value.
//
// To unmarshal a sized value, the supplied value must be a pointer to the
// destionation pointer that will point to the unmarshalled value.
func Sized(val interface{}) interface{} {
return &struct {
Value interface{} `tpm2:"sized"`
}{val}
}
// Union is implemented by structure types that correspond to TPMU prefixed TPM types.
type Union interface {
// Select is called by the marshalling code to map the supplied selector to a field. The returned value must be a pointer to
// the field to be marshalled or unmarshalled. To work correctly during marshalling and unmarshalling, implementations must
// take a pointer receiver. If no data should be marshalled or unmarshalled, it should return NilUnionValue.
Select(selector reflect.Value) interface{}
}
type containerNode struct {
value reflect.Value
index int
entry [1]uintptr
}
type containerStack []containerNode
func (s containerStack) push(node containerNode) containerStack {
return append(s, node)
}
func (s containerStack) pop() containerStack {
return s[:len(s)-1]
}
func (s containerStack) top() containerNode {
return s[len(s)-1]
}
func (s containerStack) String() string {
str := new(bytes.Buffer)
str.WriteString("=== BEGIN STACK ===\n")
for i := len(s) - 1; i >= 0; i-- {
switch {
case reflect.PtrTo(s[i].value.Type()).Implements(customMuType):
frames := runtime.CallersFrames(s[i].entry[:])
frame, _ := frames.Next()
fmt.Fprintf(str, "... %s custom type, call from %s:%d argument %d\n", s[i].value.Type(), frame.File, frame.Line, s[i].index)
case s[i].value.Kind() == reflect.Struct:
fmt.Fprintf(str, "... %s field %s\n", s[i].value.Type(), s[i].value.Type().Field(s[i].index).Name)
case s[i].value.Kind() == reflect.Slice:
fmt.Fprintf(str, "... %s index %d\n", s[i].value.Type(), s[i].index)
default:
panic("unsupported kind")
}
}
str.WriteString("=== END STACK ===\n")
return str.String()
}
// Error is returned from any function in this package to provide context
// of where an error occurred.
type Error struct {
// Index indicates the argument on which this error occurred.
Index int
Op string
total int
entry [1]uintptr
stack containerStack
leafType reflect.Type
err error
}
func (e *Error) Error() string {
s := new(bytes.Buffer)
fmt.Fprintf(s, "cannot %s argument ", e.Op)
if e.total > 1 {
fmt.Fprintf(s, "%d ", e.Index)
}
fmt.Fprintf(s, "whilst processing element of type %s: %v", e.leafType, e.err)
if len(e.stack) != 0 {
fmt.Fprintf(s, "\n\n%s", e.stack)
}
return s.String()
}
func (e *Error) Unwrap() error {
return e.err
}
// Type returns the type of the value on which this error occurred.
func (e *Error) Type() reflect.Type {
return e.leafType
}
// Depth returns the depth of the value on which this error occurred.
func (e *Error) Depth() int {
return len(e.stack)
}
// Container returns the type of the container at the specified depth.
//
// If the returned type is a structure, the returned index corresponds
// to the index of the field in that structure.
//
// If the returned type is a slice, the returned index corresponds to
// the index in that slice.
//
// If the returned type implements the CustomMarshaller and
// CustomUnmarshaller interfaces, the returned index corresponds to
// the argument index in the recursive call in to one of the marshalling
// or unmarshalling APIs. The returned frame indicates where this
// recursive call originated from.
func (e *Error) Container(depth int) (containerType reflect.Type, index int, entry runtime.Frame) {
var frame runtime.Frame
if reflect.PtrTo(e.stack[depth].value.Type()).Implements(customMuType) {
frames := runtime.CallersFrames(e.stack[depth].entry[:])
frame, _ = frames.Next()
}
return e.stack[depth].value.Type(), e.stack[depth].index, frame
}
func newError(value reflect.Value, c *context, err error) error {
if err == io.EOF {
// All io.EOF is unexpected
err = io.ErrUnexpectedEOF
}
muErr, isMuErr := err.(*Error)
stack := make(containerStack, len(c.stack))
copy(stack, c.stack)
var leafType reflect.Type
if isMuErr {
// This is an error returned from a custom type.
// Preserve the original error
err = muErr.err
// Copy the leaf type to the new error
leafType = muErr.leafType
// Append the original error stack to the new error.
stack = stack.push(containerNode{value: value, index: muErr.Index, entry: muErr.entry})
stack = append(stack, muErr.stack...)
} else {
leafType = value.Type()
}
return &Error{
Index: c.index,
Op: c.mode,
total: c.total,
entry: c.caller,
stack: stack,
leafType: leafType,
err: err}
}
type options struct {
selector string
sized bool
raw bool
}
func parseStructFieldMuOptions(f reflect.StructField) (out options) {
s := f.Tag.Get("tpm2")
for _, part := range strings.Split(s, ",") {
switch {
case strings.HasPrefix(part, "selector:"):
out.selector = part[9:]
case part == "sized":
out.sized = true
case part == "raw":
out.raw = true
}
}
return
}
type context struct {
caller [1]uintptr
mode string
index int
total int
stack containerStack
options options
}
func (c *context) enterStructField(s reflect.Value, i int) (f reflect.Value, exit func()) {
origOptions := c.options
c.options = parseStructFieldMuOptions(s.Type().Field(i))
c.stack = c.stack.push(containerNode{value: s, index: i})
return s.Field(i), func() {
c.stack = c.stack.pop()
c.options = origOptions
}
}
func (c *context) enterListElem(l reflect.Value, i int) (elem reflect.Value, exit func()) {
origOptions := c.options
c.options = options{}
c.stack = c.stack.push(containerNode{value: l, index: i})
return l.Index(i), func() {
c.stack = c.stack.pop()
c.options = origOptions
}
}
func (c *context) enterUnionElem(u reflect.Value) (elem reflect.Value, exit func(), err error) {
if len(c.stack) == 0 {
panic(fmt.Sprintf("union type %s is not inside a container", u.Type()))
}
var selectorVal reflect.Value
if c.options.selector == "" {
selectorVal = c.stack.top().value.Field(0)
} else {
selectorVal = c.stack.top().value.FieldByName(c.options.selector)
if !selectorVal.IsValid() {
panic(fmt.Sprintf("selector name %s for union type %s does not reference a valid field\n%s",
c.options.selector, u.Type(), c.stack))
}
}
p := u.Addr().Interface().(Union).Select(selectorVal)
switch {
case p == nil:
return reflect.Value{}, nil, &InvalidSelectorError{selectorVal}
case p == NilUnionValue:
return reflect.Value{}, nil, nil
}
pv := reflect.ValueOf(p)
index := -1
for i := 0; i < u.NumField(); i++ {
if u.Field(i).Addr().Interface() == pv.Interface() {
index = i
break
}
}
if index == -1 {
panic(fmt.Sprintf("Union.Select implementation for type %s returned a non-member pointer\n%s",
u.Type(), c.stack))
}
origOptions := c.options
c.options.selector = ""
c.stack = c.stack.push(containerNode{value: u, index: index})
return pv.Elem(), func() {
c.stack = c.stack.pop()
c.options = origOptions
}, nil
}
func (c *context) enterSizedType(v reflect.Value) (exit func()) {
switch {
case v.Kind() == reflect.Ptr:
case v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8:
default:
panic(fmt.Sprintf("invalid sized type: %v", v.Type()))
}
origOptions := c.options
c.options.sized = false
if v.Kind() == reflect.Slice {
c.options.raw = true
}
return func() {
c.options = origOptions
}
}
// TPMKind indicates the TPM type class associated with a Go type
type TPMKind int
const (
// TPMKindUnsupported indicates that a go type has no corresponding
// TPM type class.
TPMKindUnsupported TPMKind = iota
// TPMKindPrimitive indicates that a go type corresponds to one
// of the primitive TPM types (UINT8, BYTE, INT8, BOOL, UINT16,
// INT16, UINT32, INT32, UINT64, INT64, TPM_ALG_ID, any TPMA_
// prefixed type).
TPMKindPrimitive
// TPMKindSized indicates that a go type corresponds to a
// TPM2B prefixed TPM type.
TPMKindSized
// TPMKindList indicates that a go type corresponds to a
// TPML prefixed TPM type.
TPMKindList
// TPMKindStruct indicates that a go type corresponds to a
// TPMS or TPMT prefixed TPM type - this package doesn't
// distinguish between the two.
TPMKindStruct
// TPMKindUnion indicates that a go type corresponds to a
// TPMU prefixed TPM type.
TPMKindUnion
// TPMKindCustom correponds to a go type that defines its own
// marshalling behaviour.
TPMKindCustom
// TPMKindRaw corresponds to a go slice that is marshalled
// without a size field (it is not TPMKindSized or TPMKindList).
TPMKindRaw
)
func tpmKind(t reflect.Type, opts *options) TPMKind {
if opts.sized {
return TPMKindSized
}
if t == emptyIfaceType {
t = t.Elem()
}
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if reflect.PtrTo(t).Implements(customMuType) {
return TPMKindCustom
}
switch t.Kind() {
case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return TPMKindPrimitive
case reflect.Slice:
switch {
case t == rawBytesType:
return TPMKindRaw
case opts.raw:
return TPMKindRaw
case t.Elem().Kind() == reflect.Uint8:
return TPMKindSized
default:
return TPMKindList
}
case reflect.Struct:
if reflect.PtrTo(t).Implements(unionType) {
return TPMKindUnion
}
return TPMKindStruct
default:
return TPMKindUnsupported
}
}
// DetermineTPMKind returns the TPMKind associated with the supplied go value. It will automatically dereference pointer types.
// Single field structures will be unwrapped and the TPMKind associated with the structure field will be returned.
func DetermineTPMKind(i interface{}) TPMKind {
t := reflect.TypeOf(i)
var opts options
for {
k := tpmKind(t, &opts)
if k != TPMKindStruct {
return k
}
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.NumField() != 1 {
return k
}
f := t.Field(0)
opts = parseStructFieldMuOptions(f)
t = f.Type
}
}
type marshaller struct {
*context
w io.Writer
nbytes int
}
func (m *marshaller) Write(p []byte) (n int, err error) {
n, err = m.w.Write(p)
m.nbytes += n
return
}
func (m *marshaller) marshalSized(v reflect.Value) error {
exit := m.enterSizedType(v)
defer exit()
if v.IsNil() {
if err := binary.Write(m, binary.BigEndian, uint16(0)); err != nil {
return newError(v, m.context, err)
}
return nil
}
tmpBuf := new(bytes.Buffer)
sm := &marshaller{context: m.context, w: tmpBuf}
if err := sm.marshalValue(v); err != nil {
return err
}
if tmpBuf.Len() > math.MaxUint16 {
return newError(v, m.context, errors.New("sized value size greater than 2^16-1"))
}
if err := binary.Write(m, binary.BigEndian, uint16(tmpBuf.Len())); err != nil {
return newError(v, m.context, err)
}
if _, err := tmpBuf.WriteTo(m); err != nil {
return newError(v, m.context, err)
}
return nil
}
func (m *marshaller) marshalRawList(v reflect.Value) error {
for i := 0; i < v.Len(); i++ {
elem, exit := m.enterListElem(v, i)
if err := m.marshalValue(elem); err != nil {
exit()
return err
}
exit()
}
return nil
}
func (m *marshaller) marshalRaw(v reflect.Value) error {
switch v.Type().Elem().Kind() {
case reflect.Uint8:
if _, err := m.Write(v.Bytes()); err != nil {
return newError(v, m.context, err)
}
return nil
default:
return m.marshalRawList(v)
}
}
func (m *marshaller) marshalPtr(v reflect.Value) error {
p := v
if v.IsNil() {
p = reflect.New(v.Type().Elem())
}
return m.marshalValue(p.Elem())
}
func (m *marshaller) marshalPrimitive(v reflect.Value) error {
if err := binary.Write(m, binary.BigEndian, v.Interface()); err != nil {
return newError(v, m.context, err)
}
return nil
}
func (m *marshaller) marshalList(v reflect.Value) error {
// int is either 32-bits or 64-bits. We can't compare slice.Len() to math.MaxUint32 when int is 32-bits and it isn't
// necessary anyway. For the case where int is 64-bits, truncate to uint32 then zero extend it again to int to make
// sure the original number was preserved.
if int(uint32(v.Len())) != v.Len() {
return newError(v, m.context, errors.New("slice length greater than 2^32-1"))
}
// Marshal length field
if err := binary.Write(m, binary.BigEndian, uint32(v.Len())); err != nil {
return newError(v, m.context, err)
}
return m.marshalRawList(v)
}
func (m *marshaller) marshalStruct(v reflect.Value) error {
for i := 0; i < v.NumField(); i++ {
f, exit := m.enterStructField(v, i)
if err := m.marshalValue(f); err != nil {
exit()
return err
}
exit()
}
return nil
}
func (m *marshaller) marshalUnion(v reflect.Value) error {
// Ignore during marshalling - let the TPM unmarshalling catch it
elem, exit, _ := m.enterUnionElem(v)
if !elem.IsValid() {
return nil
}
defer exit()
return m.marshalValue(elem)
}
func (m *marshaller) marshalCustom(v reflect.Value) error {
if err := v.Interface().(CustomMarshaller).Marshal(m); err != nil {
return newError(v, m.context, err)
}
return nil
}
func (m *marshaller) marshalValue(v reflect.Value) error {
kind := tpmKind(v.Type(), &m.options)
if v.Type() == emptyIfaceType {
v = v.Elem()
}
if v.Kind() == reflect.Ptr && kind != TPMKindSized {
return m.marshalPtr(v)
}
switch kind {
case TPMKindPrimitive:
return m.marshalPrimitive(v)
case TPMKindSized:
return m.marshalSized(v)
case TPMKindList:
return m.marshalList(v)
case TPMKindStruct:
return m.marshalStruct(v)
case TPMKindUnion:
return m.marshalUnion(v)
case TPMKindCustom:
return m.marshalCustom(v)
case TPMKindRaw:
return m.marshalRaw(v)
}
panic(fmt.Sprintf("cannot marshal unsupported type %s", v.Type()))
}
func (m *marshaller) marshal(vals ...interface{}) (int, error) {
m.total = len(vals)
m.nbytes = 0
for i, v := range vals {
m.index = i
if err := m.marshalValue(reflect.ValueOf(v)); err != nil {
return m.nbytes, err
}
}
return m.nbytes, nil
}
// Reader is an interface that groups the io.Reader interface with an additional method to
// obtain the remaining number of bytes that can be read for implementations that support this.
type Reader interface {
io.Reader
Len() int
}
type unmarshaller struct {
*context
r io.Reader
sz int64
nbytes int
}
func (u *unmarshaller) Read(p []byte) (n int, err error) {
n, err = u.r.Read(p)
u.nbytes += n
return
}
func (u *unmarshaller) Len() int {
return int(u.sz - int64(u.nbytes))
}
func startingSizeOfReader(r io.Reader) (int64, error) {
switch rImpl := r.(type) {
case *os.File:
fi, err := rImpl.Stat()
if err != nil {
return 0, err
}
if fi.Mode().IsRegular() {
start, err := rImpl.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err
}
return fi.Size() - start, nil
}
case *bytes.Reader:
return int64(rImpl.Len()), nil
case *bytes.Buffer:
return int64(rImpl.Len()), nil
case *io.SectionReader:
start, _ := rImpl.Seek(0, io.SeekCurrent)
return rImpl.Size() - start, nil
case *io.LimitedReader:
sz, err := startingSizeOfReader(rImpl.R)
if err != nil {
return 0, err
}
if rImpl.N < sz {
sz = rImpl.N
}
return sz, nil
}
return 1<<63 - 1, nil
}
func makeUnmarshaller(ctx *context, r io.Reader) (*unmarshaller, error) {
sz, err := startingSizeOfReader(r)
if err != nil {
return nil, err
}
return &unmarshaller{context: ctx, r: r, sz: sz}, nil
}
func (u *unmarshaller) unmarshalSized(v reflect.Value) error {
exit := u.enterSizedType(v)
defer exit()
var size uint16
if err := binary.Read(u, binary.BigEndian, &size); err != nil {
return newError(v, u.context, err)
}
switch {
case size == 0 && !v.IsNil() && v.Kind() == reflect.Ptr:
return newError(v, u.context, errors.New("sized value is zero sized, but destination value has been pre-allocated"))
case size == 0:
return nil
case int(size) > u.Len():
return newError(v, u.context, errors.New("sized value has a size larger than the remaining bytes"))
case v.Kind() == reflect.Slice:
v.Set(reflect.MakeSlice(v.Type(), int(size), int(size)))
}
su, err := makeUnmarshaller(u.context, io.LimitReader(u, int64(size)))
if err != nil {
return newError(v, u.context, xerrors.Errorf("cannot create new reader for sized payload: %w", err))
}
return su.unmarshalValue(v)
}
func (u *unmarshaller) unmarshalRawList(v reflect.Value, n int) (reflect.Value, error) {
for i := 0; i < n; i++ {
v = reflect.Append(v, reflect.Zero(v.Type().Elem()))
elem, exit := u.enterListElem(v, i)
if err := u.unmarshalValue(elem); err != nil {
exit()
return reflect.Value{}, err
}
exit()
}
return v, nil
}
func (u *unmarshaller) unmarshalRaw(v reflect.Value) error {
switch v.Type().Elem().Kind() {
case reflect.Uint8:
if _, err := io.ReadFull(u, v.Bytes()); err != nil {
return newError(v, u.context, err)
}
return nil
default:
_, err := u.unmarshalRawList(v.Slice(0, 0), v.Len())
return err
}
}
func (u *unmarshaller) unmarshalPtr(v reflect.Value) error {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
return u.unmarshalValue(v.Elem())
}
func (u *unmarshaller) unmarshalPrimitive(v reflect.Value) error {
if err := binary.Read(u, binary.BigEndian, v.Addr().Interface()); err != nil {
return newError(v, u.context, err)
}
return nil
}
func (u *unmarshaller) unmarshalList(v reflect.Value) error {
// Unmarshal the length
var length uint32
if err := binary.Read(u, binary.BigEndian, &length); err != nil {
return newError(v, u.context, err)
}
if v.IsNil() || v.Cap() < int(length) {
v.Set(reflect.MakeSlice(v.Type(), 0, int(length)))
}
s, err := u.unmarshalRawList(v.Slice(0, 0), int(length))
if err != nil {
return err
}
v.Set(s)
return nil
}
func (u *unmarshaller) unmarshalStruct(v reflect.Value) error {
for i := 0; i < v.NumField(); i++ {
elem, exit := u.enterStructField(v, i)
if err := u.unmarshalValue(elem); err != nil {
exit()
return err
}
exit()
}
return nil
}
func (u *unmarshaller) unmarshalUnion(v reflect.Value) error {
elem, exit, err := u.enterUnionElem(v)
if err != nil {
return newError(v, u.context, err)
}
if !elem.IsValid() {
return nil
}
defer exit()
return u.unmarshalValue(elem)
}
func (u *unmarshaller) unmarshalCustom(v reflect.Value) error {
if err := v.Addr().Interface().(CustomUnmarshaller).Unmarshal(u); err != nil {
return newError(v, u.context, err)
}
return nil
}
func (u *unmarshaller) unmarshalValue(v reflect.Value) error {
kind := tpmKind(v.Type(), &u.options)
if v.Type() == emptyIfaceType {
v = v.Elem()
}
if v.Kind() == reflect.Ptr && kind != TPMKindSized {
return u.unmarshalPtr(v)
}
switch kind {
case TPMKindPrimitive:
return u.unmarshalPrimitive(v)
case TPMKindSized:
return u.unmarshalSized(v)
case TPMKindList:
return u.unmarshalList(v)
case TPMKindStruct:
return u.unmarshalStruct(v)
case TPMKindUnion:
return u.unmarshalUnion(v)
case TPMKindCustom:
return u.unmarshalCustom(v)
case TPMKindRaw:
return u.unmarshalRaw(v)
}
panic(fmt.Sprintf("cannot unmarshal unsupported type %s", v.Type()))
}
func (u *unmarshaller) unmarshal(vals ...interface{}) (int, error) {
u.total = len(vals)
u.nbytes = 0
for i, v := range vals {
u.index = i
if err := u.unmarshalValue(reflect.ValueOf(v).Elem()); err != nil {
return u.nbytes, err
}
}
return u.nbytes, nil
}
func marshalToWriter(skip int, w io.Writer, vals ...interface{}) (int, error) {
var caller [1]uintptr
runtime.Callers(skip+1, caller[:])
m := &marshaller{context: &context{caller: caller, mode: "marshal"}, w: w}
return m.marshal(vals...)
}
// MarshalToWriter marshals vals to w in the TPM wire format, according to the rules specified in the package description.
//
// Pointers are automatically dereferenced. Nil pointers are marshalled to the zero value for the pointed to type, unless
// the pointer is to a sized structure (a struct field with the 'tpm2:"sized"` tag pointing to another struct), in which case
// a value of zero size is marshalled.
//
// The number of bytes written to w are returned. If this function does not complete successfully, it will return an error and
// the number of bytes written.
//
// This function only returns an error if a sized value (sized buffer, sized structure or list) is too large for its corresponding
// size field, or if the supplied io.Writer returns an error.
func MarshalToWriter(w io.Writer, vals ...interface{}) (int, error) {
return marshalToWriter(2, w, vals...)
}
// MustMarshalToWriter is the same as MarshalToWriter, except that it panics if it encounters an error.
func MustMarshalToWriter(w io.Writer, vals ...interface{}) int {
n, err := marshalToWriter(2, w, vals...)
if err != nil {
panic(err)
}
return n
}
func marshalToBytes(skip int, vals ...interface{}) ([]byte, error) {
buf := new(bytes.Buffer)
if _, err := marshalToWriter(skip+1, buf, vals...); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// MarshalToBytes marshals vals to TPM wire format, according to the rules specified in the package description.
//
// Pointers are automatically dereferenced. Nil pointers are marshalled to the zero value for the pointed to type, unless
// the pointer is to a sized structure (a struct field with the 'tpm2:"sized"` tag pointing to another struct), in which case
// a value of zero size is marshalled.
//
// The number of bytes written to w are returned. If this function does not complete successfully, it will return an error and
// the number of bytes written.
//
// This function only returns an error if a sized value (sized buffer, sized structure or list) is too large for its corresponding
// size field.
func MarshalToBytes(vals ...interface{}) ([]byte, error) {
return marshalToBytes(2, vals...)
}
// MustMarshalToBytes is the same as MarshalToBytes, except that it panics if it encounters an error.
func MustMarshalToBytes(vals ...interface{}) []byte {
b, err := marshalToBytes(2, vals...)
if err != nil {
panic(err)
}
return b
}
func unmarshalFromReader(skip int, r io.Reader, vals ...interface{}) (int, error) {
var caller [1]uintptr
runtime.Callers(skip+1, caller[:])
for _, val := range vals {
v := reflect.ValueOf(val)
if v.Kind() != reflect.Ptr {
panic(fmt.Sprintf("cannot unmarshal to non-pointer type %s", v.Type()))
}
if v.IsNil() {
panic(fmt.Sprintf("cannot unmarshal to nil pointer of type %s", v.Type()))
}
}
u, err := makeUnmarshaller(&context{caller: caller, mode: "unmarshal"}, r)
if err != nil {
return 0, err
}
return u.unmarshal(vals...)
}
// UnmarshalFromReader unmarshals data in the TPM wire format from r to vals, according to the rules specified in the package
// description. The values supplied to this function must be pointers to the destination values.
//
// Pointers are automatically dererefenced. If a pointer is nil, then memory is allocated for the values and the pointer
// is initialized accordingly, unless the pointer is to a sized structure (a struct field with the 'tpm2:"sized"' tag pointing
// to another struct) and the values being unmarshalled has a zero size, in which case the pointer is not initialized. If
// a pointer is already initialized by the caller, then this function will unmarshal to the already allocated memory.
//
// Slices are allocated automatically, unless the caller has already allocated a slice that has a large enough capacity
// to hold the unmarshalled values, in which case the already allocated slice will be used and its length set accordingly.
//
// This can unmarshal raw slices (those without a corresponding size or length fields, represented by the RawBytes type or
// a slice value referenced from a struct field with the 'tpm2:"raw"' tag), but the caller must pre-allocate a slice of the
// correct size first. This function cannot allocate a slice because it doesn't have a way to determine the size to allocate.
//
// The number of bytes read from r are returned. If this function does not complete successfully, it will return an error and
// the number of bytes read. In this case, partial results may have been unmarshalled to the supplied destination values.
func UnmarshalFromReader(r io.Reader, vals ...interface{}) (int, error) {
return unmarshalFromReader(2, r, vals...)
}
// UnmarshalFromReader unmarshals data in the TPM wire format from b to vals, according to the rules specified in the package
// description. The values supplied to this function must be pointers to the destination values.
//
// Pointers are automatically dererefenced. If a pointer is nil, then memory is allocated for the value and the pointer
// is initialized accordingly, unless the pointer is to a sized structure (a struct field with the 'tpm2:"sized"' tag pointing
// to another struct) and the value being unmarshalled has a zero size, in which case the pointer is not initialized. If
// a pointer is already initialized by the caller, then this function will unmarshal to the already allocated memory.
//
// Slices are allocated automatically, unless the caller has already allocated a slice that has a large enough capacity
// to hold the unmarshalled values, in which case the already allocated slice will be used and its length set accordingly.
//
// This can unmarshal raw slices (those without a corresponding size or length fields, represented by the RawBytes type or
// a slice value referenced from a struct field with the 'tpm2:"raw"' tag), but the caller must pre-allocate a slice of the
// correct size first. This function cannot allocate a slice because it doesn't have a way to determine the size to allocate.
//
// The number of bytes consumed from b are returned. If this function does not complete successfully, it will return an error and
// the number of bytes consumed. In this case, partial results may have been unmarshalled to the supplied destination values.
func UnmarshalFromBytes(b []byte, vals ...interface{}) (int, error) {
buf := bytes.NewReader(b)
return unmarshalFromReader(2, buf, vals...)
}
func copyValue(skip int, dst, src interface{}) error {
buf := new(bytes.Buffer)
if _, err := marshalToWriter(skip+1, buf, src); err != nil {
return err
}
_, err := unmarshalFromReader(skip+1, buf, dst)
return err
}
// CopyValue copies the value of src to dst. The destination must be a pointer to the actual
// destination value. This works by serializing the source value in the TPM wire format
// and the deserializing it again into the destination.
//
// This will return an error for any reason that would cause MarshalToBytes or
// UnmarshalFromBytes to return an error.
func CopyValue(dst, src interface{}) error {
return copyValue(2, dst, src)
}
// MustCopyValue is the same as CopyValue except that it panics if it encounters an error.
func MustCopyValue(dst, src interface{}) {
if err := copyValue(2, dst, src); err != nil {
panic(err)
}
}
./github.com/canonical/go-tpm2/paramcrypt.go 0000664 0000000 0000000 00000007035 00000000000 017335 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"crypto/aes"
"encoding/binary"
"errors"
"fmt"
"github.com/canonical/go-tpm2/internal"
"github.com/canonical/go-tpm2/mu"
)
func isParamEncryptable(param interface{}) bool {
return mu.DetermineTPMKind(param) == mu.TPMKindSized
}
func (s *sessionParam) computeSessionValue() []byte {
var key []byte
key = append(key, s.session.Data().SessionKey...)
if s.IsAuth() {
key = append(key, s.associatedContext.(resourceContextPrivate).GetAuthValue()...)
}
return key
}
func (p *sessionParams) findDecryptSession() (*sessionParam, int) {
return p.findSessionWithAttr(AttrCommandEncrypt)
}
func (p *sessionParams) findEncryptSession() (*sessionParam, int) {
return p.findSessionWithAttr(AttrResponseEncrypt)
}
func (p *sessionParams) hasDecryptSession() bool {
s, _ := p.findDecryptSession()
return s != nil
}
func (p *sessionParams) computeEncryptNonce() {
s, i := p.findEncryptSession()
if s == nil || i == 0 || !p.sessions[0].IsAuth() {
return
}
ds, di := p.findDecryptSession()
if ds != nil && di == i {
return
}
p.sessions[0].encryptNonce = s.session.NonceTPM()
}
func (p *sessionParams) encryptCommandParameter(cpBytes []byte) error {
s, i := p.findDecryptSession()
if s == nil {
return nil
}
sessionData := s.session.Data()
hashAlg := sessionData.HashAlg
sessionValue := s.computeSessionValue()
size := binary.BigEndian.Uint16(cpBytes)
data := cpBytes[2 : size+2]
symmetric := sessionData.Symmetric
switch symmetric.Algorithm {
case SymAlgorithmAES:
if symmetric.Mode.Sym != SymModeCFB {
return errors.New("unsupported cipher mode")
}
k := internal.KDFa(hashAlg.GetHash(), sessionValue, []byte(CFBKey), sessionData.NonceCaller, sessionData.NonceTPM,
int(symmetric.KeyBits.Sym)+(aes.BlockSize*8))
offset := (symmetric.KeyBits.Sym + 7) / 8
symKey := k[0:offset]
iv := k[offset:]
if err := CryptSymmetricEncrypt(symmetric.Algorithm, symKey, iv, data); err != nil {
return fmt.Errorf("AES encryption failed: %v", err)
}
case SymAlgorithmXOR:
internal.XORObfuscation(hashAlg.GetHash(), sessionValue, sessionData.NonceCaller, sessionData.NonceTPM, data)
default:
return fmt.Errorf("unknown symmetric algorithm: %v", symmetric.Algorithm)
}
if i > 0 && p.sessions[0].IsAuth() {
p.sessions[0].decryptNonce = sessionData.NonceTPM
}
return nil
}
func (p *sessionParams) decryptResponseParameter(rpBytes []byte) error {
s, _ := p.findEncryptSession()
if s == nil {
return nil
}
sessionData := s.session.Data()
hashAlg := sessionData.HashAlg
sessionValue := s.computeSessionValue()
size := binary.BigEndian.Uint16(rpBytes)
data := rpBytes[2 : size+2]
symmetric := sessionData.Symmetric
switch symmetric.Algorithm {
case SymAlgorithmAES:
if symmetric.Mode.Sym != SymModeCFB {
return errors.New("unsupported cipher mode")
}
k := internal.KDFa(hashAlg.GetHash(), sessionValue, []byte(CFBKey), sessionData.NonceTPM, sessionData.NonceCaller,
int(symmetric.KeyBits.Sym)+(aes.BlockSize*8))
offset := (symmetric.KeyBits.Sym + 7) / 8
symKey := k[0:offset]
iv := k[offset:]
if err := CryptSymmetricDecrypt(symmetric.Algorithm, symKey, iv, data); err != nil {
return fmt.Errorf("AES encryption failed: %v", err)
}
case SymAlgorithmXOR:
internal.XORObfuscation(hashAlg.GetHash(), sessionValue, sessionData.NonceTPM, sessionData.NonceCaller, data)
default:
return fmt.Errorf("unknown symmetric algorithm: %v", symmetric.Algorithm)
}
return nil
}
./github.com/canonical/go-tpm2/resources.go 0000664 0000000 0000000 00000051425 00000000000 017167 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"bytes"
"crypto"
_ "crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"io"
"reflect"
"github.com/canonical/go-tpm2/mu"
"golang.org/x/xerrors"
)
// HandleContext corresponds to an entity that resides on the TPM. Implementations of HandleContext maintain some host-side
// state in order to be able to participate in HMAC sessions. They are invalidated when used in a command that results in the
// entity being flushed or evicted from the TPM. Once invalidated, they can no longer be used.
type HandleContext interface {
// Handle returns the handle of the corresponding entity on the TPM. If the HandleContext has been invalidated then this will
// return HandleUnassigned.
Handle() Handle
Name() Name // The name of the entity
SerializeToBytes() []byte // Return a byte slice containing the serialized form of this HandleContext
SerializeToWriter(io.Writer) error // Write the serialized form of this HandleContext to the supplied io.Writer
}
type handleContextPrivate interface {
invalidate()
}
// SessionContext is a HandleContext that corresponds to a session on the TPM.
type SessionContext interface {
HandleContext
NonceTPM() Nonce // The most recent TPM nonce value
IsAudit() bool // Whether the session has been used for audit
IsExclusive() bool // Whether the most recent response from the TPM indicated that the session is exclusive for audit purposes
SetAttrs(attrs SessionAttributes) // Set the attributes that will be used for this SessionContext
WithAttrs(attrs SessionAttributes) SessionContext // Return a duplicate of this SessionContext with the specified attributes
// IncludeAttrs returns a duplicate of this SessionContext and its attributes with the specified attributes included.
IncludeAttrs(attrs SessionAttributes) SessionContext
// ExcludeAttrs returns a duplicate of this SessionContext and its attributes with the specified attributes excluded.
ExcludeAttrs(attrs SessionAttributes) SessionContext
}
// ResourceContext is a HandleContext that corresponds to a non-session entity on the TPM.
type ResourceContext interface {
HandleContext
// SetAuthValue sets the authorization value that will be used in authorization roles where knowledge of the authorization
// value is required. Functions that create resources on the TPM and return a ResourceContext will set this automatically,
// else it will need to be set manually.
SetAuthValue([]byte)
}
type resourceContextPrivate interface {
GetAuthValue() []byte
}
type handleContextType uint8
const (
handleContextTypePartial handleContextType = iota
handleContextTypePermanent
handleContextTypeObject
handleContextTypeNvIndex
handleContextTypeSession
)
type sessionContextData struct {
IsAudit bool
IsExclusive bool
HashAlg HashAlgorithmId
SessionType SessionType
PolicyHMACType policyHMACType
IsBound bool
BoundEntity Name
SessionKey []byte
NonceCaller Nonce
NonceTPM Nonce
Symmetric *SymDef
}
type handleContextU struct {
Object *Public
NV *NVPublic
Session *sessionContextData
}
func (d *handleContextU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(handleContextType) {
case handleContextTypePartial, handleContextTypePermanent:
return mu.NilUnionValue
case handleContextTypeObject:
return &d.Object
case handleContextTypeNvIndex:
return &d.NV
case handleContextTypeSession:
return &d.Session
default:
return nil
}
}
type handleContext struct {
Type handleContextType
H Handle
N Name
Data *handleContextU
}
func (h *handleContext) Handle() Handle {
return h.H
}
func (h *handleContext) Name() Name {
return h.N
}
func (h *handleContext) SerializeToBytes() []byte {
data := mu.MustMarshalToBytes(h)
hash := crypto.SHA256.New()
hash.Write(data)
return mu.MustMarshalToBytes(HashAlgorithmSHA256, hash.Sum(nil), data)
}
func (h *handleContext) SerializeToWriter(w io.Writer) error {
data := mu.MustMarshalToBytes(h)
hash := crypto.SHA256.New()
hash.Write(data)
_, err := mu.MarshalToWriter(w, HashAlgorithmSHA256, hash.Sum(nil), data)
return err
}
func (h *handleContext) invalidate() {
h.H = HandleUnassigned
h.N = make(Name, binary.Size(Handle(0)))
binary.BigEndian.PutUint32(h.N, uint32(h.H))
}
func (h *handleContext) checkConsistency() error {
switch h.Type {
case handleContextTypePermanent:
switch h.Handle().Type() {
case HandleTypePCR, HandleTypePermanent:
default:
return errors.New("inconsistent handle type for permanent context")
}
if h.Name().Type() != NameTypeHandle || h.Name().Handle() != h.Handle() {
return errors.New("name inconsistent with handle for permanent context")
}
case handleContextTypeObject:
switch h.Handle().Type() {
case HandleTypeTransient, HandleTypePersistent:
default:
return errors.New("inconsistent handle type for object context")
}
if h.Data.Object == nil {
return errors.New("no public area for object context")
}
if !h.Data.Object.compareName(h.Name()) {
return errors.New("name inconsistent with public area for object context")
}
case handleContextTypeNvIndex:
if h.Handle().Type() != HandleTypeNVIndex {
return errors.New("inconsistent handle type for NV context")
}
if h.Data.NV == nil {
return errors.New("no public area for NV context")
}
if !h.Data.NV.compareName(h.Name()) {
return errors.New("name inconsistent with public area for NV context")
}
case handleContextTypeSession:
switch h.Handle().Type() {
case HandleTypeHMACSession, HandleTypePolicySession:
default:
return errors.New("inconsistent handle type for session context")
}
if h.Name().Type() != NameTypeHandle || h.Name().Handle() != h.Handle() {
return errors.New("name inconsistent with handle for session context")
}
scData := h.Data.Session
if scData != nil {
if !scData.IsAudit && scData.IsExclusive {
return errors.New("inconsistent audit attributes for session context")
}
if !scData.HashAlg.Available() {
return errors.New("invalid digest algorithm for session context")
}
switch scData.SessionType {
case SessionTypeHMAC, SessionTypePolicy, SessionTypeTrial:
default:
return errors.New("invalid session type for session context")
}
if scData.PolicyHMACType > policyHMACTypeMax {
return errors.New("invalid policy session HMAC type for session context")
}
if (scData.IsBound && len(scData.BoundEntity) == 0) || (!scData.IsBound && len(scData.BoundEntity) > 0) {
return errors.New("invalid bind properties for session context")
}
digestSize := scData.HashAlg.Size()
if len(scData.SessionKey) != digestSize && len(scData.SessionKey) != 0 {
return errors.New("unexpected session key size for session context")
}
if len(scData.NonceCaller) != digestSize || len(scData.NonceTPM) != digestSize {
return errors.New("unexpected nonce size for session context")
}
switch scData.Symmetric.Algorithm {
case SymAlgorithmAES, SymAlgorithmXOR, SymAlgorithmNull:
default:
return errors.New("invalid symmetric algorithm for session context")
}
if scData.Symmetric.Algorithm == SymAlgorithmAES && scData.Symmetric.Mode.Sym != SymModeCFB {
return errors.New("invalid symmetric mode for session context")
}
}
default:
return errors.New("unrecognized context type")
}
return nil
}
func makePartialHandleContext(handle Handle) *handleContext {
name := make(Name, binary.Size(Handle(0)))
binary.BigEndian.PutUint32(name, uint32(handle))
return &handleContext{
Type: handleContextTypePartial,
H: handle,
N: name}
}
type resourceContext struct {
handleContext
authValue []byte
}
func (r *resourceContext) SetAuthValue(authValue []byte) {
r.authValue = authValue
}
func (r *resourceContext) GetAuthValue() []byte {
return bytes.TrimRight(r.authValue, "\x00")
}
type permanentContext struct {
resourceContext
}
func (r *permanentContext) invalidate() {}
func makePermanentContext(handle Handle) *permanentContext {
name := make(Name, binary.Size(Handle(0)))
binary.BigEndian.PutUint32(name, uint32(handle))
return &permanentContext{
resourceContext: resourceContext{
handleContext: handleContext{
Type: handleContextTypePermanent,
H: handle,
N: name}}}
}
type objectContext struct {
resourceContext
}
func (r *objectContext) GetPublic() *Public {
return r.Data.Object
}
func makeObjectContext(handle Handle, name Name, public *Public) *objectContext {
return &objectContext{
resourceContext: resourceContext{
handleContext: handleContext{
Type: handleContextTypeObject,
H: handle,
N: name,
Data: &handleContextU{Object: public}}}}
}
func (t *TPMContext) makeObjectContextFromTPM(context HandleContext, sessions ...SessionContext) (ResourceContext, error) {
pub, name, _, err := t.ReadPublic(context, sessions...)
if err != nil {
return nil, err
}
if n, err := pub.Name(); err != nil {
return nil, &InvalidResponseError{CommandReadPublic, fmt.Sprintf("cannot compute name of returned public area: %v", err)}
} else if !bytes.Equal(n, name) {
return nil, &InvalidResponseError{CommandReadPublic, "name and public area don't match"}
}
return makeObjectContext(context.Handle(), name, pub), nil
}
type nvIndexContext struct {
resourceContext
}
func (r *nvIndexContext) GetPublic() *NVPublic {
return r.Data.NV
}
func (r *nvIndexContext) SetAttr(a NVAttributes) {
r.Data.NV.Attrs |= a
name, _ := r.Data.NV.Name()
r.N = name
}
func (r *nvIndexContext) ClearAttr(a NVAttributes) {
r.Data.NV.Attrs &= ^a
name, _ := r.Data.NV.Name()
r.N = name
}
func (r *nvIndexContext) Attrs() NVAttributes {
return r.Data.NV.Attrs
}
func makeNVIndexContext(name Name, public *NVPublic) *nvIndexContext {
return &nvIndexContext{
resourceContext: resourceContext{
handleContext: handleContext{
Type: handleContextTypeNvIndex,
H: public.Index,
N: name,
Data: &handleContextU{NV: public}}}}
}
func (t *TPMContext) makeNVIndexContextFromTPM(context HandleContext, sessions ...SessionContext) (ResourceContext, error) {
pub, name, err := t.NVReadPublic(context, sessions...)
if err != nil {
return nil, err
}
if n, err := pub.Name(); err != nil {
return nil, &InvalidResponseError{CommandNVReadPublic, fmt.Sprintf("cannot compute name of returned public area: %v", err)}
} else if !bytes.Equal(n, name) {
return nil, &InvalidResponseError{CommandNVReadPublic, "name and public area don't match"}
}
if pub.Index != context.Handle() {
return nil, &InvalidResponseError{CommandNVReadPublic, "unexpected index in public area"}
}
return makeNVIndexContext(name, pub), nil
}
type sessionContext struct {
*handleContext
attrs SessionAttributes
}
func (r *sessionContext) NonceTPM() Nonce {
d := r.Data()
if d == nil {
return nil
}
return d.NonceTPM
}
func (r *sessionContext) IsAudit() bool {
d := r.Data()
if d == nil {
return false
}
return d.IsAudit
}
func (r *sessionContext) IsExclusive() bool {
d := r.Data()
if d == nil {
return false
}
return d.IsExclusive
}
func (r *sessionContext) SetAttrs(attrs SessionAttributes) {
r.attrs = attrs
}
func (r *sessionContext) WithAttrs(attrs SessionAttributes) SessionContext {
return &sessionContext{handleContext: r.handleContext, attrs: attrs}
}
func (r *sessionContext) IncludeAttrs(attrs SessionAttributes) SessionContext {
return &sessionContext{handleContext: r.handleContext, attrs: r.attrs | attrs}
}
func (r *sessionContext) ExcludeAttrs(attrs SessionAttributes) SessionContext {
return &sessionContext{handleContext: r.handleContext, attrs: r.attrs &^ attrs}
}
func (r *sessionContext) Data() *sessionContextData {
return r.handleContext.Data.Session
}
func makeSessionContext(handle Handle, data *sessionContextData) *sessionContext {
name := make(Name, binary.Size(Handle(0)))
binary.BigEndian.PutUint32(name, uint32(handle))
return &sessionContext{
handleContext: &handleContext{
Type: handleContextTypeSession,
H: handle,
N: name,
Data: &handleContextU{Session: data}}}
}
func (t *TPMContext) makeResourceContextFromTPM(handle HandleContext, sessions ...SessionContext) (rc ResourceContext, err error) {
switch handle.Handle().Type() {
case HandleTypeNVIndex:
rc, err = t.makeNVIndexContextFromTPM(handle, sessions...)
case HandleTypeTransient, HandleTypePersistent:
rc, err = t.makeObjectContextFromTPM(handle, sessions...)
default:
panic("invalid handle type")
}
switch {
case IsTPMWarning(err, WarningReferenceH0, AnyCommandCode):
return nil, ResourceUnavailableError{handle.Handle()}
case IsTPMHandleError(err, ErrorHandle, AnyCommandCode, AnyHandleIndex):
return nil, ResourceUnavailableError{handle.Handle()}
case err != nil:
return nil, err
}
return rc, nil
}
// CreateResourceContextFromTPM creates and returns a new ResourceContext for the
// specified handle. It will execute a command to read the public area from the TPM
// in order to initialize state that is maintained on the host side. A
// ResourceUnavailableError error will be returned if the specified handle references.
//
// The public area and name returned from the TPM are checked for consistency.
//
// If any sessions are supplied, the public area is read from the TPM twice. The
// second time uses the supplied sessions.
//
// This function will panic if handle doesn't correspond to a NV index, transient object
// or persistent object.
//
// If subsequent use of the returned ResourceContext requires knowledge of the authorization
// value of the corresponding TPM resource, this should be provided by calling
// ResourceContext.SetAuthValue.
func (t *TPMContext) CreateResourceContextFromTPM(handle Handle, sessions ...SessionContext) (ResourceContext, error) {
rc, err := t.makeResourceContextFromTPM(makePartialHandleContext(handle))
if err != nil {
return nil, err
}
if len(sessions) == 0 {
return rc, nil
}
return t.makeResourceContextFromTPM(rc, sessions...)
}
// CreatePartialHandleContext creates a new HandleContext for the specified handle. The
// returned HandleContext is partial and cannot be used in any command other than
// TPMContext.FlushContext, TPMContext.ReadPublic or TPMContext.NVReadPublic.
//
// This function will panic if handle doesn't correspond to a session, transient or
// persistent object, or NV index.
func CreatePartialHandleContext(handle Handle) HandleContext {
switch handle.Type() {
case HandleTypeNVIndex, HandleTypeHMACSession, HandleTypePolicySession, HandleTypeTransient, HandleTypePersistent:
return makePartialHandleContext(handle)
default:
panic("invalid handle type")
}
}
// GetPermanentContext returns a ResourceContext for the specified permanent handle or PCR handle.
//
// This function will panic if handle does not correspond to a permanent or PCR handle.
//
// If subsequent use of the returned ResourceContext requires knowledge of the authorization value of the corresponding TPM resource,
// this should be provided by calling ResourceContext.SetAuthValue.
func (t *TPMContext) GetPermanentContext(handle Handle) ResourceContext {
switch handle.Type() {
case HandleTypePermanent, HandleTypePCR:
if rc, exists := t.permanentResources[handle]; exists {
return rc
}
rc := makePermanentContext(handle)
t.permanentResources[handle] = rc
return rc
default:
panic("invalid handle type")
}
}
// OwnerHandleContext returns the ResouceContext corresponding to the owner hiearchy.
func (t *TPMContext) OwnerHandleContext() ResourceContext {
return t.GetPermanentContext(HandleOwner)
}
// NulHandleContext returns the ResourceContext corresponding to the null hiearchy.
func (t *TPMContext) NullHandleContext() ResourceContext {
return t.GetPermanentContext(HandleNull)
}
// LockoutHandleContext returns the ResourceContext corresponding to the lockout hiearchy.
func (t *TPMContext) LockoutHandleContext() ResourceContext {
return t.GetPermanentContext(HandleLockout)
}
// EndorsementHandleContext returns the ResourceContext corresponding to the endorsement hiearchy.
func (t *TPMContext) EndorsementHandleContext() ResourceContext {
return t.GetPermanentContext(HandleEndorsement)
}
// PlatformHandleContext returns the ResourceContext corresponding to the platform hiearchy.
func (t *TPMContext) PlatformHandleContext() ResourceContext {
return t.GetPermanentContext(HandlePlatform)
}
// PlatformNVHandleContext returns the ResourceContext corresponding to the platform hiearchy.
func (t *TPMContext) PlatformNVHandleContext() ResourceContext {
return t.GetPermanentContext(HandlePlatformNV)
}
// PCRHandleContext returns the ResourceContext corresponding to the PCR at the specified index. It will panic if pcr is not a valid
// PCR index.
func (t *TPMContext) PCRHandleContext(pcr int) ResourceContext {
h := Handle(pcr)
if h.Type() != HandleTypePCR {
panic("invalid PCR index")
}
return t.GetPermanentContext(h)
}
// CreateHandleContextFromReader returns a new HandleContext created from the serialized data read from the supplied io.Reader. This
// should contain data that was previously created by HandleContext.SerializeToBytes or HandleContext.SerializeToWriter.
//
// If the supplied data corresponds to a session then a SessionContext will be returned, else a ResourceContext will be returned.
//
// If a ResourceContext is returned and subsequent use of it requires knowledge of the authorization value of the corresponding TPM
// resource, this should be provided by calling ResourceContext.SetAuthValue.
func CreateHandleContextFromReader(r io.Reader) (HandleContext, error) {
var integrityAlg HashAlgorithmId
var integrity []byte
var b []byte
if _, err := mu.UnmarshalFromReader(r, &integrityAlg, &integrity, &b); err != nil {
return nil, xerrors.Errorf("cannot unpack context blob and checksum: %w", err)
}
if !integrityAlg.Available() {
return nil, errors.New("invalid checksum algorithm")
}
h := integrityAlg.NewHash()
h.Write(b)
if !bytes.Equal(h.Sum(nil), integrity) {
return nil, errors.New("invalid checksum")
}
var data *handleContext
n, err := mu.UnmarshalFromBytes(b, &data)
if err != nil {
return nil, xerrors.Errorf("cannot unmarshal context data: %w", err)
}
if n < len(b) {
return nil, errors.New("context blob contains trailing bytes")
}
if data.Type == handleContextTypePermanent {
return nil, errors.New("cannot create a permanent context from serialized data")
}
if err := data.checkConsistency(); err != nil {
return nil, err
}
var hc HandleContext
switch data.Type {
case handleContextTypeObject:
hc = &objectContext{resourceContext: resourceContext{handleContext: *data}}
case handleContextTypeNvIndex:
hc = &nvIndexContext{resourceContext: resourceContext{handleContext: *data}}
case handleContextTypeSession:
hc = &sessionContext{handleContext: data}
default:
panic("not reached")
}
return hc, nil
}
// CreateHandleContextFromBytes returns a new HandleContext created from the serialized data read from the supplied byte slice. This
// should contain data that was previously created by HandleContext.SerializeToBytes or HandleContext.SerializeToWriter.
//
// If the supplied data corresponds to a session then a SessionContext will be returned, else a ResourceContext will be returned.
//
// If a ResourceContext is returned and subsequent use of it requires knowledge of the authorization value of the corresponding TPM
// resource, this should be provided by calling ResourceContext.SetAuthValue.
func CreateHandleContextFromBytes(b []byte) (HandleContext, int, error) {
buf := bytes.NewReader(b)
rc, err := CreateHandleContextFromReader(buf)
if err != nil {
return nil, 0, err
}
return rc, len(b) - buf.Len(), nil
}
// CreateNVIndexResourceContextFromPublic returns a new ResourceContext created from the provided public area. If subsequent use of
// the returned ResourceContext requires knowledge of the authorization value of the corresponding TPM resource, this should be
// provided by calling ResourceContext.SetAuthValue.
func CreateNVIndexResourceContextFromPublic(pub *NVPublic) (ResourceContext, error) {
name, err := pub.Name()
if err != nil {
return nil, fmt.Errorf("cannot compute name from public area: %v", err)
}
rc := makeNVIndexContext(name, pub)
if err := rc.checkConsistency(); err != nil {
return nil, err
}
return rc, nil
}
// CreateObjectResourceContextFromPublic returns a new ResourceContext created from the provided public area. If subsequent use of
// the returned ResourceContext requires knowledge of the authorization value of the corresponding TPM resource, this should be
// provided by calling ResourceContext.SetAuthValue.
func CreateObjectResourceContextFromPublic(handle Handle, pub *Public) (ResourceContext, error) {
name, err := pub.Name()
if err != nil {
return nil, fmt.Errorf("cannot compute name from public area: %v", err)
}
rc := makeObjectContext(handle, name, pub)
if err := rc.checkConsistency(); err != nil {
return nil, err
}
return rc, nil
}
./github.com/canonical/go-tpm2/run-tests 0000664 0000000 0000000 00000000074 00000000000 016507 0 ustar 00 0000000 0000000 #!/bin/sh -e
go test -v -race -p 1 ./... -check.v -args $@
./github.com/canonical/go-tpm2/strings.go 0000664 0000000 0000000 00000062421 00000000000 016644 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"bytes"
"fmt"
)
func makeDefaultFormatter(s fmt.State, f rune) string {
var builder bytes.Buffer
builder.WriteString("%")
for _, flag := range [...]int{'+', '-', '#', ' ', '0'} {
if s.Flag(flag) {
fmt.Fprintf(&builder, "%c", flag)
}
}
if width, ok := s.Width(); ok {
fmt.Fprintf(&builder, "%d", width)
}
if prec, ok := s.Precision(); ok {
fmt.Fprintf(&builder, ".%d", prec)
}
builder.WriteRune(f)
return builder.String()
}
func (m TPMManufacturer) String() string {
switch m {
case TPMManufacturerAMD:
return "AMD"
case TPMManufacturerATML:
return "Atmel"
case TPMManufacturerBRCM:
return "Broadcom"
case TPMManufacturerHPE:
return "HPE"
case TPMManufacturerIBM:
return "IBM"
case TPMManufacturerIFX:
return "Infineon"
case TPMManufacturerINTC:
return "Intel"
case TPMManufacturerLEN:
return "Lenovo"
case TPMManufacturerMSFT:
return "Microsoft"
case TPMManufacturerNSM:
return "National Semiconductor"
case TPMManufacturerNTZ:
return "Nationz"
case TPMManufacturerNTC:
return "Nuvoton Technology"
case TPMManufacturerQCOM:
return "Qualcomm"
case TPMManufacturerSMSC:
return "SMSC"
case TPMManufacturerSTM:
return "ST Microelectronics"
case TPMManufacturerSMSN:
return "Samsung"
case TPMManufacturerSNS:
return "Sinosun"
case TPMManufacturerTXN:
return "Texas Instruments"
case TPMManufacturerWEC:
return "Winbond"
case TPMManufacturerROCC:
return "Fuzhou Rockchip"
case TPMManufacturerGOOG:
return "Google"
default:
return fmt.Sprintf("0x%08x", uint32(m))
}
}
func (m TPMManufacturer) Format(s fmt.State, f rune) {
switch f {
case 's', 'v':
fmt.Fprintf(s, "%s", m.String())
default:
fmt.Fprintf(s, makeDefaultFormatter(s, f), uint32(m))
}
}
func (c CommandCode) String() string {
switch c {
case CommandNVUndefineSpaceSpecial:
return "TPM_CC_NV_UndefineSpaceSpecial"
case CommandEvictControl:
return "TPM_CC_EvictControl"
case CommandHierarchyControl:
return "TPM_CC_HierarchyControl"
case CommandNVUndefineSpace:
return "TPM_CC_NV_UndefineSpace"
case CommandClear:
return "TPM_CC_Clear"
case CommandClearControl:
return "TPM_CC_ClearControl"
case CommandClockSet:
return "TPM_CC_ClockSet"
case CommandHierarchyChangeAuth:
return "TPM_CC_HierarchyChangeAuth"
case CommandNVDefineSpace:
return "TPM_CC_NV_DefineSpace"
case CommandPCRAllocate:
return "TPM_CC_PCR_Allocate"
case CommandSetPrimaryPolicy:
return "TPM_CC_SetPrimaryPolicy"
case CommandClockRateAdjust:
return "TPM_CC_ClockRateAdjust"
case CommandCreatePrimary:
return "TPM_CC_CreatePrimary"
case CommandNVGlobalWriteLock:
return "TPM_CC_NV_GlobalWriteLock"
case CommandGetCommandAuditDigest:
return "TPM_CC_GetCommandAuditDigest"
case CommandNVIncrement:
return "TPM_CC_NV_Increment"
case CommandNVSetBits:
return "TPM_CC_NV_SetBits"
case CommandNVExtend:
return "TPM_CC_NV_Extend"
case CommandNVWrite:
return "TPM_CC_NV_Write"
case CommandNVWriteLock:
return "TPM_CC_NV_WriteLock"
case CommandDictionaryAttackLockReset:
return "TPM_CC_DictionaryAttackLockReset"
case CommandDictionaryAttackParameters:
return "TPM_CC_DictionaryAttackParameters"
case CommandNVChangeAuth:
return "TPM_CC_NV_ChangeAuth"
case CommandPCREvent:
return "TPM_CC_PCR_Event"
case CommandPCRReset:
return "TPM_CC_PCR_Reset"
case CommandSequenceComplete:
return "TPM_CC_SequenceComplete"
case CommandSetCommandCodeAuditStatus:
return "TPM_CC_SetCommandCodeAuditStatus"
case CommandIncrementalSelfTest:
return "TPM_CC_IncrementalSelfTest"
case CommandSelfTest:
return "TPM_CC_SelfTest"
case CommandStartup:
return "TPM_CC_Startup"
case CommandShutdown:
return "TPM_CC_Shutdown"
case CommandStirRandom:
return "TPM_CC_StirRandom"
case CommandActivateCredential:
return "TPM_CC_ActivateCredential"
case CommandCertify:
return "TPM_CC_Certify"
case CommandPolicyNV:
return "TPM_CC_PolicyNV"
case CommandCertifyCreation:
return "TPM_CC_CertifyCreation"
case CommandDuplicate:
return "TPM_CC_Duplicate"
case CommandGetTime:
return "TPM_CC_GetTime"
case CommandGetSessionAuditDigest:
return "TPM_CC_GetSessionAuditDigest"
case CommandNVRead:
return "TPM_CC_NV_Read"
case CommandNVReadLock:
return "TPM_CC_NV_ReadLock"
case CommandObjectChangeAuth:
return "TPM_CC_ObjectChangeAuth"
case CommandPolicySecret:
return "TPM_CC_PolicySecret"
case CommandCreate:
return "TPM_CC_Create"
case CommandECDHZGen:
return "TPM_CC_ECDH_ZGen"
case CommandHMAC:
return "TPM_CC_HMAC"
case CommandImport:
return "TPM_CC_Import"
case CommandLoad:
return "TPM_CC_Load"
case CommandQuote:
return "TPM_CC_Quote"
case CommandRSADecrypt:
return "TPM_CC_RSA_Decrypt"
case CommandHMACStart:
return "TPM_CC_HMAC_Start"
case CommandSequenceUpdate:
return "TPM_CC_SequenceUpdate"
case CommandSign:
return "TPM_CC_Sign"
case CommandUnseal:
return "TPM_CC_Unseal"
case CommandPolicySigned:
return "TPM_CC_PolicySigned"
case CommandContextLoad:
return "TPM_CC_ContextLoad"
case CommandContextSave:
return "TPM_CC_ContextSave"
case CommandECDHKeyGen:
return "TPM_CC_ECDH_KeyGen"
case CommandFlushContext:
return "TPM_CC_FlushContext"
case CommandLoadExternal:
return "TPM_CC_LoadExternal"
case CommandMakeCredential:
return "TPM_CC_MakeCredential"
case CommandNVReadPublic:
return "TPM_CC_NV_ReadPublic"
case CommandPolicyAuthorize:
return "TPM_CC_PolicyAuthorize"
case CommandPolicyAuthValue:
return "TPM_CC_PolicyAuthValue"
case CommandPolicyCommandCode:
return "TPM_CC_PolicyCommandCode"
case CommandPolicyCounterTimer:
return "TPM_CC_PolicyCounterTimer"
case CommandPolicyCpHash:
return "TPM_CC_PolicyCpHash"
case CommandPolicyLocality:
return "TPM_CC_PolicyLocality"
case CommandPolicyNameHash:
return "TPM_CC_PolicyNameHash"
case CommandPolicyOR:
return "TPM_CC_PolicyOR"
case CommandPolicyTicket:
return "TPM_CC_PolicyTicket"
case CommandReadPublic:
return "TPM_CC_ReadPublic"
case CommandRSAEncrypt:
return "TPM_CC_RSA_Encrypt"
case CommandStartAuthSession:
return "TPM_CC_StartAuthSession"
case CommandVerifySignature:
return "TPM_CC_VerifySignature"
case CommandECCParameters:
return "TPM_CC_ECC_Parameters"
case CommandGetCapability:
return "TPM_CC_GetCapability"
case CommandGetRandom:
return "TPM_CC_GetRandom"
case CommandGetTestResult:
return "TPM_CC_GetTestResult"
case CommandHash:
return "TPM_CC_Hash"
case CommandPCRRead:
return "TPM_CC_PCR_Read"
case CommandPolicyPCR:
return "TPM_CC_PolicyPCR"
case CommandPolicyRestart:
return "TPM_CC_PolicyRestart"
case CommandReadClock:
return "TPM_CC_ReadClock"
case CommandPCRExtend:
return "TPM_CC_PCR_Extend"
case CommandNVCertify:
return "TPM_CC_NV_Certify"
case CommandEventSequenceComplete:
return "TPM_CC_EventSequenceComplete"
case CommandHashSequenceStart:
return "TPM_CC_HashSequenceStart"
case CommandPolicyDuplicationSelect:
return "TPM_CC_PolicyDuplicationSelect"
case CommandPolicyGetDigest:
return "TPM_CC_PolicyGetDigest"
case CommandTestParms:
return "TPM_CC_TestParms"
case CommandCommit:
return "TPM_CC_Commit"
case CommandPolicyPassword:
return "TPM_CC_PolicyPassword"
case CommandPolicyNvWritten:
return "TPM_CC_PolicyNvWritten"
case CommandPolicyTemplate:
return "TPM_CC_PolicyTemplate"
case CommandCreateLoaded:
return "TPM_CC_CreateLoaded"
case CommandPolicyAuthorizeNV:
return "TPM_CC_PolicyAuthorizeNV"
default:
return fmt.Sprintf("0x%08x", uint32(c))
}
}
func (c CommandCode) Format(s fmt.State, f rune) {
switch f {
case 's', 'v':
fmt.Fprintf(s, "%s", c.String())
default:
fmt.Fprintf(s, makeDefaultFormatter(s, f), uint32(c))
}
}
func (e ErrorCode) String() string {
switch e {
case ErrorInitialize:
return "TPM_RC_INITIALIZE"
case ErrorFailure:
return "TPM_RC_FAILURE"
case ErrorSequence:
return "TPM_RC_SEQUENCE"
case ErrorDisabled:
return "TPM_RC_DISABLED"
case ErrorExclusive:
return "TPM_RC_EXCLUSIVE"
case ErrorAuthType:
return "TPM_RC_AUTH_TYPE"
case ErrorAuthMissing:
return "TPM_RC_AUTH_MISSING"
case ErrorPolicy:
return "TPM_RC_POLICY"
case ErrorPCR:
return "TPM_RC_PCR"
case ErrorPCRChanged:
return "TPM_RC_PCR_CHANGED"
case ErrorUpgrade:
return "TPM_RC_UPGRADE"
case ErrorTooManyContexts:
return "TPM_RC_TOO_MANY_CONTEXTS"
case ErrorAuthUnavailable:
return "TPM_RC_AUTH_UNAVAILABLE"
case ErrorReboot:
return "TPM_RC_REBOOT"
case ErrorUnbalanced:
return "TPM_RC_UNBALANCED"
case ErrorCommandSize:
return "TPM_RC_COMMAND_SIZE"
case ErrorCommandCode:
return "TPM_RC_COMMAND_CODE"
case ErrorAuthsize:
return "TPM_RC_AUTHSIZE"
case ErrorAuthContext:
return "TPM_RC_AUTH_CONTEXT"
case ErrorNVRange:
return "TPM_RC_NV_RANGE"
case ErrorNVSize:
return "TPM_RC_NV_SIZE"
case ErrorNVLocked:
return "TPM_RC_NV_LOCKED"
case ErrorNVAuthorization:
return "TPM_RC_NV_AUTHORIZATION"
case ErrorNVUninitialized:
return "TPM_RC_NV_UNINITIALIZED"
case ErrorNVSpace:
return "TPM_RC_NV_SPACE"
case ErrorNVDefined:
return "TPM_RC_NV_DEFINED"
case ErrorBadContext:
return "TPM_RC_BAD_CONTEXT"
case ErrorCpHash:
return "TPM_RC_CPHASH"
case ErrorParent:
return "TPM_RC_PARENT"
case ErrorNeedsTest:
return "TPM_RC_NEEDS_TEST"
case ErrorNoResult:
return "TPM_RC_NO_RESULT"
case ErrorSensitive:
return "TPM_RC_SENSITIVE"
// Format 1 error codes start here
case ErrorAsymmetric:
return "TPM_RC_ASYMMETRIC"
case ErrorAttributes:
return "TPM_RC_ATTRIBUTES"
case ErrorHash:
return "TPM_RC_HASH"
case ErrorValue:
return "TPM_RC_VALUE"
case ErrorHierarchy:
return "TPM_RC_HIERARCHY"
case ErrorKeySize:
return "TPM_RC_KEY_SIZE"
case ErrorMGF:
return "TPM_RC_MGF"
case ErrorMode:
return "TPM_RC_MODE"
case ErrorType:
return "TPM_RC_TYPE"
case ErrorHandle:
return "TPM_RC_HANDLE"
case ErrorKDF:
return "TPM_RC_KDF"
case ErrorRange:
return "TPM_RC_RANGE"
case ErrorAuthFail:
return "TPM_RC_AUTH_FAIL"
case ErrorNonce:
return "TPM_RC"
case ErrorPP:
return "TPM_RC_PP"
case ErrorScheme:
return "TPM_RC_SCHEME"
case ErrorSize:
return "TPM_RC_SIZE"
case ErrorSymmetric:
return "TPM_RC_SYMMETRIC"
case ErrorTag:
return "TPM_RC_TAG"
case ErrorSelector:
return "TPM_RC_SELECTOR"
case ErrorInsufficient:
return "TPM_RC_INSUFFICIENT"
case ErrorSignature:
return "TPM_RC_SIGNATURE"
case ErrorKey:
return "TPM_RC_KEY"
case ErrorPolicyFail:
return "TPM_RC_POLICY_FAIL"
case ErrorIntegrity:
return "TPM_RC_INTEGRITY"
case ErrorTicket:
return "TPM_RC_TICKET"
case ErrorReservedBits:
return "TPM_RC_RESERVED_BITS"
case ErrorBadAuth:
return "TPM_RC_BAD_AUTH"
case ErrorExpired:
return "TPM_RC_EXPIRED"
case ErrorPolicyCC:
return "TPM_RC_POLICY_CC"
case ErrorBinding:
return "TPM_RC_BINDING"
case ErrorCurve:
return "TPM_RC_CURVE"
case ErrorECCPoint:
return "TPM_RC_ECC_POINT"
case ErrorBadTag:
return "TPM_RC_BAD_TAG"
default:
return fmt.Sprintf("0x%02x", uint8(e))
}
}
func (e ErrorCode) Format(s fmt.State, f rune) {
switch f {
case 's', 'v':
fmt.Fprintf(s, "%s", e.String())
default:
fmt.Fprintf(s, makeDefaultFormatter(s, f), uint8(e))
}
}
func (e WarningCode) String() string {
switch e {
case WarningContextGap:
return "TPM_RC_CONTEXT_GAP"
case WarningObjectMemory:
return "TPM_RC_OBJECT_MEMORY"
case WarningSessionMemory:
return "TPM_RC_SESSION_MEMORY"
case WarningMemory:
return "TPM_RC_MEMORY"
case WarningSessionHandles:
return "TPM_RC_SESSION_HANDLES"
case WarningObjectHandles:
return "TPM_RC_OBJECT_HANDLES"
case WarningLocality:
return "TPM_RC_LOCALITY"
case WarningYielded:
return "TPM_RC_YIELDED"
case WarningCanceled:
return "TPM_RC_CANCELED"
case WarningTesting:
return "TPM_RC_TESTING"
case WarningReferenceH0:
return "TPM_RC_REFERENCE_H0"
case WarningReferenceH1:
return "TPM_RC_REFERENCE_H1"
case WarningReferenceH2:
return "TPM_RC_REFERENCE_H2"
case WarningReferenceH3:
return "TPM_RC_REFERENCE_H3"
case WarningReferenceH4:
return "TPM_RC_REFERENCE_H4"
case WarningReferenceH5:
return "TPM_RC_REFERENCE_H5"
case WarningReferenceH6:
return "TPM_RC_REFERENCE_H6"
case WarningReferenceS0:
return "TPM_RC_REFERENCE_S0"
case WarningReferenceS1:
return "TPM_RC_REFERENCE_S1"
case WarningReferenceS2:
return "TPM_RC_REFERENCE_S2"
case WarningReferenceS3:
return "TPM_RC_REFERENCE_S3"
case WarningReferenceS4:
return "TPM_RC_REFERENCE_S4"
case WarningReferenceS5:
return "TPM_RC_REFERENCE_S5"
case WarningReferenceS6:
return "TPM_RC_REFERENCE_S6"
case WarningNVRate:
return "TPM_RC_NV_RATE"
case WarningLockout:
return "TPM_RC_LOCKOUT"
case WarningRetry:
return "TPM_RC_RETRY"
case WarningNVUnavailable:
return "TPM_RC_NV_UNAVAILABLE"
default:
return fmt.Sprintf("0x%02x", uint8(e))
}
}
func (e WarningCode) Format(s fmt.State, f rune) {
switch f {
case 's', 'v':
fmt.Fprintf(s, "%s", e.String())
default:
fmt.Fprintf(s, makeDefaultFormatter(s, f), uint8(e))
}
}
func (h Handle) String() string {
switch h {
case HandleOwner:
return "TPM_RH_OWNER"
case HandleNull:
return "TPM_RH_NULL"
case HandleUnassigned:
return "TPM_RH_UNASSIGNED"
case HandlePW:
return "TPM_RS_PW"
case HandleLockout:
return "TPM_RH_LOCKOUT"
case HandleEndorsement:
return "TPM_RH_ENDORSEMENT"
case HandlePlatform:
return "TPM_RH_PLATFORM"
case HandlePlatformNV:
return "TPM_RH_PLATFORM_NV"
default:
return fmt.Sprintf("0x%08x", uint32(h))
}
}
func (h Handle) Format(s fmt.State, f rune) {
switch f {
case 's', 'v':
fmt.Fprintf(s, "%s", h.String())
default:
fmt.Fprintf(s, makeDefaultFormatter(s, f), uint32(h))
}
}
func (a AlgorithmId) String() string {
switch a {
case AlgorithmRSA:
return "TPM_ALG_RSA"
case AlgorithmTDES:
return "TPM_ALG_TDES"
case AlgorithmSHA1:
return "TPM_ALG_SHA1"
case AlgorithmHMAC:
return "TPM_ALG_HMAC"
case AlgorithmAES:
return "TPM_ALG_AES"
case AlgorithmMGF1:
return "TPM_ALG_MGF1"
case AlgorithmKeyedHash:
return "TPM_ALG_KEYEDHASH"
case AlgorithmXOR:
return "TPM_ALG_XOR"
case AlgorithmSHA256:
return "TPM_ALG_SHA256"
case AlgorithmSHA384:
return "TPM_ALG_SHA384"
case AlgorithmSHA512:
return "TPM_ALG_SHA512"
case AlgorithmNull:
return "TPM_ALG_NULL"
case AlgorithmSM3_256:
return "TPM_ALG_SM3_256"
case AlgorithmSM4:
return "TPM_ALG_SM4"
case AlgorithmRSASSA:
return "TPM_ALG_RSASSA"
case AlgorithmRSAES:
return "TPM_ALG_RSAES"
case AlgorithmRSAPSS:
return "TPM_ALG_RSAPSS"
case AlgorithmOAEP:
return "TPM_ALG_OAEP"
case AlgorithmECDSA:
return "TPM_ALG_ECDSA"
case AlgorithmECDH:
return "TPM_ALG_ECDH"
case AlgorithmECDAA:
return "TPM_ALG_ECDAA"
case AlgorithmSM2:
return "TPM_ALG_SM2"
case AlgorithmECSchnorr:
return "TPM_ALG_ECSCHNORR"
case AlgorithmECMQV:
return "TPM_ALG_ECMQV"
case AlgorithmKDF1_SP800_56A:
return "TPM_ALG_KDF1_SP800_56A"
case AlgorithmKDF2:
return "TPM_ALG_KDF2"
case AlgorithmKDF1_SP800_108:
return "TPM_ALG_KDF1_SP800_108"
case AlgorithmECC:
return "TPM_ALG_ECC"
case AlgorithmSymCipher:
return "TPM_ALG_SYMCIPHER"
case AlgorithmCamellia:
return "TPM_ALG_CAMELLIA"
case AlgorithmSHA3_256:
return "TPM_ALG_SHA3_256"
case AlgorithmSHA3_384:
return "TPM_ALG_SHA3_384"
case AlgorithmSHA3_512:
return "TPM_ALG_SHA3_512"
case AlgorithmCTR:
return "TPM_ALG_CTR"
case AlgorithmOFB:
return "TPM_ALG_OFB"
case AlgorithmCBC:
return "TPM_ALG_CBC"
case AlgorithmCFB:
return "TPM_ALG_CFB"
case AlgorithmECB:
return "TPM_ALG_ECB"
default:
return fmt.Sprintf("0x%04x", uint16(a))
}
}
func (a AlgorithmId) Format(s fmt.State, f rune) {
switch f {
case 's', 'v':
fmt.Fprintf(s, "%s", a.String())
default:
fmt.Fprintf(s, makeDefaultFormatter(s, f), uint16(a))
}
}
func (a HashAlgorithmId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a SymAlgorithmId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a SymObjectAlgorithmId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a SymModeId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a KDFAlgorithmId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a SigSchemeId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a KeyedHashSchemeId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a AsymSchemeId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a RSASchemeId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a ECCSchemeId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (a ObjectTypeId) Format(s fmt.State, f rune) {
AlgorithmId(a).Format(s, f)
}
func (c Capability) String() string {
switch c {
case CapabilityAlgs:
return "TPM_CAP_ALGS"
case CapabilityHandles:
return "TPM_CAP_HANDLES"
case CapabilityCommands:
return "TPM_CAP_COMMANDS"
case CapabilityPPCommands:
return "TPM_CAP_PP_COMMANDS"
case CapabilityAuditCommands:
return "TPM_CAP_AUDIT_COMMANDS"
case CapabilityPCRs:
return "TPM_CAP_PCRS"
case CapabilityTPMProperties:
return "TPM_CAP_TPM_PROPERTIES"
case CapabilityPCRProperties:
return "TPM_CAP_PCR_PROPERTIES"
case CapabilityECCCurves:
return "TPM_CAP_ECC_CURVES"
case CapabilityAuthPolicies:
return "TPM_CAP_AUTH_POLICIES"
default:
return fmt.Sprintf("0x%08x", uint32(c))
}
}
func (c Capability) Format(s fmt.State, f rune) {
switch f {
case 's', 'v':
fmt.Fprintf(s, "%s", c.String())
default:
fmt.Fprintf(s, makeDefaultFormatter(s, f), uint32(c))
}
}
var (
errorCodeDescriptions = map[ErrorCode]string{
ErrorInitialize: "TPM not initialized by TPM2_Startup or already initialized",
ErrorFailure: "commands not being accepted because of a TPM failure",
ErrorSequence: "improper use of a sequence handle",
ErrorDisabled: "the command is disabled",
ErrorExclusive: "command failed because audit sequence required exclusivity",
ErrorAuthType: "authorization handle is not correct for command",
ErrorAuthMissing: "command requires an authorization session for handle and it is not present",
ErrorPolicy: "policy failure in math operation or an invalid authPolicy value",
ErrorPCR: "PCR check fail",
ErrorPCRChanged: "PCR have changed since checked",
ErrorTooManyContexts: "context ID counter is at maximum",
ErrorAuthUnavailable: "authValue or authPolicy is not available for selected entity",
ErrorReboot: "a _TPM_Init and Startup(CLEAR) is required before the TPM can resume operation",
ErrorUnbalanced: "the protection algorithms (hash and symmetric) are not reasonably balanced. The digest size of the hash must be " +
"larger than the key size of the symmetric algorithm",
ErrorCommandSize: "command commandSize value is inconsistent with contents of the command buffer; either the size is not the same " +
"as the octets loaded by the hardware interface layer or the value is not large enough to hold a command header",
ErrorCommandCode: "command code not supported",
ErrorAuthsize: "the value of authorizationSize is out of range or the number of octets in the Authorization Area is greater than " +
"required",
ErrorAuthContext: "use of an authorization session with a context command or another command that cannot have an authorization " +
"session",
ErrorNVRange: "NV offset+size is out of range",
ErrorNVSize: "Requested allocation size is larger than allowed",
ErrorNVLocked: "NV access locked",
ErrorNVAuthorization: "NV access authorization fails in command actions (this failure does not affect lockout.action)",
ErrorNVUninitialized: "an NV Index is used before being initialized or the state saved by TPM2_Shutdown(STATE) could not be " +
"restored",
ErrorNVSpace: "insufficient space for NV allocation",
ErrorNVDefined: "NV Index or persistent object already defined",
ErrorBadContext: "context in TPM2_ContextLoad() is not valid",
ErrorCpHash: "cpHash value already set or not correct for use",
ErrorParent: "handle for parent is not a valid parent",
ErrorNeedsTest: "some function needs testing",
ErrorNoResult: "returned when an internal function cannot process a request due to an unspecified problem. This code is usually " +
"related to invalid parameters that are not properly filtered by the input unmarshaling code",
ErrorSensitive: "the sensitive area did not unmarshal correctly after decryption",
ErrorAsymmetric: "asymmetric algorithm not supported or not correct",
ErrorAttributes: "inconsistent attributes",
ErrorHash: "hash algorithm not supported or not appropriate",
ErrorValue: "value is out of range or is not correct for the context",
ErrorHierarchy: "hierarchy is not enabled or is not correct for the use",
ErrorKeySize: "key size is not supported",
ErrorMGF: "mask generation function not supported",
ErrorMode: "mode of operation not supported",
ErrorType: "the type of the value is not appropriate for the use",
ErrorHandle: "the handle is not correct for the use",
ErrorKDF: "unsupported key derivation function or function not appropriate for use",
ErrorRange: "value was out of allowed range",
ErrorAuthFail: "the authorization HMAC check failed and DA counter incremented",
ErrorNonce: "invalid nonce size or nonce value mismatch",
ErrorPP: "authorization requires assertion of PP",
ErrorScheme: "unsupported or incompatible scheme",
ErrorSize: "structure is the wrong size",
ErrorSymmetric: "unsupported symmetric algorithm or key size, or not appropriate for instance",
ErrorTag: "incorrect structure tag",
ErrorSelector: "union selector is incorrect",
ErrorInsufficient: "the TPM was unable to unmarshal a value because there were not enough octets in the input buffer",
ErrorSignature: "the signature is not valid",
ErrorKey: "key fields are not compatible with the selected use",
ErrorPolicyFail: "a policy check failed",
ErrorIntegrity: "integrity check failed",
ErrorTicket: "invalid ticket",
ErrorReservedBits: "reserved bits not set to zero as required",
ErrorBadAuth: "authorization failure without DA implications",
ErrorExpired: "the policy has expired",
ErrorPolicyCC: "the commandCode in the policy is not the commandCode of the command or the command code in a policy command " +
"references a command that is not implemented",
ErrorBinding: "public and sensitive portions of an object are not cryptographically bound",
ErrorCurve: "curve not supported",
ErrorECCPoint: "point is not on the required curve"}
warningCodeDescriptions = map[WarningCode]string{
WarningContextGap: "gap for context ID is too large",
WarningObjectMemory: "out of memory for object contexts",
WarningSessionMemory: "out of memory for session contexts",
WarningMemory: "out of shared object/session memory or need space for internal operations",
WarningSessionHandles: "out of session handles – a session must be flushed before a new session may be created",
WarningObjectHandles: "out of object handles – the handle space for objects is depleted and a reboot is required",
WarningLocality: "bad locality",
WarningYielded: "the TPM has suspended operation on the command; forward progress was made and the command may be retried",
WarningCanceled: "the command was canceled",
WarningTesting: "TPM is performing self-tests",
WarningReferenceH0: "the 1st handle in the handle area references a transient object or session that is not loaded",
WarningReferenceH1: "the 2nd handle in the handle area references a transient object or session that is not loaded",
WarningReferenceH2: "the 3rd handle in the handle area references a transient object or session that is not loaded",
WarningReferenceH3: "the 4th handle in the handle area references a transient object or session that is not loaded",
WarningReferenceH4: "the 5th handle in the handle area references a transient object or session that is not loaded",
WarningReferenceH5: "the 6th handle in the handle area references a transient object or session that is not loaded",
WarningReferenceH6: "the 7th handle in the handle area references a transient object or session that is not loaded",
WarningReferenceS0: "the 1st authorization session handle references a session that is not loaded",
WarningReferenceS1: "the 2nd authorization session handle references a session that is not loaded",
WarningReferenceS2: "the 3rd authorization session handle references a session that is not loaded",
WarningReferenceS3: "the 4th authorization session handle references a session that is not loaded",
WarningReferenceS4: "the 5th authorization session handle references a session that is not loaded",
WarningReferenceS5: "the 6th authorization session handle references a session that is not loaded",
WarningReferenceS6: "the 7th authorization session handle references a session that is not loaded",
WarningNVRate: "the TPM is rate-limiting accesses to prevent wearout of NV",
WarningLockout: "authorizations for objects subject to DA protection are not allowed at this time because the TPM is in DA " +
"lockout mode",
WarningRetry: "the TPM was not able to start the command",
WarningNVUnavailable: "the command may require writing of NV and NV is not current accessible"}
)
./github.com/canonical/go-tpm2/tcti.go 0000664 0000000 0000000 00000003511 00000000000 016111 0 ustar 00 0000000 0000000 // Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// XXX: Note that the "TCG TSS 2.0 TPM Command Transmission Interface (TCTI) API Specification"
// defines the following callbacks:
// - transmit, which is equivalent to io.Writer.
// - receive, which is equivalent to io.Reader.
// - finalize, which is equivalent to io.Closer.
// - cancel, which we don't implement at the moment because there's not a mechanism to cancel
// an operation in go-tpm2. Perhaps if we move the call to Read() to a separate go routine and pass
// a context around, we could cancel using a deadline. Not today though, and the Linux character
// device driver doesn't provide a mechanism to cancel so it probably wouldn't be worth the effort
// anyway.
// - getPollHandles, doesn't really make sense here because go's runtime does the polling on
// Read.
// - setLocality.
// - makeSticky, not implemented yet by any TCTI implementation in tss2 AFAICT.
// TCTI represents a communication channel to a TPM implementation.
type TCTI interface {
// Read is used to receive a response to a previously transmitted command. The
// implementation must support partial reading of a response, and must return io.EOF
// when there are no more bytes of a response left to read.
Read(p []byte) (int, error)
// Write is used to transmit a serialized command to the TPM implementation.
// A command must be transmitted in a single write.
Write(p []byte) (int, error)
Close() error
// SetLocality sets the locality that will be used for subsequent commands.
SetLocality(locality uint8) error
// MakeSticky requests that the underlying resource manager does not unload the resource
// associated with the supplied handle between commands.
MakeSticky(handle Handle, sticky bool) error
}
./github.com/canonical/go-tpm2/templates/ 0000775 0000000 0000000 00000000000 00000000000 016615 5 ustar 00 0000000 0000000 ./github.com/canonical/go-tpm2/templates/templates.go 0000664 0000000 0000000 00000074766 00000000000 021166 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
/*
Package template contains helpers for constructing templates to create objects with go-tpm2.
*/
package templates
import (
"github.com/canonical/go-tpm2"
)
type KeyUsage int
const (
KeyUsageSign KeyUsage = 1 << iota
KeyUsageDecrypt
KeyUsageEncrypt = KeyUsageSign
)
// NewRSAStorageKey returns a template for a RSA storage parent with the specified
// name algorithm, symmetric cipher, symmetric key size and RSA key size. If nameAlg
// is HashAlgorithmNull, then HashAlgorithmSHA256 is used. If algorithm is
// SymObjectAlgorithmNull, then SymObjectAlgorithmAES is used. If symKeyBits is zero,
// then 128 is used. If asymKeyBits is zero, then 2048 is used.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewRSAStorageKey(nameAlg tpm2.HashAlgorithmId, algorithm tpm2.SymObjectAlgorithmId, symKeyBits, asymKeyBits uint16) *tpm2.Public {
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if algorithm == tpm2.SymObjectAlgorithmNull {
algorithm = tpm2.SymObjectAlgorithmAES
}
if symKeyBits == 0 {
symKeyBits = 128
}
if asymKeyBits == 0 {
asymKeyBits = 2048
}
return &tpm2.Public{
Type: tpm2.ObjectTypeRSA,
NameAlg: nameAlg,
Attrs: tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth | tpm2.AttrRestricted | tpm2.AttrDecrypt,
Params: &tpm2.PublicParamsU{
RSADetail: &tpm2.RSAParams{
Symmetric: tpm2.SymDefObject{
Algorithm: algorithm,
KeyBits: &tpm2.SymKeyBitsU{Sym: symKeyBits},
Mode: &tpm2.SymModeU{Sym: tpm2.SymModeCFB}},
Scheme: tpm2.RSAScheme{Scheme: tpm2.RSASchemeNull},
KeyBits: asymKeyBits,
Exponent: 0}}}
}
// NewRSAStorageKeyWithDefaults returns a template for a RSA storage parent with
// SHA256 as the name algorithm, AES-128 as the symmetric cipher and 2048 bits as
// the RSA key size.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewRSAStorageKeyWithDefaults() *tpm2.Public {
return NewRSAStorageKey(tpm2.HashAlgorithmNull, tpm2.SymObjectAlgorithmNull, 0, 0)
}
// NewRestrictedRSASigningKey returns a template for a restricted RSA signing
// key with the specified name algorithm, RSA scheme and RSA key size. If nameAlg
// is HashAlgorithmNull, then HashAlgorithmSHA256 is used. If scheme is nil, then
// RSASSA is used with the digest algorithm set to the same as the name
// algorithm. If keyBits is zero, then 2048 is used.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewRestrictedRSASigningKey(nameAlg tpm2.HashAlgorithmId, scheme *tpm2.RSAScheme, keyBits uint16) *tpm2.Public {
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if scheme == nil {
scheme = &tpm2.RSAScheme{
Scheme: tpm2.RSASchemeRSASSA,
Details: &tpm2.AsymSchemeU{
RSASSA: &tpm2.SigSchemeRSASSA{HashAlg: nameAlg}}}
}
if keyBits == 0 {
keyBits = 2048
}
return &tpm2.Public{
Type: tpm2.ObjectTypeRSA,
NameAlg: nameAlg,
Attrs: tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth | tpm2.AttrSign | tpm2.AttrRestricted,
Params: &tpm2.PublicParamsU{
RSADetail: &tpm2.RSAParams{
Symmetric: tpm2.SymDefObject{Algorithm: tpm2.SymObjectAlgorithmNull},
Scheme: *scheme,
KeyBits: keyBits,
Exponent: 0}}}
}
// NewRestrictedRSASigningKeyWithDefaults returns a template for a restricted RSA
// signing key with SHA256 as the name algorithm, RSA-SSA with SHA256 as the scheme
// and 2048 bits as the key size.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewRestrictedRSASigningKeyWithDefaults() *tpm2.Public {
return NewRestrictedRSASigningKey(tpm2.HashAlgorithmNull, nil, 0)
}
// NewRSAKey returns a template for a general purpose RSA key for the specified
// usage, with the specified name algorithm, RSA scheme and RSA key size. If nameAlg
// is HashAlgorithmNull, then HashAlgorithmSHA256 is used. If keyBits is zero, then
// 2048 is used. If no usage is specified, the template will include both sign and
// decrypt attributes.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewRSAKey(nameAlg tpm2.HashAlgorithmId, usage KeyUsage, scheme *tpm2.RSAScheme, keyBits uint16) *tpm2.Public {
attrs := tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth
if usage == 0 {
usage = KeyUsageSign | KeyUsageDecrypt
}
if usage&KeyUsageSign != 0 {
attrs |= tpm2.AttrSign
}
if usage&KeyUsageDecrypt != 0 {
attrs |= tpm2.AttrDecrypt
}
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if scheme == nil {
scheme = &tpm2.RSAScheme{Scheme: tpm2.RSASchemeNull}
}
if keyBits == 0 {
keyBits = 2048
}
return &tpm2.Public{
Type: tpm2.ObjectTypeRSA,
NameAlg: nameAlg,
Attrs: attrs,
Params: &tpm2.PublicParamsU{
RSADetail: &tpm2.RSAParams{
Symmetric: tpm2.SymDefObject{Algorithm: tpm2.SymObjectAlgorithmNull},
Scheme: *scheme,
KeyBits: keyBits,
Exponent: 0}}}
}
// NewRSAKeyWithDefaults returns a template for a general purpose RSA key for the
// specified usage, with SHA256 as the name algorithm, the scheme unset and 2048 bits
// as the key size. If no usage is specified, the template will include both sign and
// decrypt attributes.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewRSAKeyWithDefaults(usage KeyUsage) *tpm2.Public {
return NewRSAKey(tpm2.HashAlgorithmNull, usage, nil, 0)
}
// NewSealedObject returns a template for a sealed object with the specified name
// algorithm. If nameAlg is HashAlgorithmNull, then HashAlgorithmSHA256 is used.
//
// The template cannot be used to create an object in a duplication group. In order to
// create an object in a duplication group, remove the AttrFixedTPM attribute. In
// order to create an object that can be moved to a new parent, remove both the
// AttrFixedTPM and AttrFixedParent attributes. In this case, an authorization policy
// that permits duplication must be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewSealedObject(nameAlg tpm2.HashAlgorithmId) *tpm2.Public {
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
return &tpm2.Public{
Type: tpm2.ObjectTypeKeyedHash,
NameAlg: nameAlg,
Attrs: tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrUserWithAuth,
Params: &tpm2.PublicParamsU{
KeyedHashDetail: &tpm2.KeyedHashParams{
Scheme: tpm2.KeyedHashScheme{Scheme: tpm2.KeyedHashSchemeNull}}}}
}
// NewECCStorageKey returns a template for a ECC storage parent with the specified
// name algorithm, symmetric cipher, symmetric key size and elliptic curve. If nameAlg
// is HashAlgorithmNull, then HashAlgorithmSHA256 is used. If algorithm is
// SymObjectAlgorithmNull, then SymObjectAlgorithmAES is used. If keyBits is zero,
// then 128 is used.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewECCStorageKey(nameAlg tpm2.HashAlgorithmId, algorithm tpm2.SymObjectAlgorithmId, keyBits uint16, curve tpm2.ECCCurve) *tpm2.Public {
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if algorithm == tpm2.SymObjectAlgorithmNull {
algorithm = tpm2.SymObjectAlgorithmAES
}
if keyBits == 0 {
keyBits = 128
}
return &tpm2.Public{
Type: tpm2.ObjectTypeECC,
NameAlg: nameAlg,
Attrs: tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth | tpm2.AttrRestricted | tpm2.AttrDecrypt,
Params: &tpm2.PublicParamsU{
ECCDetail: &tpm2.ECCParams{
Symmetric: tpm2.SymDefObject{
Algorithm: algorithm,
KeyBits: &tpm2.SymKeyBitsU{Sym: keyBits},
Mode: &tpm2.SymModeU{Sym: tpm2.SymModeCFB}},
Scheme: tpm2.ECCScheme{Scheme: tpm2.ECCSchemeNull},
CurveID: curve,
KDF: tpm2.KDFScheme{Scheme: tpm2.KDFAlgorithmNull}}}}
}
// NewECCStorageKeyWithDefaults returns a template for a ECC storage parent with
// SHA256 as the name algorithm, AES-128 as the symmetric cipher and the NIST-P256
// curve.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewECCStorageKeyWithDefaults() *tpm2.Public {
return NewECCStorageKey(tpm2.HashAlgorithmNull, tpm2.SymObjectAlgorithmNull, 0, tpm2.ECCCurveNIST_P256)
}
// NewRestrictedECCSigningKey returns a template for a restricted ECC signing
// key with the specified name algorithm, ECC scheme and elliptic curve. If nameAlg
// is HashAlgorithmNull, then HashAlgorithmSHA256 is used. If scheme is nil, then
// ECDSA is used with the digest algorithm set to the same as the name algorithm.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewRestrictedECCSigningKey(nameAlg tpm2.HashAlgorithmId, scheme *tpm2.ECCScheme, curve tpm2.ECCCurve) *tpm2.Public {
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if scheme == nil {
scheme = &tpm2.ECCScheme{
Scheme: tpm2.ECCSchemeECDSA,
Details: &tpm2.AsymSchemeU{
ECDSA: &tpm2.SigSchemeECDSA{HashAlg: nameAlg}}}
}
return &tpm2.Public{
Type: tpm2.ObjectTypeECC,
NameAlg: nameAlg,
Attrs: tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth | tpm2.AttrSign | tpm2.AttrRestricted,
Params: &tpm2.PublicParamsU{
ECCDetail: &tpm2.ECCParams{
Symmetric: tpm2.SymDefObject{Algorithm: tpm2.SymObjectAlgorithmNull},
Scheme: *scheme,
CurveID: curve,
KDF: tpm2.KDFScheme{Scheme: tpm2.KDFAlgorithmNull}}}}
}
// NewRestrictedECCSigningKeyWithDefaults returns a template for a restricted ECC
// signing key with SHA256 as the name algorithm, ECDSA with SHA256 as the scheme and
// NIST-P256 as the curve.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewRestrictedECCSigningKeyWithDefaults() *tpm2.Public {
return NewRestrictedECCSigningKey(tpm2.HashAlgorithmNull, nil, tpm2.ECCCurveNIST_P256)
}
// NewECCKey returns a template for a general purpose ECC key for the specified
// usage, with the specified name algorithm, ECC scheme and elliptic curve. If nameAlg
// is HashAlgorithmNull, then HashAlgorithmSHA256 is used. If no usage is specified,
// the template will include both sign and decrypt attributes.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewECCKey(nameAlg tpm2.HashAlgorithmId, usage KeyUsage, scheme *tpm2.ECCScheme, curve tpm2.ECCCurve) *tpm2.Public {
attrs := tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth
if usage == 0 {
usage = KeyUsageSign | KeyUsageDecrypt
}
if usage&KeyUsageSign != 0 {
attrs |= tpm2.AttrSign
}
if usage&KeyUsageDecrypt != 0 {
attrs |= tpm2.AttrDecrypt
}
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if scheme == nil {
scheme = &tpm2.ECCScheme{Scheme: tpm2.ECCSchemeNull}
}
return &tpm2.Public{
Type: tpm2.ObjectTypeECC,
NameAlg: nameAlg,
Attrs: attrs,
Params: &tpm2.PublicParamsU{
ECCDetail: &tpm2.ECCParams{
Symmetric: tpm2.SymDefObject{Algorithm: tpm2.SymObjectAlgorithmNull},
Scheme: *scheme,
CurveID: curve,
KDF: tpm2.KDFScheme{Scheme: tpm2.KDFAlgorithmNull}}}}
}
// NewECCKeyWithDefaults returns a template for a general purpose ECC key for the
// specified usage, with SHA256 as the name algorithm, the scheme unset and NIST-P256
// as the curve. If no usage is specified, the template will include both sign and
// decrypt attributes.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewECCKeyWithDefaults(usage KeyUsage) *tpm2.Public {
return NewECCKey(tpm2.HashAlgorithmNull, usage, nil, tpm2.ECCCurveNIST_P256)
}
// NewSymmetricStorageKey returns a template for a symmetric storage parent with the
// specified name algorithm, symmetric cipher and symmetric key size. If nameAlg
// is HashAlgorithmNull, then HashAlgorithmSHA256 is used. If algorithm is
// SymObjectAlgorithmNull, then SymObjectAlgorithmAES is used. If keyBits is zero,
// then 128 is used.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template will create a TPM generated key. In order to supply the key, remove
// the AttrSensitiveDataOrigin attribute.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewSymmetricStorageKey(nameAlg tpm2.HashAlgorithmId, algorithm tpm2.SymObjectAlgorithmId, keyBits uint16) *tpm2.Public {
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if algorithm == tpm2.SymObjectAlgorithmNull {
algorithm = tpm2.SymObjectAlgorithmAES
}
if keyBits == 0 {
keyBits = 128
}
return &tpm2.Public{
Type: tpm2.ObjectTypeSymCipher,
NameAlg: nameAlg,
Attrs: tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth | tpm2.AttrRestricted | tpm2.AttrDecrypt,
Params: &tpm2.PublicParamsU{
SymDetail: &tpm2.SymCipherParams{
Sym: tpm2.SymDefObject{
Algorithm: algorithm,
KeyBits: &tpm2.SymKeyBitsU{Sym: keyBits},
Mode: &tpm2.SymModeU{Sym: tpm2.SymModeCFB}}}}}
}
// NewSymmetricStorageKeyWithDefaults returns a template for a symmetric storage
// parent with SHA256 as the name algorithm and AES-128 as the symmetric cipher.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template will create a TPM generated key. In order to supply the key, remove
// the AttrSensitiveDataOrigin attribute.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewSymmetricStorageKeyWithDefaults() *tpm2.Public {
return NewSymmetricStorageKey(tpm2.HashAlgorithmNull, tpm2.SymObjectAlgorithmNull, 0)
}
// NewSymmetricKey returns a template for a general purpose symmetric key with
// the specified name algorithm, key usage, symmetic algorithm, symmetric key size
// and symmetric mode. If nameAlg is HashAlgorithmNull, then HashAlgorithmSHA256
// is used. If algorithm is SymObjectAlgorithmNull, then SymObjectAlgorithmAES is
// used. If keyBits is zero, then 128 is used. If no usage is specified, the template
// will include both sign and decrypt attributes.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template will create a TPM generated key. In order to supply the key, remove
// the AttrSensitiveDataOrigin attribute.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewSymmetricKey(nameAlg tpm2.HashAlgorithmId, usage KeyUsage, algorithm tpm2.SymObjectAlgorithmId, keyBits uint16, mode tpm2.SymModeId) *tpm2.Public {
attrs := tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth
if usage == 0 {
usage = KeyUsageEncrypt | KeyUsageDecrypt
}
if usage&KeyUsageEncrypt != 0 {
attrs |= tpm2.AttrSign
}
if usage&KeyUsageDecrypt != 0 {
attrs |= tpm2.AttrDecrypt
}
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if algorithm == tpm2.SymObjectAlgorithmNull {
algorithm = tpm2.SymObjectAlgorithmAES
}
if keyBits == 0 {
keyBits = 128
}
return &tpm2.Public{
Type: tpm2.ObjectTypeSymCipher,
NameAlg: nameAlg,
Attrs: attrs,
Params: &tpm2.PublicParamsU{
SymDetail: &tpm2.SymCipherParams{
Sym: tpm2.SymDefObject{
Algorithm: algorithm,
KeyBits: &tpm2.SymKeyBitsU{Sym: keyBits},
Mode: &tpm2.SymModeU{Sym: mode}}}}}
}
// NewSymmetricKeyWithDefaults returns a template for a general purpose symmetric
// key for the specified usage with SHA256 as the name algorithm, AES-128 as the
// cipher and CFB as the cipher mode. If no usage is specified, the template will
// include both sign and decrypt attributes.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template will create a TPM generated key. In order to supply the key, remove
// the AttrSensitiveDataOrigin attribute.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewSymmetricKeyWithDefaults(usage KeyUsage) *tpm2.Public {
return NewSymmetricKey(tpm2.HashAlgorithmNull, usage, tpm2.SymObjectAlgorithmNull, 0, tpm2.SymModeCFB)
}
// NewHMACKey returns a template for a HMAC key with the specified name algorithm
// and HMAC digest algorithm. If nameAlg is HashAlgorithmNull, then HashAlgorithmSHA256
// is used. If schemeAlg is HashAlgorithmNull, then nameAlg is used.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template will create a TPM generated key. In order to supply the key, remove
// the AttrSensitiveDataOrigin attribute.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewHMACKey(nameAlg, schemeAlg tpm2.HashAlgorithmId) *tpm2.Public {
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if schemeAlg == tpm2.HashAlgorithmNull {
schemeAlg = nameAlg
}
return &tpm2.Public{
Type: tpm2.ObjectTypeKeyedHash,
NameAlg: nameAlg,
Attrs: tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth | tpm2.AttrSign,
Params: &tpm2.PublicParamsU{
KeyedHashDetail: &tpm2.KeyedHashParams{
Scheme: tpm2.KeyedHashScheme{
Scheme: tpm2.KeyedHashSchemeHMAC,
Details: &tpm2.SchemeKeyedHashU{
HMAC: &tpm2.SchemeHMAC{
HashAlg: schemeAlg}}}}}}
}
// NewHMACKeyWithDefaults returns a template for a HMAC key with SHA256 as the
// name algorithm and the HMAC digest algorithm.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template will create a TPM generated key. In order to supply the key, remove
// the AttrSensitiveDataOrigin attribute.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewHMACKeyWithDefaults() *tpm2.Public {
return NewHMACKey(tpm2.HashAlgorithmNull, tpm2.HashAlgorithmNull)
}
// NewDerivationParentKey returns a template for derivation parent key with the
// specified name algorithm and KDF digest algorithm. If nameAlg is HashAlgorithmNull,
// then HashAlgorithmSHA256 is used. If schemeAlg is HashAlgorithmNull, then nameAlg
// is used.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template will create a key with a TPM generated seed. In order to supply the
// seed, remove the AttrSensitiveDataOrigin attribute.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewDerivationParentKey(nameAlg, schemeAlg tpm2.HashAlgorithmId) *tpm2.Public {
if nameAlg == tpm2.HashAlgorithmNull {
nameAlg = tpm2.HashAlgorithmSHA256
}
if schemeAlg == tpm2.HashAlgorithmNull {
schemeAlg = nameAlg
}
return &tpm2.Public{
Type: tpm2.ObjectTypeKeyedHash,
NameAlg: nameAlg,
Attrs: tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth | tpm2.AttrDecrypt | tpm2.AttrRestricted,
Params: &tpm2.PublicParamsU{
KeyedHashDetail: &tpm2.KeyedHashParams{
Scheme: tpm2.KeyedHashScheme{
Scheme: tpm2.KeyedHashSchemeXOR,
Details: &tpm2.SchemeKeyedHashU{
XOR: &tpm2.SchemeXOR{
HashAlg: schemeAlg,
KDF: tpm2.KDFAlgorithmKDF1_SP800_108}}}}}}
}
// NewDerivationParentKeyWithDefaults returns a template for derivation parent key
// with SHA256 as the name algorithm and KDF digest algorithm.
//
// The template cannot be used to create a key in a duplication group. In order to create
// a key in a duplication group, remove the AttrFixedTPM attribute. In order to create
// a key that is a duplication root, remove both the AttrFixedTPM and AttrFixedParent
// attributes. In this case, an authorization policy that permits duplication must
// be added.
//
// The template will create a key with a TPM generated seed. In order to supply the
// seed, remove the AttrSensitiveDataOrigin attribute.
//
// The template has the AttrUserWithAuth set in order to permit authentication for
// the user auth role using the created object's authorization value. In order to
// require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewDerivationParentKeyWithDefaults() *tpm2.Public {
return NewDerivationParentKey(tpm2.HashAlgorithmNull, tpm2.HashAlgorithmNull)
}
./github.com/canonical/go-tpm2/tpm.go 0000664 0000000 0000000 00000041216 00000000000 015752 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"reflect"
"github.com/canonical/go-tpm2/mu"
"golang.org/x/xerrors"
)
func makeInvalidArgError(name, msg string) error {
return fmt.Errorf("invalid %s argument: %s", name, msg)
}
func isSessionAllowed(commandCode CommandCode) bool {
switch commandCode {
case CommandStartup:
return false
case CommandContextLoad:
return false
case CommandContextSave:
return false
case CommandFlushContext:
return false
default:
return true
}
}
type cmdContext struct {
commandCode CommandCode
sessionParams *sessionParams
responseCode ResponseCode
responseAuthArea []AuthResponse
rpBytes []byte
}
type delimiterSentinel struct{}
// Delimiter is a sentinel value used to delimit command handle, command parameter, response handle pointer and response
// parameter pointer blocks in the variable length params argument in TPMContext.RunCommand.
var Delimiter delimiterSentinel
// ResourceContextWithAuth associates a ResourceContext with a session for authorization, and is provided to TPMContext.RunCommand in
// the command handle area for any handles that require an authorization.
type ResourceContextWithSession struct {
Context ResourceContext
Session SessionContext
}
// TODO: Implement commands from the following sections of part 3 of the TPM library spec:
// Section 14 - Asymmetric Primitives
// Section 15 - Symmetric Primitives
// Section 17 - Hash/HMAC/Event Sequences
// Section 19 - Ephemeral EC Keys
// Section 26 - Miscellaneous Management Functions
// Section 27 - Field Upgrade
// TPMContext is the main entry point by which commands are executed on a TPM device using this package. It communicates with the
// underlying device via a transmission interface, which is an implementation of io.ReadWriteCloser provided to NewTPMContext.
//
// Methods that execute commands on the TPM will return errors where the TPM responds with them. These are in the form of *TPMError,
// *TPMWarning, *TPMHandleError, *TPMSessionError, *TPMParameterError and *TPMVendorError types.
//
// Some methods also accept a variable number of optional SessionContext arguments - these are for sessions that don't provide
// authorization for a corresponding TPM resource. These sessions may be used for the purposes of session based parameter encryption
// or command auditing.
type TPMContext struct {
tcti TCTI
permanentResources map[Handle]*permanentContext
maxSubmissions uint
propertiesInitialized bool
maxBufferSize int
maxDigestSize int
maxNVBufferSize int
exclusiveSession *sessionContext
}
// Close calls Close on the transmission interface.
func (t *TPMContext) Close() error {
if err := t.tcti.Close(); err != nil {
return &TctiError{"close", err}
}
return nil
}
// RunCommandBytes is a low-level interface for executing a command. The caller is responsible for supplying a properly
// serialized command packet, which can be created with MarshalCommandPacket.
//
// If successful, this function will return the response packet. An error will only be returned if the transmission
// interface returns an error.
func (t *TPMContext) RunCommandBytes(packet CommandPacket) (ResponsePacket, error) {
if _, err := t.tcti.Write(packet); err != nil {
return nil, &TctiError{"write", err}
}
resp, err := ioutil.ReadAll(t.tcti)
if err != nil {
return nil, &TctiError{"read", err}
}
return ResponsePacket(resp), nil
}
func (t *TPMContext) runCommandWithoutProcessingAuthResponse(commandCode CommandCode, sessionParams *sessionParams, inHandles []HandleContext, params []interface{}, outHandle *Handle) (*cmdContext, error) {
handles := make(HandleList, 0, len(inHandles))
handleNames := make([]Name, 0, len(inHandles))
for _, h := range inHandles {
handles = append(handles, h.Handle())
handleNames = append(handleNames, h.Name())
}
if sessionParams.hasDecryptSession() && (len(params) == 0 || !isParamEncryptable(params[0])) {
return nil, fmt.Errorf("command %s does not support command parameter encryption", commandCode)
}
cpBytes, err := mu.MarshalToBytes(params...)
if err != nil {
return nil, xerrors.Errorf("cannot marshal parameters for command %s: %w", commandCode, err)
}
cAuthArea, err := sessionParams.buildCommandAuthArea(commandCode, handleNames, cpBytes)
if err != nil {
return nil, xerrors.Errorf("cannot build auth area for command %s: %w", commandCode, err)
}
cmd := MarshalCommandPacket(commandCode, handles, cAuthArea, cpBytes)
var responseCode ResponseCode
var rpBytes []byte
var rAuthArea []AuthResponse
for tries := uint(1); ; tries++ {
var err error
resp, err := t.RunCommandBytes(cmd)
if err != nil {
return nil, err
}
responseCode, rpBytes, rAuthArea, err = resp.Unmarshal(outHandle)
if err != nil {
return nil, &InvalidResponseError{commandCode, fmt.Sprintf("cannot unmarshal response packet: %v", err)}
}
err = DecodeResponseCode(commandCode, responseCode)
if _, invalidRc := err.(InvalidResponseCodeError); invalidRc {
return nil, &InvalidResponseError{commandCode, err.Error()}
}
if err == nil {
if len(rAuthArea) != len(sessionParams.sessions) {
return nil, &InvalidResponseError{commandCode, fmt.Sprintf("unexpected number of auth responses (got %d, expected %d)",
len(rAuthArea), len(sessionParams.sessions))}
}
break
}
if tries >= t.maxSubmissions {
return nil, err
}
if !(IsTPMWarning(err, WarningYielded, commandCode) || IsTPMWarning(err, WarningTesting, commandCode) || IsTPMWarning(err, WarningRetry, commandCode)) {
return nil, err
}
}
return &cmdContext{
commandCode: commandCode,
sessionParams: sessionParams,
responseCode: responseCode,
responseAuthArea: rAuthArea,
rpBytes: rpBytes}, nil
}
func (t *TPMContext) processAuthResponse(cmd *cmdContext, params []interface{}) error {
if len(cmd.responseAuthArea) > 0 {
if err := cmd.sessionParams.processResponseAuthArea(cmd.responseAuthArea, cmd.responseCode, cmd.rpBytes); err != nil {
return &InvalidResponseError{cmd.commandCode, fmt.Sprintf("cannot process response auth area: %v", err)}
}
}
if isSessionAllowed(cmd.commandCode) {
if t.exclusiveSession != nil {
t.exclusiveSession.Data().IsExclusive = false
}
var exclusive *sessionContext
for _, s := range cmd.sessionParams.sessions {
if s.session == nil {
continue
}
if s.session.Data().IsExclusive {
exclusive = s.session
break
}
}
t.exclusiveSession = exclusive
if t.exclusiveSession != nil {
t.exclusiveSession.Data().IsExclusive = true
}
}
rpBuf := bytes.NewReader(cmd.rpBytes)
if _, err := mu.UnmarshalFromReader(rpBuf, params...); err != nil {
return &InvalidResponseError{cmd.commandCode, fmt.Sprintf("cannot unmarshal response parameters: %v", err)}
}
if rpBuf.Len() > 0 {
return &InvalidResponseError{cmd.commandCode, fmt.Sprintf("response parameter area contains %d trailing bytes", rpBuf.Len())}
}
return nil
}
// RunCommandWithResponseCallback is a high-level generic interface for executing the command specified by commandCode. It differs
// from RunCommand with the addition of an optional callback which is executed after receiving a response from the TPM, but before
// the response is decoded and the session state is updated. This is useful for commands that change the authorization value of a
// supplied entity, where the response HMAC may be generated based on the new authorization value. It takes care of marshalling
// command handles and command parameters, as well as constructing and marshalling the authorization area and choosing the correct
// StructTag value. It takes care of unmarshalling response handles and response parameters, as well as unmarshalling the response
// authorization area and performing checks on the authorization response.
//
// The variable length params argument provides a mechanism for the caller to provide command handles, command parameters, response
// handle pointers and response parameter pointers (in that order), with each group of arguments being separated by the Delimiter
// sentinel value.
//
// Command handles are provided as HandleContext types if they do not require an authorization. For command handles that require an
// authorization, they are provided using the ResourceContextWithSession type. This links the ResourceContext to an optional
// authorization session. If the authorization value of the TPM entity is required as part of the authorization, this will be obtained
// from the supplied ResourceContext. A nil HandleContext will automatically be converted to a handle with the value of HandleNull.
//
// Command parameters are provided as the go equivalent types for the types defined in the TPM Library Specification.
//
// Response handles are provided as pointers to Handle values.
//
// Response parameters are provided as pointers to values of the go equivalent types for the types defined in the TPM Library
// Specification.
//
// If the TPM responds with a warning that indicates the command could not be started and should be retried, this function will
// resubmit the command a finite number of times before returning an error. The maximum number of retries can be set via
// TPMContext.SetMaxSubmissions.
//
// The caller can provide additional sessions that aren't associated with a TPM entity (and therefore not used for authorization) via
// the sessions parameter, for the purposes of command auditing or session based parameter encryption.
//
// In addition to returning an error if any marshalling or unmarshalling fails, or if the transmission backend returns an error,
// this function will also return an error if the TPM responds with any ResponseCode other than Success.
func (t *TPMContext) RunCommandWithResponseCallback(commandCode CommandCode, sessions []SessionContext, responseCb func(), params ...interface{}) error {
var commandHandles []HandleContext
var commandParams []interface{}
var responseHandle *Handle
var responseParams []interface{}
var sessionParams sessionParams
sentinels := 0
for _, param := range params {
if param == Delimiter {
sentinels++
continue
}
switch sentinels {
case 0:
var handle HandleContext
switch p := param.(type) {
case ResourceContextWithSession:
handle = p.Context
if err := sessionParams.validateAndAppendAuth(p); err != nil {
return fmt.Errorf("cannot process ResourceContextWithSession for command %s at index %d: %v", commandCode, len(commandHandles), err)
}
case HandleContext:
handle = p
case nil:
default:
return fmt.Errorf("cannot process command handle argument for command %s at index %d: invalid type (%s)", commandCode, len(commandHandles), reflect.TypeOf(param))
}
if handle == nil {
handle = makePermanentContext(HandleNull)
}
commandHandles = append(commandHandles, handle)
case 1:
commandParams = append(commandParams, param)
case 2:
if responseHandle != nil {
return errors.New("only one response handle argument can be supplied")
}
handle, isHandle := param.(*Handle)
if !isHandle {
return fmt.Errorf("cannot process response handle argument for command %s: invalid type (%s)", commandCode, reflect.TypeOf(param))
}
responseHandle = handle
case 3:
responseParams = append(responseParams, param)
}
}
if err := sessionParams.validateAndAppendExtra(sessions); err != nil {
return fmt.Errorf("cannot process non-auth SessionContext parameters for command %s: %v", commandCode, err)
}
ctx, err := t.runCommandWithoutProcessingAuthResponse(commandCode, &sessionParams, commandHandles, commandParams, responseHandle)
if err != nil {
return err
}
if responseCb != nil {
responseCb()
}
return t.processAuthResponse(ctx, responseParams)
}
// RunCommand is the high-level generic interface for executing the command specified by commandCode. All of the methods on TPMContext
// exported by this package that execute commands on the TPM are essentially wrappers around this function. It takes care of
// marshalling command handles and command parameters, as well as constructing and marshalling the authorization area and choosing
// the correct StructTag value. It takes care of unmarshalling response handles and response parameters, as well as unmarshalling the
// response authorization area and performing checks on the authorization response.
//
// The variable length params argument provides a mechanism for the caller to provide command handles, command parameters, response
// handle pointers and response parameter pointers (in that order), with each group of arguments being separated by the Delimiter
// sentinel value.
//
// Command handles are provided as HandleContext types if they do not require an authorization. For command handles that require an
// authorization, they are provided using the ResourceContextWithSession type. This links the ResourceContext to an optional
// authorization session. If the authorization value of the TPM entity is required as part of the authorization, this will be obtained
// from the supplied ResourceContext. A nil HandleContext will automatically be converted to a handle with the value of HandleNull.
//
// Command parameters are provided as the go equivalent types for the types defined in the TPM Library Specification.
//
// Response handles are provided as pointers to Handle values.
//
// Response parameters are provided as pointers to values of the go equivalent types for the types defined in the TPM Library
// Specification.
//
// If the TPM responds with a warning that indicates the command could not be started and should be retried, this function will
// resubmit the command a finite number of times before returning an error. The maximum number of retries can be set via
// TPMContext.SetMaxSubmissions.
//
// The caller can provide additional sessions that aren't associated with a TPM entity (and therefore not used for authorization) via
// the sessions parameter, for the purposes of command auditing or session based parameter encryption.
//
// In addition to returning an error if any marshalling or unmarshalling fails, or if the transmission backend returns an error,
// this function will also return an error if the TPM responds with any ResponseCode other than Success.
func (t *TPMContext) RunCommand(commandCode CommandCode, sessions []SessionContext, params ...interface{}) error {
return t.RunCommandWithResponseCallback(commandCode, sessions, nil, params...)
}
// SetMaxSubmissions sets the maximum number of times that RunCommand will attempt to submit a command before failing with an error.
// The default value is 5.
func (t *TPMContext) SetMaxSubmissions(max uint) {
t.maxSubmissions = max
}
// InitProperties executes a TPM2_GetCapability command to initialize properties used internally by TPMContext. This is normally done
// automatically by functions that require these properties when they are used for the first time, but this function is provided so
// that the command can be audited, and so the exclusivity of an audit session can be preserved.
func (t *TPMContext) InitProperties(sessions ...SessionContext) error {
props, err := t.GetCapabilityTPMProperties(PropertyFixed, CapabilityMaxProperties, sessions...)
if err != nil {
return err
}
for _, prop := range props {
switch prop.Property {
case PropertyInputBuffer:
t.maxBufferSize = int(prop.Value)
case PropertyMaxDigest:
t.maxDigestSize = int(prop.Value)
case PropertyNVBufferMax:
t.maxNVBufferSize = int(prop.Value)
}
}
if t.maxBufferSize == 0 {
t.maxBufferSize = 1024
}
if t.maxDigestSize == 0 {
return &InvalidResponseError{Command: CommandGetCapability, msg: "missing or invalid TPM_PT_MAX_DIGEST property"}
}
if t.maxNVBufferSize == 0 {
return &InvalidResponseError{Command: CommandGetCapability, msg: "missing or invalid TPM_PT_NV_BUFFER_MAX property"}
}
t.propertiesInitialized = true
return nil
}
func (t *TPMContext) initPropertiesIfNeeded() error {
if t.propertiesInitialized {
return nil
}
return t.InitProperties()
}
func newTpmContext(tcti TCTI) *TPMContext {
r := new(TPMContext)
r.tcti = tcti
r.permanentResources = make(map[Handle]*permanentContext)
r.maxSubmissions = 5
return r
}
// NewTPMContext creates a new instance of TPMContext, which communicates with the
// TPM using the transmission interface provided via the tcti parameter. The
// transmission interface must not be nil - it is expected that the caller checks
// the error returned from the function that is used to create it.
func NewTPMContext(tcti TCTI) *TPMContext {
if tcti == nil {
panic("nil transmission interface")
}
t := new(TPMContext)
t.tcti = tcti
t.permanentResources = make(map[Handle]*permanentContext)
t.maxSubmissions = 5
return t
}
./github.com/canonical/go-tpm2/types.go 0000664 0000000 0000000 00000007212 00000000000 016314 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"errors"
"github.com/canonical/go-tpm2/mu"
)
// TPMManufacturer corresponds to the TPM manufacturer and is returned when querying the value PropertyManufacturer with
// TPMContext.GetCapabilityTPMProperties
type TPMManufacturer uint32
const (
TPMManufacturerAMD TPMManufacturer = 0x414D4400 // AMD
TPMManufacturerATML TPMManufacturer = 0x41544D4C // Atmel
TPMManufacturerBRCM TPMManufacturer = 0x4252434D // Broadcom
TPMManufacturerHPE TPMManufacturer = 0x48504500 // HPE
TPMManufacturerIBM TPMManufacturer = 0x49424d00 // IBM
TPMManufacturerIFX TPMManufacturer = 0x49465800 // Infineon
TPMManufacturerINTC TPMManufacturer = 0x494E5443 // Intel
TPMManufacturerLEN TPMManufacturer = 0x4C454E00 // Lenovo
TPMManufacturerMSFT TPMManufacturer = 0x4D534654 // Microsoft
TPMManufacturerNSM TPMManufacturer = 0x4E534D20 // National Semiconductor
TPMManufacturerNTZ TPMManufacturer = 0x4E545A00 // Nationz
TPMManufacturerNTC TPMManufacturer = 0x4E544300 // Nuvoton Technology
TPMManufacturerQCOM TPMManufacturer = 0x51434F4D // Qualcomm
TPMManufacturerSMSC TPMManufacturer = 0x534D5343 // SMSC
TPMManufacturerSTM TPMManufacturer = 0x53544D20 // ST Microelectronics
TPMManufacturerSMSN TPMManufacturer = 0x534D534E // Samsung
TPMManufacturerSNS TPMManufacturer = 0x534E5300 // Sinosun
TPMManufacturerTXN TPMManufacturer = 0x54584E00 // Texas Instruments
TPMManufacturerWEC TPMManufacturer = 0x57454300 // Winbond
TPMManufacturerROCC TPMManufacturer = 0x524F4343 // Fuzhou Rockchip
TPMManufacturerGOOG TPMManufacturer = 0x474F4F47 // Google
)
// PCRValues contains a collection of PCR values, keyed by HashAlgorithmId and PCR index.
type PCRValues map[HashAlgorithmId]map[int]Digest
// SelectionList computes a list of PCR selections corresponding to this set of PCR values.
func (v PCRValues) SelectionList() PCRSelectionList {
var out PCRSelectionList
for h := range v {
s := PCRSelection{Hash: h}
for p := range v[h] {
s.Select = append(s.Select, p)
}
out = append(out, s)
}
return out.Sort()
}
// ToListAndSelection converts this set of PCR values to a list of PCR selections and list of PCR
// values, in a form that can be serialized.
func (v PCRValues) ToListAndSelection() (pcrs PCRSelectionList, digests DigestList) {
pcrs = v.SelectionList()
for _, p := range pcrs {
for _, s := range p.Select {
digests = append(digests, v[p.Hash][s])
}
}
return
}
// SetValuesFromListAndSelection sets PCR values from the supplied list of PCR selections and list
// of values.
func (v PCRValues) SetValuesFromListAndSelection(pcrs PCRSelectionList, digests DigestList) (int, error) {
// Copy the selections so that each selection is ordered correctly
mu.MustCopyValue(&pcrs, pcrs)
i := 0
for _, p := range pcrs {
if _, ok := v[p.Hash]; !ok {
v[p.Hash] = make(map[int]Digest)
}
for _, s := range p.Select {
if len(digests) == 0 {
return 0, errors.New("insufficient digests")
}
d := digests[0]
digests = digests[1:]
if len(d) != p.Hash.Size() {
return 0, errors.New("incorrect digest size")
}
v[p.Hash][s] = d
i++
}
}
return i, nil
}
// SetValue sets the PCR value for the specified PCR and PCR bank.
func (v PCRValues) SetValue(alg HashAlgorithmId, pcr int, digest Digest) {
if _, ok := v[alg]; !ok {
v[alg] = make(map[int]Digest)
}
v[alg][pcr] = digest
}
// PublicTemplate exists to allow either Public or PublicDerived structures
// to be used as the template value for TPMContext.CreateLoaded.
type PublicTemplate interface {
ToTemplate() (Template, error)
}
./github.com/canonical/go-tpm2/types_algs.go 0000664 0000000 0000000 00000044030 00000000000 017321 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"reflect"
"github.com/canonical/go-tpm2/mu"
)
// This file contains types defined in section 11 (Algorithm Parameters
// and Structures) in part 2 of the library spec.
// 11.1) Symmetric
// SymKeyBitsU is a union type that corresponds to the TPMU_SYM_KEY_BITS type
// and is used to specify symmetric encryption key sizes. The selector type is
// AlgorithmId. Mapping of selector values to fields is as follows:
// - AlgorithmAES: Sym
// - AlgorithmSM4: Sym
// - AlgorithmCamellia: Sym
// - AlgorithmXOR: XOR
// - AlgorithmNull: none
type SymKeyBitsU struct {
Sym uint16
XOR HashAlgorithmId
}
func (b *SymKeyBitsU) Select(selector reflect.Value) interface{} {
switch selector.Convert(reflect.TypeOf(AlgorithmId(0))).Interface().(AlgorithmId) {
case AlgorithmAES:
fallthrough
case AlgorithmSM4:
fallthrough
case AlgorithmCamellia:
return &b.Sym
case AlgorithmXOR:
return &b.XOR
case AlgorithmNull:
return mu.NilUnionValue
default:
return nil
}
}
// SymModeU is a union type that corresponds to the TPMU_SYM_MODE type. The selector
// type is AlgorithmId. The mapping of selector values to fields is as follows:
// - AlgorithmAES: Sym
// - AlgorithmSM4: Sym
// - AlgorithmCamellia: Sym
// - AlgorithmXOR: none
// - AlgorithmNull: none
type SymModeU struct {
Sym SymModeId
}
func (m *SymModeU) Select(selector reflect.Value) interface{} {
switch selector.Convert(reflect.TypeOf(AlgorithmId(0))).Interface().(AlgorithmId) {
case AlgorithmAES:
fallthrough
case AlgorithmSM4:
fallthrough
case AlgorithmCamellia:
return &m.Sym
case AlgorithmXOR:
fallthrough
case AlgorithmNull:
return mu.NilUnionValue
default:
return nil
}
}
// SymDef corresponds to the TPMT_SYM_DEF type, and is used to select the algorithm
// used for parameter encryption.
type SymDef struct {
Algorithm SymAlgorithmId // Symmetric algorithm
KeyBits *SymKeyBitsU // Symmetric key size
Mode *SymModeU // Symmetric mode
}
// SymDefObject corresponds to the TPMT_SYM_DEF_OBJECT type, and is used to define an
// object's symmetric algorithm.
type SymDefObject struct {
Algorithm SymObjectAlgorithmId // Symmetric algorithm
KeyBits *SymKeyBitsU // Symmetric key size
Mode *SymModeU // Symmetric mode
}
// SymKey corresponds to the TPM2B_SYM_KEY type.
type SymKey []byte
// SymCipherParams corresponds to the TPMS_SYMCIPHER_PARMS type, and contains the
// parameters for a symmetric object.
type SymCipherParams struct {
Sym SymDefObject
}
// Label corresponds to the TPM2B_LABEL type.
type Label []byte
// Derive corresponds to the TPMS_DERIVE type.
type Derive struct {
Label Label
Context Label
}
// SensitiveCreate corresponds to the TPMS_SENSITIVE_CREATE type and is used to define
// the values to be placed in the sensitive area of a created object.
type SensitiveCreate struct {
UserAuth Auth // Authorization value
Data SensitiveData // Secret data
}
// SensitiveData corresponds to the TPM2B_SENSITIVE_DATA type.
type SensitiveData []byte
// SchemeHash corresponds to the TPMS_SCHEME_HASH type, and is used for schemes that only
// require a hash algorithm to complete their definition.
type SchemeHash struct {
HashAlg HashAlgorithmId // Hash algorithm used to digest the message
}
// SchemeECDAA corresponds to the TPMS_SCHEME_ECDAA type.
type SchemeECDAA struct {
HashAlg HashAlgorithmId // Hash algorithm used to digest the message
Count uint16
}
// KeyedHashSchemeId corresponds to the TPMI_ALG_KEYEDHASH_SCHEME type
type KeyedHashSchemeId AlgorithmId
const (
KeyedHashSchemeHMAC KeyedHashSchemeId = KeyedHashSchemeId(AlgorithmHMAC) // TPM_ALG_HMAC
KeyedHashSchemeXOR KeyedHashSchemeId = KeyedHashSchemeId(AlgorithmXOR) // TPM_ALG_XOR
KeyedHashSchemeNull KeyedHashSchemeId = KeyedHashSchemeId(AlgorithmNull) // TPM_ALG_NULL
)
// SchemeHMAC corresponds to the TPMS_SCHEME_HMAC type.
type SchemeHMAC SchemeHash
// SchemeXOR corresponds to the TPMS_SCHEME_XOR type, and is used to define the XOR encryption
// scheme.
type SchemeXOR struct {
HashAlg HashAlgorithmId // Hash algorithm used to digest the message
KDF KDFAlgorithmId // Hash algorithm used for the KDF
}
// SchemeKeyedHashU is a union type that corresponds to the TPMU_SCHEME_KEYED_HASH type.
// The selector type is KeyedHashSchemeId. The mapping of selector values to fields is
// as follows:
// - KeyedHashSchemeHMAC: HMAC
// - KeyedHashSchemeXOR: XOR
// - KeyedHashSchemeNull: none
type SchemeKeyedHashU struct {
HMAC *SchemeHMAC
XOR *SchemeXOR
}
func (d *SchemeKeyedHashU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(KeyedHashSchemeId) {
case KeyedHashSchemeHMAC:
return &d.HMAC
case KeyedHashSchemeXOR:
return &d.XOR
case KeyedHashSchemeNull:
return mu.NilUnionValue
default:
return nil
}
}
// KeyedHashScheme corresponds to the TPMT_KEYEDHASH_SCHEME type.
type KeyedHashScheme struct {
Scheme KeyedHashSchemeId // Scheme selector
Details *SchemeKeyedHashU // Scheme specific parameters
}
// 11.2 Assymetric
// 11.2.1 Signing Schemes
type SigSchemeRSASSA SchemeHash
type SigSchemeRSAPSS SchemeHash
type SigSchemeECDSA SchemeHash
type SigSchemeECDAA SchemeECDAA
type SigSchemeSM2 SchemeHash
type SigSchemeECSchnorr SchemeHash
// SigSchemeU is a union type that corresponds to the TPMU_SIG_SCHEME type. The
// selector type is SigSchemeId. The mapping of selector value to fields is as follows:
// - SigSchemeAlgRSASSA: RSASSA
// - SigSchemeAlgRSAPSS: RSAPSS
// - SigSchemeAlgECDSA: ECDSA
// - SigSchemeAlgECDAA: ECDAA
// - SigSchemeAlgSM2: SM2
// - SigSchemeAlgECSchnorr: ECSchnorr
// - SigSchemeAlgHMAC: HMAC
// - SigSchemeAlgNull: none
type SigSchemeU struct {
RSASSA *SigSchemeRSASSA
RSAPSS *SigSchemeRSAPSS
ECDSA *SigSchemeECDSA
ECDAA *SigSchemeECDAA
SM2 *SigSchemeSM2
ECSchnorr *SigSchemeECSchnorr
HMAC *SchemeHMAC
}
func (s *SigSchemeU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(SigSchemeId) {
case SigSchemeAlgRSASSA:
return &s.RSASSA
case SigSchemeAlgRSAPSS:
return &s.RSAPSS
case SigSchemeAlgECDSA:
return &s.ECDSA
case SigSchemeAlgECDAA:
return &s.ECDAA
case SigSchemeAlgSM2:
return &s.SM2
case SigSchemeAlgECSchnorr:
return &s.ECSchnorr
case SigSchemeAlgHMAC:
return &s.HMAC
case SigSchemeAlgNull:
return mu.NilUnionValue
default:
return nil
}
}
// Any returns the signature scheme associated with scheme as a *SchemeHash.
// It panics if the specified scheme is invalid (SigSchemeId.IsValid returns
// false), or the appropriate field isn't set.
func (s SigSchemeU) Any(scheme SigSchemeId) *SchemeHash {
if !scheme.IsValid() {
panic("invalid scheme")
}
switch scheme {
case SigSchemeAlgRSASSA:
return (*SchemeHash)(&(*s.RSASSA))
case SigSchemeAlgRSAPSS:
return (*SchemeHash)(&(*s.RSAPSS))
case SigSchemeAlgECDSA:
return (*SchemeHash)(&(*s.ECDSA))
case SigSchemeAlgECDAA:
return &SchemeHash{HashAlg: s.ECDAA.HashAlg}
case SigSchemeAlgSM2:
return (*SchemeHash)(&(*s.SM2))
case SigSchemeAlgECSchnorr:
return (*SchemeHash)(&(*s.ECSchnorr))
case SigSchemeAlgHMAC:
return (*SchemeHash)(&(*s.HMAC))
default:
panic("not reached")
}
}
// SigScheme corresponds to the TPMT_SIG_SCHEME type.
type SigScheme struct {
Scheme SigSchemeId // Scheme selector
Details *SigSchemeU // Scheme specific parameters
}
// 11.2.2 Encryption Schemes
type EncSchemeRSAES Empty
type EncSchemeOAEP SchemeHash
type KeySchemeECDH SchemeHash
type KeySchemeECMQV SchemeHash
// 11.2.3 Key Derivation Schemes
type SchemeMGF1 SchemeHash
type SchemeKDF1_SP800_56A SchemeHash
type SchemeKDF2 SchemeHash
type SchemeKDF1_SP800_108 SchemeHash
// KDFSchemeU is a union type that corresponds to the TPMU_KDF_SCHEME
// type. The selector type is KDFAlgorithmId. The mapping of selector
// value to field is as follows:
// - KDFAlgorithmMGF1: MGF1
// - KDFAlgorithmKDF1_SP800_56A: KDF1_SP800_56A
// - KDFAlgorithmKDF2: KDF2
// - KDFAlgorithmKDF1_SP800_108: KDF1_SP800_108
// - KDFAlgorithmNull: none
type KDFSchemeU struct {
MGF1 *SchemeMGF1
KDF1_SP800_56A *SchemeKDF1_SP800_56A
KDF2 *SchemeKDF2
KDF1_SP800_108 *SchemeKDF1_SP800_108
}
func (s *KDFSchemeU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(KDFAlgorithmId) {
case KDFAlgorithmMGF1:
return &s.MGF1
case KDFAlgorithmKDF1_SP800_56A:
return &s.KDF1_SP800_56A
case KDFAlgorithmKDF2:
return &s.KDF2
case KDFAlgorithmKDF1_SP800_108:
return &s.KDF1_SP800_108
case KDFAlgorithmNull:
return mu.NilUnionValue
default:
return nil
}
}
// KDFScheme corresponds to the TPMT_KDF_SCHEME type.
type KDFScheme struct {
Scheme KDFAlgorithmId // Scheme selector
Details *KDFSchemeU // Scheme specific parameters.
}
// AsymSchemeId corresponds to the TPMI_ALG_ASYM_SCHEME type
type AsymSchemeId AlgorithmId
// IsValid determines if the scheme is a valid asymmetric scheme.
func (s AsymSchemeId) IsValid() bool {
switch s {
case AsymSchemeRSASSA:
case AsymSchemeRSAES:
case AsymSchemeRSAPSS:
case AsymSchemeOAEP:
case AsymSchemeECDSA:
case AsymSchemeECDH:
case AsymSchemeECDAA:
case AsymSchemeSM2:
case AsymSchemeECSchnorr:
case AsymSchemeECMQV:
default:
return false
}
return true
}
// HasDigest determines if the asymmetric scheme is associated with
// a digest algorithm.
func (s AsymSchemeId) HasDigest() bool {
switch s {
case AsymSchemeRSASSA:
case AsymSchemeRSAPSS:
case AsymSchemeOAEP:
case AsymSchemeECDSA:
case AsymSchemeECDH:
case AsymSchemeECDAA:
case AsymSchemeSM2:
case AsymSchemeECSchnorr:
case AsymSchemeECMQV:
default:
return false
}
return true
}
const (
AsymSchemeNull AsymSchemeId = AsymSchemeId(AlgorithmNull) // TPM_ALG_NULL
AsymSchemeRSASSA AsymSchemeId = AsymSchemeId(AlgorithmRSASSA) // TPM_ALG_RSASSA
AsymSchemeRSAES AsymSchemeId = AsymSchemeId(AlgorithmRSAES) // TPM_ALG_RSAES
AsymSchemeRSAPSS AsymSchemeId = AsymSchemeId(AlgorithmRSAPSS) // TPM_ALG_RSAPSS
AsymSchemeOAEP AsymSchemeId = AsymSchemeId(AlgorithmOAEP) // TPM_ALG_OAEP
AsymSchemeECDSA AsymSchemeId = AsymSchemeId(AlgorithmECDSA) // TPM_ALG_ECDSA
AsymSchemeECDH AsymSchemeId = AsymSchemeId(AlgorithmECDH) // TPM_ALG_ECDH
AsymSchemeECDAA AsymSchemeId = AsymSchemeId(AlgorithmECDAA) // TPM_ALG_ECDAA
AsymSchemeSM2 AsymSchemeId = AsymSchemeId(AlgorithmSM2) // TPM_ALG_SM2
AsymSchemeECSchnorr AsymSchemeId = AsymSchemeId(AlgorithmECSchnorr) // TPM_ALG_ECSCHNORR
AsymSchemeECMQV AsymSchemeId = AsymSchemeId(AlgorithmECMQV) // TPM_ALG_ECMQV
)
// AsymSchemeU is a union type that corresponds to the TPMU_ASYM_SCHEME type. The
// selector type is AsymSchemeId. The mapping of selector values to fields is as follows:
// - AsymSchemeRSASSA: RSASSA
// - AsymSchemeRSAES: RSAES
// - AsymSchemeRSAPSS: RSAPSS
// - AsymSchemeOAEP: OAEP
// - AsymSchemeECDSA: ECDSA
// - AsymSchemeECDH: ECDH
// - AsymSchemeECDAA: ECDAA
// - AsymSchemeSM2: SM2
// - AsymSchemeECSchnorr: ECSchnorr
// - AsymSchemeECMQV: ECMQV
// - AsymSchemeNull: none
type AsymSchemeU struct {
RSASSA *SigSchemeRSASSA
RSAES *EncSchemeRSAES
RSAPSS *SigSchemeRSAPSS
OAEP *EncSchemeOAEP
ECDSA *SigSchemeECDSA
ECDH *KeySchemeECDH
ECDAA *SigSchemeECDAA
SM2 *SigSchemeSM2
ECSchnorr *SigSchemeECSchnorr
ECMQV *KeySchemeECMQV
}
func (s *AsymSchemeU) Select(selector reflect.Value) interface{} {
switch selector.Convert(reflect.TypeOf(AsymSchemeId(0))).Interface().(AsymSchemeId) {
case AsymSchemeRSASSA:
return &s.RSASSA
case AsymSchemeRSAES:
return &s.RSAES
case AsymSchemeRSAPSS:
return &s.RSAPSS
case AsymSchemeOAEP:
return &s.OAEP
case AsymSchemeECDSA:
return &s.ECDSA
case AsymSchemeECDH:
return &s.ECDH
case AsymSchemeECDAA:
return &s.ECDAA
case AsymSchemeSM2:
return &s.SM2
case AsymSchemeECSchnorr:
return &s.ECSchnorr
case AsymSchemeECMQV:
return &s.ECMQV
case AsymSchemeNull:
return mu.NilUnionValue
default:
return nil
}
}
// Any returns the asymmetric scheme associated with scheme as a *SchemeHash.
// It panics if the specified scheme does not have an associated digest algorithm
// (AsymSchemeId.HasDigest returns false), or if the appropriate field isn't set.
func (s AsymSchemeU) Any(scheme AsymSchemeId) *SchemeHash {
if !scheme.HasDigest() {
panic("invalid asymmetric scheme")
}
switch scheme {
case AsymSchemeRSASSA:
return (*SchemeHash)(&(*s.RSASSA))
case AsymSchemeRSAPSS:
return (*SchemeHash)(&(*s.RSAPSS))
case AsymSchemeOAEP:
return (*SchemeHash)(&(*s.OAEP))
case AsymSchemeECDSA:
return (*SchemeHash)(&(*s.ECDSA))
case AsymSchemeECDH:
return (*SchemeHash)(&(*s.ECDH))
case AsymSchemeECDAA:
return &SchemeHash{HashAlg: s.ECDAA.HashAlg}
case AsymSchemeSM2:
return (*SchemeHash)(&(*s.SM2))
case AsymSchemeECSchnorr:
return (*SchemeHash)(&(*s.ECSchnorr))
case AsymSchemeECMQV:
return (*SchemeHash)(&(*s.ECMQV))
default:
panic("not reached")
}
}
// AsymScheme corresponds to the TPMT_ASYM_SCHEME type.
type AsymScheme struct {
Scheme AsymSchemeId // Scheme selector
Details *AsymSchemeU // Scheme specific parameters
}
// 11.2.4 RSA
// RSASchemeId corresponds to the TPMI_ALG_RSA_SCHEME type.
type RSASchemeId AsymSchemeId
const (
RSASchemeNull RSASchemeId = RSASchemeId(AlgorithmNull) // TPM_ALG_NULL
RSASchemeRSASSA RSASchemeId = RSASchemeId(AlgorithmRSASSA) // TPM_ALG_RSASSA
RSASchemeRSAES RSASchemeId = RSASchemeId(AlgorithmRSAES) // TPM_ALG_RSAES
RSASchemeRSAPSS RSASchemeId = RSASchemeId(AlgorithmRSAPSS) // TPM_ALG_RSAPSS
RSASchemeOAEP RSASchemeId = RSASchemeId(AlgorithmOAEP) // TPM_ALG_OAEP
)
// RSAScheme corresponds to the TPMT_RSA_SCHEME type.
type RSAScheme struct {
Scheme RSASchemeId // Scheme selector
Details *AsymSchemeU // Scheme specific parameters.
}
// PublicKeyRSA corresponds to the TPM2B_PUBLIC_KEY_RSA type.
type PublicKeyRSA []byte
// PrivateKeyRSA corresponds to the TPM2B_PRIVATE_KEY_RSA type.
type PrivateKeyRSA []byte
// 11.2.5 ECC
// ECCParameter corresponds to the TPM2B_ECC_PARAMETER type.
type ECCParameter []byte
// ECCPoint corresponds to the TPMS_ECC_POINT type, and contains the coordinates
// that define an ECC point.
type ECCPoint struct {
X ECCParameter // X coordinate
Y ECCParameter // Y coordinate
}
// ECCSchemeId corresponds to the TPMI_ALG_ECC_SCHEME type.
type ECCSchemeId AsymSchemeId
const (
ECCSchemeNull ECCSchemeId = ECCSchemeId(AlgorithmNull) // TPM_ALG_NULL
ECCSchemeECDSA ECCSchemeId = ECCSchemeId(AlgorithmECDSA) // TPM_ALG_ECDSA
ECCSchemeECDH ECCSchemeId = ECCSchemeId(AlgorithmECDH) // TPM_ALG_ECDH
ECCSchemeECDAA ECCSchemeId = ECCSchemeId(AlgorithmECDAA) // TPM_ALG_ECDAA
ECCSchemeSM2 ECCSchemeId = ECCSchemeId(AlgorithmSM2) // TPM_ALG_SM2
ECCSchemeECSchnorr ECCSchemeId = ECCSchemeId(AlgorithmECSchnorr) // TPM_ALG_ECSCHNORR
ECCSchemeECMQV ECCSchemeId = ECCSchemeId(AlgorithmECMQV) // TPM_ALG_ECMQV
)
// ECCScheme corresponds to the TPMT_ECC_SCHEME type.
type ECCScheme struct {
Scheme ECCSchemeId // Scheme selector
Details *AsymSchemeU // Scheme specific parameters.
}
// 11.3 Signatures
// SignatureRSA corresponds to the TPMS_SIGNATURE_RSA type.
type SignatureRSA struct {
Hash HashAlgorithmId // Hash algorithm used to digest the message
Sig PublicKeyRSA // Signature, which is the same size as the public key
}
// SignatureECC corresponds to the TPMS_SIGNATURE_ECC type.
type SignatureECC struct {
Hash HashAlgorithmId // Hash is the digest algorithm used in the signature process
SignatureR ECCParameter
SignatureS ECCParameter
}
type SignatureRSASSA SignatureRSA
type SignatureRSAPSS SignatureRSA
type SignatureECDSA SignatureECC
type SignatureECDAA SignatureECC
type SignatureSM2 SignatureECC
type SignatureECSchnorr SignatureECC
// SignatureU is a union type that corresponds to TPMU_SIGNATURE. The selector
// type is SigSchemeId. The mapping of selector values to fields is as follows:
// - SigSchemeAlgRSASSA: RSASSA
// - SigSchemeAlgRSAPSS: RSAPSS
// - SigSchemeAlgECDSA: ECDSA
// - SigSchemeAlgECDAA: ECDAA
// - SigSchemeAlgSM2: SM2
// - SigSchemeAlgECSchnorr: ECSchnorr
// - SigSchemeAlgHMAC: HMAC
// - SigSchemeAlgNull: none
type SignatureU struct {
RSASSA *SignatureRSASSA
RSAPSS *SignatureRSAPSS
ECDSA *SignatureECDSA
ECDAA *SignatureECDAA
SM2 *SignatureSM2
ECSchnorr *SignatureECSchnorr
HMAC *TaggedHash
}
func (s *SignatureU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(SigSchemeId) {
case SigSchemeAlgRSASSA:
return &s.RSASSA
case SigSchemeAlgRSAPSS:
return &s.RSAPSS
case SigSchemeAlgECDSA:
return &s.ECDSA
case SigSchemeAlgECDAA:
return &s.ECDAA
case SigSchemeAlgSM2:
return &s.SM2
case SigSchemeAlgECSchnorr:
return &s.ECSchnorr
case SigSchemeAlgHMAC:
return &s.HMAC
case SigSchemeAlgNull:
return mu.NilUnionValue
default:
return nil
}
}
// Any returns the signature associated with scheme as a *SchemeHash. It
// panics if scheme is SigSchemeAlgNull or the appropriate field isn't
// set.
func (s SignatureU) Any(scheme SigSchemeId) *SchemeHash {
if !scheme.IsValid() {
panic("invalid signature scheme")
}
switch scheme {
case SigSchemeAlgRSASSA:
return &SchemeHash{HashAlg: s.RSASSA.Hash}
case SigSchemeAlgRSAPSS:
return &SchemeHash{HashAlg: s.RSAPSS.Hash}
case SigSchemeAlgECDSA:
return &SchemeHash{HashAlg: s.ECDSA.Hash}
case SigSchemeAlgECDAA:
return &SchemeHash{HashAlg: s.ECDAA.Hash}
case SigSchemeAlgSM2:
return &SchemeHash{HashAlg: s.SM2.Hash}
case SigSchemeAlgECSchnorr:
return &SchemeHash{HashAlg: s.ECSchnorr.Hash}
case SigSchemeAlgHMAC:
return &SchemeHash{HashAlg: s.HMAC.HashAlg}
default:
panic("not reached")
}
}
// Signature corresponds to the TPMT_SIGNATURE type. It is returned by the attestation
// commands, and is a parameter for TPMContext.VerifySignature and TPMContext.PolicySigned.
type Signature struct {
SigAlg SigSchemeId // Signature algorithm
Signature *SignatureU // Actual signature
}
// 11.4) Key/Secret Exchange
// EncryptedSecret corresponds to the TPM2B_ENCRYPTED_SECRET type.
type EncryptedSecret []byte
./github.com/canonical/go-tpm2/types_attributes.go 0000664 0000000 0000000 00000017021 00000000000 020561 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// This file contains types defined in section 8 (Attributes) in
// part 2 of the library spec.
// AlgorithmAttributes corresponds to the TPMA_ALGORITHM type and
// represents the attributes for an algorithm.
type AlgorithmAttributes uint32
const (
AttrAsymmetric AlgorithmAttributes = 1 << 0
AttrSymmetric AlgorithmAttributes = 1 << 1
AttrHash AlgorithmAttributes = 1 << 2
AttrObject AlgorithmAttributes = 1 << 3
AttrSigning AlgorithmAttributes = 1 << 8
AttrEncrypting AlgorithmAttributes = 1 << 9
AttrMethod AlgorithmAttributes = 1 << 10
)
// ObjectAttributes corresponds to the TPMA_OBJECT type, and represents
// the attributes for an object.
type ObjectAttributes uint32
const (
AttrFixedTPM ObjectAttributes = 1 << 1 // fixedTPM
AttrStClear ObjectAttributes = 1 << 2 // stClear
AttrFixedParent ObjectAttributes = 1 << 4 // fixedParent
AttrSensitiveDataOrigin ObjectAttributes = 1 << 5 // sensitiveDataOrigin
AttrUserWithAuth ObjectAttributes = 1 << 6 // userWithAuth
AttrAdminWithPolicy ObjectAttributes = 1 << 7 // adminWithPolicy
AttrNoDA ObjectAttributes = 1 << 10 // noDA
AttrEncryptedDuplication ObjectAttributes = 1 << 11 // encryptedDuplication
AttrRestricted ObjectAttributes = 1 << 16 // restricted
AttrDecrypt ObjectAttributes = 1 << 17 // decrypt
AttrSign ObjectAttributes = 1 << 18 // sign
)
// SessionAttributes corresponds to the TPMA_SESSION type, and represents
// the attributes for a session.
type SessionAttributes uint8
func (a SessionAttributes) canonicalize() SessionAttributes {
if a&AttrAuditExclusive > 0 {
a |= AttrAudit
}
if a&AttrAuditReset > 0 {
a |= AttrAudit
}
return a
}
const (
// AttrContinueSession corresponds to continueSession and specifies that the session should not be flushed
// from the TPM after it is used. If a session is used without this flag, it will be flushed from the TPM
// after the command completes successfully. In this case, the HandleContext associated with the session
// will be invalidated.
AttrContinueSession SessionAttributes = 1 << iota
// AttrAuditExclusive corresponds to auditExclusive and indicates that the command should only be executed
// if the session is exclusive at the start of the command. A session becomes exclusive when it is used for
// auditing for the first time, or if the AttrAuditReset attribute is provided. A session will remain
// exclusive until the TPM executes any command where the exclusive session isn't used for auditing, if
// that command allows for audit sessions to be provided.
//
// Setting this on SessionContext implies AttrAudit.
AttrAuditExclusive
// AttrAuditReset corresponds to auditReset and indicates that the audit digest of the session should be reset.
// The session will subsequently become exclusive. A session will remain exclusive until the TPM executes any
// command where the exclusive session isn't used for auditing, if that command allows for audit sessions to be
// provided.
//
// Setting this on SessionContext implies AttrAudit.
AttrAuditReset
// AttrCommandEncrypt corresponds to decrypt and specifies that the session should be used for encryption of the
// first command parameter before being sent from the host to the TPM. This can only be used for parameters that
// have types corresponding to TPM2B prefixed TCG types, and requires a session that was configured with a valid
// symmetric algorithm via the symmetric argument of TPMContext.StartAuthSession.
AttrCommandEncrypt SessionAttributes = 1 << (iota + 2)
// AttrResponseEncrypt corresponds to encrypt and specifies that the session should be used for encryption of the
// first response parameter before being sent from the TPM to the host. This can only be used for parameters that
// have types corresponding to TPM2B prefixed TCG types, and requires a session that was configured with a valid
// symmetric algorithm via the symmetric argument of TPMContext.StartAuthSession. This package automatically
// decrypts the received encrypted response parameter.
AttrResponseEncrypt
// AttrAudit corresponds to audit and indicates that the session should be used for auditing. If this is the first
// time that the session is used for auditing, then this attribute will result in the session becoming exclusive.
// A session will remain exclusive until the TPM executes any command where the exclusive session isn't used for
// auditing, if that command allows for audit sessions to be provided.
AttrAudit
)
// Locality corresponds to the TPMA_LOCALITY type.
type Locality uint8
const (
LocalityZero Locality = 0 // TPM_LOC_ZERO
LocalityOne Locality = 1 // TPM_LOC_ONE
LocalityTwo Locality = 2 // TPM_LOC_TWO
LocalityThree Locality = 3 // TPM_LOC_THREE
LocalityFour Locality = 4 // TPM_LOC_FOUR
)
// PermanentAttributes corresponds to the TPMA_PERMANENT type and is returned
// when querying the value of PropertyPermanent.
type PermanentAttributes uint32
const (
AttrOwnerAuthSet PermanentAttributes = 1 << 0 // ownerAuthSet
AttrEndorsementAuthSet PermanentAttributes = 1 << 1 // endorsementAuthSet
AttrLockoutAuthSet PermanentAttributes = 1 << 2 // lockoutAuthSet
AttrDisableClear PermanentAttributes = 1 << 8 // disableClear
AttrInLockout PermanentAttributes = 1 << 9 // inLockout
AttrTPMGeneratedEPS PermanentAttributes = 1 << 10 // tpmGeneratedEPS
)
// StatupClearAttributes corresponds to the TPMA_STARTUP_CLEAR type and
// is used to report details of properties that reset after a Startup(CLEAR).
// It is returned when querying the value of PropertyStartupClear.
type StartupClearAttributes uint32
const (
AttrPhEnable StartupClearAttributes = 1 << 0 // phEnable
AttrShEnable StartupClearAttributes = 1 << 1 // shEnable
AttrEhEnable StartupClearAttributes = 1 << 2 // ehEnable
AttrPhEnableNV StartupClearAttributes = 1 << 3 // phEnableNV
AttrOrderly StartupClearAttributes = 1 << 31 // orderly
)
// MemoryAttributes corresponds to the TPMA_MEMORY type and is used to
// report details about memory management. It is returned when querying
// the value of PropertyMemory.
type MemoryAttributes uint32
const (
AttrSharedRAM MemoryAttributes = 1 << 0 // sharedRAM
AttrSharedNV MemoryAttributes = 1 << 1 // sharedNV
AttrObjectCopiedToRAM MemoryAttributes = 1 << 2 // objectCopiedToRam
)
// CommandAttributes corresponds to the TPMA_CC type and represents the
// attributes of a command. It also encodes the command code to which these
// attributes belong, and the number of command handles for the command.
type CommandAttributes uint32
// CommandCode returns the command code that a set of attributes belongs to.
func (a CommandAttributes) CommandCode() CommandCode {
return CommandCode(a & (AttrV | 0xffff))
}
// NumberOfCommandHandles returns the number of command handles for the
// command that a set of attributes belong to.
func (a CommandAttributes) NumberOfCommandHandles() int {
return int((a & 0x0e000000) >> 25)
}
const (
AttrNV CommandAttributes = 1 << 22
AttrExtensive CommandAttributes = 1 << 23
AttrFlushed CommandAttributes = 1 << 24
AttrRHandle CommandAttributes = 1 << 28
AttrV CommandAttributes = 1 << 29
)
// ModeAttributes correspnds to TPMA_MODES and is returned when querying
// the value of PropertyModes.
type ModeAttributes uint32
const (
ModeFIPS140_2 ModeAttributes = 1 << 0 // FIPS_140_2
)
./github.com/canonical/go-tpm2/types_constants.go 0000664 0000000 0000000 00000057667 00000000000 020433 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"crypto/elliptic"
)
// This file contains types defined in section 6 (Contants) in
// part 2 of the library spec.
// TPMGenerated corresponds to the TPM_GENERATED type.
type TPMGenerated uint32
const (
TPMGeneratedValue TPMGenerated = 0xff544347 // TPM_GENERATED_VALUE
)
// AlgorithmId corresponds to the TPM_ALG_ID type.
type AlgorithmId uint16
const (
AlgorithmError AlgorithmId = 0x0000 // TPM_ALG_ERROR
AlgorithmRSA AlgorithmId = 0x0001 // TPM_ALG_RSA
AlgorithmTDES AlgorithmId = 0x0003 // TPM_ALG_TDES
AlgorithmSHA1 AlgorithmId = 0x0004 // TPM_ALG_SHA1
AlgorithmHMAC AlgorithmId = 0x0005 // TPM_ALG_HMAC
AlgorithmAES AlgorithmId = 0x0006 // TPM_ALG_AES
AlgorithmMGF1 AlgorithmId = 0x0007 // TPM_ALG_MGF1
AlgorithmKeyedHash AlgorithmId = 0x0008 // TPM_ALG_KEYEDHASH
AlgorithmXOR AlgorithmId = 0x000a // TPM_ALG_XOR
AlgorithmSHA256 AlgorithmId = 0x000b // TPM_ALG_SHA256
AlgorithmSHA384 AlgorithmId = 0x000c // TPM_ALG_SHA384
AlgorithmSHA512 AlgorithmId = 0x000d // TPM_ALG_SHA512
AlgorithmNull AlgorithmId = 0x0010 // TPM_ALG_NULL
AlgorithmSM3_256 AlgorithmId = 0x0012 // TPM_ALG_SM3_256
AlgorithmSM4 AlgorithmId = 0x0013 // TPM_ALG_SM4
AlgorithmRSASSA AlgorithmId = 0x0014 // TPM_ALG_RSASSA
AlgorithmRSAES AlgorithmId = 0x0015 // TPM_ALG_RSAES
AlgorithmRSAPSS AlgorithmId = 0x0016 // TPM_ALG_RSAPSS
AlgorithmOAEP AlgorithmId = 0x0017 // TPM_ALG_OAEP
AlgorithmECDSA AlgorithmId = 0x0018 // TPM_ALG_ECDSA
AlgorithmECDH AlgorithmId = 0x0019 // TPM_ALG_ECDH
AlgorithmECDAA AlgorithmId = 0x001a // TPM_ALG_ECDAA
AlgorithmSM2 AlgorithmId = 0x001b // TPM_ALG_SM2
AlgorithmECSchnorr AlgorithmId = 0x001c // TPM_ALG_ECSCHNORR
AlgorithmECMQV AlgorithmId = 0x001d // TPM_ALG_ECMQV
AlgorithmKDF1_SP800_56A AlgorithmId = 0x0020 // TPM_ALG_KDF1_SP800_56A
AlgorithmKDF2 AlgorithmId = 0x0021 // TPM_ALG_KDF2
AlgorithmKDF1_SP800_108 AlgorithmId = 0x0022 // TPM_ALG_KDF1_SP800_108
AlgorithmECC AlgorithmId = 0x0023 // TPM_ALG_ECC
AlgorithmSymCipher AlgorithmId = 0x0025 // TPM_ALG_SYMCIPHER
AlgorithmCamellia AlgorithmId = 0x0026 // TPM_ALG_CAMELLIA
AlgorithmSHA3_256 AlgorithmId = 0x0027 // TPM_ALG_SHA3_256
AlgorithmSHA3_384 AlgorithmId = 0x0028 // TPM_ALG_SHA3_384
AlgorithmSHA3_512 AlgorithmId = 0x0029 // TPM_ALG_SHA3_512
AlgorithmCTR AlgorithmId = 0x0040 // TPM_ALG_CTR
AlgorithmOFB AlgorithmId = 0x0041 // TPM_ALG_OFB
AlgorithmCBC AlgorithmId = 0x0042 // TPM_ALG_CBC
AlgorithmCFB AlgorithmId = 0x0043 // TPM_ALG_CFB
AlgorithmECB AlgorithmId = 0x0044 // TPM_ALG_ECB
AlgorithmFirst AlgorithmId = AlgorithmRSA
)
// ECCCurve corresponds to the TPM_ECC_CURVE type.
type ECCCurve uint16
// GoCurve returns the equivalent elliptic.Curve for this ECC curve.
func (c ECCCurve) GoCurve() elliptic.Curve {
return eccCurves[c]
}
const (
ECCCurveNIST_P192 ECCCurve = 0x0001 // TPM_ECC_NIST_P192
ECCCurveNIST_P224 ECCCurve = 0x0002 // TPM_ECC_NIST_P224
ECCCurveNIST_P256 ECCCurve = 0x0003 // TPM_ECC_NIST_P256
ECCCurveNIST_P384 ECCCurve = 0x0004 // TPM_ECC_NIST_P384
ECCCurveNIST_P521 ECCCurve = 0x0005 // TPM_ECC_NIST_P521
ECCCurveBN_P256 ECCCurve = 0x0010 // TPM_ECC_BN_P256
ECCCurveBN_P638 ECCCurve = 0x0011 // TPM_ECC_BN_P638
ECCCurveSM2_P256 ECCCurve = 0x0020 // TPM_ECC_SM2_P256
ECCCurveFirst ECCCurve = ECCCurveNIST_P192
)
// CommandCode corresponds to the TPM_CC type.
type CommandCode uint32
const (
CommandFirst CommandCode = 0x0000011A
CommandNVUndefineSpaceSpecial CommandCode = 0x0000011F // TPM_CC_NV_UndefineSpaceSpecial
CommandEvictControl CommandCode = 0x00000120 // TPM_CC_EvictControl
CommandHierarchyControl CommandCode = 0x00000121 // TPM_CC_HierarchyControl
CommandNVUndefineSpace CommandCode = 0x00000122 // TPM_CC_NV_UndefineSpace
CommandClear CommandCode = 0x00000126 // TPM_CC_Clear
CommandClearControl CommandCode = 0x00000127 // TPM_CC_ClearControl
CommandClockSet CommandCode = 0x00000128 // TPM_CC_ClockSet
CommandHierarchyChangeAuth CommandCode = 0x00000129 // TPM_CC_HierarchyChangeAuth
CommandNVDefineSpace CommandCode = 0x0000012A // TPM_CC_NV_DefineSpace
CommandPCRAllocate CommandCode = 0x0000012B // TPM_CC_PCR_Allocate
CommandSetPrimaryPolicy CommandCode = 0x0000012E // TPM_CC_SetPrimaryPolicy
CommandClockRateAdjust CommandCode = 0x00000130 // TPM_CC_ClockRateAdjust
CommandCreatePrimary CommandCode = 0x00000131 // TPM_CC_CreatePrimary
CommandNVGlobalWriteLock CommandCode = 0x00000132 // TPM_CC_NV_GlobalWriteLock
CommandGetCommandAuditDigest CommandCode = 0x00000133 // TPM_CC_GetCommandAuditDigest
CommandNVIncrement CommandCode = 0x00000134 // TPM_CC_NV_Increment
CommandNVSetBits CommandCode = 0x00000135 // TPM_CC_NV_SetBits
CommandNVExtend CommandCode = 0x00000136 // TPM_CC_NV_Extend
CommandNVWrite CommandCode = 0x00000137 // TPM_CC_NV_Write
CommandNVWriteLock CommandCode = 0x00000138 // TPM_CC_NV_WriteLock
CommandDictionaryAttackLockReset CommandCode = 0x00000139 // TPM_CC_DictionaryAttackLockReset
CommandDictionaryAttackParameters CommandCode = 0x0000013A // TPM_CC_DictionaryAttackParameters
CommandNVChangeAuth CommandCode = 0x0000013B // TPM_CC_NV_ChangeAuth
CommandPCREvent CommandCode = 0x0000013C // TPM_CC_PCR_Event
CommandPCRReset CommandCode = 0x0000013D // TPM_CC_PCR_Reset
CommandSequenceComplete CommandCode = 0x0000013E // TPM_CC_SequenceComplete
CommandSetCommandCodeAuditStatus CommandCode = 0x00000140 // TPM_CC_SetCommandCodeAuditStatus
CommandIncrementalSelfTest CommandCode = 0x00000142 // TPM_CC_IncrementalSelfTest
CommandSelfTest CommandCode = 0x00000143 // TPM_CC_SelfTest
CommandStartup CommandCode = 0x00000144 // TPM_CC_Startup
CommandShutdown CommandCode = 0x00000145 // TPM_CC_Shutdown
CommandStirRandom CommandCode = 0x00000146 // TPM_CC_StirRandom
CommandActivateCredential CommandCode = 0x00000147 // TPM_CC_ActivateCredential
CommandCertify CommandCode = 0x00000148 // TPM_CC_Certify
CommandPolicyNV CommandCode = 0x00000149 // TPM_CC_PolicyNV
CommandCertifyCreation CommandCode = 0x0000014A // TPM_CC_CertifyCreation
CommandDuplicate CommandCode = 0x0000014B // TPM_CC_Duplicate
CommandGetTime CommandCode = 0x0000014C // TPM_CC_GetTime
CommandGetSessionAuditDigest CommandCode = 0x0000014D // TPM_CC_GetSessionAuditDigest
CommandNVRead CommandCode = 0x0000014E // TPM_CC_NV_Read
CommandNVReadLock CommandCode = 0x0000014F // TPM_CC_NV_ReadLock
CommandObjectChangeAuth CommandCode = 0x00000150 // TPM_CC_ObjectChangeAuth
CommandPolicySecret CommandCode = 0x00000151 // TPM_CC_PolicySecret
CommandCreate CommandCode = 0x00000153 // TPM_CC_Create
CommandECDHZGen CommandCode = 0x00000154 // TPM_CC_ECDH_ZGen
CommandHMAC CommandCode = 0x00000155 // TPM_CC_HMAC
CommandImport CommandCode = 0x00000156 // TPM_CC_Import
CommandLoad CommandCode = 0x00000157 // TPM_CC_Load
CommandQuote CommandCode = 0x00000158 // TPM_CC_Quote
CommandRSADecrypt CommandCode = 0x00000159 // TPM_CC_RSA_Decrypt
CommandHMACStart CommandCode = 0x0000015B // TPM_CC_HMAC_Start
CommandSequenceUpdate CommandCode = 0x0000015C // TPM_CC_SequenceUpdate
CommandSign CommandCode = 0x0000015D // TPM_CC_Sign
CommandUnseal CommandCode = 0x0000015E // TPM_CC_Unseal
CommandPolicySigned CommandCode = 0x00000160 // TPM_CC_PolicySigned
CommandContextLoad CommandCode = 0x00000161 // TPM_CC_ContextLoad
CommandContextSave CommandCode = 0x00000162 // TPM_CC_ContextSave
CommandECDHKeyGen CommandCode = 0x00000163 // TPM_CC_ECDH_KeyGen
CommandFlushContext CommandCode = 0x00000165 // TPM_CC_FlushContext
CommandLoadExternal CommandCode = 0x00000167 // TPM_CC_LoadExternal
CommandMakeCredential CommandCode = 0x00000168 // TPM_CC_MakeCredential
CommandNVReadPublic CommandCode = 0x00000169 // TPM_CC_NV_ReadPublic
CommandPolicyAuthorize CommandCode = 0x0000016A // TPM_CC_PolicyAuthorize
CommandPolicyAuthValue CommandCode = 0x0000016B // TPM_CC_PolicyAuthValue
CommandPolicyCommandCode CommandCode = 0x0000016C // TPM_CC_PolicyCommandCode
CommandPolicyCounterTimer CommandCode = 0x0000016D // TPM_CC_PolicyCounterTimer
CommandPolicyCpHash CommandCode = 0x0000016E // TPM_CC_PolicyCpHash
CommandPolicyLocality CommandCode = 0x0000016F // TPM_CC_PolicyLocality
CommandPolicyNameHash CommandCode = 0x00000170 // TPM_CC_PolicyNameHash
CommandPolicyOR CommandCode = 0x00000171 // TPM_CC_PolicyOR
CommandPolicyTicket CommandCode = 0x00000172 // TPM_CC_PolicyTicket
CommandReadPublic CommandCode = 0x00000173 // TPM_CC_ReadPublic
CommandRSAEncrypt CommandCode = 0x00000174 // TPM_CC_RSA_Encrypt
CommandStartAuthSession CommandCode = 0x00000176 // TPM_CC_StartAuthSession
CommandVerifySignature CommandCode = 0x00000177 // TPM_CC_VerifySignature
CommandECCParameters CommandCode = 0x00000178 // TPM_CC_ECC_Parameters
CommandGetCapability CommandCode = 0x0000017A // TPM_CC_GetCapability
CommandGetRandom CommandCode = 0x0000017B // TPM_CC_GetRandom
CommandGetTestResult CommandCode = 0x0000017C // TPM_CC_GetTestResult
CommandHash CommandCode = 0x0000017D // TPM_CC_Hash
CommandPCRRead CommandCode = 0x0000017E // TPM_CC_PCR_Read
CommandPolicyPCR CommandCode = 0x0000017F // TPM_CC_PolicyPCR
CommandPolicyRestart CommandCode = 0x00000180 // TPM_CC_PolicyRestart
CommandReadClock CommandCode = 0x00000181 // TPM_CC_ReadClock
CommandPCRExtend CommandCode = 0x00000182 // TPM_CC_PCR_Extend
CommandNVCertify CommandCode = 0x00000184 // TPM_CC_NV_Certify
CommandEventSequenceComplete CommandCode = 0x00000185 // TPM_CC_EventSequenceComplete
CommandHashSequenceStart CommandCode = 0x00000186 // TPM_CC_HashSequenceStart
CommandPolicyDuplicationSelect CommandCode = 0x00000188 // TPM_CC_PolicyDuplicationSelect
CommandPolicyGetDigest CommandCode = 0x00000189 // TPM_CC_PolicyGetDigest
CommandTestParms CommandCode = 0x0000018A // TPM_CC_TestParms
CommandCommit CommandCode = 0x0000018B // TPM_CC_Commit
CommandPolicyPassword CommandCode = 0x0000018C // TPM_CC_PolicyPassword
CommandPolicyNvWritten CommandCode = 0x0000018F // TPM_CC_PolicyNvWritten
CommandPolicyTemplate CommandCode = 0x00000190 // TPM_CC_PolicyTemplate
CommandCreateLoaded CommandCode = 0x00000191 // TPM_CC_CreateLoaded
CommandPolicyAuthorizeNV CommandCode = 0x00000192 // TPM_CC_PolicyAuthorizeNV
)
// ResponseCode corresponds to the TPM_RC type.
type ResponseCode uint32
const (
// The lower 7-bits of format-zero error codes are the error number.
responseCodeE0 ResponseCode = 0x7f
// The lower 6-bits of format-one error codes are the error number.
responseCodeE1 ResponseCode = 0x3f
// Bit 6 of format-one errors is zero for errors associated with a handle
// or session, or one for errors associated with a parameter.
responseCodeP ResponseCode = 1 << 6
// Bit 7 indicates whether the error is a format-zero (0) or format-one code (1)
responseCodeF ResponseCode = 1 << 7
// Bit 8 of format-zero errors is zero for TPM1.2 errors and one for TPM2 errors.
responseCodeV ResponseCode = 1 << 8
// Bit 10 of format-zero errors is zero for TCG defined errors and one for vendor
// defined error.
responseCodeT ResponseCode = 1 << 10
// Bit 11 of format-zero errors is zero for errors and one for warnings.
responseCodeS ResponseCode = 1 << 11
responseCodeIndex uint8 = 0xf
responseCodeIndexShift uint8 = 8
// Bits 8 to 11 of format-one errors represent the parameter number if P is set
// or the handle or session number otherwise.
responseCodeN ResponseCode = ResponseCode(responseCodeIndex) << responseCodeIndexShift
)
// E returns the E field of the response code, corresponding to the error number.
func (rc ResponseCode) E() uint8 {
if rc.F() {
return uint8(rc & responseCodeE1)
}
return uint8(rc & responseCodeE0)
}
// F returns the F field of the response code, corresponding to the format.
// If it is set, this is a format-one response code. If it is not set, this
// is a format-zero response code.
func (rc ResponseCode) F() bool {
return rc&responseCodeF != 0
}
// V returns the V field of the response code. If this is set in a format-zero
// response code, then it is a TPM2 code returned when the response tag is
// TPM_ST_NO_SESSIONS. If it is not set in a format-zero response code, then it
// is a TPM1.2 code returned when the response tag is TPM_TAG_RSP_COMMAND.
func (rc ResponseCode) V() bool {
return rc&responseCodeV != 0
}
// T returns the T field of the response code. If this is set in a format-zero
// response code, then the code is defined by the TPM vendor. If it is not set
// in a format-zero response code, then the code is defined by the TCG.
func (rc ResponseCode) T() bool {
return rc&responseCodeT != 0
}
// S returns the S field of the response code. If this is set in a format-zero
// response code, then the code indicates a warning. If it is not set in a
// format-zero response code, then the code indicates an error.
func (rc ResponseCode) S() bool {
return rc&responseCodeS != 0
}
// P returns the P field of the response code. If this is set in a format-one
// response code, then the code is associated with a command parameter. If it is
// not set in a format-one error code, then the code is associated with a command
// handle or session.
func (rc ResponseCode) P() bool {
return rc&responseCodeP != 0
}
// N returns the N field of the response code. If the P field is set in a
// format-one response code, then this indicates the parameter number from 0x1
// to 0xf. If the P field is not set in a format-one response code, then the
// lower 3 bits indicate the handle or session number (0x1 to 0x7 for handles
// and 0x9 to 0xf for sessions).
func (rc ResponseCode) N() uint8 {
return uint8(rc & responseCodeN >> responseCodeIndexShift)
}
const (
ResponseSuccess ResponseCode = 0
ResponseBadTag ResponseCode = 0x1e
)
// ArithmeticOp corresponds to the TPM_EO type.
type ArithmeticOp uint16
const (
OpEq ArithmeticOp = 0x0000 // TPM_EO_EQ
OpNeq ArithmeticOp = 0x0001 // TPM_EO_NEQ
OpSignedGT ArithmeticOp = 0x0002 // TPM_EO_SIGNED_GT
OpUnsignedGT ArithmeticOp = 0x0003 // TPM_EO_UNSIGNED_GT
OpSignedLT ArithmeticOp = 0x0004 // TPM_EO_SIGNED_LT
OpUnsignedLT ArithmeticOp = 0x0005 // TPM_EO_UNSIGNED_LT
OpSignedGE ArithmeticOp = 0x0006 // TPM_EO_SIGNED_GE
OpUnsignedGE ArithmeticOp = 0x0007 // TPM_EO_UNSIGNED_GE
OpSignedLE ArithmeticOp = 0x0008 // TPM_EO_SIGNED_LE
OpUnsignedLE ArithmeticOp = 0x0009 // TPM_EO_UNSIGNED_LE
OpBitset ArithmeticOp = 0x000a // TPM_EO_BITSET
OpBitclear ArithmeticOp = 0x000b // TPM_EO_BITCLEAR
)
// StructTag corresponds to the TPM_ST type.
type StructTag uint16
const (
TagRspCommand StructTag = 0x00c4 // TPM_ST_RSP_COMMAND
TagNoSessions StructTag = 0x8001 // TPM_ST_NO_SESSIONS
TagSessions StructTag = 0x8002 // TPM_ST_SESSIONS
TagAttestNV StructTag = 0x8014 // TPM_ST_ATTEST_NV
TagAttestCommandAudit StructTag = 0x8015 // TPM_ST_ATTEST_COMMAND_AUDIT
TagAttestSessionAudit StructTag = 0x8016 // TPM_ST_ATTEST_SESSION_AUDIT
TagAttestCertify StructTag = 0x8017 // TPM_ST_ATTEST_CERTIFY
TagAttestQuote StructTag = 0x8018 // TPM_ST_ATTEST_QUOTE
TagAttestTime StructTag = 0x8019 // TPM_ST_ATTEST_TIME
TagAttestCreation StructTag = 0x801a // TPM_ST_ATTEST_CREATION
TagCreation StructTag = 0x8021 // TPM_ST_CREATION
TagVerified StructTag = 0x8022 // TPM_ST_VERIFIED
TagAuthSecret StructTag = 0x8023 // TPM_ST_AUTH_SECRET
TagHashcheck StructTag = 0x8024 // TPM_ST_HASHCHECK
TagAuthSigned StructTag = 0x8025 // TPM_ST_AUTH_SIGNED
)
// StartupType corresponds to the TPM_SU type.
type StartupType uint16
const (
StartupClear StartupType = iota
StartupState
)
// SessionType corresponds to the TPM_SE type.
type SessionType uint8
const (
SessionTypeHMAC SessionType = 0x00 // TPM_SE_HMAC
SessionTypePolicy SessionType = 0x01 // TPM_SE_POLICY
SessionTypeTrial SessionType = 0x03 // TPM_SE_TRIAL
)
// Capability corresponds to the TPM_CAP type.
type Capability uint32
const (
CapabilityAlgs Capability = 0 // TPM_CAP_ALGS
CapabilityHandles Capability = 1 // TPM_CAP_HANDLES
CapabilityCommands Capability = 2 // TPM_CAP_COMMANDS
CapabilityPPCommands Capability = 3 // TPM_CAP_PP_COMMANDS
CapabilityAuditCommands Capability = 4 // TPM_CAP_AUDIT_COMMANDS
CapabilityPCRs Capability = 5 // TPM_CAP_PCRS
CapabilityTPMProperties Capability = 6 // TPM_CAP_TPM_PROPERTIES
CapabilityPCRProperties Capability = 7 // TPM_CAP_PCR_PROPERTIES
CapabilityECCCurves Capability = 8 // TPM_CAP_ECC_CURVES
CapabilityAuthPolicies Capability = 9 // TPM_CAP_AUTH_POLICIES
)
// Property corresponds to the TPM_PT type.
type Property uint32
const (
// These constants represent properties that only change when the firmware in the TPM changes.
PropertyFamilyIndicator Property = 0x100 // TPM_PT_FAMILY_INDICATOR
PropertyLevel Property = 0x101 // TPM_PT_LEVEL
PropertyRevision Property = 0x102 // TPM_PT_REVISION
PropertyDayOfYear Property = 0x103 // TPM_PT_DAY_OF_YEAR
PropertyYear Property = 0x104 // TPM_PT_YEAR
PropertyManufacturer Property = 0x105 // TPM_PT_MANUFACTURER
PropertyVendorString1 Property = 0x106 // TPM_PT_VENDOR_STRING_1
PropertyVendorString2 Property = 0x107 // TPM_PT_VENDOR_STRING_2
PropertyVendorString3 Property = 0x108 // TPM_PT_VENDOR_STRING_3
PropertyVendorString4 Property = 0x109 // TPM_PT_VENDOR_STRING_4
PropertyVendorTPMType Property = 0x10a // TPM_PT_VENDOR_TPM_TYPE
PropertyFirmwareVersion1 Property = 0x10b // TPM_PT_FIRMWARE_VERSION_1
PropertyFirmwareVersion2 Property = 0x10c // TPM_PT_FIRMWARE_VERSION_2
PropertyInputBuffer Property = 0x10d // TPM_PT_INPUT_BUFFER
PropertyHRTransientMin Property = 0x10e // TPM_PT_HR_TRANSIENT_MIN
PropertyHRPersistentMin Property = 0x10f // TPM_PT_HR_PERSISTENT_MIN
PropertyHRLoadedMin Property = 0x110 // TPM_PT_HR_LOADED_MIN
PropertyActiveSessionsMax Property = 0x111 // TPM_PT_ACTIVE_SESSIONS_MAX
PropertyPCRCount Property = 0x112 // TPM_PT_PCR_COUNT
PropertyPCRSelectMin Property = 0x113 // TPM_PT_PCR_SELECT_MIN
PropertyContextGapMax Property = 0x114 // TPM_PT_CONTEXT_GAP_MAX
PropertyNVCountersMax Property = 0x116 // TPM_PT_NV_COUNTERS_MAX
PropertyNVIndexMax Property = 0x117 // TPM_PT_NV_INDEX_MAX
PropertyMemory Property = 0x118 // TPM_PT_MEMORY
PropertyClockUpdate Property = 0x119 // TPM_PT_CLOCK_UPDATE
PropertyContextHash Property = 0x11a // TPM_PT_CONTEXT_HASH
PropertyContextSym Property = 0x11b // TPM_PT_CONTEXT_SYM
PropertyContextSymSize Property = 0x11c // TPM_PT_CONTEXT_SYM_SIZE
PropertyOrderlyCount Property = 0x11d // TPM_PT_ORDERLY_COUNT
PropertyMaxCommandSize Property = 0x11e // TPM_PT_MAX_COMMAND_SIZE
PropertyMaxResponseSize Property = 0x11f // TPM_PT_MAX_RESPONSE_SIZE
PropertyMaxDigest Property = 0x120 // TPM_PT_MAX_DIGEST
PropertyMaxObjectContext Property = 0x121 // TPM_PT_MAX_OBJECT_CONTEXT
PropertyMaxSessionContext Property = 0x122 // TPM_PT_MAX_SESSION_CONTEXT
PropertyPSFamilyIndicator Property = 0x123 // TPM_PT_PS_FAMILY_INDICATOR
PropertyPSLevel Property = 0x124 // TPM_PT_PS_LEVEL
PropertyPSRevision Property = 0x125 // TPM_PT_PS_REVISION
PropertyPSDayOfYear Property = 0x126 // TPM_PT_PS_DAY_OF_YEAR
PropertyPSYear Property = 0x127 // TPM_PT_PS_YEAR
PropertySplitMax Property = 0x128 // TPM_PT_SPLIT_MAX
PropertyTotalCommands Property = 0x129 // TPM_PT_TOTAL_COMMANDS
PropertyLibraryCommands Property = 0x12a // TPM_PT_LIBRARY_COMMANDS
PropertyVendorCommands Property = 0x12b // TPM_PT_VENDOR_COMMANDS
PropertyNVBufferMax Property = 0x12c // TPM_PT_NV_BUFFER_MAX
PropertyModes Property = 0x12d // TPM_PT_MODES
PropertyMaxCapBuffer Property = 0x12e // TPM_PT_MAX_CAP_BUFFER
PropertyFixed Property = PropertyFamilyIndicator
)
const (
// These constants represent properties that change for reasons other than a firmware upgrade. Some of
// them may not persist across power cycles.
PropertyPermanent Property = 0x200 // TPM_PT_PERMANENT
PropertyStartupClear Property = 0x201 // TPM_PT_STARTUP_CLEAR
PropertyHRNVIndex Property = 0x202 // TPM_PT_HR_NV_INDEX
PropertyHRLoaded Property = 0x203 // TPM_PT_HR_LOADED
PropertyHRLoadedAvail Property = 0x204 // TPM_PT_HR_LOADED_AVAIL
PropertyHRActive Property = 0x205 // TPM_PT_HR_ACTIVE
PropertyHRActiveAvail Property = 0x206 // TPM_PT_HR_ACTIVE_AVAIL
PropertyHRTransientAvail Property = 0x207 // TPM_PT_HR_TRANSIENT_AVAIL
PropertyHRPersistent Property = 0x208 // TPM_PT_HR_PERSISTENT
PropertyHRPersistentAvail Property = 0x209 // TPM_PT_HR_PERSISTENT_AVAIL
PropertyNVCounters Property = 0x20a // TPM_PT_NV_COUNTERS
PropertyNVCountersAvail Property = 0x20b // TPM_PT_NV_COUNTERS_AVAIL
PropertyAlgorithmSet Property = 0x20c // TPM_PT_ALGORITHM_SET
PropertyLoadedCurves Property = 0x20d // TPM_PT_LOADED_CURVES
PropertyLockoutCounter Property = 0x20e // TPM_PT_LOCKOUT_COUNTER
PropertyMaxAuthFail Property = 0x20f // TPM_PT_MAX_AUTH_FAIL
PropertyLockoutInterval Property = 0x210 // TPM_PT_LOCKOUT_INTERVAL
PropertyLockoutRecovery Property = 0x211 // TPM_PT_LOCKOUT_RECOVERY
PropertyNVWriteRecovery Property = 0x212 // TPM_PT_NV_WRITE_RECOVERY
PropertyAuditCounter0 Property = 0x213 // TPM_PT_AUDIT_COUNTER_0
PropertyAuditCounter1 Property = 0x214 // TPM_PT_AUDIT_COUNTER_1
PropertyVar Property = PropertyPermanent
)
// PropertyPCR corresponds to the TPM_PT_PCR type.
type PropertyPCR uint32
const (
PropertyPCRSave PropertyPCR = 0x00 // TPM_PT_PCR_SAVE
PropertyPCRExtendL0 PropertyPCR = 0x01 // TPM_PT_PCR_EXTEND_L0
PropertyPCRResetL0 PropertyPCR = 0x02 // TPM_PT_PCR_RESET_L0
PropertyPCRExtendL1 PropertyPCR = 0x03 // TPM_PT_PCR_EXTEND_L1
PropertyPCRResetL1 PropertyPCR = 0x04 // TPM_PT_PCR_RESET_L1
PropertyPCRExtendL2 PropertyPCR = 0x05 // TPM_PT_PCR_EXTEND_L2
PropertyPCRResetL2 PropertyPCR = 0x06 // TPM_PT_PCR_RESET_L2
PropertyPCRExtendL3 PropertyPCR = 0x07 // TPM_PT_PCR_EXTEND_L3
PropertyPCRResetL3 PropertyPCR = 0x08 // TPM_PT_PCR_RESET_L3
PropertyPCRExtendL4 PropertyPCR = 0x09 // TPM_PT_PCR_EXTEND_L4
PropertyPCRResetL4 PropertyPCR = 0x0a // TPM_PT_PCR_RESET_L4
PropertyPCRNoIncrement PropertyPCR = 0x11 // TPM_PT_PCR_NO_INCREMENT
PropertyPCRDRTMReset PropertyPCR = 0x12 // TPM_PT_PCR_DRTM_RESET
PropertyPCRPolicy PropertyPCR = 0x13 // TPM_PT_PCR_POLICY
PropertyPCRAuth PropertyPCR = 0x14 // TPM_PT_PCR_AUTH
PropertyPCRFirst PropertyPCR = PropertyPCRSave
)
./github.com/canonical/go-tpm2/types_context.go 0000664 0000000 0000000 00000001322 00000000000 020054 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// This file contains types defined in section 14 (Context Data)
// in part 2 of the library spec.
// ContextData corresponds to the TPM2B_CONTEXT_DATA type.
type ContextData []byte
// Context corresponds to the TPMS_CONTEXT type and is used in TPMContext.ContextLoad and TPMContext.ContextSave.
type Context struct {
Sequence uint64 // Sequence number of the context
SavedHandle Handle // Handle indicating if this is a session or object
Hierarchy Handle // Hierarchy of the context
Blob ContextData // Encrypted context data and integrity HMAC
}
./github.com/canonical/go-tpm2/types_creation.go 0000664 0000000 0000000 00000001645 00000000000 020204 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// This file contains types defined in section 15 (Creation Data)
// in part 2 of the library spec.
// CreationData corresponds to the TPMS_CREATION_DATA type, which provides
// information about the creation environment of an object.
type CreationData struct {
PCRSelect PCRSelectionList // PCRs included in PCRDigest
// Digest of the selected PCRs using the name algorithm of the object associated with this data.
PCRDigest Digest
Locality Locality // Locality at which the object was created
ParentNameAlg AlgorithmId // Name algorithm of the parent
ParentName Name // Name of the parent
ParentQualifiedName Name // Qualified name of the parent
OutsideInfo Data // External information provided by the caller
}
./github.com/canonical/go-tpm2/types_handles.go 0000664 0000000 0000000 00000003366 00000000000 020020 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
// This file contains types defined in section 7 (Handles) in
// part 2 of the library spec.
// Handle corresponds to the TPM_HANDLE type, and is a numeric
// identifier that references a resource on the TPM.
type Handle uint32
// Type returns the type of the handle.
func (h Handle) Type() HandleType {
return HandleType(h >> 24)
}
const (
HandleOwner Handle = 0x40000001 // TPM_RH_OWNER
HandleNull Handle = 0x40000007 // TPM_RH_NULL
HandleUnassigned Handle = 0x40000008 // TPM_RH_UNASSIGNED
HandlePW Handle = 0x40000009 // TPM_RS_PW
HandleLockout Handle = 0x4000000a // TPM_RH_LOCKOUT
HandleEndorsement Handle = 0x4000000b // TPM_RH_ENDORSEMENT
HandlePlatform Handle = 0x4000000c // TPM_RH_PLATFORM
HandlePlatformNV Handle = 0x4000000d // TPM_RH_PLATFORM_NV
)
// HandleType corresponds to the TPM_HT type, and is used to
// identify the type of a Handle.
type HandleType uint8
// BaseHandle returns the first handle for the handle type.
func (h HandleType) BaseHandle() Handle {
return Handle(h) << 24
}
const (
HandleTypePCR HandleType = 0x00 // TPM_HT_PCR
HandleTypeNVIndex HandleType = 0x01 // TPM_HT_NV_INDEX
HandleTypeHMACSession HandleType = 0x02 // TPM_HT_HMAC_SESSION
HandleTypeLoadedSession HandleType = 0x02 // TPM_HT_LOADED_SESSION
HandleTypePolicySession HandleType = 0x03 // TPM_HT_POLICY_SESSION
HandleTypeSavedSession HandleType = 0x03 // TPM_HT_SAVED_SESSION
HandleTypePermanent HandleType = 0x40 // TPM_HT_PERMANENT
HandleTypeTransient HandleType = 0x80 // TPM_HT_TRANSIENT
HandleTypePersistent HandleType = 0x81 // TPM_HT_PERSISTENT
)
./github.com/canonical/go-tpm2/types_interface.go 0000664 0000000 0000000 00000022120 00000000000 020327 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"crypto"
"crypto/cipher"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"errors"
"hash"
)
// This file contains types defined in section 9 (Interface Types) in
// part 2 of the library spec. Interface types are used by the TPM
// implementation to check that a value is appropriate for the context
// during unmarshalling. This package has limited support for some
// algorithm interfaces by defining context specific algorithm types
// based on the AlgorithmId type. Note that no interface types with
// TPM_HANDLE as the underlying type are supported, as this package
// doesn't use handles in most APIs.
// HashAlgorithmId corresponds to the TPMI_ALG_HASH type
type HashAlgorithmId AlgorithmId
// GetHash returns the equivalent crypto.Hash value for this algorithm if one
// exists, and 0 if one does not exist.
func (a HashAlgorithmId) GetHash() crypto.Hash {
switch a {
case HashAlgorithmSHA1:
return crypto.SHA1
case HashAlgorithmSHA256:
return crypto.SHA256
case HashAlgorithmSHA384:
return crypto.SHA384
case HashAlgorithmSHA512:
return crypto.SHA512
case HashAlgorithmSHA3_256:
return crypto.SHA3_256
case HashAlgorithmSHA3_384:
return crypto.SHA3_384
case HashAlgorithmSHA3_512:
return crypto.SHA3_512
default:
return 0
}
}
// IsValid determines if the digest algorithm is valid. This
// should be checked by code that deserializes an algorithm before
// calling Size if it does not want to panic.
func (a HashAlgorithmId) IsValid() bool {
switch a {
case HashAlgorithmSHA1:
case HashAlgorithmSHA256:
case HashAlgorithmSHA384:
case HashAlgorithmSHA512:
case HashAlgorithmSM3_256:
case HashAlgorithmSHA3_256:
case HashAlgorithmSHA3_384:
case HashAlgorithmSHA3_512:
default:
return false
}
return true
}
// Available determines if the TPM digest algorithm has an equivalent go crypto.Hash
// that is linked into the current binary.
func (a HashAlgorithmId) Available() bool {
return a.GetHash().Available()
}
// NewHash constructs a new hash.Hash implementation for this algorithm. It will panic if
// HashAlgorithmId.Available returns false.
func (a HashAlgorithmId) NewHash() hash.Hash {
return a.GetHash().New()
}
// Size returns the size of the algorithm. It will panic if IsValid returns false.
func (a HashAlgorithmId) Size() int {
switch a {
case HashAlgorithmSHA1:
return 20
case HashAlgorithmSHA256:
return 32
case HashAlgorithmSHA384:
return 48
case HashAlgorithmSHA512:
return 64
case HashAlgorithmSM3_256:
return 32
case HashAlgorithmSHA3_256:
return 32
case HashAlgorithmSHA3_384:
return 48
case HashAlgorithmSHA3_512:
return 64
default:
panic("unknown hash algorithm")
}
}
const (
HashAlgorithmNull HashAlgorithmId = HashAlgorithmId(AlgorithmNull) // TPM_ALG_NULL
HashAlgorithmSHA1 HashAlgorithmId = HashAlgorithmId(AlgorithmSHA1) // TPM_ALG_SHA1
HashAlgorithmSHA256 HashAlgorithmId = HashAlgorithmId(AlgorithmSHA256) // TPM_ALG_SHA256
HashAlgorithmSHA384 HashAlgorithmId = HashAlgorithmId(AlgorithmSHA384) // TPM_ALG_SHA384
HashAlgorithmSHA512 HashAlgorithmId = HashAlgorithmId(AlgorithmSHA512) // TPM_ALG_SHA512
HashAlgorithmSM3_256 HashAlgorithmId = HashAlgorithmId(AlgorithmSM3_256) // TPM_ALG_SM3_256
HashAlgorithmSHA3_256 HashAlgorithmId = HashAlgorithmId(AlgorithmSHA3_256) // TPM_ALG_SHA3_256
HashAlgorithmSHA3_384 HashAlgorithmId = HashAlgorithmId(AlgorithmSHA3_384) // TPM_ALG_SHA3_384
HashAlgorithmSHA3_512 HashAlgorithmId = HashAlgorithmId(AlgorithmSHA3_512) // TPM_ALG_SHA3_512
)
// SymAlgorithmId corresponds to the TPMI_ALG_SYM type
type SymAlgorithmId AlgorithmId
// IsValidBlockCipher determines if this algorithm is a valid block cipher.
// This should be checked by code that deserializes an algorithm before calling
// BlockSize if it does not want to panic.
func (a SymAlgorithmId) IsValidBlockCipher() bool {
switch a {
case SymAlgorithmTDES:
case SymAlgorithmAES:
case SymAlgorithmSM4:
case SymAlgorithmCamellia:
default:
return false
}
return true
}
// Available indicates whether the TPM symmetric cipher has a registered go implementation.
func (a SymAlgorithmId) Available() bool {
_, ok := symmetricAlgs[a]
return ok
}
// BlockSize indicates the block size of the symmetric cipher. This will panic if
// IsValidBlockCipher returns false.
func (a SymAlgorithmId) BlockSize() int {
switch a {
case SymAlgorithmTDES:
return 8
case SymAlgorithmAES:
return 16
case SymAlgorithmSM4:
return 16
case SymAlgorithmCamellia:
return 16
default:
panic("invalid symmetric algorithm")
}
}
// NewCipher constructs a new symmetric cipher with the supplied key, if there is a go
// implementation registered.
func (a SymAlgorithmId) NewCipher(key []byte) (cipher.Block, error) {
fn, ok := symmetricAlgs[a]
if !ok {
return nil, errors.New("unavailable cipher")
}
return fn(key)
}
const (
SymAlgorithmTDES SymAlgorithmId = SymAlgorithmId(AlgorithmTDES) // TPM_ALG_TDES
SymAlgorithmAES SymAlgorithmId = SymAlgorithmId(AlgorithmAES) // TPM_ALG_AES
SymAlgorithmXOR SymAlgorithmId = SymAlgorithmId(AlgorithmXOR) // TPM_ALG_XOR
SymAlgorithmNull SymAlgorithmId = SymAlgorithmId(AlgorithmNull) // TPM_ALG_NULL
SymAlgorithmSM4 SymAlgorithmId = SymAlgorithmId(AlgorithmSM4) // TPM_ALG_SM4
SymAlgorithmCamellia SymAlgorithmId = SymAlgorithmId(AlgorithmCamellia) // TPM_ALG_CAMELLIA
)
// SymObjectAlgorithmId corresponds to the TPMI_ALG_SYM_OBJECT type
type SymObjectAlgorithmId AlgorithmId
// IsValidBlockCipher determines if this algorithm is a valid block cipher.
// This should be checked by code that deserializes an algorithm before calling
// BlockSize if it does not want to panic.
func (a SymObjectAlgorithmId) IsValidBlockCipher() bool {
return SymAlgorithmId(a).IsValidBlockCipher()
}
// Available indicates whether the TPM symmetric cipher has a registered go implementation.
func (a SymObjectAlgorithmId) Available() bool {
return SymAlgorithmId(a).Available()
}
// BlockSize indicates the block size of the symmetric cipher. This will panic if
// IsValidBlockCipher returns false.
func (a SymObjectAlgorithmId) BlockSize() int {
return SymAlgorithmId(a).BlockSize()
}
// NewCipher constructs a new symmetric cipher with the supplied key, if there is a go
// implementation registered.
func (a SymObjectAlgorithmId) NewCipher(key []byte) (cipher.Block, error) {
return SymAlgorithmId(a).NewCipher(key)
}
const (
SymObjectAlgorithmAES SymObjectAlgorithmId = SymObjectAlgorithmId(AlgorithmAES) // TPM_ALG_AES
SymObjectAlgorithmNull SymObjectAlgorithmId = SymObjectAlgorithmId(AlgorithmNull) // TPM_ALG_NULL
SymObjectAlgorithmSM4 SymObjectAlgorithmId = SymObjectAlgorithmId(AlgorithmSM4) // TPM_ALG_SM4
SymObjectAlgorithmCamellia SymObjectAlgorithmId = SymObjectAlgorithmId(AlgorithmCamellia) // TPM_ALG_CAMELLIA
)
// SymModeId corresponds to the TPMI_ALG_SYM_MODE type
type SymModeId AlgorithmId
const (
SymModeNull SymModeId = SymModeId(AlgorithmNull) // TPM_ALG_NULL
SymModeCTR SymModeId = SymModeId(AlgorithmCTR) // TPM_ALG_CTR
SymModeOFB SymModeId = SymModeId(AlgorithmOFB) // TPM_ALG_OFB
SymModeCBC SymModeId = SymModeId(AlgorithmCBC) // TPM_ALG_CBC
SymModeCFB SymModeId = SymModeId(AlgorithmCFB) // TPM_ALG_CFB
SymModeECB SymModeId = SymModeId(AlgorithmECB) // TPM_ALG_ECB
)
// KDFAlgorithmId corresppnds to the TPMI_ALG_KDF type
type KDFAlgorithmId AlgorithmId
const (
KDFAlgorithmMGF1 KDFAlgorithmId = KDFAlgorithmId(AlgorithmMGF1) // TPM_ALG_MGF1
KDFAlgorithmNull KDFAlgorithmId = KDFAlgorithmId(AlgorithmNull) // TPM_ALG_NULL
KDFAlgorithmKDF1_SP800_56A KDFAlgorithmId = KDFAlgorithmId(AlgorithmKDF1_SP800_56A) // TPM_ALG_KDF1_SP800_56A
KDFAlgorithmKDF2 KDFAlgorithmId = KDFAlgorithmId(AlgorithmKDF2) // TPM_ALG_KDF2
KDFAlgorithmKDF1_SP800_108 KDFAlgorithmId = KDFAlgorithmId(AlgorithmKDF1_SP800_108) // TPM_ALG_KDF1_SP800_108
)
// SigSchemeId corresponds to the TPMI_ALG_SIG_SCHEME type
type SigSchemeId AlgorithmId
// IsValid determines if the scheme is a valid signature scheme.
func (s SigSchemeId) IsValid() bool {
switch s {
case SigSchemeAlgHMAC:
case SigSchemeAlgRSASSA:
case SigSchemeAlgRSAPSS:
case SigSchemeAlgECDSA:
case SigSchemeAlgECDAA:
case SigSchemeAlgSM2:
case SigSchemeAlgECSchnorr:
default:
return false
}
return true
}
const (
SigSchemeAlgHMAC SigSchemeId = SigSchemeId(AlgorithmHMAC) // TPM_ALG_HMAC
SigSchemeAlgNull SigSchemeId = SigSchemeId(AlgorithmNull) // TPM_ALG_NULL
SigSchemeAlgRSASSA SigSchemeId = SigSchemeId(AlgorithmRSASSA) // TPM_ALG_RSASSA
SigSchemeAlgRSAPSS SigSchemeId = SigSchemeId(AlgorithmRSAPSS) // TPM_ALG_RSAPSS
SigSchemeAlgECDSA SigSchemeId = SigSchemeId(AlgorithmECDSA) // TPM_ALG_ECDSA
SigSchemeAlgECDAA SigSchemeId = SigSchemeId(AlgorithmECDAA) // TPM_ALG_ECDAA
SigSchemeAlgSM2 SigSchemeId = SigSchemeId(AlgorithmSM2) // TPM_ALG_SM2
SigSchemeAlgECSchnorr SigSchemeId = SigSchemeId(AlgorithmECSchnorr) // TPM_ALG_ECSCHNORR
)
./github.com/canonical/go-tpm2/types_nv.go 0000664 0000000 0000000 00000007524 00000000000 017025 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"bytes"
"fmt"
"github.com/canonical/go-tpm2/mu"
)
// This file contains types defined in section 13 (NV Storage Structures)
// in part 2 of the library spec.
// NVType corresponds to the TPM_NT type.
type NVType uint32
// WithAttrs returns NVAttributes for this type with the specified attributes set.
func (t NVType) WithAttrs(attrs NVAttributes) NVAttributes {
return NVAttributes(t<<4) | attrs
}
const (
NVTypeOrdinary NVType = 0 // TPM_NT_ORDINARY
NVTypeCounter NVType = 1 // TPM_NT_COUNTER
NVTypeBits NVType = 2 // TPM_NT_BITS
NVTypeExtend NVType = 4 // TPM_NT_EXTEND
NVTypePinFail NVType = 8 // TPM_NT_PIN_FAIL
NVTypePinPass NVType = 9 // TPM_NT_PIN_PASS
)
// NVPinCounterParams corresponds to the TPMS_NV_PIN_COUNTER_PARAMETERS type.
type NVPinCounterParams struct {
Count uint32
Limit uint32
}
// NVAttributes corresponds to the TPMA_NV type, and represents the attributes of a NV index. When exchanged with the TPM, some bits
// are reserved to encode the type of the NV index (NVType).
type NVAttributes uint32
// Type returns the NVType encoded in a NVAttributes value.
func (a NVAttributes) Type() NVType {
return NVType((a & 0xf0) >> 4)
}
// AttrsOnly returns the NVAttributes without the encoded NVType.
func (a NVAttributes) AttrsOnly() NVAttributes {
return a & ^NVAttributes(0xf0)
}
const (
AttrNVPPWrite NVAttributes = 1 << 0 // TPMA_NV_PPWRITE
AttrNVOwnerWrite NVAttributes = 1 << 1 // TPMA_NV_OWNERWRITE
AttrNVAuthWrite NVAttributes = 1 << 2 // TPMA_NV_AUTHWRITE
AttrNVPolicyWrite NVAttributes = 1 << 3 // TPMA_NV_POLICY_RITE
AttrNVPolicyDelete NVAttributes = 1 << 10 // TPMA_NV_POLICY_DELETE
AttrNVWriteLocked NVAttributes = 1 << 11 // TPMA_NV_WRITELOCKED
AttrNVWriteAll NVAttributes = 1 << 12 // TPMA_NV_WRITEALL
AttrNVWriteDefine NVAttributes = 1 << 13 // TPMA_NV_WRITEDEFINE
AttrNVWriteStClear NVAttributes = 1 << 14 // TPMA_NV_WRITE_STCLEAR
AttrNVGlobalLock NVAttributes = 1 << 15 // TPMA_NV_GLOBALLOCK
AttrNVPPRead NVAttributes = 1 << 16 // TPMA_NV_PPREAD
AttrNVOwnerRead NVAttributes = 1 << 17 // TPMA_NV_OWNERREAD
AttrNVAuthRead NVAttributes = 1 << 18 // TPMA_NV_AUTHREAD
AttrNVPolicyRead NVAttributes = 1 << 19 // TPMA_NV_POLICYREAD
AttrNVNoDA NVAttributes = 1 << 25 // TPMA_NV_NO_DA
AttrNVOrderly NVAttributes = 1 << 26 // TPMA_NV_ORDERLY
AttrNVClearStClear NVAttributes = 1 << 27 // TPMA_NV_CLEAR_STCLEAR
AttrNVReadLocked NVAttributes = 1 << 28 // TPMA_NV_READLOCKED
AttrNVWritten NVAttributes = 1 << 29 // TPMA_NV_WRITTEN
AttrNVPlatformCreate NVAttributes = 1 << 30 // TPMA_NV_PLATFORMCREATE
AttrNVReadStClear NVAttributes = 1 << 31 // TPMA_NV_READ_STCLEAR
)
// NVPublic corresponds to the TPMS_NV_PUBLIC type, which describes a NV index.
type NVPublic struct {
Index Handle // Handle of the NV index
NameAlg HashAlgorithmId // NameAlg is the digest algorithm used to compute the name of the index
Attrs NVAttributes // Attributes of this index
AuthPolicy Digest // Authorization policy for this index
Size uint16 // Size of this index
}
// Name computes the name of this NV index
func (p *NVPublic) Name() (Name, error) {
if !p.NameAlg.Available() {
return nil, fmt.Errorf("unsupported name algorithm or algorithm not linked into binary: %v", p.NameAlg)
}
hasher := p.NameAlg.NewHash()
if _, err := mu.MarshalToWriter(hasher, p); err != nil {
return nil, fmt.Errorf("cannot marshal public object: %v", err)
}
return mu.MustMarshalToBytes(p.NameAlg, mu.RawBytes(hasher.Sum(nil))), nil
}
func (p *NVPublic) compareName(name Name) bool {
n, err := p.Name()
if err != nil {
return false
}
return bytes.Equal(n, name)
}
./github.com/canonical/go-tpm2/types_objects.go 0000664 0000000 0000000 00000026131 00000000000 020026 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"fmt"
"math/big"
"reflect"
"github.com/canonical/go-tpm2/mu"
)
// This file contains types defined in section 12 (Key/Object Complex)
// in part 2 of the library spec.
// ObjectTypeId corresponds to the TPMI_ALG_PUBLIC type.
type ObjectTypeId AlgorithmId
// IsAsymmetric determines if the type corresponds to an asymmetric
// object.
func (t ObjectTypeId) IsAsymmetric() bool {
return t == ObjectTypeRSA || t == ObjectTypeECC
}
const (
ObjectTypeRSA ObjectTypeId = ObjectTypeId(AlgorithmRSA) // TPM_ALG_RSA
ObjectTypeKeyedHash ObjectTypeId = ObjectTypeId(AlgorithmKeyedHash) // TPM_ALG_KEYEDHASH
ObjectTypeECC ObjectTypeId = ObjectTypeId(AlgorithmECC) // TPM_ALG_ECC
ObjectTypeSymCipher ObjectTypeId = ObjectTypeId(AlgorithmSymCipher) // TPM_ALG_SYMCIPHER
)
// PublicIDU is a union type that corresponds to the TPMU_PUBLIC_ID type. The selector type
// is ObjectTypeId. The mapping of selector values to fields is as follows:
// - ObjectTypeRSA: RSA
// - ObjectTypeKeyedHash: KeyedHash
// - ObjectTypeECC: ECC
// - ObjectTypeSymCipher: Sym
type PublicIDU struct {
KeyedHash Digest
Sym Digest
RSA PublicKeyRSA
ECC *ECCPoint
}
func (p *PublicIDU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(ObjectTypeId) {
case ObjectTypeRSA:
return &p.RSA
case ObjectTypeKeyedHash:
return &p.KeyedHash
case ObjectTypeECC:
return &p.ECC
case ObjectTypeSymCipher:
return &p.Sym
default:
return nil
}
}
// KeyedHashParams corresponds to the TPMS_KEYEDHASH_PARMS type, and defines the public
// parameters for a keyedhash object.
type KeyedHashParams struct {
Scheme KeyedHashScheme // Signing method for a keyed hash signing object
}
// AsymParams corresponds to the TPMS_ASYM_PARMS type, and defines the common public
// parameters for an asymmetric key.
type AsymParams struct {
Symmetric SymDefObject // Symmetric algorithm for a restricted decrypt key.
// For a key with the AttrSign attribute: a signing scheme.
// For a key with the AttrDecrypt attribute: a key exchange protocol.
// For a key with both AttrSign and AttrDecrypt attributes: AlgorithmNull.
Scheme AsymScheme
}
// RSAParams corresponds to the TPMS_RSA_PARMS type, and defines the public parameters
// for an RSA key.
type RSAParams struct {
Symmetric SymDefObject // Symmetric algorithm for a restricted decrypt key.
// For an unrestricted signing key: AlgorithmRSAPSS, AlgorithmRSASSA or AlgorithmNull.
// For a restricted signing key: AlgorithmRSAPSS or AlgorithmRSASSA.
// For an unrestricted decrypt key: AlgorithmRSAES, AlgorithmOAEP or AlgorithmNull.
// For a restricted decrypt key: AlgorithmNull.
Scheme RSAScheme
KeyBits uint16 // Number of bits in the public modulus
Exponent uint32 // Public exponent. When the value is zero, the exponent is 65537
}
// ECCParams corresponds to the TPMS_ECC_PARMS type, and defines the public parameters for an
// ECC key.
type ECCParams struct {
Symmetric SymDefObject // Symmetric algorithm for a restricted decrypt key.
// For a key with the AttrSign attribute: a signing scheme.
// For a key with the AttrDecrypt attribute: a key exchange protocol or AlgorithmNull.
// For a storage key: AlgorithmNull.
Scheme ECCScheme
CurveID ECCCurve // ECC curve ID
KDF KDFScheme // Unused - always KDFAlgorithmNull
}
// PublicParamsU is a union type that corresponds to the TPMU_PUBLIC_PARMS type. The selector
// type is ObjectTypeId.
// The mapping of selector values to fields is as follows:
// - ObjectTypeRSA: RSADetail
// - ObjectTypeKeyedHash: KeyedHashDetail
// - ObjectTypeECC: ECCDetail
// - ObjectTypeSymCipher: SymDetail
type PublicParamsU struct {
KeyedHashDetail *KeyedHashParams
SymDetail *SymCipherParams
RSADetail *RSAParams
ECCDetail *ECCParams
}
func (p *PublicParamsU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(ObjectTypeId) {
case ObjectTypeRSA:
return &p.RSADetail
case ObjectTypeKeyedHash:
return &p.KeyedHashDetail
case ObjectTypeECC:
return &p.ECCDetail
case ObjectTypeSymCipher:
return &p.SymDetail
default:
return nil
}
}
// AsymDetail returns the parameters associated with the specified object type
// as *AsymParams. It panics if the type is not ObjectTypeRSA or ObjectTypeECC,
// or the appropriate field isn't set.
func (p PublicParamsU) AsymDetail(t ObjectTypeId) *AsymParams {
switch t {
case ObjectTypeRSA:
return &AsymParams{
Symmetric: p.RSADetail.Symmetric,
Scheme: AsymScheme{
Scheme: AsymSchemeId(p.RSADetail.Scheme.Scheme),
Details: p.RSADetail.Scheme.Details}}
case ObjectTypeECC:
return &AsymParams{
Symmetric: p.ECCDetail.Symmetric,
Scheme: AsymScheme{
Scheme: AsymSchemeId(p.ECCDetail.Scheme.Scheme),
Details: p.ECCDetail.Scheme.Details}}
default:
panic("invalid type")
}
}
// PublicParams corresponds to the TPMT_PUBLIC_PARMS type.
type PublicParams struct {
Type ObjectTypeId // Type specifier
Parameters *PublicParamsU // Algorithm details
}
// Public corresponds to the TPMT_PUBLIC type, and defines the public area for an object.
type Public struct {
Type ObjectTypeId // Type of this object
NameAlg HashAlgorithmId // NameAlg is the algorithm used to compute the name of this object
Attrs ObjectAttributes // Object attributes
AuthPolicy Digest // Authorization policy for this object
Params *PublicParamsU // Type specific parameters
Unique *PublicIDU // Type specific unique identifier
}
// Name computes the name of this object
func (p *Public) Name() (Name, error) {
if !p.NameAlg.Available() {
return nil, fmt.Errorf("unsupported name algorithm or algorithm not linked into binary: %v", p.NameAlg)
}
hasher := p.NameAlg.NewHash()
if _, err := mu.MarshalToWriter(hasher, p); err != nil {
return nil, fmt.Errorf("cannot marshal public object: %v", err)
}
return mu.MustMarshalToBytes(p.NameAlg, mu.RawBytes(hasher.Sum(nil))), nil
}
func (p *Public) compareName(name Name) bool {
n, err := p.Name()
if err != nil {
return false
}
return bytes.Equal(n, name)
}
func (p *Public) ToTemplate() (Template, error) {
b, err := mu.MarshalToBytes(p)
if err != nil {
return nil, fmt.Errorf("cannot marshal object: %v", err)
}
return b, nil
}
func (p *Public) isParent() bool {
if !p.NameAlg.IsValid() {
return false
}
return p.Attrs&(AttrRestricted|AttrDecrypt) == AttrRestricted|AttrDecrypt
}
// IsAsymmetric indicates that this public area is associated with an asymmetric
// key.
func (p *Public) IsAsymmetric() bool {
return p.Type.IsAsymmetric()
}
// IsStorageParent indicates that this public area is associated with an object that can be
// a storage parent.
func (p *Public) IsStorageParent() bool {
if !p.isParent() {
return false
}
switch p.Type {
case ObjectTypeRSA, ObjectTypeECC, ObjectTypeSymCipher:
return true
default:
return false
}
}
// IsDerivationParent indicates that this public area is associated with an object that can be
// a derivation parent.
func (p *Public) IsDerivationParent() bool {
if !p.isParent() {
return false
}
if p.Type != ObjectTypeKeyedHash {
return false
}
return true
}
// Public returns a corresponding public key for the TPM public area.
// This will panic if the public area does not correspond to an asymmetric
// key.
func (p *Public) Public() crypto.PublicKey {
switch p.Type {
case ObjectTypeRSA:
exp := int(p.Params.RSADetail.Exponent)
if exp == 0 {
exp = DefaultRSAExponent
}
return &rsa.PublicKey{
N: new(big.Int).SetBytes(p.Unique.RSA),
E: exp}
case ObjectTypeECC:
return &ecdsa.PublicKey{
Curve: p.Params.ECCDetail.CurveID.GoCurve(),
X: new(big.Int).SetBytes(p.Unique.ECC.X),
Y: new(big.Int).SetBytes(p.Unique.ECC.Y)}
default:
panic("object is not a public key")
}
}
// PublicDerived is similar to Public but can be used as a template to create a derived object
// with TPMContext.CreateLoaded
type PublicDerived struct {
Type ObjectTypeId // Type of this object
NameAlg HashAlgorithmId // NameAlg is the algorithm used to compute the name of this object
Attrs ObjectAttributes // Object attributes
AuthPolicy Digest // Authorization policy for this object
Params *PublicParamsU // Type specific parameters
// Unique contains the derivation values. These take precedence over any values specified
// in SensitiveCreate.Data when creating a derived object,
Unique *Derive
}
// Name computes the name of this object
func (p *PublicDerived) Name() (Name, error) {
if !p.NameAlg.Available() {
return nil, fmt.Errorf("unsupported name algorithm or algorithm not linked into binary: %v", p.NameAlg)
}
hasher := p.NameAlg.NewHash()
if _, err := mu.MarshalToWriter(hasher, p); err != nil {
return nil, fmt.Errorf("cannot marshal public object: %v", err)
}
return mu.MustMarshalToBytes(p.NameAlg, mu.RawBytes(hasher.Sum(nil))), nil
}
func (p *PublicDerived) ToTemplate() (Template, error) {
b, err := mu.MarshalToBytes(p)
if err != nil {
return nil, fmt.Errorf("cannot marshal object: %v", err)
}
return b, nil
}
// Template corresponds to the TPM2B_TEMPLATE type
type Template []byte
// 12.3) Private Area Structures
// PrivateVendorSpecific corresponds to the TPM2B_PRIVATE_VENDOR_SPECIFIC type.
type PrivateVendorSpecific []byte
// SensitiveCompositeU is a union type that corresponds to the TPMU_SENSITIVE_COMPOSITE
// type. The selector type is ObjectTypeId. The mapping of selector values to fields is
// as follows:
// - ObjectTypeRSA: RSA
// - ObjectTypeECC: ECC
// - ObjectTypeKeyedHash: Bits
// - ObjectTypeSymCipher: Sym
type SensitiveCompositeU struct {
RSA PrivateKeyRSA
ECC ECCParameter
Bits SensitiveData
Sym SymKey
}
func (s *SensitiveCompositeU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(ObjectTypeId) {
case ObjectTypeRSA:
return &s.RSA
case ObjectTypeECC:
return &s.ECC
case ObjectTypeKeyedHash:
return &s.Bits
case ObjectTypeSymCipher:
return &s.Sym
default:
return nil
}
}
// Any returns the value associated with the specified object type as
// PrivateVendorSpecific.
func (s SensitiveCompositeU) Any(t ObjectTypeId) PrivateVendorSpecific {
switch t {
case ObjectTypeRSA:
return PrivateVendorSpecific(s.RSA)
case ObjectTypeECC:
return PrivateVendorSpecific(s.ECC)
case ObjectTypeKeyedHash:
return PrivateVendorSpecific(s.Bits)
case ObjectTypeSymCipher:
return PrivateVendorSpecific(s.Sym)
default:
return nil
}
}
// Sensitive corresponds to the TPMT_SENSITIVE type.
type Sensitive struct {
Type ObjectTypeId // Same as the corresponding Type in the Public object
AuthValue Auth // Authorization value
SeedValue Digest // For a parent object, the seed value for protecting descendant objects
Sensitive *SensitiveCompositeU // Type specific private data
}
// Private corresponds to the TPM2B_PRIVATE type.
type Private []byte
// 12.4) Identity Object
// IDObjectRaw corresponds to the TPM2B_ID_OBJECT type.
type IDObjectRaw []byte
./github.com/canonical/go-tpm2/types_structures.go 0000664 0000000 0000000 00000052344 00000000000 020625 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tpm2
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"math/big"
"reflect"
"sort"
"golang.org/x/xerrors"
"github.com/canonical/go-tpm2/mu"
)
// This file contains types defined in section 10 (Structures) in
// part 2 of the library spec.
type Empty struct{}
// 10.3) Hash/Digest structures
// TaggedHash corresponds to the TPMT_HA type.
type TaggedHash struct {
HashAlg HashAlgorithmId // Algorithm of the digest contained with Digest
Digest []byte // Digest data
}
// TaggedHash represents the TPMT_HA type in the TCG spec. In the spec, TPMT_HA.digest is a union type
// (TPMU_HA), which is a union of all of the different hash algorithms. Each member of that union is an
// array of raw bytes. As no length is encoded, we need a custom marshaller implementation that unmarshals the
// correct number of bytes depending on the hash algorithm
func (p TaggedHash) Marshal(w io.Writer) error {
if err := binary.Write(w, binary.BigEndian, p.HashAlg); err != nil {
return xerrors.Errorf("cannot marshal digest algorithm: %w", err)
}
if !p.HashAlg.IsValid() {
return fmt.Errorf("cannot determine digest size for unknown algorithm %v", p.HashAlg)
}
if p.HashAlg.Size() != len(p.Digest) {
return fmt.Errorf("invalid digest size %d", len(p.Digest))
}
if _, err := w.Write(p.Digest); err != nil {
return xerrors.Errorf("cannot write digest: %w", err)
}
return nil
}
func (p *TaggedHash) Unmarshal(r mu.Reader) error {
if err := binary.Read(r, binary.BigEndian, &p.HashAlg); err != nil {
return xerrors.Errorf("cannot unmarshal digest algorithm: %w", err)
}
if !p.HashAlg.IsValid() {
return fmt.Errorf("cannot determine digest size for unknown algorithm %v", p.HashAlg)
}
p.Digest = make(Digest, p.HashAlg.Size())
if _, err := io.ReadFull(r, p.Digest); err != nil {
return xerrors.Errorf("cannot read digest: %w", err)
}
return nil
}
// 10.4 Sized Buffers
// Digest corresponds to the TPM2B_DIGEST type. The largest size of this supported
// by the TPM can be determined by calling TPMContext.GetMaxDigest.
type Digest []byte
// Data corresponds to the TPM2B_DATA type. The largest size of this supported by
// the TPM can be determined by calling TPMContext.GetMaxData.
type Data []byte
// Nonce corresponds to the TPM2B_NONCE type.
type Nonce Digest
// Auth corresponds to the TPM2B_AUTH type.
type Auth Digest
// Operand corresponds to the TPM2B_OPERAND type.
type Operand Digest
const (
// EventMaxSize indicates the maximum size of arguments of the Event type.
EventMaxSize = 1024
)
// Event corresponds to the TPM2B_EVENT type. The largest size of this is indicated
// by EventMaxSize.
type Event []byte
// MaxBuffer corresponds to the TPM2B_MAX_BUFFER type. The largest size of this supported
// by the TPM can be determined by calling TPMContext.GetInputBuffer.
type MaxBuffer []byte
// MaxNVBuffer corresponds to the TPM2B_MAX_NV_BUFFER type. The largest size of this
// supported by the TPM can be determined by calling TPMContext.GetNVBufferMax.
type MaxNVBuffer []byte
// Timeout corresponds to the TPM2B_TIMEOUT type. The spec defines this
// as having a maximum size of 8 bytes. It is always 8 bytes in the
// reference implementation and so could be represented as a uint64,
// but we have to preserve the original buffer because there is no
// guarantees that it is always 8 bytes, and the actual TPM buffer
// must be recreated accurately in order for ticket validation to
// work correctly in TPMContext.PolicyTicket.
type Timeout []byte
// Value returns the value as a uint64. The spec defines the TPM2B_TIMEOUT
// type as having a size of up to 8 bytes. If an implementation creates a
// larger value then the result of this is undefined.
func (t Timeout) Value() uint64 {
return new(big.Int).SetBytes(t).Uint64()
}
// 10.5) Names
// Name corresponds to the TPM2B_NAME type.
type Name []byte
// NameType describes the type of a name.
type NameType int
const (
// NameTypeInvalid means that a Name is invalid.
NameTypeInvalid NameType = iota
// NameTypeHandle means that a Name is a handle.
NameTypeHandle
// NameTypeDigest means that a Name is a digest.
NameTypeDigest
)
// Type determines the type of this name.
func (n Name) Type() NameType {
if len(n) < binary.Size(HashAlgorithmId(0)) {
return NameTypeInvalid
}
if len(n) == binary.Size(Handle(0)) {
return NameTypeHandle
}
alg := HashAlgorithmId(binary.BigEndian.Uint16(n))
if !alg.IsValid() {
return NameTypeInvalid
}
if len(n)-binary.Size(HashAlgorithmId(0)) != alg.Size() {
return NameTypeInvalid
}
return NameTypeDigest
}
// Handle returns the handle of the resource that this name corresponds to. If
// Type does not return NameTypeHandle, it will panic.
func (n Name) Handle() Handle {
if n.Type() != NameTypeHandle {
panic("name is not a handle")
}
return Handle(binary.BigEndian.Uint32(n))
}
// Algorithm returns the digest algorithm of this name. If Type does not return
// NameTypeDigest, it will return HashAlgorithmNull.
func (n Name) Algorithm() HashAlgorithmId {
if n.Type() != NameTypeDigest {
return HashAlgorithmNull
}
return HashAlgorithmId(binary.BigEndian.Uint16(n))
}
// Digest returns the name as a digest without the algorithm identifier. If
// Type does not return NameTypeDigest, it will panic.
func (n Name) Digest() Digest {
if n.Type() != NameTypeDigest {
panic("name is not a valid digest")
}
return Digest(n[binary.Size(HashAlgorithmId(0)):])
}
// 10.6) PCR Structures
// PCRSelect is a slice of PCR indexes. It is marshalled to and from the
// TPMS_PCR_SELECT type, which is a bitmap of the PCR indices contained within
// this slice.
type PCRSelect []int
func (d PCRSelect) Marshal(w io.Writer) error {
bytes := make([]byte, 3)
for _, i := range d {
octet := i / 8
for octet >= len(bytes) {
bytes = append(bytes, byte(0))
}
bit := uint(i % 8)
bytes[octet] |= 1 << bit
}
if err := binary.Write(w, binary.BigEndian, uint8(len(bytes))); err != nil {
return xerrors.Errorf("cannot write size of PCR selection bit mask: %w", err)
}
if _, err := w.Write(bytes); err != nil {
return xerrors.Errorf("cannot write PCR selection bit mask: %w", err)
}
return nil
}
func (d *PCRSelect) Unmarshal(r mu.Reader) error {
var size uint8
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
return xerrors.Errorf("cannot read size of PCR selection bit mask: %w", err)
}
if int(size) > r.Len() {
return errors.New("size field is larger than the remaining bytes")
}
bytes := make([]byte, size)
if _, err := io.ReadFull(r, bytes); err != nil {
return xerrors.Errorf("cannot read PCR selection bit mask: %w", err)
}
*d = make(PCRSelect, 0)
for i, octet := range bytes {
for bit := uint(0); bit < 8; bit++ {
if octet&(1<= pr {
break
}
}
if found {
break
}
}
if !found {
added := false
for i, so := range out {
if so.Hash != sr.Hash {
continue
}
out[i].Select = append(so.Select, pr)
added = true
break
}
if !added {
out = append(out, PCRSelection{Hash: sr.Hash, Select: []int{pr}})
}
}
}
}
mu.MustCopyValue(&out, out)
return out
}
// Remove will remove the PCR selections in r from the PCR selections in l, and return a
// new set of selections.
func (l PCRSelectionList) Remove(r PCRSelectionList) (out PCRSelectionList) {
mu.MustCopyValue(&out, l)
mu.MustCopyValue(&r, r)
for _, sr := range r {
for _, pr := range sr.Select {
for i, so := range out {
if so.Hash != sr.Hash {
continue
}
for j, po := range so.Select {
if po == pr {
if j < len(so.Select)-1 {
copy(out[i].Select[j:], so.Select[j+1:])
}
out[i].Select = so.Select[:len(so.Select)-1]
}
if po >= pr {
break
}
}
}
}
}
for i, so := range out {
if len(so.Select) > 0 {
continue
}
if i < len(out)-1 {
copy(out[i:], out[i+1:])
}
out = out[:len(out)-1]
}
return
}
// IsEmpty returns true if the list of PCR selections selects no PCRs.
func (l PCRSelectionList) IsEmpty() bool {
for _, s := range l {
if len(s.Select) > 0 {
return false
}
}
return true
}
// AlgorithmPropertyList is a slice of AlgorithmProperty values, and corresponds to
// the TPML_ALG_PROPERTY type.
type AlgorithmPropertyList []AlgorithmProperty
// TaggedTPMPropertyList is a slice of TaggedProperty values, and corresponds to the
// TPML_TAGGED_TPM_PROPERTY type.
type TaggedTPMPropertyList []TaggedProperty
// TaggedPCRPropertyList is a slice of TaggedPCRSelect values, and corresponds to the
// TPML_TAGGED_PCR_PROPERTY type.
type TaggedPCRPropertyList []TaggedPCRSelect
// ECCCurveList is a slice of ECCCurve values, and corresponds to the TPML_ECC_CURVE type.
type ECCCurveList []ECCCurve
// TaggedPolicyList is a slice of TaggedPolicy values, and corresponds to the
// TPML_TAGGED_POLICY type.
type TaggedPolicyList []TaggedPolicy
// 10.10) Capabilities Structures
// Capabilities is a union type that corresponds to the TPMU_CAPABILITIES type. The
// selector type is Capability. Mapping of selector values to fields is as follows:
// - CapabilityAlgs: Algorithms
// - CapabilityHandles: Handles
// - CapabilityCommands: Command
// - CapabilityPPCommands: PPCommands
// - CapabilityAuditCommands: AuditCommands
// - CapabilityPCRs: AssignedPCR
// - CapabilityTPMProperties: TPMProperties
// - CapabilityPCRProperties: PCRProperties
// - CapabilityECCCurves: ECCCurves
// - CapabilityAuthPolicies: AuthPolicies
type CapabilitiesU struct {
Algorithms AlgorithmPropertyList
Handles HandleList
Command CommandAttributesList
PPCommands CommandCodeList
AuditCommands CommandCodeList
AssignedPCR PCRSelectionList
TPMProperties TaggedTPMPropertyList
PCRProperties TaggedPCRPropertyList
ECCCurves ECCCurveList
AuthPolicies TaggedPolicyList
}
func (c *CapabilitiesU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(Capability) {
case CapabilityAlgs:
return &c.Algorithms
case CapabilityHandles:
return &c.Handles
case CapabilityCommands:
return &c.Command
case CapabilityPPCommands:
return &c.PPCommands
case CapabilityAuditCommands:
return &c.AuditCommands
case CapabilityPCRs:
return &c.AssignedPCR
case CapabilityTPMProperties:
return &c.TPMProperties
case CapabilityPCRProperties:
return &c.PCRProperties
case CapabilityECCCurves:
return &c.ECCCurves
case CapabilityAuthPolicies:
return &c.AuthPolicies
default:
return nil
}
}
// CapabilityData corresponds to the TPMS_CAPABILITY_DATA type, and is returned by
// TPMContext.GetCapability.
type CapabilityData struct {
Capability Capability // Capability
Data *CapabilitiesU // Capability data
}
// 10.11 Clock/Counter Structures
// ClockInfo corresponds to the TPMS_CLOCK_INFO type.
type ClockInfo struct {
Clock uint64 // Time value in milliseconds that increments whilst the TPM is powered
ResetCount uint32 // Number of TPM resets since the TPM was last cleared
// RestartCount is the number of TPM restarts or resumes since the last TPM reset or the last time the TPM was cleared.
RestartCount uint32
// Safe indicates the the value reported by Clock is guaranteed to be unique for the current owner.
Safe bool
}
// TimeInfo corresponds to the TPMS_TIME_INFO type.
type TimeInfo struct {
Time uint64 // Time value in milliseconds since the last TPM startup
ClockInfo ClockInfo // Clock information
}
// 10.12 Attestation Structures
// TimeAttestInfo corresponds to the TPMS_TIME_ATTEST_INFO type, and is returned by
// TPMContext.GetTime.
type TimeAttestInfo struct {
Time TimeInfo // Time information
FirmwareVersion uint64 // TPM vendor specific value indicating the version of the firmware
}
// CertifyInfo corresponds to the TPMS_CERTIFY_INFO type, and is returned by TPMContext.Certify.
type CertifyInfo struct {
Name Name // Name of the certified object
QualifiedName Name // Qualified name of the certified object
}
// QuoteInfo corresponds to the TPMS_QUOTE_INFO type, and is returned by TPMContext.Quote.
type QuoteInfo struct {
PCRSelect PCRSelectionList // PCRs included in PCRDigest
PCRDigest Digest // Digest of the selected PCRs, using the hash algorithm of the signing key
}
// CommandAuditInfo corresponds to the TPMS_COMMAND_AUDIT_INFO type, and is returned by
// TPMContext.GetCommandAuditDigest.
type CommandAuditInfo struct {
AuditCounter uint64 // Monotonic audit counter
DigestAlg AlgorithmId // Hash algorithm used for the command audit
AuditDigest Digest // Current value of the audit digest
CommandDigest Digest // Digest of command codes being audited, using DigestAlg
}
// SessionAuditInfo corresponds to the TPMS_SESSION_AUDIT_INFO type, and is returned by
// TPMContext.GetSessionAuditDigest.
type SessionAuditInfo struct {
// ExclusiveSession indicates the current exclusive status of the session. It is true if all of the commands recorded in
// SessionDigest were executed without any intervening commands that did not use
// the audit session.
ExclusiveSession bool
SessionDigest Digest // Current value of the session audit digest
}
// CreationInfo corresponds to the TPMS_CREATION_INFO type, and is returned by TPMContext.CertifyCreation.
type CreationInfo struct {
ObjectName Name // Name of the object
CreationHash Digest
}
// NVCertifyInfo corresponds to the TPMS_NV_CERTIFY_INFO type, and is returned by TPMContext.NVCertify.
type NVCertifyInfo struct {
IndexName Name // Name of the NV index
Offset uint16 // Offset parameter of TPMContext.NVCertify
NVContents MaxNVBuffer // Contents of the NV index
}
// AttestU is a union type that corresponds to the TPMU_ATTEST type. The selector type is StructTag.
// Mapping of selector values to fields is as follows:
// - TagAttestNV: NV
// - TagAttestCommandAudit: CommandAudit
// - TagAttestSessionAudit: SessionAudit
// - TagAttestCertify: Certify
// - TagAttestQuote: Quote
// - TagAttestTime: Time
// - TagAttestCreation: Creation
type AttestU struct {
Certify *CertifyInfo
Creation *CreationInfo
Quote *QuoteInfo
CommandAudit *CommandAuditInfo
SessionAudit *SessionAuditInfo
Time *TimeAttestInfo
NV *NVCertifyInfo
}
func (a *AttestU) Select(selector reflect.Value) interface{} {
switch selector.Interface().(StructTag) {
case TagAttestNV:
return &a.NV
case TagAttestCommandAudit:
return &a.CommandAudit
case TagAttestSessionAudit:
return &a.SessionAudit
case TagAttestCertify:
return &a.Certify
case TagAttestQuote:
return &a.Quote
case TagAttestTime:
return &a.Time
case TagAttestCreation:
return &a.Creation
default:
return nil
}
}
// Attest corresponds to the TPMS_ATTEST type, and is returned by the attestation commands. The
// signature of the attestation is over this structure.
type Attest struct {
Magic TPMGenerated // Always TPMGeneratedValue
Type StructTag // Type of the attestation structure
QualifiedSigner Name // Qualified name of the signing key
ExtraData Data // External information provided by the caller
ClockInfo ClockInfo // Clock information
FirmwareVersion uint64 // TPM vendor specific value indicating the version of the firmware
Attested *AttestU `tpm2:"selector:Type"` // Type specific attestation data
}
// 10.13) Authorization Structures
// AuthCommand corresppnds to the TPMS_AUTH_COMMAND type, and represents an authorization
// for a command.
type AuthCommand struct {
SessionHandle Handle
Nonce Nonce
SessionAttributes SessionAttributes
HMAC Auth
}
// AuthResponse corresponds to the TPMS_AUTH_RESPONSE type, and represents an authorization
// response for a command.
type AuthResponse struct {
Nonce Nonce
SessionAttributes SessionAttributes
HMAC Auth
}
./github.com/canonical/go-tpm2/util/ 0000775 0000000 0000000 00000000000 00000000000 015574 5 ustar 00 0000000 0000000 ./github.com/canonical/go-tpm2/util/cphash.go 0000664 0000000 0000000 00000002221 00000000000 017366 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"errors"
"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/mu"
)
// ComputeCpHash computes a command parameter digest from the specified command code, the supplied
// handles (identified by their names) and parameters using the specified digest algorithm.
//
// The required parameters is defined in part 3 of the TPM 2.0 Library Specification for the
// specific command.
//
// The result of this is useful for extended authorization commands that bind an authorization to
// a command and set of command parameters, such as TPMContext.PolicySigned, TPMContext.PolicySecret,
// TPMContext.PolicyTicket and TPMContext.PolicyCpHash.
func ComputeCpHash(alg tpm2.HashAlgorithmId, command tpm2.CommandCode, handles []tpm2.Name, params ...interface{}) (tpm2.Digest, error) {
if !alg.Available() {
return nil, errors.New("algorithm is not available")
}
cpBytes, err := mu.MarshalToBytes(params...)
if err != nil {
return nil, err
}
return tpm2.ComputeCpHash(alg, command, handles, cpBytes), nil
}
./github.com/canonical/go-tpm2/util/credential.go 0000664 0000000 0000000 00000002775 00000000000 020250 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"errors"
"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/mu"
)
// MakeCredential performs the duties of a certificate authority in order to
// create an activation credential. It produces a seed and encrypts this with
// the supplied public key. The seed and supplied object name is then used to
// apply an outer wrapper to the credential.
//
// The encrypted credential blob and encrypted seed are returned, and these can
// be passed to the TPM2_ActivateCredential on the TPM on which both the private
// part of key and the object associated with objectName are loaded.
func MakeCredential(key *tpm2.Public, credential tpm2.Digest, objectName tpm2.Name) (credentialBlob tpm2.IDObjectRaw, secret tpm2.EncryptedSecret, err error) {
if !key.IsStorageParent() || !key.IsAsymmetric() {
return nil, nil, errors.New("key must be an asymmetric storage parent")
}
if !key.NameAlg.Available() {
return nil, nil, errors.New("name algorithm for key is not available")
}
secret, seed, err := tpm2.CryptSecretEncrypt(key, []byte(tpm2.IdentityKey))
if err != nil {
return nil, nil, err
}
credentialBlob = mu.MustMarshalToBytes(credential)
credentialBlob, err = ProduceOuterWrap(key.NameAlg, &key.Params.AsymDetail(key.Type).Symmetric, objectName, seed, false, credentialBlob)
if err != nil {
return nil, nil, err
}
return credentialBlob, secret, nil
}
./github.com/canonical/go-tpm2/util/crypto.go 0000664 0000000 0000000 00000003467 00000000000 017455 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"errors"
"math/big"
"golang.org/x/xerrors"
"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/internal"
"github.com/canonical/go-tpm2/mu"
)
func zeroExtendBytes(x *big.Int, l int) (out []byte) {
out = make([]byte, l)
tmp := x.Bytes()
copy(out[len(out)-len(tmp):], tmp)
return
}
// CryptSecretDecrypt recovers a secret value from the supplied secret structure
// using the private key. It can be used to recover secrets created by the TPM,
// such as those created by the TPM2_Duplicate command.
func CryptSecretDecrypt(priv crypto.PrivateKey, hashAlg tpm2.HashAlgorithmId, label []byte, secret tpm2.EncryptedSecret) ([]byte, error) {
if !hashAlg.Available() {
return nil, errors.New("digest algorithm is not available")
}
switch p := priv.(type) {
case *rsa.PrivateKey:
h := hashAlg.NewHash()
label0 := make([]byte, len(label)+1)
copy(label0, label)
return rsa.DecryptOAEP(h, rand.Reader, p, secret, label0)
case *ecdsa.PrivateKey:
var ephPoint tpm2.ECCPoint
if _, err := mu.UnmarshalFromBytes(secret, &ephPoint); err != nil {
return nil, xerrors.Errorf("cannot unmarshal ephemeral point: %w", err)
}
ephX := new(big.Int).SetBytes(ephPoint.X)
ephY := new(big.Int).SetBytes(ephPoint.Y)
if !p.Curve.IsOnCurve(ephX, ephY) {
return nil, errors.New("ephemeral point is not on curve")
}
sz := p.Curve.Params().BitSize / 8
mulX, _ := p.Curve.ScalarMult(ephX, ephY, p.D.Bytes())
return internal.KDFe(hashAlg.GetHash(), zeroExtendBytes(mulX, sz), label,
ephPoint.X, zeroExtendBytes(p.X, sz), hashAlg.Size()*8), nil
default:
return nil, errors.New("unsupported key type")
}
}
./github.com/canonical/go-tpm2/util/doc.go 0000664 0000000 0000000 00000000136 00000000000 016670 0 ustar 00 0000000 0000000 /*
Package util contains helper functions that are useful when using go-tpm2.
*/
package util
./github.com/canonical/go-tpm2/util/duplication.go 0000664 0000000 0000000 00000010571 00000000000 020442 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"crypto"
"errors"
"golang.org/x/xerrors"
"github.com/canonical/go-tpm2"
)
// UnwrapDuplicationObjectToSensitive unwraps the supplied duplication object and returns the
// corresponding sensitive area. If inSymSeed is supplied, then it is assumed that the object
// has an outer wrapper. In this case, privKey, parentNameAlg and parentSymmetricAlg must be
// supplied - privKey is the key with which inSymSeed is protected, parentNameAlg is the name
// algorithm for the parent key (and must not be HashAlgorithmNull), and parentSymmetricAlg
// defines the symmetric algorithm for the parent key (and the Algorithm field must not be
// SymObjectAlgorithmNull).
//
// If symmetricAlg is supplied and the Algorithm field is not SymObjectAlgorithmNull, then it is
// assumed that the object has an inner wrapper. In this case, the symmetric key for the inner
// wrapper must be supplied using the encryptionKey argument.
func UnwrapDuplicationObjectToSensitive(duplicate tpm2.Private, public *tpm2.Public, privKey crypto.PrivateKey, parentNameAlg tpm2.HashAlgorithmId, parentSymmetricAlg *tpm2.SymDefObject, encryptionKey tpm2.Data, inSymSeed tpm2.EncryptedSecret, symmetricAlg *tpm2.SymDefObject) (*tpm2.Sensitive, error) {
var seed []byte
if len(inSymSeed) > 0 {
if privKey == nil {
return nil, errors.New("parent private key is required for outer wrapper")
}
var err error
seed, err = CryptSecretDecrypt(privKey, parentNameAlg, []byte(tpm2.DuplicateString), inSymSeed)
if err != nil {
return nil, xerrors.Errorf("cannot decrypt symmetric seed: %w", err)
}
}
name, err := public.Name()
if err != nil {
return nil, xerrors.Errorf("cannot compute name: %w", err)
}
return DuplicateToSensitive(duplicate, name, parentNameAlg, parentSymmetricAlg, seed, symmetricAlg, encryptionKey)
}
// CreateDuplicationObjectFromSensitive creates a duplication object that can be imported in to a
// TPM from the supplied sensitive area.
//
// If symmetricAlg is supplied and the Algorithm field is not SymObjectAlgorithmNull, this function
// will apply an inner wrapper to the duplication object. If encryptionKeyIn is supplied, it will be
// used as the symmetric key for the inner wrapper. It must have a size appropriate for the selected
// symmetric algorithm. If encryptionKeyIn is not supplied, a symmetric key will be created and
// returned
//
// If parentPublic is supplied, an outer wrapper will be applied to the duplication object. The
// parentPublic argument should correspond to the public area of the storage key to which the
// duplication object will be imported. When applying the outer wrapper, the seed used to derice the
// symmetric key and HMAC key will be encrypted using parentPublic and returned.
func CreateDuplicationObjectFromSensitive(sensitive *tpm2.Sensitive, public, parentPublic *tpm2.Public, encryptionKeyIn tpm2.Data, symmetricAlg *tpm2.SymDefObject) (encryptionKeyOut tpm2.Data, duplicate tpm2.Private, outSymSeed tpm2.EncryptedSecret, err error) {
if public.Attrs&(tpm2.AttrFixedTPM|tpm2.AttrFixedParent) != 0 {
return nil, nil, nil, errors.New("object must be a duplication root")
}
if public.Attrs&tpm2.AttrEncryptedDuplication != 0 {
if symmetricAlg == nil || symmetricAlg.Algorithm == tpm2.SymObjectAlgorithmNull {
return nil, nil, nil, errors.New("symmetric algorithm must be supplied for an object with AttrEncryptedDuplication")
}
if parentPublic == nil {
return nil, nil, nil, errors.New("parent object must be supplied for an object with AttrEncryptedDuplication")
}
}
name, err := public.Name()
if err != nil {
return nil, nil, nil, xerrors.Errorf("cannot compute name: %w", err)
}
var seed []byte
if parentPublic != nil {
if !parentPublic.IsStorageParent() || !parentPublic.IsAsymmetric() {
return nil, nil, nil, errors.New("parent object must be an asymmetric storage key")
}
outSymSeed, seed, err = tpm2.CryptSecretEncrypt(parentPublic, []byte(tpm2.DuplicateString))
if err != nil {
return nil, nil, nil, xerrors.Errorf("cannot create encrypted symmetric seed: %w", err)
}
}
encryptionKeyOut, duplicate, err = SensitiveToDuplicate(sensitive, name, parentPublic, seed, symmetricAlg, encryptionKeyIn)
if err != nil {
return nil, nil, nil, err
}
return encryptionKeyOut, duplicate, outSymSeed, nil
}
./github.com/canonical/go-tpm2/util/keys.go 0000664 0000000 0000000 00000016250 00000000000 017102 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/templates"
)
// NewExternalRSAPublicKey creates a public area from the supplied RSA
// public key with the specified name algorithm, key usage and scheme,
// for use with the TPM2_LoadExternal command. If nameAlg is
// HashAlgorithmNull, then HashAlgorithmSHA256 is used. If no usage is
// specified, the public area will include both sign and decrypt attributes.
func NewExternalRSAPublicKey(nameAlg tpm2.HashAlgorithmId, usage templates.KeyUsage, scheme *tpm2.RSAScheme, key *rsa.PublicKey) *tpm2.Public {
pub := templates.NewRSAKey(nameAlg, usage, scheme, uint16(len(key.N.Bytes())*8))
pub.Attrs &^= (tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth)
pub.Params.RSADetail.Exponent = uint32(key.E)
pub.Unique = &tpm2.PublicIDU{RSA: key.N.Bytes()}
return pub
}
// NewExternalRSAPublicKeyWithDefaults creates a public area from the
// supplied RSA with the specified key usage, SHA256 as the name algorithm
// and the scheme unset, for use with the TPM2_LoadExternal command. If no
// usage is specified, the public area will include both sign and decrypt
// attributes.
func NewExternalRSAPublicKeyWithDefaults(usage templates.KeyUsage, key *rsa.PublicKey) *tpm2.Public {
return NewExternalRSAPublicKey(tpm2.HashAlgorithmNull, usage, nil, key)
}
// NewExternalECCPublicKey creates a public area from the supplied
// elliptic public key with the specified name algorithm, key usage
// and scheme, for use with the TPM2_LoadExternal command. If nameAlg
// is HashAlgorithmNull, then HashAlgorithmSHA256 is used. If no usage is
// specified, the public area will include both sign and decrypt attributes.
func NewExternalECCPublicKey(nameAlg tpm2.HashAlgorithmId, usage templates.KeyUsage, scheme *tpm2.ECCScheme, key *ecdsa.PublicKey) *tpm2.Public {
var curve tpm2.ECCCurve
switch key.Curve {
case elliptic.P224():
curve = tpm2.ECCCurveNIST_P224
case elliptic.P256():
curve = tpm2.ECCCurveNIST_P256
case elliptic.P384():
curve = tpm2.ECCCurveNIST_P384
case elliptic.P521():
curve = tpm2.ECCCurveNIST_P521
default:
panic("unsupported curve")
}
pub := templates.NewECCKey(nameAlg, usage, scheme, curve)
pub.Attrs &^= (tpm2.AttrFixedTPM | tpm2.AttrFixedParent | tpm2.AttrSensitiveDataOrigin | tpm2.AttrUserWithAuth)
pub.Unique = &tpm2.PublicIDU{
ECC: &tpm2.ECCPoint{
X: zeroExtendBytes(key.X, key.Params().BitSize/8),
Y: zeroExtendBytes(key.Y, key.Params().BitSize/8)}}
return pub
}
// NewExternalECCPublicKeyWithDefaults creates a public area from the
// supplied elliptic public key with the specified key usage, SHA256
// as the name algorithm and the scheme unset, for use with the
// TPM2_LoadExternal command. If no usage is specified, the public area
// will include both sign and decrypt attributes.
func NewExternalECCPublicKeyWithDefaults(usage templates.KeyUsage, key *ecdsa.PublicKey) *tpm2.Public {
return NewExternalECCPublicKey(tpm2.HashAlgorithmNull, usage, nil, key)
}
// NewExternalSealedObject creates both the public and sensitive areas for a
// sealed object containing the supplied data, with the specified name
// algorithm and authorization value. If nameAlgorithm is HashAlgorithmNull,
// then HashAlgorithmSHA256 is used.
//
// It will panic if authValue is larger than the size of the name algorithm.
//
// The returned public and sensitive areas can be made into a duplication
// object with CreateDuplicationObjectFromSensitive for importing into a TPM.
//
// The public area has the AttrUserWithAuth set in order to permit authentication
// for the user auth role using the sensitive area's authorization value. In order
// to require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewExternalSealedObject(nameAlg tpm2.HashAlgorithmId, authValue tpm2.Auth, data []byte) (*tpm2.Public, *tpm2.Sensitive) {
pub := templates.NewSealedObject(nameAlg)
pub.Attrs &^= (tpm2.AttrFixedTPM | tpm2.AttrFixedParent)
if len(authValue) > pub.NameAlg.Size() {
panic("authValue too large")
}
sensitive := &tpm2.Sensitive{
Type: tpm2.ObjectTypeKeyedHash,
AuthValue: make(tpm2.Auth, pub.NameAlg.Size()),
SeedValue: make(tpm2.Digest, pub.NameAlg.Size()),
Sensitive: &tpm2.SensitiveCompositeU{Bits: data}}
copy(sensitive.AuthValue, authValue)
rand.Read(sensitive.SeedValue)
h := nameAlg.NewHash()
h.Write(sensitive.SeedValue)
h.Write(sensitive.Sensitive.Bits)
pub.Unique = &tpm2.PublicIDU{KeyedHash: h.Sum(nil)}
return pub, sensitive
}
// NewExternalHMACKey creates both the public and sensitive areas for the
// supplied HMAC key with the specified name algorithm, scheme algorithm
// and auth value. If nameAlg is HashAlgorithmNull, then HashAlgorithmSHA256
// is used. If schemeAlg is HashAlgorithmNull, then nameAlg is used.
//
// It will panic if authValue is larger than the size of the name algorithm.
//
// The returned public and sensitive areas can be made into a duplication
// object with CreateDuplicationObjectFromSensitive for importing into a TPM.
//
// The public area has the AttrUserWithAuth set in order to permit authentication
// for the user auth role using the sensitive area's authorization value. In order
// to require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewExternalHMACKey(nameAlg, schemeAlg tpm2.HashAlgorithmId, authValue tpm2.Auth, key []byte) (*tpm2.Public, *tpm2.Sensitive) {
pub := templates.NewHMACKey(nameAlg, schemeAlg)
pub.Attrs &^= (tpm2.AttrFixedTPM | tpm2.AttrFixedParent)
if len(authValue) > pub.NameAlg.Size() {
panic("authValue too large")
}
sensitive := &tpm2.Sensitive{
Type: tpm2.ObjectTypeKeyedHash,
AuthValue: make(tpm2.Auth, pub.NameAlg.Size()),
SeedValue: make(tpm2.Digest, pub.NameAlg.Size()),
Sensitive: &tpm2.SensitiveCompositeU{Bits: key}}
copy(sensitive.AuthValue, authValue)
rand.Read(sensitive.SeedValue)
h := pub.NameAlg.NewHash()
h.Write(sensitive.SeedValue)
h.Write(sensitive.Sensitive.Bits)
pub.Unique = &tpm2.PublicIDU{KeyedHash: h.Sum(nil)}
return pub, sensitive
}
// NewExternalHMACKeyWithDefaults creates both the public and sensitive
// areas for the supplied HMAC key with the specified auth value and with
// SHA256 as both the name and scheme algorithm.
//
// It will panic if authValue is larger than the size of the name algorithm.
//
// The returned public and sensitive areas can be made into a duplication
// object with CreateDuplicationObjectFromSensitive for importing into a TPM.
//
// The public area has the AttrUserWithAuth set in order to permit authentication
// for the user auth role using the sensitive area's authorization value. In order
// to require authentication for the user auth role using an authorization policy,
// remove the AttrUserWithAuth attribute.
func NewExternalHMACKeyWithDefaults(authValue tpm2.Auth, key []byte) (*tpm2.Public, *tpm2.Sensitive) {
return NewExternalHMACKey(tpm2.HashAlgorithmNull, tpm2.HashAlgorithmNull, authValue, key)
}
./github.com/canonical/go-tpm2/util/object.go 0000664 0000000 0000000 00000024126 00000000000 017376 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"errors"
"hash"
"io/ioutil"
"golang.org/x/xerrors"
"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/internal"
"github.com/canonical/go-tpm2/mu"
)
// UnwrapOuter removes an outer wrapper from the supplied sensitive data blob. The
// supplied name is associated with the data.
//
// It checks the integrity HMAC is valid using the specified digest algorithm and
// a key derived from the supplied seed and returns an error if the check fails.
//
// It then decrypts the data blob using the specified symmetric algorithm and a
// key derived from the supplied seed and name.
func UnwrapOuter(hashAlg tpm2.HashAlgorithmId, symmetricAlg *tpm2.SymDefObject, name tpm2.Name, seed []byte, useIV bool, data []byte) ([]byte, error) {
if !hashAlg.Available() {
return nil, errors.New("digest algorithm is not available")
}
if !symmetricAlg.Algorithm.IsValidBlockCipher() {
return nil, errors.New("symmetric algorithm is not a valid block cipher")
}
r := bytes.NewReader(data)
var integrity []byte
if _, err := mu.UnmarshalFromReader(r, &integrity); err != nil {
return nil, xerrors.Errorf("cannot unmarshal integrity digest: %w", err)
}
data, _ = ioutil.ReadAll(r)
hmacKey := internal.KDFa(hashAlg.GetHash(), seed, []byte(tpm2.IntegrityKey), nil, nil, hashAlg.Size()*8)
h := hmac.New(func() hash.Hash { return hashAlg.NewHash() }, hmacKey)
h.Write(data)
h.Write(name)
if !bytes.Equal(h.Sum(nil), integrity) {
return nil, errors.New("integrity digest is invalid")
}
r = bytes.NewReader(data)
iv := make([]byte, symmetricAlg.Algorithm.BlockSize())
if useIV {
if _, err := mu.UnmarshalFromReader(r, &iv); err != nil {
return nil, xerrors.Errorf("cannot unmarshal IV: %w", err)
}
if len(iv) != symmetricAlg.Algorithm.BlockSize() {
return nil, errors.New("IV has the wrong size")
}
}
data, _ = ioutil.ReadAll(r)
symKey := internal.KDFa(hashAlg.GetHash(), seed, []byte(tpm2.StorageKey), name, nil, int(symmetricAlg.KeyBits.Sym))
if err := tpm2.CryptSymmetricDecrypt(tpm2.SymAlgorithmId(symmetricAlg.Algorithm), symKey, iv, data); err != nil {
return nil, xerrors.Errorf("cannot decrypt: %w", err)
}
return data, nil
}
// ProduceOuterWrap adds an outer wrapper to the supplied data. The supplied name
// is associated with the data.
//
// It encrypts the data using the specified symmetric algorithm and a key derived
// from the supplied seed and name.
//
// It then prepends an integrity HMAC of the encrypted data and the supplied
// name using the specified digest algorithm and a key derived from the supplied
// seed.
func ProduceOuterWrap(hashAlg tpm2.HashAlgorithmId, symmetricAlg *tpm2.SymDefObject, name tpm2.Name, seed []byte, useIV bool, data []byte) ([]byte, error) {
if !hashAlg.Available() {
return nil, errors.New("digest algorithm is not available")
}
if !symmetricAlg.Algorithm.IsValidBlockCipher() {
return nil, errors.New("symmetric algorithm is not a valid block cipher")
}
iv := make([]byte, symmetricAlg.Algorithm.BlockSize())
if useIV {
if _, err := rand.Read(iv); err != nil {
return nil, xerrors.Errorf("cannot generate IV: %w", err)
}
}
symKey := internal.KDFa(hashAlg.GetHash(), seed, []byte(tpm2.StorageKey), name, nil, int(symmetricAlg.KeyBits.Sym))
if err := tpm2.CryptSymmetricEncrypt(tpm2.SymAlgorithmId(symmetricAlg.Algorithm), symKey, iv, data); err != nil {
return nil, xerrors.Errorf("cannot encrypt: %w", err)
}
if useIV {
data = mu.MustMarshalToBytes(iv, mu.RawBytes(data))
}
hmacKey := internal.KDFa(hashAlg.GetHash(), seed, []byte(tpm2.IntegrityKey), nil, nil, hashAlg.Size()*8)
h := hmac.New(func() hash.Hash { return hashAlg.NewHash() }, hmacKey)
h.Write(data)
h.Write(name)
integrity := h.Sum(nil)
return mu.MustMarshalToBytes(integrity, mu.RawBytes(data)), nil
}
func PrivateToSensitive(private tpm2.Private, name tpm2.Name, hashAlg tpm2.HashAlgorithmId, symmetricAlg *tpm2.SymDefObject, seed []byte) (sensitive *tpm2.Sensitive, err error) {
data, err := UnwrapOuter(hashAlg, symmetricAlg, name, seed, true, private)
if err != nil {
return nil, xerrors.Errorf("cannot unwrap outer wrapper: %w", err)
}
if _, err := mu.UnmarshalFromBytes(data, mu.Sized(&sensitive)); err != nil {
return nil, xerrors.Errorf("cannot unmarhsal sensitive: %w", err)
}
return sensitive, nil
}
func SensitiveToPrivate(sensitive *tpm2.Sensitive, name tpm2.Name, hashAlg tpm2.HashAlgorithmId, symmetricAlg *tpm2.SymDefObject, seed []byte) (tpm2.Private, error) {
private, err := mu.MarshalToBytes(mu.Sized(sensitive))
if err != nil {
return nil, xerrors.Errorf("cannot marshal sensitive: %w", err)
}
private, err = ProduceOuterWrap(hashAlg, symmetricAlg, name, seed, true, private)
if err != nil {
return nil, xerrors.Errorf("cannot apply outer wrapper: %w", err)
}
return private, nil
}
// DuplicateToSensitive unwraps the supplied duplication blob. The supplied name
// is the name of the duplication object.
//
// If a seed is supplied, it removes the outer wrapper using the specified parent
// name algorithm and parent symmetric algorithm - these correspond to properties of
// the new parent's public area.
//
// If symmetricAlg is supplied, it removes the inner wrapper - first by decrypting
// it with the supplied innerSymKey, and then checking the inner integrity digest
// is valid and returning an error if it isn't.
func DuplicateToSensitive(duplicate tpm2.Private, name tpm2.Name, parentNameAlg tpm2.HashAlgorithmId, parentSymmetricAlg *tpm2.SymDefObject, seed []byte, symmetricAlg *tpm2.SymDefObject, innerSymKey tpm2.Data) (sensitive *tpm2.Sensitive, err error) {
if len(seed) > 0 {
// Remove outer wrapper
if parentSymmetricAlg == nil {
return nil, errors.New("missing parent symmetric algorithm")
}
var err error
duplicate, err = UnwrapOuter(parentNameAlg, parentSymmetricAlg, name, seed, false, duplicate)
if err != nil {
return nil, xerrors.Errorf("cannot unwrap outer wrapper: %w", err)
}
}
if symmetricAlg != nil && symmetricAlg.Algorithm != tpm2.SymObjectAlgorithmNull {
// Remove inner wrapper
if name.Algorithm() == tpm2.HashAlgorithmNull {
return nil, errors.New("invalid name")
}
if !name.Algorithm().Available() {
return nil, errors.New("name algorithm is not available")
}
if !symmetricAlg.Algorithm.IsValidBlockCipher() {
return nil, errors.New("inner symmetric algorithm is not a valid block cipher")
}
if err := tpm2.CryptSymmetricDecrypt(tpm2.SymAlgorithmId(symmetricAlg.Algorithm), innerSymKey, make([]byte, symmetricAlg.Algorithm.BlockSize()), duplicate); err != nil {
return nil, xerrors.Errorf("cannot decrypt inner wrapper: %w", err)
}
r := bytes.NewReader(duplicate)
var innerIntegrity []byte
if _, err := mu.UnmarshalFromReader(r, &innerIntegrity); err != nil {
return nil, xerrors.Errorf("cannot unmarshal inner integrity digest: %w", err)
}
duplicate, _ = ioutil.ReadAll(r)
h := name.Algorithm().NewHash()
h.Write(duplicate)
h.Write(name)
if !bytes.Equal(h.Sum(nil), innerIntegrity) {
return nil, errors.New("inner integrity digest is invalid")
}
}
if _, err := mu.UnmarshalFromBytes(duplicate, mu.Sized(&sensitive)); err != nil {
return nil, xerrors.Errorf("cannot unmarhsal sensitive: %w", err)
}
return sensitive, nil
}
// SensitiveToDuplicate creates a duplication blob from the supplied sensitive structure.
// The supplied name is the name of the object associated with sensitive.
//
// If symmetricAlg is defined, an inner wrapper will be applied, first by prepending
// an inner integrity digest computed with the object's name algorithm from the sensitive
// data and its name, and then encrypting the data with innerSymKey. If innerSymKey isn't
// supplied, a random key will be created and returned.
//
// If a seed is supplied, an outer wrapper will be applied using the name algorithm and
// symmetric algorithm of parent.
func SensitiveToDuplicate(sensitive *tpm2.Sensitive, name tpm2.Name, parent *tpm2.Public, seed []byte, symmetricAlg *tpm2.SymDefObject, innerSymKey tpm2.Data) (innerSymKeyOut tpm2.Data, duplicate tpm2.Private, err error) {
applyInnerWrapper := false
if symmetricAlg != nil && symmetricAlg.Algorithm != tpm2.SymObjectAlgorithmNull {
applyInnerWrapper = true
}
applyOuterWrapper := false
if len(seed) > 0 {
applyOuterWrapper = true
}
duplicate, err = mu.MarshalToBytes(mu.Sized(sensitive))
if err != nil {
return nil, nil, xerrors.Errorf("cannot marshal sensitive: %w", err)
}
if applyInnerWrapper {
if name.Algorithm() == tpm2.HashAlgorithmNull {
return nil, nil, errors.New("invalid name")
}
if !name.Algorithm().Available() {
return nil, nil, errors.New("name algorithm is not available")
}
if !symmetricAlg.Algorithm.IsValidBlockCipher() {
return nil, nil, errors.New("inner symmetric algorithm is not a valid block cipher")
}
// Apply inner wrapper
h := name.Algorithm().NewHash()
h.Write(duplicate)
h.Write(name)
innerIntegrity := h.Sum(nil)
duplicate = mu.MustMarshalToBytes(innerIntegrity, mu.RawBytes(duplicate))
if len(innerSymKey) == 0 {
innerSymKey = make([]byte, symmetricAlg.KeyBits.Sym/8)
if _, err := rand.Read(innerSymKey); err != nil {
return nil, nil, xerrors.Errorf("cannot obtain symmetric key for inner wrapper: %w", err)
}
innerSymKeyOut = innerSymKey
} else if len(innerSymKey) != int(symmetricAlg.KeyBits.Sym/8) {
return nil, nil, errors.New("the supplied symmetric key for inner wrapper has the wrong length")
}
if err := tpm2.CryptSymmetricEncrypt(tpm2.SymAlgorithmId(symmetricAlg.Algorithm), innerSymKey, make([]byte, symmetricAlg.Algorithm.BlockSize()), duplicate); err != nil {
return nil, nil, xerrors.Errorf("cannot apply inner wrapper: %w", err)
}
}
if applyOuterWrapper {
// Apply outer wrapper
var err error
duplicate, err = ProduceOuterWrap(parent.NameAlg, &parent.Params.AsymDetail(parent.Type).Symmetric, name, seed, false, duplicate)
if err != nil {
return nil, nil, xerrors.Errorf("cannot apply outer wrapper: %w", err)
}
}
return innerSymKeyOut, duplicate, nil
}
./github.com/canonical/go-tpm2/util/pcr_digest.go 0000664 0000000 0000000 00000003547 00000000000 020257 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"errors"
"fmt"
"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/mu"
)
// ComputePCRDigest computes a digest using the specified algorithm from the provided set of PCR
// values and the provided PCR selections. The digest is computed the same way as PCRComputeCurrentDigest
// as defined in the TPM reference implementation. It is most useful for computing an input to
// TPMContext.PolicyPCR or TrialAuthPolicy.PolicyPCR, and for validating quotes and creation data.
func ComputePCRDigest(alg tpm2.HashAlgorithmId, pcrs tpm2.PCRSelectionList, values tpm2.PCRValues) (tpm2.Digest, error) {
if !alg.Available() {
return nil, errors.New("algorithm is not available")
}
h := alg.NewHash()
mu.MustCopyValue(&pcrs, pcrs)
for _, s := range pcrs {
if _, ok := values[s.Hash]; !ok {
return nil, fmt.Errorf("the provided values don't contain digests for the selected PCR bank %v", s.Hash)
}
for _, i := range s.Select {
d, ok := values[s.Hash][i]
if !ok {
return nil, fmt.Errorf("the provided values don't contain a digest for PCR%d in bank %v", i, s.Hash)
}
h.Write(d)
}
}
return h.Sum(nil), nil
}
// ComputePCRDigestFromAllValues computes a digest using the specified algorithm from all of the
// provided set of PCR values. The digest is computed the same way as PCRComputeCurrentDigest as
// defined in the TPM reference implementation. It returns the PCR selection associated with the
// computed digest.
func ComputePCRDigestFromAllValues(alg tpm2.HashAlgorithmId, values tpm2.PCRValues) (tpm2.PCRSelectionList, tpm2.Digest, error) {
pcrs := values.SelectionList()
digest, err := ComputePCRDigest(alg, pcrs, values)
if err != nil {
return nil, nil, err
}
return pcrs, digest, nil
}
./github.com/canonical/go-tpm2/util/policy.go 0000664 0000000 0000000 00000016574 00000000000 017437 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"encoding/binary"
"hash"
"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/mu"
)
// TrialAuthPolicy provides a mechanism for computing authorization policy digests without
// having to execute a trial authorization policy session on the TPM. An advantage of this
// is that it is possible to compute digests for PolicySecret and PolicyNV assertions
// without knowledge of the authorization value of the authorizing entities used for those
// commands.
type TrialAuthPolicy struct {
alg tpm2.HashAlgorithmId
digest tpm2.Digest
// A policy can only contain one of TPM2_PolicyCpHash, TPM2_PolicyNameHash or
// TPM2_PolicyTemplate
hashOccupied bool
}
// ComputeAuthPolicy creates a new context for computing an authorization policy digest.
// It will panic if the specified algorithm is not available. The caller should check
// this beforehand.
func ComputeAuthPolicy(alg tpm2.HashAlgorithmId) *TrialAuthPolicy {
if !alg.Available() {
panic("unsupported digest algorithm or algorithm not linked in to binary")
}
return &TrialAuthPolicy{alg: alg, digest: make(tpm2.Digest, alg.Size())}
}
func (p *TrialAuthPolicy) beginUpdate() (hash.Hash, func()) {
h := p.alg.NewHash()
h.Write(p.digest)
return h, func() {
p.digest = h.Sum(nil)
}
}
func (p *TrialAuthPolicy) beginUpdateForCommand(commandCode tpm2.CommandCode) (hash.Hash, func()) {
h, end := p.beginUpdate()
binary.Write(h, binary.BigEndian, commandCode)
return h, end
}
func (p *TrialAuthPolicy) update(commandCode tpm2.CommandCode, name tpm2.Name, ref tpm2.Nonce) {
h, end := p.beginUpdateForCommand(commandCode)
h.Write(name)
end()
h, end = p.beginUpdate()
h.Write(ref)
end()
}
func (p *TrialAuthPolicy) reset() {
p.digest = make(tpm2.Digest, len(p.digest))
}
// GetDigest returns the current digest computed for the policy assertions executed so far.
func (p *TrialAuthPolicy) GetDigest() tpm2.Digest {
return p.digest
}
// SetDigests overwrites the current digest. It will panic if the supplied digest is
// not the correct size.
func (p *TrialAuthPolicy) SetDigest(d tpm2.Digest) {
if len(d) != p.alg.Size() {
panic("invalid digest length")
}
p.digest = d
}
// Reset clears the current digest.
func (p *TrialAuthPolicy) Reset() {
p.reset()
}
// PolicySigned computes a TPM2_PolicySigned assertion executed for a key with
// the specified name and the specified policyRef.
func (p *TrialAuthPolicy) PolicySigned(authName tpm2.Name, policyRef tpm2.Nonce) {
p.update(tpm2.CommandPolicySigned, authName, policyRef)
}
// PolicySecret computes a TPM2_PolicySecret assertion executed for an object
// with the specified name and the specified policyRef.
func (p *TrialAuthPolicy) PolicySecret(authName tpm2.Name, policyRef tpm2.Nonce) {
p.update(tpm2.CommandPolicySecret, authName, policyRef)
}
// PolicyOR computes a TPM2_PolicyOR assertion executed for the specified
// digests. It will panic if there are fewer than 2 or more than 8 digests,
// or if any digest has the wrong size.
func (p *TrialAuthPolicy) PolicyOR(pHashList tpm2.DigestList) {
if len(pHashList) < 2 || len(pHashList) > 8 {
panic("invalid number of digests")
}
p.reset()
h, end := p.beginUpdateForCommand(tpm2.CommandPolicyOR)
for _, digest := range pHashList {
if len(digest) != p.alg.Size() {
panic("invalid digest length")
}
h.Write(digest)
}
end()
}
// PolicyPCR computes a TPM2_PolicyPCR assertion executed for the specified
// PCR selection and with PCR values associated with the specified PCR digest.
func (p *TrialAuthPolicy) PolicyPCR(pcrDigest tpm2.Digest, pcrs tpm2.PCRSelectionList) {
if len(pcrDigest) != p.alg.Size() {
panic("invalid PCR digest length")
}
h, end := p.beginUpdateForCommand(tpm2.CommandPolicyPCR)
mu.MustMarshalToWriter(h, pcrs)
h.Write(pcrDigest)
end()
}
// PolicyNV computes a TPM2_PolicyNV assertion executed for an index for the
// specified name, with the specified comparison operation.
func (p *TrialAuthPolicy) PolicyNV(nvIndexName tpm2.Name, operandB tpm2.Operand, offset uint16, operation tpm2.ArithmeticOp) {
h := p.alg.NewHash()
h.Write(operandB)
binary.Write(h, binary.BigEndian, offset)
binary.Write(h, binary.BigEndian, operation)
args := h.Sum(nil)
h, end := p.beginUpdateForCommand(tpm2.CommandPolicyNV)
h.Write(args)
h.Write(nvIndexName)
end()
}
// PolicyCounterTimer computes a TPM2_PolicyCounterTimer assertion for the
// specified comparison operation.
func (p *TrialAuthPolicy) PolicyCounterTimer(operandB tpm2.Operand, offset uint16, operation tpm2.ArithmeticOp) {
h := p.alg.NewHash()
h.Write(operandB)
binary.Write(h, binary.BigEndian, offset)
binary.Write(h, binary.BigEndian, operation)
args := h.Sum(nil)
h, end := p.beginUpdateForCommand(tpm2.CommandPolicyCounterTimer)
h.Write(args)
end()
}
// PolicyCommandCode computes a TPM2_PolicyCommandCode assertion for the
// specified command code.
func (p *TrialAuthPolicy) PolicyCommandCode(code tpm2.CommandCode) {
h, end := p.beginUpdateForCommand(tpm2.CommandPolicyCommandCode)
binary.Write(h, binary.BigEndian, code)
end()
}
// PolicyCpHash computes a TPM2_PolicyCpHash assertion for the command parameters
// associated with the specified hash.
func (p *TrialAuthPolicy) PolicyCpHash(cpHashA tpm2.Digest) {
if len(cpHashA) != p.alg.Size() {
panic("invalid digest length")
}
if p.hashOccupied {
panic("policy already has a hash")
}
p.hashOccupied = true
h, end := p.beginUpdateForCommand(tpm2.CommandPolicyCpHash)
h.Write(cpHashA)
end()
}
// PolicyNameHash computes a TPM2_PolicyNameHash assertion with for the entities
// associated with the specified name digest.
func (p *TrialAuthPolicy) PolicyNameHash(nameHash tpm2.Digest) {
if len(nameHash) != p.alg.Size() {
panic("invalid digest length")
}
if p.hashOccupied {
panic("policy already has a hash")
}
p.hashOccupied = true
h, end := p.beginUpdateForCommand(tpm2.CommandPolicyNameHash)
h.Write(nameHash)
end()
}
// PolicyDuplicationSelect computes a TPM2_PolicyDuplicationSelect assertion for
// the object and parent object with the specified names.
func (p *TrialAuthPolicy) PolicyDuplicationSelect(objectName, newParentName tpm2.Name, includeObject bool) {
h, end := p.beginUpdateForCommand(tpm2.CommandPolicyDuplicationSelect)
if includeObject {
h.Write(objectName)
}
h.Write(newParentName)
binary.Write(h, binary.BigEndian, includeObject)
end()
}
// PolicyAuthorize computes a TPM2_PolicyAuthorize assertion for the key with the
// specified name and the specified policyRef.
func (p *TrialAuthPolicy) PolicyAuthorize(policyRef tpm2.Nonce, keySign tpm2.Name) {
p.update(tpm2.CommandPolicyAuthorize, keySign, policyRef)
}
// PolicyAuthValue computes a TPM2_PolicyAuthValue assertion.
func (p *TrialAuthPolicy) PolicyAuthValue() {
_, end := p.beginUpdateForCommand(tpm2.CommandPolicyAuthValue)
end()
}
// PolicyPassword computes a TPM2_PolicyPassword assertion.
func (p *TrialAuthPolicy) PolicyPassword() {
// This extends the same value as PolicyAuthValue - see section 23.18 of part 3 of the "TPM 2.0 Library
// Specification"
_, end := p.beginUpdateForCommand(tpm2.CommandPolicyAuthValue)
end()
}
// PolicyNvWritten computes a TPM2_PolicyNvWritten assertion
func (p *TrialAuthPolicy) PolicyNvWritten(writtenSet bool) {
h, end := p.beginUpdateForCommand(tpm2.CommandPolicyNvWritten)
binary.Write(h, binary.BigEndian, writtenSet)
end()
}
./github.com/canonical/go-tpm2/util/qn.go 0000664 0000000 0000000 00000002734 00000000000 016547 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"errors"
"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/mu"
)
// ComputeQualifiedName can compute the qualified name of an object with
// the specified name that is protected by a parent with the specified
// qualified name.
func ComputeQualifiedName(name, parentQn tpm2.Name) (tpm2.Name, error) {
if name.Algorithm() == tpm2.HashAlgorithmNull {
return nil, errors.New("invalid name")
}
if !name.Algorithm().Available() {
return nil, errors.New("name algorithm is not available")
}
h := name.Algorithm().NewHash()
h.Write(parentQn)
h.Write(name)
return mu.MarshalToBytes(name.Algorithm(), mu.RawBytes(h.Sum(nil)))
}
// ComputeQualifiedNameFull can compute the qualified name of an object with
// the specified name that is protected in the specified hierarchy by the chain
// of parent objects with the specified names. The ancestor names are ordered
// from the primary key towards the immediate parent.
func ComputeQualifiedNameFull(name tpm2.Name, hierarchy tpm2.Handle, ancestors ...tpm2.Name) (tpm2.Name, error) {
lastQn := tpm2.Name(mu.MustMarshalToBytes(hierarchy))
for len(ancestors) > 0 {
current := ancestors[0]
ancestors = ancestors[1:]
var err error
lastQn, err = ComputeQualifiedName(current, lastQn)
if err != nil {
return nil, err
}
}
return ComputeQualifiedName(name, lastQn)
}
./github.com/canonical/go-tpm2/util/signatures.go 0000664 0000000 0000000 00000020524 00000000000 020312 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package util
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"encoding/binary"
"errors"
"math/big"
"github.com/canonical/go-tpm2"
"github.com/canonical/go-tpm2/mu"
)
// Sign creates a signature of the supplied digest using the supplied private key and
// signature scheme. Note that only RSA-SSA, RSA-PSS, ECDSA and HMAC signatures can
// be created. The returned signature can be verified on a TPM using the associated
// public key.
//
// In order to create a HMAC, the supplied private key should be a byte slice containing
// the HMAC key.
func Sign(key crypto.PrivateKey, scheme *tpm2.SigScheme, digest []byte) (*tpm2.Signature, error) {
hashAlg := scheme.Details.Any(scheme.Scheme).HashAlg
if !hashAlg.Available() {
return nil, errors.New("digest algorithm is not available")
}
if len(digest) != hashAlg.Size() {
return nil, errors.New("invalid digest length")
}
switch k := key.(type) {
case *rsa.PrivateKey:
switch scheme.Scheme {
case tpm2.SigSchemeAlgRSASSA:
sig, err := rsa.SignPKCS1v15(rand.Reader, k, hashAlg.GetHash(), digest)
if err != nil {
return nil, err
}
return &tpm2.Signature{
SigAlg: tpm2.SigSchemeAlgRSASSA,
Signature: &tpm2.SignatureU{
RSASSA: &tpm2.SignatureRSASSA{
Hash: hashAlg,
Sig: sig}}}, nil
case tpm2.SigSchemeAlgRSAPSS:
options := rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
sig, err := rsa.SignPSS(rand.Reader, k, hashAlg.GetHash(), digest, &options)
if err != nil {
return nil, err
}
return &tpm2.Signature{
SigAlg: tpm2.SigSchemeAlgRSAPSS,
Signature: &tpm2.SignatureU{
RSAPSS: &tpm2.SignatureRSAPSS{
Hash: hashAlg,
Sig: sig}}}, nil
default:
return nil, errors.New("unsupported RSA signature scheme")
}
case *ecdsa.PrivateKey:
switch scheme.Scheme {
case tpm2.SigSchemeAlgECDSA:
r, s, err := ecdsa.Sign(rand.Reader, k, digest)
if err != nil {
return nil, err
}
return &tpm2.Signature{
SigAlg: tpm2.SigSchemeAlgECDSA,
Signature: &tpm2.SignatureU{
ECDSA: &tpm2.SignatureECDSA{
Hash: hashAlg,
SignatureR: r.Bytes(),
SignatureS: s.Bytes()}}}, nil
default:
return nil, errors.New("unsupported ECC signature scheme")
}
case []byte:
switch scheme.Scheme {
case tpm2.SigSchemeAlgHMAC:
h := hmac.New(hashAlg.NewHash, k)
h.Write(digest)
return &tpm2.Signature{
SigAlg: tpm2.SigSchemeAlgHMAC,
Signature: &tpm2.SignatureU{
HMAC: &tpm2.TaggedHash{
HashAlg: hashAlg,
Digest: h.Sum(nil)}}}, nil
default:
return nil, errors.New("unsupported keyed hash scheme")
}
default:
return nil, errors.New("unsupported private key type")
}
}
// VerifySignature verifies a signature created by a TPM using the supplied public
// key. Note that only RSA-SSA, RSA-PSS, ECDSA and HMAC signatures are supported.
//
// In order to verify a HMAC signature, the supplied public key should be a byte
// slice containing the HMAC key.
func VerifySignature(key crypto.PublicKey, digest []byte, signature *tpm2.Signature) (ok bool, err error) {
if !signature.Signature.Any(signature.SigAlg).HashAlg.Available() {
return false, errors.New("digest algorithm is not available")
}
switch k := key.(type) {
case *rsa.PublicKey:
switch signature.SigAlg {
case tpm2.SigSchemeAlgRSASSA:
if err := rsa.VerifyPKCS1v15(k, signature.Signature.RSASSA.Hash.GetHash(), digest, signature.Signature.RSASSA.Sig); err != nil {
if err == rsa.ErrVerification {
return false, nil
}
return false, err
}
return true, nil
case tpm2.SigSchemeAlgRSAPSS:
options := rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
if err := rsa.VerifyPSS(k, signature.Signature.RSAPSS.Hash.GetHash(), digest, signature.Signature.RSAPSS.Sig, &options); err != nil {
if err == rsa.ErrVerification {
return false, nil
}
return false, err
}
return true, nil
default:
return false, errors.New("unsupported RSA signature algorithm")
}
case *ecdsa.PublicKey:
switch signature.SigAlg {
case tpm2.SigSchemeAlgECDSA:
ok = ecdsa.Verify(k, digest, new(big.Int).SetBytes(signature.Signature.ECDSA.SignatureR),
new(big.Int).SetBytes(signature.Signature.ECDSA.SignatureS))
return ok, nil
default:
return false, errors.New("unsupported ECC signature algorithm")
}
case []byte:
switch signature.SigAlg {
case tpm2.SigSchemeAlgHMAC:
scheme := &tpm2.SigScheme{
Scheme: tpm2.SigSchemeAlgHMAC,
Details: &tpm2.SigSchemeU{
HMAC: &tpm2.SchemeHMAC{
HashAlg: signature.Signature.HMAC.HashAlg}}}
test, err := Sign(k, scheme, digest)
if err != nil {
return false, err
}
return bytes.Equal(signature.Signature.HMAC.Digest, test.Signature.HMAC.Digest), nil
default:
return false, errors.New("unsupported keyed hash signature algorithm")
}
default:
return false, errors.New("invalid public key type")
}
}
// SignPolicyAuthorization creates a signed authorization using the supplied key and signature
// scheme. The signed authorization can be used in a TPM2_PolicySigned assertion. The authorizing
// party can apply contraints on how the session that includes this authorization can be used.
//
// If nonceTPM is supplied, then the signed authorization can only be used for the session
// associated with the supplied nonce.
//
// If expiration is non-zero, then the signed authorization is only valid for the specified
// number of seconds from when nonceTPM was generated.
//
// If cpHash is supplied, then the signed authorization is only valid for use in a command
// with the associated set of command parameters.
func SignPolicyAuthorization(key crypto.PrivateKey, scheme *tpm2.SigScheme, nonceTPM tpm2.Nonce, cpHashA tpm2.Digest, policyRef tpm2.Nonce, expiration int32) (*tpm2.Signature, error) {
hashAlg := scheme.Details.Any(scheme.Scheme).HashAlg
if !hashAlg.Available() {
return nil, errors.New("digest algorithm is not available")
}
h := hashAlg.NewHash()
h.Write(nonceTPM)
binary.Write(h, binary.BigEndian, expiration)
h.Write(cpHashA)
h.Write(policyRef)
return Sign(key, scheme, h.Sum(nil))
}
// ComputePolicyAuthorizeDigest computes a digest to sign from the supplied
// authorization policy digest and policy reference. The resulting digest
// can be signed to authorize the supplied policy with the TPM2_PolicyAuthorize
// assertion.
func ComputePolicyAuthorizeDigest(alg tpm2.HashAlgorithmId, approvedPolicy tpm2.Digest, policyRef tpm2.Nonce) (tpm2.Digest, error) {
if !alg.Available() {
return nil, errors.New("digest algorithm is not available")
}
h := alg.NewHash()
h.Write(approvedPolicy)
h.Write(policyRef)
return h.Sum(nil), nil
}
// PolicyAuthorize authorizes an authorization policy digest with the supplied key and
// signature scheme. The resulting digest and signature can be verified by the TPM in
// order to produce a ticket that can then be supplied to a TPM2_PolicyAuthorize assertion.
//
// The digest algorithm used for the signature must match the name algorithm in
// the public area associated with the supplied private key.
func PolicyAuthorize(key crypto.PrivateKey, scheme *tpm2.SigScheme, approvedPolicy tpm2.Digest, policyRef tpm2.Nonce) (tpm2.Digest, *tpm2.Signature, error) {
hashAlg := scheme.Details.Any(scheme.Scheme).HashAlg
if !hashAlg.Available() {
return nil, nil, errors.New("digest algorithm is not available")
}
digest, _ := ComputePolicyAuthorizeDigest(hashAlg, approvedPolicy, policyRef)
sig, err := Sign(key, scheme, digest)
if err != nil {
return nil, nil, err
}
return digest, sig, nil
}
// VerifyAttestationSignature verifies the signature for the supplied attestation
// structure as generated by one of the TPM's attestation commands. Note that only
// RSA-SSA, RSA-PSS, ECDSA and HMAC signatures are supported.
//
// In order to verify a HMAC signature, the supplied public key should be a byte
// slice containing the HMAC key.
func VerifyAttestationSignature(key crypto.PublicKey, attest *tpm2.Attest, signature *tpm2.Signature) (ok bool, err error) {
hashAlg := signature.Signature.Any(signature.SigAlg).HashAlg
if !hashAlg.Available() {
return false, errors.New("digest algorithm is not available")
}
h := hashAlg.NewHash()
mu.MustMarshalToWriter(h, attest)
return VerifySignature(key, h.Sum(nil), signature)
}
./github.com/canonical/tcglog-parser/ 0000775 0000000 0000000 00000000000 00000000000 016103 5 ustar 00 0000000 0000000 ./github.com/canonical/tcglog-parser/.gitignore 0000664 0000000 0000000 00000000117 00000000000 020072 0 ustar 00 0000000 0000000 tcglog-dump/tcglog-dump
tcglog-parser.test
tcglog-check/tcglog-check
vendor/*/
./github.com/canonical/tcglog-parser/LICENSE 0000664 0000000 0000000 00000021501 00000000000 017107 0 ustar 00 0000000 0000000 All files in this repository are licensed as follows. If you contribute
to this repository, it is assumed that you license your contribution
under the same license unless you state otherwise.
All files Copyright (C) 2019 Canonical Ltd. unless otherwise specified in the file.
This software is licensed under the LGPLv3, included below.
As a special exception to the GNU Lesser General Public License version 3
("LGPL3"), the copyright holders of this Library give you permission to
convey to a third party a Combined Work that links statically or dynamically
to this Library without providing any Minimal Corresponding Source or
Minimal Application Code as set out in 4d or providing the installation
information set out in section 4e, provided that you comply with the other
provisions of LGPL3 and provided that you meet, for the Application the
terms and conditions of the license(s) which apply to the Application.
Except as stated in this special exception, the provisions of LGPL3 will
continue to comply in full to this Library. If you modify this Library, you
may apply this exception to your version of this Library, but you are not
obliged to do so. If you do not wish to do so, delete this exception
statement from your version. This exception does not (and cannot) modify any
license terms which apply to the Application, with which you must still
comply.
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
./github.com/canonical/tcglog-parser/README.md 0000664 0000000 0000000 00000001645 00000000000 017370 0 ustar 00 0000000 0000000 # TCG Log Parser
This repository contains a go library for parsing TCG event logs. Also included is a simple command line tool that prints details of log entries to the console.
## Relevant specifications
* [TCG PC Client Platform Firmware Profile Specification](https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf)
* [TCG EFI Platform Specification For TPM Family 1.1 or 1.2](https://trustedcomputinggroup.org/wp-content/uploads/TCG_EFI_Platform_1_22_Final_-v15.pdf)
* [TCG PC Client Specific Implementation Specification for Conventional BIOS](https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf)
* [Unified Extensible Firmware Interface (UEFI) Specification](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf)
* [Platform Initialization (PI) Specification](https://uefi.org/sites/default/files/resources/PI_Spec_1_6.pdf)
./github.com/canonical/tcglog-parser/constants.go 0000664 0000000 0000000 00000007215 00000000000 020453 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"math"
)
const (
EventTypePrebootCert EventType = 0x00000000 // EV_PREBOOT_CERT
EventTypePostCode EventType = 0x00000001 // EV_POST_CODE
// EventTypeUnused = 0x00000002
EventTypeNoAction EventType = 0x00000003 // EV_NO_ACTION
EventTypeSeparator EventType = 0x00000004 // EV_SEPARATOR
EventTypeAction EventType = 0x00000005 // EV_ACTION
EventTypeEventTag EventType = 0x00000006 // EV_EVENT_TAG
EventTypeSCRTMContents EventType = 0x00000007 // EV_S_CRTM_CONTENTS
EventTypeSCRTMVersion EventType = 0x00000008 // EV_S_CRTM_VERSION
EventTypeCPUMicrocode EventType = 0x00000009 // EV_CPU_MICROCODE
EventTypePlatformConfigFlags EventType = 0x0000000a // EV_PLATFORM_CONFIG_FLAGS
EventTypeTableOfDevices EventType = 0x0000000b // EV_TABLE_OF_DEVICES
EventTypeCompactHash EventType = 0x0000000c // EV_COMPACT_HASH
EventTypeIPL EventType = 0x0000000d // EV_IPL
EventTypeIPLPartitionData EventType = 0x0000000e // EV_IPL_PARTITION_DATA
EventTypeNonhostCode EventType = 0x0000000f // EV_NONHOST_CODE
EventTypeNonhostConfig EventType = 0x00000010 // EV_NONHOST_CONFIG
EventTypeNonhostInfo EventType = 0x00000011 // EV_NONHOST_INFO
EventTypeOmitBootDeviceEvents EventType = 0x00000012 // EV_OMIT_BOOT_DEVICE_EVENTS
EventTypeEFIEventBase EventType = 0x80000000 // EV_EFI_EVENT_BASE
EventTypeEFIVariableDriverConfig EventType = 0x80000001 // EV_EFI_VARIABLE_DRIVER_CONFIG
EventTypeEFIVariableBoot EventType = 0x80000002 // EV_EFI_VARIABLE_BOOT
EventTypeEFIBootServicesApplication EventType = 0x80000003 // EV_EFI_BOOT_SERVICES_APPLICATION
EventTypeEFIBootServicesDriver EventType = 0x80000004 // EV_EFI_BOOT_SERVICES_DRIVER
EventTypeEFIRuntimeServicesDriver EventType = 0x80000005 // EV_EFI_RUNTIME_SERVICES_DRIVER
EventTypeEFIGPTEvent EventType = 0x80000006 // EV_EFI_GPT_EVENT
EventTypeEFIAction EventType = 0x80000007 // EV_EFI_ACTION
EventTypeEFIPlatformFirmwareBlob EventType = 0x80000008 // EV_EFI_PLATFORM_FIRMWARE_BLOB
EventTypeEFIHandoffTables EventType = 0x80000009 // EV_EFI_HANDOFF_TABLES
EventTypeEFIPlatformFirmwareBlob2 EventType = 0x8000000a // EV_EFI_PLATFORM_FIRMWARE_BLOB2
EventTypeEFIHandoffTables2 EventType = 0x8000000b // EV_EFI_HANDOFF_TABLES2
EventTypeEFIVariableBoot2 EventType = 0x8000000c // EV_EFI_VARIABLE_BOOT2
EventTypeEFIHCRTMEvent EventType = 0x80000010 // EV_EFI_HCRTM_EVENT
EventTypeEFIVariableAuthority EventType = 0x800000e0 // EV_EFI_VARIABLE_AUTHORITY
EventTypeEFISPDMFirmwareBlob EventType = 0x800000e1 // EV_EFI_SPDM_FIRMWARE_BLOB
EventTypeEFISPDMFirmwareConfig EventType = 0x800000e2 // EV_EFI_SPDM_FIRMWARE_CONFIG
)
const (
SeparatorEventNormalValue uint32 = 0
SeparatorEventErrorValue uint32 = 1
SeparatorEventAltNormalValue uint32 = math.MaxUint32
)
var (
EFICallingEFIApplicationEvent = StringEventData("Calling EFI Application from Boot Option")
EFIReturningFromEFIApplicationEvent = StringEventData("Returning from EFI Application from Boot Option")
EFIExitBootServicesInvocationEvent = StringEventData("Exit Boot Services Invocation")
EFIExitBootServicesFailedEvent = StringEventData("Exit Boot Services Returned with Failure")
EFIExitBootServicesSucceededEvent = StringEventData("Exit Boot Services Returned with Success")
FirmwareDebuggerEvent = StringEventData("UEFI Debug Mode")
)
./github.com/canonical/tcglog-parser/event.go 0000664 0000000 0000000 00000013267 00000000000 017564 0 ustar 00 0000000 0000000 // Copyright 2019-2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"sort"
"github.com/canonical/go-tpm2"
"golang.org/x/xerrors"
"github.com/canonical/tcglog-parser/internal/ioerr"
)
type eventHeader struct {
PCRIndex PCRIndex
EventType EventType
}
type eventHeaderCryptoAgile struct {
eventHeader
Count uint32
}
// Event corresponds to a single event in an event log.
type Event struct {
PCRIndex PCRIndex // PCR index to which this event was measured
EventType EventType // The type of this event
Digests DigestMap // The digests corresponding to this event for the supported algorithms
Data EventData // The data recorded with this event
}
func (e *Event) Write(w io.Writer) error {
digest, ok := e.Digests[tpm2.HashAlgorithmSHA1]
if !ok {
return errors.New("missing SHA-1 digest")
}
if len(digest) != tpm2.HashAlgorithmSHA1.Size() {
return errors.New("invalid digest size")
}
data := new(bytes.Buffer)
if err := e.Data.Write(data); err != nil {
return xerrors.Errorf("cannot serialize event data: %w", err)
}
hdr := eventHeader{
PCRIndex: e.PCRIndex,
EventType: e.EventType}
if err := binary.Write(w, binary.LittleEndian, &hdr); err != nil {
return err
}
if _, err := w.Write(digest); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, uint32(data.Len())); err != nil {
return err
}
_, err := w.Write(data.Bytes())
return err
}
func (e *Event) WriteCryptoAgile(w io.Writer) error {
var algs []tpm2.HashAlgorithmId
for alg, digest := range e.Digests {
if !alg.IsValid() {
continue
}
if len(digest) != alg.Size() {
return fmt.Errorf("invalid digest size for %v", alg)
}
algs = append(algs, alg)
}
sort.Slice(algs, func(i, j int) bool { return algs[i] < algs[j] })
data := new(bytes.Buffer)
if err := e.Data.Write(data); err != nil {
return xerrors.Errorf("cannot serialize event data: %w", err)
}
hdr := eventHeaderCryptoAgile{
eventHeader: eventHeader{
PCRIndex: e.PCRIndex,
EventType: e.EventType},
Count: uint32(len(algs))}
if err := binary.Write(w, binary.LittleEndian, &hdr); err != nil {
return err
}
for _, alg := range algs {
if err := binary.Write(w, binary.LittleEndian, alg); err != nil {
return err
}
if _, err := w.Write(e.Digests[alg]); err != nil {
return err
}
}
if err := binary.Write(w, binary.LittleEndian, uint32(data.Len())); err != nil {
return err
}
_, err := w.Write(data.Bytes())
return err
}
func isPCRIndexInRange(index PCRIndex) bool {
const maxPCRIndex PCRIndex = 31
return index <= maxPCRIndex
}
func ReadEvent(r io.Reader, options *LogOptions) (*Event, error) {
var header eventHeader
if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
return nil, err
}
if !isPCRIndexInRange(header.PCRIndex) {
return nil, fmt.Errorf("log entry has an out-of-range PCR index (%d)", header.PCRIndex)
}
digest := make(Digest, tpm2.HashAlgorithmSHA1.Size())
if _, err := io.ReadFull(r, digest); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
digests := make(DigestMap)
digests[tpm2.HashAlgorithmSHA1] = digest
var eventSize uint32
if err := binary.Read(r, binary.LittleEndian, &eventSize); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
event := make([]byte, eventSize)
if _, err := io.ReadFull(r, event); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return &Event{
PCRIndex: header.PCRIndex,
EventType: header.EventType,
Digests: digests,
Data: decodeEventData(event, header.PCRIndex, header.EventType, digests, options),
}, nil
}
func ReadEventCryptoAgile(r io.Reader, digestSizes []EFISpecIdEventAlgorithmSize, options *LogOptions) (*Event, error) {
var header eventHeaderCryptoAgile
if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
return nil, err
}
if !isPCRIndexInRange(header.PCRIndex) {
return nil, fmt.Errorf("log entry has an out-of-range PCR index (%d)", header.PCRIndex)
}
digests := make(DigestMap)
for i := uint32(0); i < header.Count; i++ {
var algorithmId tpm2.HashAlgorithmId
if err := binary.Read(r, binary.LittleEndian, &algorithmId); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
var digestSize uint16
var j int
for j = 0; j < len(digestSizes); j++ {
if digestSizes[j].AlgorithmId == algorithmId {
digestSize = digestSizes[j].DigestSize
break
}
}
if j == len(digestSizes) {
return nil, fmt.Errorf("event contains a digest for an unrecognized algorithm (%v)", algorithmId)
}
digest := make(Digest, digestSize)
if _, err := io.ReadFull(r, digest); err != nil {
return nil, ioerr.EOFIsUnexpected("cannot read digest for algorithm %v: %w", algorithmId, err)
}
if _, exists := digests[algorithmId]; exists {
return nil, fmt.Errorf("event contains more than one digest value for algorithm %v", algorithmId)
}
digests[algorithmId] = digest
}
for _, s := range digestSizes {
if _, exists := digests[s.AlgorithmId]; !exists {
return nil, fmt.Errorf("event is missing a digest value for algorithm %v", s.AlgorithmId)
}
}
for alg, _ := range digests {
if alg.IsValid() {
continue
}
delete(digests, alg)
}
var eventSize uint32
if err := binary.Read(r, binary.LittleEndian, &eventSize); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
event := make([]byte, eventSize)
if _, err := io.ReadFull(r, event); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return &Event{
PCRIndex: header.PCRIndex,
EventType: header.EventType,
Digests: digests,
Data: decodeEventData(event, header.PCRIndex, header.EventType, digests, options),
}, nil
}
./github.com/canonical/tcglog-parser/eventdata.go 0000664 0000000 0000000 00000005601 00000000000 020407 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"crypto"
"fmt"
"io"
"unicode"
"unicode/utf8"
)
// EventData represents all event data types that appear in a log. Some implementations of this are exported so that event data
// contents can be inspected programatically.
//
// If an error is encountered when decoding the data associated with an event, the event data will implement the error interface
// which can be used for obtaining information about the decoding error.
type EventData interface {
fmt.Stringer
// Bytes is the raw event data bytes as they appear in the event log.
Bytes() []byte
// Write will serialize this event data to the supplied io.Writer.
Write(w io.Writer) error
}
type rawEventData []byte
func (b rawEventData) Bytes() []byte {
return []byte(b)
}
// invalidEventData corresponds to an event data blob that failed to decode correctly.
type invalidEventData struct {
rawEventData
err error
}
func (e *invalidEventData) String() string {
return fmt.Sprintf("Invalid event data: %v", e.err)
}
func (e *invalidEventData) Write(w io.Writer) error {
_, err := w.Write(e.rawEventData)
return err
}
func (e *invalidEventData) Error() string {
return e.err.Error()
}
func (e *invalidEventData) Unwrap() error {
return e.err
}
// OpaqueEventData is event data whose format is unknown or implementation defined.
type OpaqueEventData []byte
func (d OpaqueEventData) String() string {
// This blob is opaque, but try to print something if it's filled
// with printable characters.
data := d
var s []byte
for len(data) > 0 {
r, sz := utf8.DecodeRune(data)
if r == 0 {
break
}
if !unicode.IsPrint(r) {
return ""
}
s = append(s, data[:sz]...)
data = data[sz:]
}
return string(s)
}
func (d OpaqueEventData) Bytes() []byte {
return []byte(d)
}
func (d OpaqueEventData) Write(w io.Writer) error {
_, err := w.Write(d)
return err
}
// ComputeEventDigest computes the digest associated with the supplied event data bytes,
// for events where the digest is a tagged hash of the event data.
func ComputeEventDigest(alg crypto.Hash, data []byte) []byte {
h := alg.New()
h.Write(data)
return h.Sum(nil)
}
func decodeEventData(data []byte, pcrIndex PCRIndex, eventType EventType, digests DigestMap, options *LogOptions) EventData {
if options.EnableGrub && (pcrIndex == 8 || pcrIndex == 9) {
if out := decodeEventDataGRUB(data, pcrIndex, eventType); out != nil {
return out
}
}
if options.EnableSystemdEFIStub && pcrIndex == options.SystemdEFIStubPCR {
if out := decodeEventDataSystemdEFIStub(data, eventType); out != nil {
return out
}
}
out, err := decodeEventDataTCG(data, pcrIndex, eventType, digests)
if err != nil {
return &invalidEventData{rawEventData: data, err: err}
}
if out != nil {
return out
}
return OpaqueEventData(data)
}
./github.com/canonical/tcglog-parser/grubeventdata.go 0000664 0000000 0000000 00000003407 00000000000 021271 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"fmt"
"io"
"strings"
)
var (
kernelCmdlinePrefix = "kernel_cmdline: "
grubCmdPrefix = "grub_cmd: "
)
// GrubStringEventType indicates the type of data measured by GRUB in to a log by GRUB.
type GrubStringEventType string
const (
// GrubCmd indicates that the data measured by GRUB is associated with a GRUB command.
GrubCmd GrubStringEventType = "grub_cmd"
// KernelCmdline indicates that the data measured by GRUB is associated with a kernel commandline.
KernelCmdline = "kernel_cmdline"
)
// GrubStringEventData represents the data associated with an event measured by GRUB.
type GrubStringEventData struct {
rawEventData
Type GrubStringEventType
Str string
}
func (e *GrubStringEventData) String() string {
return fmt.Sprintf("%s{ %s }", string(e.Type), e.Str)
}
func (e *GrubStringEventData) Write(w io.Writer) error {
_, err := io.WriteString(w, fmt.Sprintf("%s: %s\x00", string(e.Type), e.Str))
return err
}
func decodeEventDataGRUB(data []byte, pcrIndex PCRIndex, eventType EventType) EventData {
if eventType != EventTypeIPL {
return nil
}
switch pcrIndex {
case 8:
str := string(data)
switch {
case strings.HasPrefix(str, kernelCmdlinePrefix):
return &GrubStringEventData{rawEventData: data, Type: KernelCmdline, Str: strings.TrimSuffix(strings.TrimPrefix(str, kernelCmdlinePrefix), "\x00")}
case strings.HasPrefix(str, grubCmdPrefix):
return &GrubStringEventData{rawEventData: data, Type: GrubCmd, Str: strings.TrimSuffix(strings.TrimPrefix(str, grubCmdPrefix), "\x00")}
default:
return nil
}
case 9:
return StringEventData(data)
default:
panic("unhandled PCR index")
}
}
./github.com/canonical/tcglog-parser/internal/ 0000775 0000000 0000000 00000000000 00000000000 017717 5 ustar 00 0000000 0000000 ./github.com/canonical/tcglog-parser/internal/ioerr/ 0000775 0000000 0000000 00000000000 00000000000 021037 5 ustar 00 0000000 0000000 ./github.com/canonical/tcglog-parser/internal/ioerr/ioerr.go 0000664 0000000 0000000 00000004734 00000000000 022516 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package ioerr
import (
"io"
"unicode"
"unicode/utf8"
"golang.org/x/xerrors"
)
// Return the index of the first %w in format, or -1 if none.
// TODO: handle "%[N]w".
func parsePercentW(format string) int {
// Loosely copied from golang.org/x/xerrors/fmt.go.
n := 0
sz := 0
var isW bool
for i := 0; i < len(format); i += sz {
if format[i] != '%' {
sz = 1
continue
}
// "%%" is not a format directive.
if i+1 < len(format) && format[i+1] == '%' {
sz = 2
continue
}
sz, isW = parsePrintfVerb(format[i:])
if isW {
return n
}
n++
}
return -1
}
// Parse the printf verb starting with a % at s[0].
// Return how many bytes it occupies and whether the verb is 'w'.
func parsePrintfVerb(s string) (int, bool) {
// Assume only that the directive is a sequence of non-letters followed by a single letter.
sz := 0
var r rune
for i := 1; i < len(s); i += sz {
r, sz = utf8.DecodeRuneInString(s[i:])
if unicode.IsLetter(r) {
return i + sz, r == 'w'
}
}
return len(s), false
}
// EOFIsUnexpected converts io.EOF errors into io.ErrUnexpected, which is
// useful when using binary.Read to decode aprts of a structure that aren't
// at the start and when a io.EOF error is not expected.
//
// It can be called in one of 2 ways - either with a single argument which
// must be an error, or with a format string and an arbitrary number of
// arguments. In this second mode, the function is a wrapper around
// xerrors.Errorf.
//
// This only works on raw io.EOF errors - ie, it won't work on errors that
// have been wrapped.
func EOFIsUnexpected(args ...interface{}) error {
switch {
case len(args) > 1:
format := args[0].(string)
idx := parsePercentW(format)
if idx >= 0 {
if err, isErr := args[idx+1].(error); isErr && err == io.EOF {
args[idx+1] = io.ErrUnexpectedEOF
}
}
return xerrors.Errorf(format, args[1:]...)
case len(args) == 1:
switch err := args[0].(type) {
case error:
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
case nil:
return nil
default:
panic("invalid type")
}
default:
panic("no arguments")
}
}
// PassRawEOF is a wrapper around xerrors.Errorf that will return a raw
// io.EOF if this is the error.
func PassRawEOF(format string, args ...interface{}) error {
err := xerrors.Errorf(format, args...)
if xerrors.Is(err, io.EOF) {
return io.EOF
}
return err
}
./github.com/canonical/tcglog-parser/logreader.go 0000664 0000000 0000000 00000010044 00000000000 020375 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"io"
"github.com/canonical/go-tpm2"
)
// LogOptions allows the behaviour of Log to be controlled.
type LogOptions struct {
EnableGrub bool // Enable support for interpreting events recorded by GRUB
EnableSystemdEFIStub bool // Enable support for interpreting events recorded by systemd's EFI linux loader stub
SystemdEFIStubPCR PCRIndex // Specify the PCR that systemd's EFI linux loader stub measures to
}
func fixupSpecIdEvent(event *Event, algorithms AlgorithmIdList) {
for _, alg := range algorithms {
if alg == tpm2.HashAlgorithmSHA1 {
continue
}
if _, ok := event.Digests[alg]; ok {
continue
}
event.Digests[alg] = make(Digest, alg.Size())
}
}
type PlatformType int
const (
PlatformTypeUnknown PlatformType = iota
PlatformTypeBIOS
PlatformTypeEFI
)
// Spec corresponds to the TCG specification that an event log conforms to.
type Spec struct {
PlatformType PlatformType
Major uint8
Minor uint8
Errata uint8
}
// IsBIOS indicates that a log conforms to "TCG PC Client Specific Implementation Specification
// for Conventional BIOS".
// See https://www.trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf
func (s Spec) IsBIOS() bool { return s.PlatformType == PlatformTypeBIOS }
// IsEFI_1_2 indicates that a log conforms to "TCG EFI Platform Specification For TPM Family 1.1 or
// 1.2".
// See https://trustedcomputinggroup.org/wp-content/uploads/TCG_EFI_Platform_1_22_Final_-v15.pdf
func (s Spec) IsEFI_1_2() bool {
return s.PlatformType == PlatformTypeEFI && s.Major == 1 && s.Minor == 2
}
// IsEFI_2 indicates that a log conforms to "TCG PC Client Platform Firmware Profile Specification"
// See https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf
func (s Spec) IsEFI_2() bool {
return s.PlatformType == PlatformTypeEFI && s.Major == 2
}
// Log corresponds to a parsed event log.
type Log struct {
Spec Spec // The specification to which this log conforms
Algorithms AlgorithmIdList // The digest algorithms that appear in the log
Events []*Event // The list of events in the log
}
// ReadLog reads an event log read from r using the supplied options. The log must
// be in the format defined in one of the PC Client Platform Firmware Profile
// specifications. If an error occurs during parsing, this may return an incomplete
// list of events with the error.
func ReadLog(r io.Reader, options *LogOptions) (*Log, error) {
event, err := ReadEvent(r, options)
if err != nil {
return nil, err
}
var spec Spec
var digestSizes []EFISpecIdEventAlgorithmSize
switch d := event.Data.(type) {
case *SpecIdEvent00:
spec = Spec{
PlatformType: PlatformTypeBIOS,
Major: d.SpecVersionMajor,
Minor: d.SpecVersionMinor,
Errata: d.SpecErrata}
case *SpecIdEvent02:
spec = Spec{
PlatformType: PlatformTypeEFI,
Major: d.SpecVersionMajor,
Minor: d.SpecVersionMinor,
Errata: d.SpecErrata}
case *SpecIdEvent03:
spec = Spec{
PlatformType: PlatformTypeEFI,
Major: d.SpecVersionMajor,
Minor: d.SpecVersionMinor,
Errata: d.SpecErrata}
digestSizes = d.DigestSizes
}
var algorithms AlgorithmIdList
if spec.IsEFI_2() {
for _, s := range digestSizes {
if s.AlgorithmId.IsValid() {
algorithms = append(algorithms, s.AlgorithmId)
}
}
} else {
algorithms = AlgorithmIdList{tpm2.HashAlgorithmSHA1}
}
if spec.IsEFI_2() {
fixupSpecIdEvent(event, algorithms)
}
log := &Log{Spec: spec, Algorithms: algorithms, Events: []*Event{event}}
for {
var event *Event
var err error
if spec.IsEFI_2() {
event, err = ReadEventCryptoAgile(r, digestSizes, options)
} else {
event, err = ReadEvent(r, options)
}
switch {
case err == io.EOF:
return log, nil
case err != nil:
return log, err
default:
log.Events = append(log.Events, event)
}
}
}
./github.com/canonical/tcglog-parser/logwriter.go 0000664 0000000 0000000 00000003735 00000000000 020460 0 ustar 00 0000000 0000000 // Copyright 2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"errors"
"fmt"
"io"
"golang.org/x/xerrors"
)
// WriteLog writes an event log to w from the supplied events, in a
// format that can be read again by ReadLog. The first event must be
// a Specification ID event. If the Specification ID event specifies
// a crypto-agile log, the digest algorithms must all be supported by
// go and the specified sizes must all be correct. Each of the
// supplied events must contain a digest for each algorithm. For a
// non crypto-agile log, each of the supplied events must contain
// a SHA-1 digest.
//
// This function is only useful for generating reproducible data for
// use in tests.
func WriteLog(w io.Writer, events []*Event) error {
if len(events) == 0 {
return nil
}
var cryptoAgile bool
var digestSizes []EFISpecIdEventAlgorithmSize
switch d := events[0].Data.(type) {
case *SpecIdEvent00:
_ = d
case *SpecIdEvent02:
_ = d
case *SpecIdEvent03:
cryptoAgile = true
digestSizes = d.DigestSizes
for _, digest := range digestSizes {
if !digest.AlgorithmId.IsValid() {
return fmt.Errorf("unsupported algorithm %v", digest.AlgorithmId)
}
if digest.DigestSize != uint16(digest.AlgorithmId.Size()) {
return fmt.Errorf("invalid size for algorithm %v", digest.AlgorithmId)
}
}
default:
return errors.New("first event must be a spec ID event")
}
for i, event := range events {
if cryptoAgile && i > 0 {
for _, digest := range digestSizes {
if _, ok := event.Digests[digest.AlgorithmId]; !ok {
return fmt.Errorf("event %d has missing digest for algorithm %v", i, digest.AlgorithmId)
}
}
if err := event.WriteCryptoAgile(w); err != nil {
return xerrors.Errorf("cannot write event %d: %w", i, err)
}
} else {
if err := event.Write(w); err != nil {
return xerrors.Errorf("cannot write event %d: %w", i, err)
}
}
}
return nil
}
./github.com/canonical/tcglog-parser/sdefistub.go 0000664 0000000 0000000 00000004522 00000000000 020425 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"bytes"
"crypto"
"encoding/binary"
"io"
)
// SystemdEFIStubCommandline represents a kernel commandline measured by the
// systemd EFI stub linux loader.
type SystemdEFIStubCommandline struct {
rawEventData
Str string
}
func (e *SystemdEFIStubCommandline) String() string {
return "kernel commandline: " + e.Str
}
func (e *SystemdEFIStubCommandline) Write(w io.Writer) error {
if err := binary.Write(w, binary.LittleEndian, convertStringToUtf16(e.Str)); err != nil {
return err
}
_, err := w.Write([]byte{0x00})
return err
}
// ComputeSystemdEFIStubCommandlineDigest computes the digest measured by the systemd EFI stub
// linux loader for the specified kernel commandline. The commandline is supplied to the stub
// via the LoadOptions as a UTF-16 or UCS-2 string and is measured as such before being converted
// to ASCII and passed to the kernel. Note that it assumes that the calling bootloader includes
// a UTF-16 NULL terminator at the end of LoadOptions and sets LoadOptionsSize to StrLen(LoadOptions)+1.
func ComputeSystemdEFIStubCommandlineDigest(alg crypto.Hash, commandline string) []byte {
h := alg.New()
// Both GRUB's chainloader and systemd's EFI bootloader include a UTF-16 NULL terminator at
// the end of LoadOptions and set LoadOptionsSize to StrLen(LoadOptions)+1. The EFI stub loader
// measures LoadOptionsSize number of bytes, meaning that the 2 NULL bytes are measured.
// Include those here.
binary.Write(h, binary.LittleEndian, append(convertStringToUtf16(commandline), 0))
return h.Sum(nil)
}
func decodeEventDataSystemdEFIStub(data []byte, eventType EventType) *SystemdEFIStubCommandline {
if eventType != EventTypeIPL {
return nil
}
// data is a UTF-16 string in little-endian form terminated with a single zero byte,
// so we should have an odd number of bytes.
if len(data)%2 != 1 {
return nil
}
if data[len(data)-1] != 0x00 {
return nil
}
// Omit the zero byte added by the EFI stub and then convert to native byte order.
reader := bytes.NewReader(data[:len(data)-1])
utf16Str := make([]uint16, len(data)/2)
binary.Read(reader, binary.LittleEndian, &utf16Str)
return &SystemdEFIStubCommandline{rawEventData: data, Str: convertUtf16ToString(utf16Str)}
}
./github.com/canonical/tcglog-parser/tcgeventdata.go 0000664 0000000 0000000 00000016416 00000000000 021113 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"bytes"
"crypto"
"encoding/binary"
"errors"
"fmt"
"io"
"strings"
"github.com/canonical/go-tpm2"
"golang.org/x/xerrors"
"github.com/canonical/tcglog-parser/internal/ioerr"
)
var separatorErrorDigests = make(map[tpm2.HashAlgorithmId]tpm2.Digest)
// StringEventData corresponds to event data that is an non-NULL terminated ASCII string.
type StringEventData string
func (d StringEventData) String() string {
return string(d)
}
func (d StringEventData) Write(w io.Writer) error {
_, err := io.WriteString(w, string(d))
return err
}
func (d StringEventData) Bytes() []byte {
return []byte(d)
}
// ComputeStringEventDigest computes the digest associated with the supplied string, for
// events where the digest is a tagged hash of the string. The function assumes that the
// string is ASCII encoded and measured without a terminating NULL byte.
func ComputeStringEventDigest(alg crypto.Hash, str string) []byte {
h := alg.New()
io.WriteString(h, str)
return h.Sum(nil)
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf
// (section 11.3.4 "EV_NO_ACTION Event Types")
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf
// (section 9.4.5 "EV_NO_ACTION Event Types")
func decodeEventDataNoAction(data []byte) (EventData, error) {
r := bytes.NewReader(data)
// Signature field
var sig [16]byte
if _, err := io.ReadFull(r, sig[:]); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
signature := strings.TrimRight(string(sig[:]), "\x00")
switch signature {
case "Spec ID Event00":
out, err := decodeSpecIdEvent00(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode Spec ID Event00 data: %w", err)
}
return out, nil
case "Spec ID Event02":
out, err := decodeSpecIdEvent02(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode Spec ID Event02 data: %w", err)
}
return out, nil
case "Spec ID Event03":
out, err := decodeSpecIdEvent03(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode Spec ID Event03 data: %w", err)
}
return out, nil
case "SP800-155 Event":
out, err := decodeBIMReferenceManifestEvent(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode SP800-155 Event data: %w", err)
}
return out, nil
case "StartupLocality":
out, err := decodeStartupLocalityEvent(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode StartupLocality data: %w", err)
}
return out, nil
default:
return nil, nil
}
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf (section 11.3.3 "EV_ACTION event types")
// https://trustedcomputinggroup.org/wp-content/uploads/PC-ClientSpecific_Platform_Profile_for_TPM_2p0_Systems_v51.pdf (section 9.4.3 "EV_ACTION Event Types")
func decodeEventDataAction(data []byte) StringEventData {
return StringEventData(data)
}
func decodeEventDataHostPlatformSpecificCompactHash(data []byte) StringEventData {
return StringEventData(data)
}
// SeparatorEventData is the event data associated with a EV_SEPARATOR event.
type SeparatorEventData struct {
rawEventData
Value uint32 // The separator value measured to the TPM
}
func NewErrorSeparatorEventData(err []byte) *SeparatorEventData {
return &SeparatorEventData{rawEventData: err, Value: SeparatorEventErrorValue}
}
// IsError indicates that this event was associated with an error condition.
// The value returned from Bytes() contains an implementation defined indication
// of the actual error.
func (e *SeparatorEventData) IsError() bool {
return e.Value == SeparatorEventErrorValue
}
func (e *SeparatorEventData) String() string {
if !e.IsError() {
return ""
}
return fmt.Sprintf("ERROR: 0x%x", e.rawEventData)
}
func (e *SeparatorEventData) Write(w io.Writer) error {
switch e.Value {
case SeparatorEventNormalValue, SeparatorEventAltNormalValue:
return binary.Write(w, binary.LittleEndian, e.Value)
case SeparatorEventErrorValue:
_, err := w.Write(e.rawEventData)
return err
default:
return errors.New("invalid value")
}
}
// ComputeSeparatorEventDigest computes the digest associated with the separator event. The value
// argument should be one of SeparatorEventNormalValue, SeparatorEventAltNormalValue or
// SeparatorEventErrorValue.
func ComputeSeparatorEventDigest(alg crypto.Hash, value uint32) []byte {
h := alg.New()
binary.Write(h, binary.LittleEndian, value)
return h.Sum(nil)
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf
// (section 3.3.2.2 2 Error Conditions" , section 8.2.3 "Measuring Boot Events")
// https://trustedcomputinggroup.org/wp-content/uploads/PC-ClientSpecific_Platform_Profile_for_TPM_2p0_Systems_v51.pdf:
// (section 2.3.2 "Error Conditions", section 2.3.4 "PCR Usage", section 7.2
// "Procedure for Pre-OS to OS-Present Transition")
func decodeEventDataSeparator(data []byte, digests DigestMap) (*SeparatorEventData, error) {
var alg tpm2.HashAlgorithmId
for a, _ := range digests {
if !alg.IsValid() || a.Size() > alg.Size() {
alg = a
}
}
errorDigest, ok := separatorErrorDigests[alg]
if !ok {
h := alg.NewHash()
binary.Write(h, binary.LittleEndian, SeparatorEventErrorValue)
separatorErrorDigests[alg] = h.Sum(nil)
errorDigest = separatorErrorDigests[alg]
}
if bytes.Equal(digests[alg], errorDigest) {
return &SeparatorEventData{rawEventData: data, Value: SeparatorEventErrorValue}, nil
}
if len(data) != binary.Size(uint32(0)) {
return nil, errors.New("data is the wrong size")
}
value := binary.LittleEndian.Uint32(data)
switch value {
case SeparatorEventNormalValue, SeparatorEventErrorValue, SeparatorEventAltNormalValue:
default:
return nil, fmt.Errorf("invalid separator value: %d", value)
}
return &SeparatorEventData{rawEventData: data, Value: value}, nil
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf (section 11.3.1 "Event Types")
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_EFI_Platform_1_22_Final_-v15.pdf (section 7.2 "Event Types")
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf (section 9.4.1 "Event Types")
func decodeEventDataTCG(data []byte, pcrIndex PCRIndex, eventType EventType, digests DigestMap) (out EventData, err error) {
switch eventType {
case EventTypeNoAction:
return decodeEventDataNoAction(data)
case EventTypeSeparator:
return decodeEventDataSeparator(data, digests)
case EventTypeAction, EventTypeEFIAction:
return decodeEventDataAction(data), nil
case EventTypeCompactHash:
if pcrIndex == 6 {
return decodeEventDataHostPlatformSpecificCompactHash(data), nil
}
case EventTypeEFIVariableDriverConfig, EventTypeEFIVariableBoot, EventTypeEFIVariableAuthority, EventTypeEFIVariableBoot2:
return decodeEventDataEFIVariable(data)
case EventTypeEFIBootServicesApplication, EventTypeEFIBootServicesDriver, EventTypeEFIRuntimeServicesDriver:
return decodeEventDataEFIImageLoad(data)
case EventTypeEFIGPTEvent:
return decodeEventDataEFIGPT(data)
default:
}
if err != nil {
err = xerrors.Errorf("cannot decode %v event data: %w", eventType, err)
}
return
}
./github.com/canonical/tcglog-parser/tcgeventdata_bios.go 0000664 0000000 0000000 00000004616 00000000000 022126 0 ustar 00 0000000 0000000 // Copyright 2019-2021 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"github.com/canonical/tcglog-parser/internal/ioerr"
)
type rawSpecIdEvent00Hdr struct {
PlatformClass uint32
SpecVersionMinor uint8
SpecVersionMajor uint8
SpecErrata uint8
Reserved uint8
VendorInfoSize uint8
}
// SpecIdEvent00 corresponds to the TCG_PCClientSpecIdEventStruct type and is the
// event data for a Specification ID Version EV_NO_ACTION event for BIOS platforms.
type SpecIdEvent00 struct {
rawEventData
PlatformClass uint32
SpecVersionMinor uint8
SpecVersionMajor uint8
SpecErrata uint8
VendorInfo []byte
}
func (e *SpecIdEvent00) String() string {
return fmt.Sprintf("PCClientSpecIdEvent{ platformClass=%d, specVersionMinor=%d, specVersionMajor=%d, specErrata=%d }",
e.PlatformClass, e.SpecVersionMinor, e.SpecVersionMajor, e.SpecErrata)
}
func (e *SpecIdEvent00) Write(w io.Writer) error {
vendorInfoSize := len(e.VendorInfo)
if vendorInfoSize > math.MaxUint8 {
return errors.New("VendorInfo too large")
}
var signature [16]byte
copy(signature[:], []byte("Spec ID Event00"))
if _, err := w.Write(signature[:]); err != nil {
return err
}
spec := rawSpecIdEvent00Hdr{
PlatformClass: e.PlatformClass,
SpecVersionMinor: e.SpecVersionMinor,
SpecVersionMajor: e.SpecVersionMajor,
SpecErrata: e.SpecErrata,
VendorInfoSize: uint8(vendorInfoSize)}
if err := binary.Write(w, binary.LittleEndian, &spec); err != nil {
return err
}
_, err := w.Write(e.VendorInfo)
return err
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf
// (section 11.3.4.1 "Specification Event")
func decodeSpecIdEvent00(data []byte, r io.Reader) (out *SpecIdEvent00, err error) {
var spec rawSpecIdEvent00Hdr
if err := binary.Read(r, binary.LittleEndian, &spec); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out = &SpecIdEvent00{
rawEventData: data,
PlatformClass: spec.PlatformClass,
SpecVersionMinor: spec.SpecVersionMinor,
SpecVersionMajor: spec.SpecVersionMajor,
SpecErrata: spec.SpecErrata,
VendorInfo: make([]byte, spec.VendorInfoSize)}
if _, err := io.ReadFull(r, out.VendorInfo); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return out, nil
}
./github.com/canonical/tcglog-parser/tcgeventdata_efi.go 0000664 0000000 0000000 00000041031 00000000000 021725 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"bytes"
"crypto"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"math"
"strings"
"unicode/utf8"
"github.com/canonical/go-efilib"
"github.com/canonical/go-tpm2"
"golang.org/x/xerrors"
"github.com/canonical/tcglog-parser/internal/ioerr"
)
var (
surr1 uint16 = 0xd800
surr2 uint16 = 0xdc00
surr3 uint16 = 0xe000
)
// UEFI_VARIABLE_DATA specifies the number of *characters* for a UTF-16 sequence rather than the size of
// the buffer. Extract a UTF-16 sequence of the correct length, given a buffer and the number of characters.
// The returned buffer can be passed to utf16.Decode.
func extractUTF16Buffer(r io.ReadSeeker, nchars uint64) ([]uint16, error) {
var out []uint16
for i := nchars; i > 0; i-- {
var c uint16
if err := binary.Read(r, binary.LittleEndian, &c); err != nil {
return nil, err
}
out = append(out, c)
if c >= surr1 && c < surr2 {
if err := binary.Read(r, binary.LittleEndian, &c); err != nil {
return nil, err
}
if c < surr2 || c >= surr3 {
// Invalid surrogate sequence. utf16.Decode doesn't consume this
// byte when inserting the replacement char
if _, err := r.Seek(-1, io.SeekCurrent); err != nil {
return nil, err
}
continue
}
// Valid surrogate sequence
out = append(out, c)
}
}
return out, nil
}
type rawSpecIdEvent02Hdr struct {
PlatformClass uint32
SpecVersionMinor uint8
SpecVersionMajor uint8
SpecErrata uint8
UintnSize uint8
VendorInfoSize uint8
}
// SpecIdEvent02 corresponds to the TCG_EfiSpecIdEventStruct type and is the
// event data for a Specification ID Version EV_NO_ACTION event on EFI platforms
// for TPM family 1.2.
type SpecIdEvent02 struct {
rawEventData
PlatformClass uint32
SpecVersionMinor uint8
SpecVersionMajor uint8
SpecErrata uint8
UintnSize uint8
VendorInfo []byte
}
func (e *SpecIdEvent02) String() string {
return fmt.Sprintf("EfiSpecIdEvent{ platformClass=%d, specVersionMinor=%d, specVersionMajor=%d, specErrata=%d, uintnSize=%d }",
e.PlatformClass, e.SpecVersionMinor, e.SpecVersionMajor, e.SpecErrata, e.UintnSize)
}
func (e *SpecIdEvent02) Write(w io.Writer) error {
vendorInfoSize := len(e.VendorInfo)
if vendorInfoSize > math.MaxUint8 {
return errors.New("VendorInfo too large")
}
var signature [16]byte
copy(signature[:], []byte("Spec ID Event02"))
if _, err := w.Write(signature[:]); err != nil {
return err
}
spec := rawSpecIdEvent02Hdr{
PlatformClass: e.PlatformClass,
SpecVersionMinor: e.SpecVersionMinor,
SpecVersionMajor: e.SpecVersionMajor,
SpecErrata: e.SpecErrata,
UintnSize: e.UintnSize,
VendorInfoSize: uint8(vendorInfoSize)}
if err := binary.Write(w, binary.LittleEndian, &spec); err != nil {
return err
}
_, err := w.Write(e.VendorInfo)
return err
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_EFI_Platform_1_22_Final_-v15.pdf
// (section 7.4 "EV_NO_ACTION Event Types")
func decodeSpecIdEvent02(data []byte, r io.Reader) (out *SpecIdEvent02, err error) {
var spec rawSpecIdEvent02Hdr
if err := binary.Read(r, binary.LittleEndian, &spec); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out = &SpecIdEvent02{
rawEventData: data,
PlatformClass: spec.PlatformClass,
SpecVersionMinor: spec.SpecVersionMinor,
SpecVersionMajor: spec.SpecVersionMajor,
SpecErrata: spec.SpecErrata,
UintnSize: spec.UintnSize,
VendorInfo: make([]byte, spec.VendorInfoSize)}
if _, err := io.ReadFull(r, out.VendorInfo); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return out, nil
}
// EFISpecIdEventAlgorithmSize represents a digest algorithm and its length and corresponds to the
// TCG_EfiSpecIdEventAlgorithmSize type.
type EFISpecIdEventAlgorithmSize struct {
AlgorithmId tpm2.HashAlgorithmId
DigestSize uint16
}
type rawSpecIdEvent03Hdr struct {
PlatformClass uint32
SpecVersionMinor uint8
SpecVersionMajor uint8
SpecErrata uint8
UintnSize uint8
NumberOfAlgorithms uint32
}
// SpecIdEvent03 corresponds to the TCG_EfiSpecIdEvent type and is the
// event data for a Specification ID Version EV_NO_ACTION event on EFI platforms
// for TPM family 2.0.
type SpecIdEvent03 struct {
rawEventData
PlatformClass uint32
SpecVersionMinor uint8
SpecVersionMajor uint8
SpecErrata uint8
UintnSize uint8
DigestSizes []EFISpecIdEventAlgorithmSize // The digest algorithms contained within this log
VendorInfo []byte
}
func (e *SpecIdEvent03) String() string {
var builder bytes.Buffer
fmt.Fprintf(&builder, "EfiSpecIdEvent{ platformClass=%d, specVersionMinor=%d, specVersionMajor=%d, specErrata=%d, uintnSize=%d, digestSizes=[",
e.PlatformClass, e.SpecVersionMinor, e.SpecVersionMajor, e.SpecErrata, e.UintnSize)
for i, algSize := range e.DigestSizes {
if i > 0 {
builder.WriteString(", ")
}
fmt.Fprintf(&builder, "{ algorithmId=0x%04x, digestSize=%d }",
uint16(algSize.AlgorithmId), algSize.DigestSize)
}
builder.WriteString("] }")
return builder.String()
}
func (e *SpecIdEvent03) Write(w io.Writer) error {
vendorInfoSize := len(e.VendorInfo)
if vendorInfoSize > math.MaxUint8 {
return errors.New("VendorInfo too large")
}
var signature [16]byte
copy(signature[:], []byte("Spec ID Event03"))
if _, err := w.Write(signature[:]); err != nil {
return err
}
spec := rawSpecIdEvent03Hdr{
PlatformClass: e.PlatformClass,
SpecVersionMinor: e.SpecVersionMinor,
SpecVersionMajor: e.SpecVersionMajor,
SpecErrata: e.SpecErrata,
UintnSize: e.UintnSize,
NumberOfAlgorithms: uint32(len(e.DigestSizes))}
if err := binary.Write(w, binary.LittleEndian, &spec); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, e.DigestSizes); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, uint8(vendorInfoSize)); err != nil {
return err
}
_, err := w.Write(e.VendorInfo)
return err
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf
// (secion 9.4.5.1 "Specification ID Version Event")
func decodeSpecIdEvent03(data []byte, r io.Reader) (out *SpecIdEvent03, err error) {
var spec rawSpecIdEvent03Hdr
if err := binary.Read(r, binary.LittleEndian, &spec); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out = &SpecIdEvent03{
rawEventData: data,
PlatformClass: spec.PlatformClass,
SpecVersionMinor: spec.SpecVersionMinor,
SpecVersionMajor: spec.SpecVersionMajor,
SpecErrata: spec.SpecErrata,
UintnSize: spec.UintnSize}
if spec.NumberOfAlgorithms < 1 {
return nil, errors.New("numberOfAlgorithms is zero")
}
out.DigestSizes = make([]EFISpecIdEventAlgorithmSize, spec.NumberOfAlgorithms)
if err := binary.Read(r, binary.LittleEndian, out.DigestSizes); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
for _, d := range out.DigestSizes {
if d.AlgorithmId.IsValid() && d.AlgorithmId.Size() != int(d.DigestSize) {
return nil, fmt.Errorf("digestSize for algorithmId %v does not match expected size", d.AlgorithmId)
}
}
var vendorInfoSize uint8
if err := binary.Read(r, binary.LittleEndian, &vendorInfoSize); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
out.VendorInfo = make([]byte, vendorInfoSize)
if _, err := io.ReadFull(r, out.VendorInfo); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return out, nil
}
// StartupLocalityEventData is the event data for a StartupLocality EV_NO_ACTION event.
type StartupLocalityEventData struct {
rawEventData
StartupLocality uint8
}
func (e *StartupLocalityEventData) String() string {
return fmt.Sprintf("EfiStartupLocalityEvent{ StartupLocality: %d }", e.StartupLocality)
}
func (e *StartupLocalityEventData) Write(w io.Writer) error {
var signature [16]byte
copy(signature[:], []byte("StartupLocality"))
if _, err := w.Write(signature[:]); err != nil {
return err
}
return binary.Write(w, binary.LittleEndian, e.StartupLocality)
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf
// (section 9.4.5.3 "Startup Locality Event")
func decodeStartupLocalityEvent(data []byte, r io.Reader) (*StartupLocalityEventData, error) {
var locality uint8
if err := binary.Read(r, binary.LittleEndian, &locality); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return &StartupLocalityEventData{rawEventData: data, StartupLocality: locality}, nil
}
// SP800_155_PlatformIdEventData corresponds to the event data for a SP800-155-Event
// EV_NO_ACTION event
type SP800_155_PlatformIdEventData struct {
rawEventData
VendorId uint32
ReferenceManifestGuid efi.GUID
}
func (e *SP800_155_PlatformIdEventData) String() string {
return fmt.Sprintf("Sp800_155_PlatformId_Event{ VendorId: %d, ReferenceManifestGuid: %s }", e.VendorId, e.ReferenceManifestGuid)
}
func (e *SP800_155_PlatformIdEventData) Write(w io.Writer) error {
var signature [16]byte
copy(signature[:], []byte("SP800-155 Event"))
if _, err := w.Write(signature[:]); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, e.VendorId); err != nil {
return err
}
_, err := w.Write(e.ReferenceManifestGuid[:])
return err
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf
// (section 9.4.5.2 "BIOS Integrity Measurement Reference Manifest Event")
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_EFI_Platform_1_22_Final_-v15.pdf
// (section 7.4 "EV_NO_ACTION Event Types")
func decodeBIMReferenceManifestEvent(data []byte, r io.Reader) (*SP800_155_PlatformIdEventData, error) {
var d struct {
VendorId uint32
Guid efi.GUID
}
if err := binary.Read(r, binary.LittleEndian, &d); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return &SP800_155_PlatformIdEventData{rawEventData: data, VendorId: d.VendorId, ReferenceManifestGuid: d.Guid}, nil
}
// EFIVariableData corresponds to the EFI_VARIABLE_DATA type and is the event data associated with the measurement of an
// EFI variable.
type EFIVariableData struct {
rawEventData
VariableName efi.GUID
UnicodeName string
VariableData []byte
}
func (e *EFIVariableData) String() string {
return fmt.Sprintf("UEFI_VARIABLE_DATA{ VariableName: %s, UnicodeName: \"%s\", VariableData:\n\t%s}",
e.VariableName, e.UnicodeName, strings.Replace(hex.Dump(e.VariableData), "\n", "\n\t", -1))
}
func (e *EFIVariableData) Write(w io.Writer) error {
if _, err := w.Write(e.VariableName[:]); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, uint64(utf8.RuneCount([]byte(e.UnicodeName)))); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, uint64(len(e.VariableData))); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, convertStringToUtf16(e.UnicodeName)); err != nil {
return err
}
_, err := w.Write(e.VariableData)
return err
}
// ComputeEFIVariableDataDigest computes the EFI_VARIABLE_DATA digest associated with the supplied
// parameters
func ComputeEFIVariableDataDigest(alg crypto.Hash, name string, guid efi.GUID, data []byte) []byte {
h := alg.New()
varData := EFIVariableData{VariableName: guid, UnicodeName: name, VariableData: data}
varData.Write(h)
return h.Sum(nil)
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_EFI_Platform_1_22_Final_-v15.pdf (section 7.8 "Measuring EFI Variables")
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf (section 9.2.6 "Measuring UEFI Variables")
func decodeEventDataEFIVariable(data []byte) (*EFIVariableData, error) {
r := bytes.NewReader(data)
d := &EFIVariableData{rawEventData: data}
variableName, err := efi.ReadGUID(r)
if err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
d.VariableName = variableName
var unicodeNameLength uint64
if err := binary.Read(r, binary.LittleEndian, &unicodeNameLength); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
var variableDataLength uint64
if err := binary.Read(r, binary.LittleEndian, &variableDataLength); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
utf16Name, err := extractUTF16Buffer(r, unicodeNameLength)
if err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
d.UnicodeName = convertUtf16ToString(utf16Name)
d.VariableData = make([]byte, variableDataLength)
if _, err := io.ReadFull(r, d.VariableData); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return d, nil
}
type rawEFIImageLoadEventHdr struct {
LocationInMemory efi.PhysicalAddress
LengthInMemory uint64
LinkTimeAddress uint64
LengthOfDevicePath uint64
}
type EFIImageLoadEvent struct {
rawEventData
LocationInMemory efi.PhysicalAddress
LengthInMemory uint64
LinkTimeAddress uint64
DevicePath efi.DevicePath
}
func (e *EFIImageLoadEvent) String() string {
return fmt.Sprintf("UEFI_IMAGE_LOAD_EVENT{ ImageLocationInMemory: 0x%016x, ImageLengthInMemory: %d, "+
"ImageLinkTimeAddress: 0x%016x, DevicePath: %s }", e.LocationInMemory, e.LengthInMemory, e.LinkTimeAddress, e.DevicePath)
}
func (e *EFIImageLoadEvent) Write(w io.Writer) error {
dpw := new(bytes.Buffer)
if err := e.DevicePath.Write(dpw); err != nil {
return xerrors.Errorf("cannot write device path: %w", err)
}
ev := rawEFIImageLoadEventHdr{
LocationInMemory: e.LocationInMemory,
LengthInMemory: e.LengthInMemory,
LinkTimeAddress: e.LinkTimeAddress,
LengthOfDevicePath: uint64(dpw.Len())}
if err := binary.Write(w, binary.LittleEndian, &ev); err != nil {
return err
}
_, err := dpw.WriteTo(w)
return err
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_EFI_Platform_1_22_Final_-v15.pdf (section 4 "Measuring PE/COFF Image Files")
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf (section 9.2.3 "UEFI_IMAGE_LOAD_EVENT Structure")
func decodeEventDataEFIImageLoad(data []byte) (*EFIImageLoadEvent, error) {
r := bytes.NewReader(data)
var e rawEFIImageLoadEventHdr
if err := binary.Read(r, binary.LittleEndian, &e); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
lr := io.LimitReader(r, int64(e.LengthOfDevicePath))
path, err := efi.ReadDevicePath(lr)
if err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
return &EFIImageLoadEvent{
rawEventData: data,
LocationInMemory: e.LocationInMemory,
LengthInMemory: e.LengthInMemory,
LinkTimeAddress: e.LinkTimeAddress,
DevicePath: path}, nil
}
// EFIGPTData corresponds to UEFI_GPT_DATA and is the event data for EV_EFI_GPT_EVENT events.
type EFIGPTData struct {
rawEventData
Hdr efi.PartitionTableHeader
Partitions []*efi.PartitionEntry
}
func (e *EFIGPTData) String() string {
var builder bytes.Buffer
fmt.Fprintf(&builder, "UEFI_GPT_DATA{\n\tHdr: %s,\n\tPartitions: [", &e.Hdr)
for _, part := range e.Partitions {
fmt.Fprintf(&builder, "\n\t\t%s", part)
}
fmt.Fprintf(&builder, "\n\t]\n}")
return builder.String()
}
func (e *EFIGPTData) Write(w io.Writer) error {
if err := e.Hdr.Write(w); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, uint64(len(e.Partitions))); err != nil {
return err
}
for _, entry := range e.Partitions {
w2 := new(bytes.Buffer)
if err := entry.Write(w2); err != nil {
return err
}
if w2.Len() > int(e.Hdr.SizeOfPartitionEntry) {
return errors.New("SizeOfPartitionEntry too small")
}
b := make([]byte, e.Hdr.SizeOfPartitionEntry)
copy(b, w2.Bytes())
if _, err := w.Write(b); err != nil {
return err
}
}
return nil
}
func decodeEventDataEFIGPT(data []byte) (*EFIGPTData, error) {
r := bytes.NewReader(data)
d := &EFIGPTData{rawEventData: data}
// UEFI_GPT_DATA.UEFIPartitionHeader
hdr, err := efi.ReadPartitionTableHeader(r, false)
if err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
d.Hdr = *hdr
// UEFI_GPT_DATA.NumberOfPartitions
var numberOfParts uint64
if err := binary.Read(r, binary.LittleEndian, &numberOfParts); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
if numberOfParts > math.MaxUint32 {
return nil, errors.New("invalid EFI_GPT_DATA.NumberOfPartitons")
}
// UEFI_GPT_DATA.Partitions
partitions, err := efi.ReadPartitionEntries(r, uint32(numberOfParts), hdr.SizeOfPartitionEntry)
if err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
d.Partitions = partitions
return d, nil
}
// ComputeEFIGPTDataDigest computes a UEFI_GPT_DATA digest from the supplied data.
func ComputeEFIGPTDataDigest(alg crypto.Hash, data *EFIGPTData) ([]byte, error) {
h := alg.New()
if err := data.Write(h); err != nil {
return nil, err
}
return h.Sum(nil), nil
}
./github.com/canonical/tcglog-parser/types.go 0000664 0000000 0000000 00000006225 00000000000 017603 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"fmt"
"github.com/canonical/go-tpm2"
)
// PCRIndex corresponds to the index of a PCR on the TPM.
type PCRIndex uint32
// EventType corresponds to the type of an event in an event log.
type EventType uint32
// Digest is the result of hashing some data.
type Digest []byte
// DigestMap is a map of algorithms to digests.
type DigestMap map[tpm2.HashAlgorithmId]Digest
func (e EventType) String() string {
switch e {
case EventTypePrebootCert:
return "EV_PREBOOT_CERT"
case EventTypePostCode:
return "EV_POST_CODE"
case EventTypeNoAction:
return "EV_NO_ACTION"
case EventTypeSeparator:
return "EV_SEPARATOR"
case EventTypeAction:
return "EV_ACTION"
case EventTypeEventTag:
return "EV_EVENT_TAG"
case EventTypeSCRTMContents:
return "EV_S_CRTM_CONTENTS"
case EventTypeSCRTMVersion:
return "EV_S_CRTM_VERSION"
case EventTypeCPUMicrocode:
return "EV_CPU_MICROCODE"
case EventTypePlatformConfigFlags:
return "EV_PLATFORM_CONFIG_FLAGS"
case EventTypeTableOfDevices:
return "EV_TABLE_OF_DEVICES"
case EventTypeCompactHash:
return "EV_COMPACT_HASH"
case EventTypeIPL:
return "EV_IPL"
case EventTypeIPLPartitionData:
return "EV_IPL_PARTITION_DATA"
case EventTypeNonhostCode:
return "EV_NONHOST_CODE"
case EventTypeNonhostConfig:
return "EV_NONHOST_CONFIG"
case EventTypeNonhostInfo:
return "EV_NONHOST_INFO"
case EventTypeOmitBootDeviceEvents:
return "EV_OMIT_BOOT_DEVICE_EVENTS"
case EventTypeEFIVariableDriverConfig:
return "EV_EFI_VARIABLE_DRIVER_CONFIG"
case EventTypeEFIVariableBoot:
return "EV_EFI_VARIABLE_BOOT"
case EventTypeEFIBootServicesApplication:
return "EV_EFI_BOOT_SERVICES_APPLICATION"
case EventTypeEFIBootServicesDriver:
return "EV_EFI_BOOT_SERVICES_DRIVER"
case EventTypeEFIRuntimeServicesDriver:
return "EV_EFI_RUNTIME_SERVICES_DRIVER"
case EventTypeEFIGPTEvent:
return "EF_EFI_GPT_EVENT"
case EventTypeEFIAction:
return "EV_EFI_ACTION"
case EventTypeEFIPlatformFirmwareBlob:
return "EV_EFI_PLATFORM_FIRMWARE_BLOB"
case EventTypeEFIHandoffTables:
return "EV_EFI_HANDOFF_TABLES"
case EventTypeEFIPlatformFirmwareBlob2:
return "EV_EFI_PLATFORM_FIRMWARE_BLOB2"
case EventTypeEFIHandoffTables2:
return "EV_EFI_HANDOFF_TABLES2"
case EventTypeEFIVariableBoot2:
return "EV_EFI_VARIABLE_BOOT2"
case EventTypeEFIHCRTMEvent:
return "EV_EFI_HCRTM_EVENT"
case EventTypeEFIVariableAuthority:
return "EV_EFI_VARIABLE_AUTHORITY"
case EventTypeEFISPDMFirmwareBlob:
return "EV_EFI_SPDM_FIRMWARE_BLOB"
case EventTypeEFISPDMFirmwareConfig:
return "EV_EFI_SPDM_FIRMWARE_CONFIG"
default:
return fmt.Sprintf("%08x", uint32(e))
}
}
func (e EventType) Format(s fmt.State, f rune) {
switch f {
case 's', 'v':
fmt.Fprintf(s, "%s", e.String())
default:
fmt.Fprintf(s, makeDefaultFormatter(s, f), uint32(e))
}
}
// AlgorithmListId is a slice of tpm2.HashAlgorithmId values,
type AlgorithmIdList []tpm2.HashAlgorithmId
func (l AlgorithmIdList) Contains(a tpm2.HashAlgorithmId) bool {
for _, alg := range l {
if alg == a {
return true
}
}
return false
}
./github.com/canonical/tcglog-parser/utils.go 0000664 0000000 0000000 00000002150 00000000000 017570 0 ustar 00 0000000 0000000 // Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"bytes"
"fmt"
"unicode/utf16"
"unicode/utf8"
)
func makeDefaultFormatter(s fmt.State, f rune) string {
var builder bytes.Buffer
builder.WriteString("%%")
for _, flag := range [...]int{'+', '-', '#', ' ', '0'} {
if s.Flag(flag) {
fmt.Fprintf(&builder, "%c", flag)
}
}
if width, ok := s.Width(); ok {
fmt.Fprintf(&builder, "%d", width)
}
if prec, ok := s.Precision(); ok {
fmt.Fprintf(&builder, ".%d", prec)
}
fmt.Fprintf(&builder, "%c", f)
return builder.String()
}
func convertStringToUtf16(str string) []uint16 {
var unicodePoints []rune
for len(str) > 0 {
r, s := utf8.DecodeRuneInString(str)
unicodePoints = append(unicodePoints, r)
str = str[s:]
}
return utf16.Encode(unicodePoints)
}
func convertUtf16ToString(u []uint16) string {
var utf8Str []byte
for _, r := range utf16.Decode(u) {
utf8Char := make([]byte, utf8.RuneLen(r))
utf8.EncodeRune(utf8Char, r)
utf8Str = append(utf8Str, utf8Char...)
}
return string(utf8Str)
}
./github.com/godbus/ 0000775 0000000 0000000 00000000000 00000000000 012666 5 ustar 00 0000000 0000000 ./github.com/godbus/dbus/ 0000775 0000000 0000000 00000000000 00000000000 013623 5 ustar 00 0000000 0000000 ./github.com/godbus/dbus/.travis.yml 0000664 0000000 0000000 00000000767 00000000000 015746 0 ustar 00 0000000 0000000 dist: precise
language: go
go_import_path: github.com/godbus/dbus
sudo: true
go:
- 1.6.3
- 1.7.3
- tip
env:
global:
matrix:
- TARGET=amd64
- TARGET=arm64
- TARGET=arm
- TARGET=386
- TARGET=ppc64le
matrix:
fast_finish: true
allow_failures:
- go: tip
exclude:
- go: tip
env: TARGET=arm
- go: tip
env: TARGET=arm64
- go: tip
env: TARGET=386
- go: tip
env: TARGET=ppc64le
addons:
apt:
packages:
- dbus
- dbus-x11
before_install:
./github.com/godbus/dbus/CONTRIBUTING.md 0000664 0000000 0000000 00000002714 00000000000 016060 0 ustar 00 0000000 0000000 # How to Contribute
## Getting Started
- Fork the repository on GitHub
- Read the [README](README.markdown) for build and test instructions
- Play with the project, submit bugs, submit patches!
## Contribution Flow
This is a rough outline of what a contributor's workflow looks like:
- Create a topic branch from where you want to base your work (usually master).
- Make commits of logical units.
- Make sure your commit messages are in the proper format (see below).
- Push your changes to a topic branch in your fork of the repository.
- Make sure the tests pass, and add any new tests as appropriate.
- Submit a pull request to the original repository.
Thanks for your contributions!
### Format of the Commit Message
We follow a rough convention for commit messages that is designed to answer two
questions: what changed and why. The subject line should feature the what and
the body of the commit should describe the why.
```
scripts: add the test-cluster command
this uses tmux to setup a test cluster that you can easily kill and
start for debugging.
Fixes #38
```
The format can be described more formally as follows:
```
: