Skip to content

Commit

Permalink
Add support for hash sequence (#219)
Browse files Browse the repository at this point in the history
* Add support for hash sequence

* Split encodeSequenceUpdateOrComplete to favor readability

* Add support for event senquences (adds EventSequenceComplete)

* Sort newly added command values by numerical order

* Add tests for HashSequenceStart, SequenceUpdate and SequenceComplete

* Fix tpm2 package import

* Fix hashCount type used during TPML_DIGEST_VALUES decoding

* Add requested nit changes
  • Loading branch information
langbeck committed Oct 14, 2020
1 parent 6aef8a0 commit 289acaa
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 0 deletions.
4 changes: 4 additions & 0 deletions tpm2/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ const (
CmdDictionaryAttackLockReset tpmutil.Command = 0x00000139
CmdDictionaryAttackParameters tpmutil.Command = 0x0000013A
CmdPCREvent tpmutil.Command = 0x0000013C
CmdSequenceComplete tpmutil.Command = 0x0000013E
CmdStartup tpmutil.Command = 0x00000144
CmdShutdown tpmutil.Command = 0x00000145
CmdActivateCredential tpmutil.Command = 0x00000147
Expand All @@ -375,6 +376,7 @@ const (
CmdLoad tpmutil.Command = 0x00000157
CmdQuote tpmutil.Command = 0x00000158
CmdRSADecrypt tpmutil.Command = 0x00000159
CmdSequenceUpdate tpmutil.Command = 0x0000015C
CmdSign tpmutil.Command = 0x0000015D
CmdUnseal tpmutil.Command = 0x0000015E
CmdContextLoad tpmutil.Command = 0x00000161
Expand All @@ -397,6 +399,8 @@ const (
CmdPolicyPCR tpmutil.Command = 0x0000017F
CmdReadClock tpmutil.Command = 0x00000181
CmdPCRExtend tpmutil.Command = 0x00000182
CmdEventSequenceComplete tpmutil.Command = 0x00000185
CmdHashSequenceStart tpmutil.Command = 0x00000186
CmdPolicyGetDigest tpmutil.Command = 0x00000189
CmdPolicyPassword tpmutil.Command = 0x0000018C
CmdEncryptDecrypt2 tpmutil.Command = 0x00000193
Expand Down
70 changes: 70 additions & 0 deletions tpm2/test/tpm2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"crypto/sha1"
"crypto/sha256"
"flag"
"fmt"
"hash"
"io"
"math/big"
Expand Down Expand Up @@ -419,6 +420,75 @@ func TestHash(t *testing.T) {
})
}

func testHashSequence(t *testing.T, rw io.ReadWriter, hierarchy tpmutil.Handle, hashAlg Algorithm, data []byte) ([]byte, *Ticket) {
const (
maxDigestBuffer = 1024
seqAuth = ""
)

seq, err := HashSequenceStart(rw, seqAuth, hashAlg)
if err != nil {
t.Fatalf("HashSequenceStart failed: %v", err)
}
defer FlushContext(rw, seq)

for len(data) > maxDigestBuffer {
if err = SequenceUpdate(rw, seqAuth, seq, data[:maxDigestBuffer]); err != nil {
t.Fatalf("SequenceUpdate failed: %v", err)
}
data = data[maxDigestBuffer:]
}

digest, ticket, err := SequenceComplete(rw, seqAuth, seq, hierarchy, data)
if err != nil {
t.Fatalf("SequenceComplete failed: %v", err)
}
return digest, ticket
}

func TestHashSequence(t *testing.T) {
rw := openTPM(t)
defer rw.Close()

run := func(t *testing.T, data []byte, hierarchy tpmutil.Handle, wantValidation bool) {
gotDigest, gotValidation := testHashSequence(t, rw, hierarchy, AlgSHA256, data)
wantDigest := sha256.Sum256(data)

if !bytes.Equal(gotDigest, wantDigest[:]) {
t.Errorf("Hash(%q) returned digest %x, want %x", data, gotDigest, wantDigest)
}
if wantValidation && len(gotValidation.Digest) == 0 {
t.Errorf("Hash(%q) unexpectedly returned empty validation ticket", data)
}
if !wantValidation && len(gotValidation.Digest) != 0 {
t.Errorf("Hash(%q) unexpectedly returned non-empty validation ticket", data)
}
}

bufferSizes := []int{512, 1024, 2048, 4096}
for _, bufferSize := range bufferSizes {
buffer := make([]byte, bufferSize)

if _, err := rand.Read(buffer); err != nil {
t.Fatalf("rand.Read failed: %v", err)
}

t.Run(fmt.Sprintf("Null hierarchy [bufferSize=%d]", bufferSize), func(t *testing.T) {
run(t, buffer, HandleNull, false)
})
t.Run(fmt.Sprintf("Owner hierarchy [bufferSize=%d]", bufferSize), func(t *testing.T) {
run(t, buffer, HandleOwner, true)
})

// TCG generated values from now on
copy(buffer, []byte("\xffTCG"))

t.Run(fmt.Sprintf("Starts with TPM_GENERATED_VALUE [bufferSize=%d]", bufferSize), func(t *testing.T) {
run(t, buffer, HandleOwner, false)
})
}
}

func skipOnUnsupportedAlg(t testing.TB, rw io.ReadWriter, alg Algorithm) {
moreData := true
for i := uint32(0); moreData; i++ {
Expand Down
138 changes: 138 additions & 0 deletions tpm2/tpm2.go
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,144 @@ func Hash(rw io.ReadWriter, alg Algorithm, buf tpmutil.U16Bytes, hierarchy tpmut
return decodeHash(resp)
}

// HashSequenceStart starts a hash or an event sequence. If hashAlg is an
// implemented hash, then a hash sequence is started. If hashAlg is
// TPM_ALG_NULL, then an event sequence is started.
func HashSequenceStart(rw io.ReadWriter, sequenceAuth string, hashAlg Algorithm) (seqHandle tpmutil.Handle, err error) {
resp, err := runCommand(rw, TagNoSessions, CmdHashSequenceStart, tpmutil.U16Bytes(sequenceAuth), hashAlg)
if err != nil {
return 0, err
}
var handle tpmutil.Handle
_, err = tpmutil.Unpack(resp, &handle)
return handle, err
}

func encodeSequenceUpdate(sequenceAuth string, seqHandle tpmutil.Handle, buf tpmutil.U16Bytes) ([]byte, error) {
ha, err := tpmutil.Pack(seqHandle)
if err != nil {
return nil, err
}
auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(sequenceAuth)})
if err != nil {
return nil, err
}
params, err := tpmutil.Pack(buf)
if err != nil {
return nil, err
}
return concat(ha, auth, params)
}

// SequenceUpdate is used to add data to a hash or HMAC sequence.
func SequenceUpdate(rw io.ReadWriter, sequenceAuth string, seqHandle tpmutil.Handle, buffer []byte) error {
cmd, err := encodeSequenceUpdate(sequenceAuth, seqHandle, buffer)
if err != nil {
return err
}
_, err = runCommand(rw, TagSessions, CmdSequenceUpdate, tpmutil.RawBytes(cmd))
return err
}

func decodeSequenceComplete(resp []byte) ([]byte, *Ticket, error) {
var digest tpmutil.U16Bytes
var validation Ticket
var paramSize uint32

if _, err := tpmutil.Unpack(resp, &paramSize, &digest, &validation); err != nil {
return nil, nil, err
}
return digest, &validation, nil
}

func encodeSequenceComplete(sequenceAuth string, seqHandle, hierarchy tpmutil.Handle, buf tpmutil.U16Bytes) ([]byte, error) {
ha, err := tpmutil.Pack(seqHandle)
if err != nil {
return nil, err
}
auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(sequenceAuth)})
if err != nil {
return nil, err
}
params, err := tpmutil.Pack(buf, hierarchy)
if err != nil {
return nil, err
}
return concat(ha, auth, params)
}

