Skip to content

Commit

Permalink
Add TPM2_HMAC_Start implementation (#351)
Browse files Browse the repository at this point in the history
* Add TPM2_HMAC_Start implementation

- See definition in Part 3, Commands, section 17.2

* Review: change transient constant naming

* Review: change transient handle Name computation

* Fix Docker go version to match linter dependencies
  • Loading branch information
nckrss authored Feb 22, 2024
1 parent 638c2b8 commit 08987ce
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:latest
FROM golang:1.21
# We need OpenSSL headers to build the simulator
RUN apt-get update && apt-get install -y \
libssl-dev \
Expand Down
12 changes: 12 additions & 0 deletions tpm2/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,18 @@ const (
TPMHTAC TPMHT = 0x90
)

// Saved Context transient object handles.
// See definition in Part 2: Structures, section 14.6.2
// Context Handle Values come from table 211
const (
// an ordinary transient object
TPMIDHSavedTransient TPMIDHSaved = 0x80000000
// a sequence object
TPMIDHSavedSequence TPMIDHSaved = 0x80000001
// a transient object with the stClear attribute SET
TPMIDHSavedTransientClear TPMIDHSaved = 0x80000002
)

// TPMHandle represents a TPM_HANDLE.
// See definition in Part 2: Structures, section 7.1.
type TPMHandle uint32
Expand Down
15 changes: 11 additions & 4 deletions tpm2/structures.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,21 @@ func (h TPMHandle) HandleValue() uint32 {
// only PCR, session, and permanent values have known constant Names.
// See definition in part 1: Architecture, section 16.
func (h TPMHandle) KnownName() *TPM2BName {
switch (byte)(h >> 24) {
case 0x00, 0x02, 0x03, 0x40:
switch (TPMHT)(h >> 24) {
case TPMHTPCR, TPMHTHMACSession, TPMHTPolicySession, TPMHTPermanent:
result := make([]byte, 4)
binary.BigEndian.PutUint32(result, h.HandleValue())
return &TPM2BName{Buffer: result}
default:
return nil
case TPMHTTransient:
// The Name of a sequence object is an Empty Buffer
// See part 1: Architecture, section 32.4.5
if h == TPMIDHSavedSequence {
return &TPM2BName{
Buffer: []byte{},
}
}
}
return nil
}

// TPMAAlgorithm represents a TPMA_ALGORITHM.
Expand Down
141 changes: 141 additions & 0 deletions tpm2/test/hmac_start_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package tpm2test

import (
"bytes"
"crypto/rand"
"fmt"
"testing"

. "github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpm2/transport"
"github.com/google/go-tpm/tpm2/transport/simulator"
)

func TestHmacStart(t *testing.T) {
thetpm, err := simulator.OpenSimulator()
if err != nil {
t.Fatalf("could not connect to TPM simulator: %v", err)
}
defer thetpm.Close()

run := func(t *testing.T, data []byte, password []byte, hierarchy TPMHandle, thetpm transport.TPM) []byte {
maxInputBuffer := 1024

sas, sasCloser, err := HMACSession(thetpm, TPMAlgSHA256, 16)
if err != nil {
t.Fatalf("could not create hmac key authorization session: %v", err)
}
defer func() {
_ = sasCloser()
}()

createPrimary := CreatePrimary{
PrimaryHandle: AuthHandle{
Handle: hierarchy,
Auth: sas,
},
InPublic: New2B(TPMTPublic{
Type: TPMAlgKeyedHash,
NameAlg: TPMAlgSHA256,
ObjectAttributes: TPMAObject{
SignEncrypt: true,
FixedTPM: true,
FixedParent: true,
SensitiveDataOrigin: true,
UserWithAuth: true,
},
Parameters: NewTPMUPublicParms(TPMAlgKeyedHash,
&TPMSKeyedHashParms{
Scheme: TPMTKeyedHashScheme{
Scheme: TPMAlgHMAC,
Details: NewTPMUSchemeKeyedHash(TPMAlgHMAC,
&TPMSSchemeHMAC{
HashAlg: TPMAlgSHA256,
}),
},
}),
}),
}

rspCP, err := createPrimary.Execute(thetpm)
if err != nil {
t.Fatalf("CreatePrimary HMAC key failed: %v", err)
}

flushContext := FlushContext{FlushHandle: rspCP.ObjectHandle}
defer func() {
_, _ = flushContext.Execute(thetpm)
}()

hmacStart := HmacStart{
Handle: AuthHandle{
Handle: rspCP.ObjectHandle,
Name: rspCP.Name,
Auth: sas,
},
Auth: TPM2BAuth{
Buffer: password,
},
HashAlg: TPMAlgNull,
}

rspHS, err := hmacStart.Execute(thetpm)
if err != nil {
t.Fatalf("HmacStart failed: %v", err)
}

authHandle := AuthHandle{
Handle: rspHS.SequenceHandle,
Auth: PasswordAuth(password),
}
for len(data) > maxInputBuffer {
sequenceUpdate := SequenceUpdate{
SequenceHandle: authHandle,
Buffer: TPM2BMaxBuffer{
Buffer: data[:maxInputBuffer],
},
}
_, err = sequenceUpdate.Execute(thetpm)
if err != nil {
t.Fatalf("SequenceUpdate failed: %v", err)
}

data = data[maxInputBuffer:]
}

sequenceComplete := SequenceComplete{
SequenceHandle: authHandle,
Buffer: TPM2BMaxBuffer{
Buffer: data,
},
Hierarchy: hierarchy,
}

rspSC, err := sequenceComplete.Execute(thetpm)
if err != nil {
t.Fatalf("SequenceComplete failed: %v", err)
}
return rspSC.Result.Buffer

}

bufferSizes := []int{512, 1024, 2048, 4096}
password := make([]byte, 8)
_, _ = rand.Read(password)

for _, bufferSize := range bufferSizes {
data := make([]byte, bufferSize)
for _, hierarchy := range []TPMHandle{TPMRHNull, TPMRHOwner, TPMRHEndorsement} {
t.Run(fmt.Sprintf("Null hierarchy [bufferSize=%d]", bufferSize), func(t *testing.T) {
_, _ = rand.Read(data)
// HMAC Key is not exported and can not be externally validated,
// run HMAC twice with same data and confirm they are the same
hmac1 := run(t, data, password, hierarchy, thetpm)
hmac2 := run(t, data, password, hierarchy, thetpm)
if !bytes.Equal(hmac1, hmac2) {
t.Errorf("hmac %x is not expected %x", hmac1, hmac2)
}
})
}
}
}
30 changes: 30 additions & 0 deletions tpm2/tpm2.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,36 @@ type HashSequenceStartResponse struct {
SequenceHandle TPMIDHObject
}

// HmacStart is the input to TPM2_HMAC_Start.
// See definition in Part 3, Commands, section 17.2.2
type HmacStart struct {
// HMAC key handle requiring an authorization session for the USER role
Handle AuthHandle `gotpm:"handle,auth"`
// authorization value for subsequent use of the sequence
Auth TPM2BAuth
// the hash algorithm to use for the hmac sequence
HashAlg TPMIAlgHash
}

// Command implements the Command interface.
func (HmacStart) Command() TPMCC { return TPMCCHMACStart }

// Execute executes the command and returns the response.
func (cmd HmacStart) Execute(t transport.TPM, s ...Session) (*HmacStartResponse, error) {
var rsp HmacStartResponse
if err := execute[HmacStartResponse](t, cmd, &rsp, s...); err != nil {
return nil, err
}
return &rsp, nil
}

// HmacStartResponse is the response from TPM2_HMAC_Start.
// See definition in Part 3, Commands, section 17.2.2
type HmacStartResponse struct {
// a handle to reference the sequence
SequenceHandle TPMIDHObject `gotpm:"handle"`
}

// SequenceUpdate is the input to TPM2_SequenceUpdate.
// See definition in Part 3, Commands, section 17.4
type SequenceUpdate struct {
Expand Down

0 comments on commit 08987ce

Please sign in to comment.