Skip to content

Commit

Permalink
Support RTMR CEL eventlog replay
Browse files Browse the repository at this point in the history
Add new RTMR CEL AppendEventRTMR
Rename AppendEvent to AppendEventPCR

Signed-off-by: Jiankun Lu <jiankun@google.com>
  • Loading branch information
jkl73 committed Oct 2, 2024
1 parent ffca982 commit f39eb56
Show file tree
Hide file tree
Showing 18 changed files with 418 additions and 122 deletions.
149 changes: 95 additions & 54 deletions cel/canonical_eventlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,27 @@ import (
"fmt"
"io"

pb "github.com/google/go-tpm-tools/proto/tpm"
"github.com/google/go-configfs-tsm/configfs/configfsi"
"github.com/google/go-tdx-guest/rtmr"
"github.com/google/go-tpm/legacy/tpm2"
"github.com/google/go-tpm/tpmutil"
)

const (
// CEL spec 5.1
recnumTypeValue uint8 = 0
pcrTypeValue uint8 = 1
recnumTypeValue uint8 = 0
// PcrTypeValue indicates a PCR event index
PcrTypeValue uint8 = 1
_ uint8 = 2 // nvindex field is not supported yet
digestsTypeValue uint8 = 3
// RtmrTypeValue indicates a RTMR event index
RtmrTypeValue uint8 = 108 // not in the CEL spec

tlvTypeFieldLength int = 1
tlvLengthFieldLength int = 4

recnumValueLength uint32 = 8 // support up to 2^64 records
pcrValueLength uint32 = 1 // support up to 256 PCRs
recnumValueLength uint32 = 8 // support up to 2^64 records
regIndexValueLength uint32 = 1 // support up to 256 registers
)

