diff --git a/Dockerfile b/Dockerfile index 347204c5..a5f20213 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 \ diff --git a/tpm2/constants.go b/tpm2/constants.go index bb517c36..df87c36c 100644 --- a/tpm2/constants.go +++ b/tpm2/constants.go @@ -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 diff --git a/tpm2/structures.go b/tpm2/structures.go index 3f9bd04b..b8b9d33b 100644 --- a/tpm2/structures.go +++ b/tpm2/structures.go @@ -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. diff --git a/tpm2/test/hmac_start_test.go b/tpm2/test/hmac_start_test.go new file mode 100644 index 00000000..88ef4a44 --- /dev/null +++ b/tpm2/test/hmac_start_test.go @@ -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) + } + }) + } + } +} diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 9b0556b0..45a4bdfe 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -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 {