// SequenceComplete adds the last part of data, if any, to a hash/HMAC sequence
// and returns the result.
func SequenceComplete(rw io.ReadWriter, sequenceAuth string, seqHandle, hierarchy tpmutil.Handle, buffer []byte) (digest []byte, validation *Ticket, err error) {
cmd, err := encodeSequenceComplete(sequenceAuth, seqHandle, hierarchy, buffer)
if err != nil {
return nil, nil, err
}
resp, err := runCommand(rw, TagSessions, CmdSequenceComplete, tpmutil.RawBytes(cmd))
if err != nil {
return nil, nil, err
}
return decodeSequenceComplete(resp)
}

func encodeEventSequenceComplete(auths []AuthCommand, pcrHandle, seqHandle tpmutil.Handle, buf tpmutil.U16Bytes) ([]byte, error) {
ha, err := tpmutil.Pack(pcrHandle, seqHandle)
if err != nil {
return nil, err
}
auth, err := encodeAuthArea(auths...)
if err != nil {
return nil, err
}
params, err := tpmutil.Pack(buf)
if err != nil {
return nil, err
}
return concat(ha, auth, params)
}

func decodeEventSequenceComplete(resp []byte) ([]*HashValue, error) {
var paramSize uint32
var hashCount uint32
var err error

buf := bytes.NewBuffer(resp)
if err := tpmutil.UnpackBuf(buf, &paramSize, &hashCount); err != nil {
return nil, err
}

buf.Truncate(int(paramSize))
digests := make([]*HashValue, hashCount)
for i := uint32(0); i < hashCount; i++ {
if digests[i], err = decodeHashValue(buf); err != nil {
return nil, err
}
}

return digests, nil
}

// EventSequenceComplete adds the last part of data, if any, to an Event
// Sequence and returns the result in a digest list. If pcrHandle references a
// PCR and not AlgNull, then the returned digest list is processed in the same
// manner as the digest list input parameter to PCRExtend() with the pcrHandle
// in each bank extended with the associated digest value.
func EventSequenceComplete(rw io.ReadWriter, pcrAuth, sequenceAuth string, pcrHandle, seqHandle tpmutil.Handle, buffer []byte) (digests []*HashValue, err error) {
auth := []AuthCommand{
{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(pcrAuth)},
{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(sequenceAuth)},
}
cmd, err := encodeEventSequenceComplete(auth, pcrHandle, seqHandle, buffer)
if err != nil {
return nil, err
}
resp, err := runCommand(rw, TagSessions, CmdEventSequenceComplete, tpmutil.RawBytes(cmd))
if err != nil {
return nil, err
}
return decodeEventSequenceComplete(resp)
}

// Startup initializes a TPM (usually done by the OS).
func Startup(rw io.ReadWriter, typ StartupType) error {
_, err := runCommand(rw, TagNoSessions, CmdStartup, typ)
Expand Down

0 comments on commit 289acaa

Please sign in to comment.