// TLV definition according to CEL spec TCG_IWG_CEL_v1_r0p37, page 16.
Expand Down Expand Up @@ -100,10 +104,13 @@ func UnmarshalFirstTLV(buf *bytes.Buffer) (tlv TLV, err error) {

// Record represents a Canonical Eventlog Record.
type Record struct {
RecNum uint64
PCR uint8
Digests map[crypto.Hash][]byte
Content TLV
RecNum uint64
// Generic Measurement Register index number, register type
// is determined by IndexType
Index uint8
IndexType uint8
Digests map[crypto.Hash][]byte
Content TLV
}

// Content is a interface for the content in CELR.
Expand All @@ -117,25 +124,62 @@ type CEL struct {
Records []Record
}

// AppendEvent appends a new record to the CEL.
func (c *CEL) AppendEvent(tpm io.ReadWriteCloser, pcr int, hashAlgos []crypto.Hash, event Content) error {
if len(hashAlgos) == 0 {
return fmt.Errorf("need to specify at least one hash algorithm")
}
// generateDigestMap computes hashes with the given hash algos and the given event
func generateDigestMap(hashAlgos []crypto.Hash, event Content) (map[crypto.Hash][]byte, error) {
digestsMap := make(map[crypto.Hash][]byte)

for _, hashAlgo := range hashAlgos {
digest, err := event.GenerateDigest(hashAlgo)
if err != nil {
return err
return digestsMap, err
}
digestsMap[hashAlgo] = digest
}
return digestsMap, nil
}

tpm2Alg, err := tpm2.HashToAlgorithm(hashAlgo)
// AppendEventRTMR appends a new RTMR record to the CEL. rtmrIndex indicates the RTMR to extend.
// The index showing up in the record will be rtmrIndex + 1.
func (c *CEL) AppendEventRTMR(client configfsi.Client, rtmrIndex int, event Content) error {
digestsMap, err := generateDigestMap([]crypto.Hash{crypto.SHA384}, event)
if err != nil {
return err
}

eventTlv, err := event.GetTLV()
if err != nil {
return err
}

err = rtmr.ExtendDigestClient(client, rtmrIndex, digestsMap[crypto.SHA384])
if err != nil {
return err
}

celrRTMR := Record{
RecNum: uint64(len(c.Records)),
Index: uint8(rtmrIndex) + 1,
Digests: digestsMap,
Content: eventTlv,
IndexType: RtmrTypeValue,
}

c.Records = append(c.Records, celrRTMR)
return nil
}

// AppendEventPCR appends a new PCR record to the CEL.
func (c *CEL) AppendEventPCR(tpm io.ReadWriteCloser, pcr int, hashAlgos []crypto.Hash, event Content) error {
digestsMap, err := generateDigestMap(hashAlgos, event)
if err != nil {
return err
}

for hs, dgst := range digestsMap {
tpm2Alg, err := tpm2.HashToAlgorithm(hs)
if err != nil {
return err
}
if err := tpm2.PCRExtend(tpm, tpmutil.Handle(pcr), tpm2Alg, digest, ""); err != nil {
if err := tpm2.PCRExtend(tpm, tpmutil.Handle(pcr), tpm2Alg, dgst, ""); err != nil {
return fmt.Errorf("failed to extend event to PCR%d: %v", pcr, err)
}
}
Expand All @@ -145,14 +189,15 @@ func (c *CEL) AppendEvent(tpm io.ReadWriteCloser, pcr int, hashAlgos []crypto.Ha
return err
}

celr := Record{
RecNum: uint64(len(c.Records)),
PCR: uint8(pcr),
Digests: digestsMap,
Content: eventTlv,
celrPCR := Record{
RecNum: uint64(len(c.Records)),
Index: uint8(pcr),
Digests: digestsMap,
Content: eventTlv,
IndexType: PcrTypeValue,
}

c.Records = append(c.Records, celr)
c.Records = append(c.Records, celrPCR)
return nil
}

Expand All @@ -177,24 +222,24 @@ func unmarshalRecNum(tlv TLV) (uint64, error) {
return binary.BigEndian.Uint64(tlv.Value), nil
}

func createPCRField(pcrNum uint8) TLV {
return TLV{pcrTypeValue, []byte{pcrNum}}
func createIndexField(indexType uint8, indexNum uint8) TLV {
return TLV{indexType, []byte{indexNum}}
}

// UnmarshalPCR takes in a TLV with its type equals to the PCR type value (1), and
// return its PCR number.
func unmarshalPCR(tlv TLV) (pcrNum uint8, err error) {
if tlv.Type != pcrTypeValue {
return 0, fmt.Errorf("type of the TLV [%d] indicates it is not a PCR field [%d]",
tlv.Type, pcrTypeValue)
func unmarshalIndex(tlv TLV) (indexType uint8, pcrNum uint8, err error) {
if tlv.Type != PcrTypeValue && tlv.Type != RtmrTypeValue {
return 0, 0, fmt.Errorf("type of the TLV [%d] indicates it is not a PCR [%d] or a RTMR [%d] field ",
tlv.Type, PcrTypeValue, RtmrTypeValue)
}
if uint32(len(tlv.Value)) != pcrValueLength {
return 0, fmt.Errorf(
"length of the value of the TLV [%d] doesn't match the defined length [%d] of value for a PCR field",
len(tlv.Value), pcrValueLength)
if uint32(len(tlv.Value)) != regIndexValueLength {
return 0, 0, fmt.Errorf(
"length of the value of the TLV [%d] doesn't match the defined length [%d] of value for a register index field",
len(tlv.Value), regIndexValueLength)
}

return tlv.Value[0], nil
return tlv.Type, tlv.Value[0], nil
}

func createDigestField(digestMap map[crypto.Hash][]byte) (TLV, error) {
Expand Down Expand Up @@ -254,7 +299,8 @@ func (r *Record) EncodeCELR(buf *bytes.Buffer) error {
if err != nil {
return err
}
pcrField, err := createPCRField(r.PCR).MarshalBinary()

indexField, err := createIndexField(r.IndexType, r.Index).MarshalBinary()
if err != nil {
return err
}
Expand All @@ -274,7 +320,7 @@ func (r *Record) EncodeCELR(buf *bytes.Buffer) error {
if err != nil {
return err
}
_, err = buf.Write(pcrField)
_, err = buf.Write(indexField)
if err != nil {
return err
}
Expand Down Expand Up @@ -329,11 +375,11 @@ func DecodeToCELR(buf *bytes.Buffer) (r Record, err error) {
return Record{}, err
}

pcr, err := UnmarshalFirstTLV(buf)
regIndex, err := UnmarshalFirstTLV(buf)
if err != nil {
return Record{}, err
}
r.PCR, err = unmarshalPCR(pcr)
r.IndexType, r.Index, err = unmarshalIndex(regIndex)
if err != nil {
return Record{}, err
}
Expand All @@ -355,35 +401,30 @@ func DecodeToCELR(buf *bytes.Buffer) (r Record, err error) {
}

// Replay takes the digests from a Canonical Event Log and carries out the
// extend sequence for each PCR in the log. It then compares the final digests
// against a bank of PCR values to see if they match.
func (c *CEL) Replay(bank *pb.PCRs) error {
tpm2Alg := tpm2.Algorithm(bank.GetHash())
cryptoHash, err := tpm2Alg.Hash()
if err != nil {
return err
}
// extend sequence for each register (PCR, RTMR) in the log. It then compares
// the final digests against a bank of register values to see if they match.
func (c *CEL) Replay(register map[uint32][]byte, cryptoHash crypto.Hash) error {
replayed := make(map[uint8][]byte)
for _, record := range c.Records {
if _, ok := replayed[record.PCR]; !ok {
replayed[record.PCR] = make([]byte, cryptoHash.Size())
if _, ok := replayed[record.Index]; !ok {
replayed[record.Index] = make([]byte, cryptoHash.Size())
}
hasher := cryptoHash.New()
digestsMap := record.Digests
digest, ok := digestsMap[cryptoHash]
if !ok {
return fmt.Errorf("the CEL record did not contain a %v digest", cryptoHash)
}
hasher.Write(replayed[record.PCR])
hasher.Write(replayed[record.Index])
hasher.Write(digest)
replayed[record.PCR] = hasher.Sum(nil)
replayed[record.Index] = hasher.Sum(nil)
}

var failedReplayPcrs []uint8
for replayPcr, replayDigest := range replayed {
bankDigest, ok := bank.Pcrs[uint32(replayPcr)]
bankDigest, ok := register[uint32(replayPcr)]
if !ok {
return fmt.Errorf("the CEL contained record(s) for PCR%d without a matching PCR in the bank to verify", replayPcr)
return fmt.Errorf("the CEL contained record(s) for register %d without a matching register in the bank to verify", replayPcr)
}
if !bytes.Equal(bankDigest, replayDigest) {
failedReplayPcrs = append(failedReplayPcrs, replayPcr)
Expand All @@ -394,7 +435,7 @@ func (c *CEL) Replay(bank *pb.PCRs) error {
return nil
}

return fmt.Errorf("CEL replay failed for these PCRs in bank %v: %v", cryptoHash, failedReplayPcrs)
return fmt.Errorf("CEL replay failed for these registers in bank %v: %v", cryptoHash, failedReplayPcrs)
}

// VerifyDigests checks the digest generated by the given record's content to make sure they are equal to
Expand Down
Loading

0 comments on commit f39eb56

Please sign in to comment.