Skip to content

Commit

Permalink
Introduce Direct TPM2 API (#266)
Browse files Browse the repository at this point in the history
* Introduce Direct TPM2 API

This commit introduces a new method of interacting with a TPM 2.0.
Instead of plumbing each TPM API into one or more Go functions, this
code defines structures for every TPM 2.0 command request and response.
These map 1:1 with the actual command parameters comprehended by the
TPM, so any invocation of any command is possible (once all the command
structures are written).

This commit introduces enough of the TPM 2.0 API surface to put together
some interesting end-to-end tests, mostly around sealing.

Another objective of the Direct API is to facilitate use of the TPM's
session-based command transport features (e.g., audit and encryption
sessions). See the test code for examples of how to easily use these
APIs to, e.g., set up an EK-salted session for session-encrypted unseal.

Change-Id: I1549dd596869d79ddd41ff3c5f9ffdadc9628ed4

* fix problems identified by go vet

* fix some more issues identified by go vet

* fix some more issues surfaced by vet

* fix more go vet issues

* one last round of go vet fixes

* Use subpackages and put the Execute function on the command types (#1)

* WIP: put stuff into subpackages to make the names nice

* use internal monolithic package to avoid cycles

* complete the tpm2 package

* fix test references to renamed types

* switch to command.Execute pattern

* fix some unkeyed field values detected by go vet

* stop panicking from Hash

* Draft implementation of Joe's embedded-reserved-field bitwise solution

* Turn command handles into an interface to avoid caller stuttering
  • Loading branch information
chrisfenner authored Feb 19, 2022
1 parent 99c5a9b commit 0b55c34
Show file tree
Hide file tree
Showing 34 changed files with 7,881 additions and 51 deletions.
Binary file added .tempnotes.txt.swp
Binary file not shown.
46 changes: 46 additions & 0 deletions direct/helpers/crypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package helpers

import (
"crypto/elliptic"
"crypto/rsa"
"math/big"

"github.com/google/go-tpm/direct/structures/tpm2b"
"github.com/google/go-tpm/direct/structures/tpms"
)

// RSAPub converts a TPM RSA public key into one recognized by the rsa package.
func RSAPub(parms *tpms.RSAParms, pub *tpm2b.PublicKeyRSA) (*rsa.PublicKey, error) {
result := rsa.PublicKey{
N: big.NewInt(0).SetBytes(pub.Buffer),
E: int(parms.Exponent),
}
// TPM considers 65537 to be the default RSA public exponent, and 0 in
// the parms
// indicates so.
if result.E == 0 {
result.E = 65537
}
return &result, nil
}

// ECDHPub is a convenience wrapper around the necessary info to perform point
// multiplication with the elliptic package.
type ECDHPub struct {
Curve elliptic.Curve
X, Y *big.Int
}

// ECCPub converts a TPM ECC public key into one recognized by the elliptic
// package's point-multiplication functions, for use in ECDH.
func ECCPub(parms *tpms.ECCParms, pub *tpms.ECCPoint) (*ECDHPub, error) {
curve, err := parms.CurveID.Curve()
if err != nil {
return nil, err
}
return &ECDHPub{
Curve: curve,
X: big.NewInt(0).SetBytes(pub.X.Buffer),
Y: big.NewInt(0).SetBytes(pub.Y.Buffer),
}, nil
}
14 changes: 14 additions & 0 deletions direct/helpers/names.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package helpers

import (
"encoding/binary"

"github.com/google/go-tpm/direct/structures/tpm"
)

// PrimaryHandleName returns the TPM Name of a primary handle.
func PrimaryHandleName(h tpm.Handle) []byte {
result := make([]byte, 4)
binary.BigEndian.PutUint32(result, uint32(h))
return result
}
12 changes: 12 additions & 0 deletions direct/helpers/wrappers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package helpers

import "github.com/google/go-tpm/direct/structures/tpm"

// This file provides wrapper functions for concrete types used by tpm2, for
// setting union member pointers.

// NewKeyBits allocates and returns the address of a new tpm.KeyBits.
func NewKeyBits(v tpm.KeyBits) *tpm.KeyBits { return &v }

// NewAlgID allocates and returns the address of a new tpm.AlgID.
func NewAlgID(v tpm.AlgID) *tpm.AlgID { return &v }
83 changes: 83 additions & 0 deletions direct/structures/internal/bitfield.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package internal

import (
"fmt"
)

// Bitfield represents a TPM bitfield (i.e., TPMA_*) type.
type Bitfield interface {
// Length returns the length of the bitfield.
Length() int
}

// BitGetter represents a TPM bitfield (i.e., TPMA_*) type that can be read.
type BitGetter interface {
Bitfield
// GetReservedBit returns the value of the given reserved bit.
// If the bit is not reserved, returns false.
GetReservedBit(pos int) bool
}

// BitSetter represents a TPM bitfield (i.e., TPMA_*) type that can be written.
type BitSetter interface {
Bitfield
// GetReservedBit sets the value of the given reserved bit.
SetReservedBit(pos int, val bool)
}

func checkPos(pos int, len int) {
if pos >= len || pos < 0 {
panic(fmt.Errorf("bit %d out of range for %d-bit field", pos, len))
}
}

// bitfield8 represents an 8-bit bitfield which may have reserved bits.
// 8-bit TPMA_* types embed this one, and the reserved bits are stored in it.
type bitfield8 uint8

// Length implements the Bitfield interface.
func (bitfield8) Length() int {
return 8
}

// GetReservedBit implements the BitGetter interface.
func (r bitfield8) GetReservedBit(pos int) bool {
checkPos(pos, 8)
return r&(1<<pos) != 0
}

// SetReservedBit implements the BitSetter interface.
func (r *bitfield8) SetReservedBit(pos int, val bool) {
checkPos(pos, 8)
if val {
*r |= 1 << pos
} else {
*r &= ^(1 << pos)
}
}

// bitfield32 represents a 32-bit bitfield which may have reserved bits.
// 32-bit TPMA_* types embed this one, and the reserved bits are stored in it.
type bitfield32 uint32

// Length implements the Bitfield interface.
func (bitfield32) Length() int {
return 32
}

// GetReservedBit implements the BitGetter interface.
func (r bitfield32) GetReservedBit(pos int) bool {
checkPos(pos, 32)
return r&(1<<pos) != 0
}

// SetReservedBit implements the BitSetter interface.
func (r *bitfield32) SetReservedBit(pos int, val bool) {
checkPos(pos, 32)
if val {
*r |= 1 << pos
} else {
*r &= ^(1 << pos)
}
}

Loading

0 comments on commit 0b55c34

Please sign in to comment.