410 changes: 410 additions & 0 deletions attest/eventlog.go
@@ -0,0 +1,410 @@
package attest

import (
"bytes"
"crypto"
"crypto/rsa"
"crypto/sha1"
"encoding/binary"
"fmt"
"io"
"sort"

// Ensure hashes are available.
_ "crypto/sha256"

"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil"
)

// TPM algorithms. See the TPM 2.0 specification section 6.3.
//
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf#page=42
const (
algSHA1 uint16 = 0x0004
ericchiang marked this conversation as resolved.
Show resolved Hide resolved
algSHA256 uint16 = 0x000B
)

// EventType indicates what kind of data an event is reporting.
type EventType uint32

// Event is a single event from a TCG event log. This reports descrete items such
// as BIOs measurements or EFI states.
type Event struct {
// PCR index of the event.
Index int
// Type of the event.
Type EventType

// Data of the event. For certain kinds of events, this must match the event
// digest to be valid.
Data []byte
// Digest is the verified digest of the event data. While an event can have
// multiple for different hash values, this is the one that was matched to the
// PCR value.
Digest []byte

// TODO(ericchiang): Provide examples or links for which event types must
// match their data to their digest.
}

// EventLog contains the data required to parse and validate an event log.
type EventLog struct {
// AIKPublic is the activated public key that has been proven to be under the
// control of the TPM.
AIKPublic crypto.PublicKey
// AIKHash is the hash used to generate the quote.
AIKHash crypto.Hash

// Quote is a signature over the values of a PCR.
Quote *Quote
// PCRs are the hash values in a given number of registers.
PCRs []PCR
// Nonce is additional data used to validate the quote signature. It's used
// by the server to prevent clients from re-playing quotes.
Nonce []byte

// MeasurementLog contains the raw event log data, which is matched against
// the PCRs for validation.
MeasurementLog []byte
}

// Validate verifies the signature of the quote agains the public key, that the
// quote matches the PCRs, parses the measurement log, and replays the PCRs.
//
// Events for PCRs not in the quote are dropped.
func (e *EventLog) Validate() (events []Event, err error) {
var pcrs []PCR
switch e.Quote.Version {
case TPMVersion12:
pcrs, err = e.validate12Quote()
case TPMVersion20:
pcrs, err = e.validate20Quote()
default:
return nil, fmt.Errorf("quote used unknown tpm version 0x%x", e.Quote.Version)
}
if err != nil {
return nil, fmt.Errorf("invalid quote: %v", err)
}
rawEvents, err := parseEventLog(e.MeasurementLog)
if err != nil {
return nil, fmt.Errorf("parsing measurement log: %v", err)
}
events, err = replayEvents(rawEvents, pcrs)
if err != nil {
return nil, fmt.Errorf("pcrs failed to replay: %v", err)
}
return events, nil
}

type rawAttestationData struct {
ericchiang marked this conversation as resolved.
Show resolved Hide resolved
Version [4]byte // This MUST be 1.1.0.0
Fixed [4]byte // This SHALL always be the string ‘QUOT’
Digest [20]byte // PCR Composite Hash
Nonce [20]byte // Nonce Hash
}

var (
fixedQuote = [4]byte{'Q', 'U', 'O', 'T'}
)

type rawPCRComposite struct {
Size uint16 // always 3
PCRMask [3]byte
Values tpmutil.U32Bytes
}

func (e *EventLog) validate12Quote() (pcrs []PCR, err error) {
pub, ok := e.AIKPublic.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("unsupported public key type: %T", e.AIKPublic)
}
quote := sha1.Sum(e.Quote.Quote)
if err := rsa.VerifyPKCS1v15(pub, crypto.SHA1, quote[:], e.Quote.Signature); err != nil {
return nil, fmt.Errorf("invalid quote signature: %v", err)
}

var att rawAttestationData
if _, err := tpmutil.Unpack(e.Quote.Quote, &att); err != nil {
return nil, fmt.Errorf("parsing quote: %v", err)
}
// TODO(ericchiang): validate Version field.
if att.Nonce != sha1.Sum(e.Nonce) {
return nil, fmt.Errorf("invalid nonce")
}
if att.Fixed != fixedQuote {
return nil, fmt.Errorf("quote wasn't a QUOT object: %x", att.Fixed)
}

// See 5.4.1 Creating a PCR composite hash
sort.Slice(e.PCRs, func(i, j int) bool { return e.PCRs[i].Index < e.PCRs[j].Index })
var (
pcrMask [3]byte // bitmap indicating which PCRs are active
values []byte // appended values of all PCRs
)
for _, pcr := range e.PCRs {
if pcr.Index < 0 || pcr.Index >= 24 {
return nil, fmt.Errorf("invalid PCR index: %d", pcr.Index)
}
pcrMask[pcr.Index/8] |= 1 << uint(pcr.Index%8)
values = append(values, pcr.Digest...)
}
composite, err := tpmutil.Pack(rawPCRComposite{3, pcrMask, values})
if err != nil {
return nil, fmt.Errorf("marshaling PCRss: %v", err)
}
if att.Digest != sha1.Sum(composite) {
return nil, fmt.Errorf("PCRs passed didn't match quote: %v", err)
}
return e.PCRs, nil
}

func (e *EventLog) validate20Quote() (pcrs []PCR, err error) {
sig, err := tpm2.DecodeSignature(bytes.NewBuffer(e.Quote.Signature))
if err != nil {
return nil, fmt.Errorf("parse quote signature: %v", err)
}

sigHash := e.AIKHash.New()
sigHash.Write(e.Quote.Quote)

switch pub := e.AIKPublic.(type) {
case *rsa.PublicKey:
if sig.RSA == nil {
return nil, fmt.Errorf("rsa public key provided for ec signature")
}
sigBytes := []byte(sig.RSA.Signature)
if err := rsa.VerifyPKCS1v15(pub, e.AIKHash, sigHash.Sum(nil), sigBytes); err != nil {
return nil, fmt.Errorf("invalid quote signature: %v", err)
}
default:
// TODO(ericchiang): support ecdsa
return nil, fmt.Errorf("unsupported public key type %T", pub)
}

att, err := tpm2.DecodeAttestationData(e.Quote.Quote)
if err != nil {
return nil, fmt.Errorf("parsing quote signature: %v", err)
}
if att.Type != tpm2.TagAttestQuote {
return nil, fmt.Errorf("attestation isn't a quote, tag of type 0x%x", att.Type)
}
if !bytes.Equal([]byte(att.ExtraData), e.Nonce) {
return nil, fmt.Errorf("nonce didn't match: %v", err)
}

pcrByIndex := map[int][]byte{}
for _, pcr := range e.PCRs {
pcrByIndex[pcr.Index] = pcr.Digest
}

n := len(att.AttestedQuoteInfo.PCRDigest)
hash, ok := hashBySize[n]
if !ok {
return nil, fmt.Errorf("quote used unsupported hash algorithm length: %d", n)
}
var validatedPCRs []PCR
h := hash.New()
for _, index := range att.AttestedQuoteInfo.PCRSelection.PCRs {
digest, ok := pcrByIndex[index]
if !ok {
return nil, fmt.Errorf("quote was over PCR %d which wasn't provided", index)
}
if len(digest) != hash.Size() {
return nil, fmt.Errorf("mismatch pcr and quote hash, pcr hash length=%d, quote hash length=%d", len(digest), hash.Size())
}
h.Write(digest)
validatedPCRs = append(validatedPCRs, PCR{Index: index, Digest: digest})
}

if !bytes.Equal(h.Sum(nil), att.AttestedQuoteInfo.PCRDigest) {
return nil, fmt.Errorf("quote digest didn't match pcrs provided")
}
return validatedPCRs, nil
}

var hashBySize = map[int]crypto.Hash{
crypto.SHA1.Size(): crypto.SHA1,
crypto.SHA256.Size(): crypto.SHA256,
}

func extend(pcr, replay []byte, e rawEvent) ([]byte, Event, error) {
h, ok := hashBySize[len(pcr)]
if !ok {
return nil, Event{}, fmt.Errorf("pcr %d was not a known hash size: %d", e.index, len(pcr))
}
for _, digest := range e.digests {
if len(digest) != len(pcr) {
continue
}
hash := h.New()
if len(replay) != 0 {
hash.Write(replay)
} else {
b := make([]byte, h.Size())
hash.Write(b)
}
hash.Write(digest)
return hash.Sum(nil), Event{e.index, e.typ, e.data, digest}, nil
}
return nil, Event{}, fmt.Errorf("no event digest matches pcr length: %d", len(pcr))
}

func replayEvents(rawEvents []rawEvent, pcrs []PCR) ([]Event, error) {
events := []Event{}
replay := map[int][]byte{}
pcrByIndex := map[int][]byte{}
for _, pcr := range pcrs {
pcrByIndex[pcr.Index] = pcr.Digest
}

for i, e := range rawEvents {
pcrValue, ok := pcrByIndex[e.index]
if !ok {
// Ignore events for PCRs that weren't included in the quote.
continue
}
replayValue, event, err := extend(pcrValue, replay[e.index], e)
if err != nil {
return nil, fmt.Errorf("replaying event %d: %v", i, err)
}
replay[e.index] = replayValue
events = append(events, event)
}

var invalidReplays []int
for i, value := range replay {
if !bytes.Equal(value, pcrByIndex[i]) {
invalidReplays = append(invalidReplays, i)
}
}
if len(invalidReplays) > 0 {
return nil, fmt.Errorf("the following registers failed to replay: %d", invalidReplays)
}
return events, nil
}

// EV_NO_ACTION is a special event type that indicates information to the parser
// instead of holding a measurement. For TPM 2.0, this event type is used to signal
// switching from SHA1 format to a variable length digest.
//
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf#page=110
const eventTypeNoAction = 0x03

func parseEventLog(b []byte) ([]rawEvent, error) {
r := bytes.NewBuffer(b)
parseFn := parseRawEvent
e, err := parseFn(r)
if err != nil {
return nil, fmt.Errorf("parse first event: %v", err)
}
var events []rawEvent
if e.typ == eventTypeNoAction {
// Switch to parsing crypto agile events. Don't include this in the
// replayed events since it's intentionally switching from SHA1 to
// SHA256 and will fail to extend a SHA256 PCR value.
//
// NOTE(ericchiang): to be strict, we could parse the event data as a
// TCG_EfiSpecIDEventStruct and validate the algorithms. But for now,
// assume this indicates a switch from SHA1 format to SHA1/SHA256.
//
// https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=18
parseFn = parseRawEvent2
} else {
events = append(events, e)
}
for r.Len() != 0 {
e, err := parseFn(r)
if err != nil {
return nil, err
}
events = append(events, e)
}
return events, nil
}

type rawEvent struct {
index int
typ EventType
data []byte
digests [][]byte
}

// TPM 1.2 event log format. See "5.1 SHA1 Event Log Entry Format"
// https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=15
type rawEventHeader struct {
PCRIndex uint32
Type uint32
Digest [20]byte
EventSize uint32
}

func parseRawEvent(r io.Reader) (event rawEvent, err error) {
var h rawEventHeader
if err = binary.Read(r, binary.LittleEndian, &h); err != nil {
return event, err
}
data := make([]byte, int(h.EventSize))
if _, err := io.ReadFull(r, data); err != nil {
return event, err
}
return rawEvent{
typ: EventType(h.Type),
data: data,
index: int(h.PCRIndex),
digests: [][]byte{h.Digest[:]},
}, nil
}

// TPM 2.0 event log format. See "5.2 Crypto Agile Log Entry Format"
// https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=15
type rawEvent2Header struct {
PCRIndex uint32
Type uint32
}

func parseRawEvent2(r io.Reader) (event rawEvent, err error) {
var h rawEvent2Header
if err = binary.Read(r, binary.LittleEndian, &h); err != nil {
return event, err
}
event.typ = EventType(h.Type)
event.index = int(h.PCRIndex)

// parse the event digests
var numDigests uint32
if err := binary.Read(r, binary.LittleEndian, &numDigests); err != nil {
return event, err
}
for i := 0; i < int(numDigests); i++ {
var algID uint16
if err := binary.Read(r, binary.LittleEndian, &algID); err != nil {
return event, err
}
var digest []byte
switch algID {
case algSHA1:
digest = make([]byte, crypto.SHA1.Size())
case algSHA256:
digest = make([]byte, crypto.SHA256.Size())
default:
// ignore signatures that aren't SHA1 or SHA256
continue
}
if _, err := io.ReadFull(r, digest); err != nil {
return event, err
}
event.digests = append(event.digests, digest)
}

// parse event data
var eventSize uint32
if err = binary.Read(r, binary.LittleEndian, &eventSize); err != nil {
return event, err
}
event.data = make([]byte, int(eventSize))
if _, err := io.ReadFull(r, event.data); err != nil {
return event, err
}
return event, err
}
94 changes: 94 additions & 0 deletions attest/eventlog_test.go
@@ -0,0 +1,94 @@
package attest

import (
"encoding/json"
"io/ioutil"
"testing"

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

// Dump describes the layout of serialized information from the dump command.
type Dump struct {
Static struct {
TPMVersion TPMVersion
EKPem []byte
}

AIK AttestationParameters

Quote struct {
Nonce []byte
Alg HashAlg
Quote []byte
Signature []byte
}

Log struct {
PCRs []PCR
PCRAlg tpm2.Algorithm
Raw []byte // The measured boot log in binary form.
}
}

func TestParseEventLogWindows(t *testing.T) {
testParseEventLog(t, "testdata/windows_gcp_shielded_vm.json")
}

func TestParseEventLogLinux(t *testing.T) {
testParseEventLog(t, "testdata/linux_tpm12.json")
}

func testParseEventLog(t *testing.T, testdata string) {
data, err := ioutil.ReadFile(testdata)
if err != nil {
t.Fatalf("reading test data: %v", err)
}
var dump Dump
if err := json.Unmarshal(data, &dump); err != nil {
t.Fatalf("parsing test data: %v", err)
}
if _, err := parseEventLog(dump.Log.Raw); err != nil {
t.Fatalf("parsing event log: %v", err)
}
}

func TestEventLogLinux(t *testing.T) {
testEventLog(t, "testdata/linux_tpm12.json")
}

func TestEventLog(t *testing.T) {
testEventLog(t, "testdata/windows_gcp_shielded_vm.json")
}

func testEventLog(t *testing.T, testdata string) {
data, err := ioutil.ReadFile(testdata)
if err != nil {
t.Fatalf("reading test data: %v", err)
}
var dump Dump
if err := json.Unmarshal(data, &dump); err != nil {
t.Fatalf("parsing test data: %v", err)
}

aik, err := ParseAIKPublic(dump.Static.TPMVersion, dump.AIK.Public)
if err != nil {
t.Fatalf("parsing AIK: %v", err)
}

el := EventLog{
AIKPublic: aik.Public,
AIKHash: aik.Hash,
Quote: &Quote{
Version: dump.Static.TPMVersion,
Quote: dump.Quote.Quote,
Signature: dump.Quote.Signature,
},
Nonce: dump.Quote.Nonce,
PCRs: dump.Log.PCRs,
MeasurementLog: dump.Log.Raw,
}
if _, err := el.Validate(); err != nil {
t.Fatalf("validating event log: %v", err)
}
}
1 change: 1 addition & 0 deletions attest/testdata/linux_tpm12.json
@@ -0,0 +1 @@
{"Static":{"TPMVersion":1,"EKPem":"LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQWxpeWp3c1o3ZU5WMVkxRGVSMUtrL0JIdHFhcnRhREZQTXh5S05tWXdSbHZiTDJ1UXBObGIKT2MwaXpWVlZaelJORTY2T2pmcUZCT045K3d5ZUdsQXZGMW5LdWtxM3BLTlBFMVNwUm80eUUvbWZVdW14d1pPUgpzWnlNZXZPelNhbVQxaWdhMHZ0c1Q4TktvVGdRQTFjQUNLNXNYeGpDRDRjMmxYb1dtV3dlRWJMc3N6andualBVCkNNU1pWWnZlQ2RLM1NtL05WamhVdnRab1JodTc3NEZXOUl1NUhZaFVQWXFRNWVBaEVtMlFQT1NQOE10MmJIWTcKM3UzYlkwd0pvNVlxRzNaRkJQYjJLaUxlY3hacFZYY0k5VHNxbmlLcW5XLzJkdlhMRyt5WHVxVTBpVkhiSTZZTQozTjdTUmVGODhNMXBaQTZXSTI5emNtQThHN0NXL3NVWW1RSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K"},"AIK":{"Public":"AAAAAQABAAIAAAAMAAAIAAAAAAIAAAAAAAABAJt+7mczx4uRZR3Y2MUSBBBipAK588vhMHYMI4tDGwXBF+4Ri7spbIrYoRykafNupO57w+sS0GwpAotbkm7LLWXjNkioFVxige6uKsh+gqlNOy98tou9y4kgQFHmJbchxmz9o9Mo2Oi/MLC9thq/iYt5b2nmiNILnQZS+YGg1lPIoBxOhkVEoTSzpAFV1CstJJRhqkKlPmeR9fm1lnJeUapN0BD0WZ0PSWYmS/iUno1OnDp/mrqIrX28HPIaU3hDNVnQvgRmtl+e35VpoqXpW58Ej5jnLaipJsG7PWVd33JTu5ezTk3OG3C4zjgrHq4eTszV7F1BPUwq4Zmb2e6FsxU=","UseTCSDActivationFormat":true,"CreateData":null,"CreateAttestation":null,"CreateSignature":null},"Quote":{"Nonce":"","Alg":4,"Quote":"AQEAAFFVT1TUe8hZBOBgoRyPd07c7+kmB3KYG9o5o+5ea0sNMlW/75VgGJCv2AcJ","Signature":"AYWsgkmbV1kr65B4tHA3dYN4LD2+FaZQz8mq80Bu+M+yqBxGdGsELKC0TSLfXRXTbmA8fQAH7X++tM2M9z5PaIWtqULfGfkn/ao8dhFE0T/gm2c47/BgG7oyxtdnSlzFaQwQQzdprkHq6bBZDMA7AhcUXKfnxIllh9DsHGgPSrrOT44mwAnmpf/3eNDQ++2b+Sy01csAXtkVCB4tRyOgf48KpS0uEQ647bYsjCJatMOAXx+YV3XB7c9Drj4hsORsOpXaaFOTpN5HOAmIQqsS0tJKM78i1Sbp1/E/WlqLjffTwoeP2OJlsT85Xkmss0vhy9xBLcHV1oe0X5PUziZfjg=="},"Log":{"PCRs":[{"Index":11,"Digest":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","DigestAlg":3},{"Index":0,"Digest":"g1hNOUmsEYL7BJe1mz33M2uGSPo=","DigestAlg":3},{"Index":2,"Digest":"sqg7Dr8vg3Qpmlsr38MeqVWtcjY=","DigestAlg":3},{"Index":15,"Digest":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","DigestAlg":3},{"Index":22,"Digest":"//////////////////////////8=","DigestAlg":3},{"Index":1,"Digest":"DaB6FWt2viN2iGOSkoJNPmDLm0w=","DigestAlg":3},{"Index":19,"Digest":"//////////////////////////8=","DigestAlg":3},{"Index":18,"Digest":"//////////////////////////8=","DigestAlg":3},{"Index":23,"Digest":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","DigestAlg":3},{"Index":8,"Digest":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","DigestAlg":3},{"Index":4,"Digest":"krsrnniakXVjtxmHfpilZCyBCp8=","DigestAlg":3},{"Index":5,"Digest":"wkFtAPfMHl/BdtCt4He+zj8ksXM=","DigestAlg":3},{"Index":6,"Digest":"sqg7Dr8vg3Qpmlsr38MeqVWtcjY=","DigestAlg":3},{"Index":21,"Digest":"//////////////////////////8=","DigestAlg":3},{"Index":7,"Digest":"mhb64z08eV0diLoORWo98L745Yc=","DigestAlg":3},{"Index":13,"Digest":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","DigestAlg":3},{"Index":14,"Digest":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","DigestAlg":3},{"Index":16,"Digest":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","DigestAlg":3},{"Index":17,"Digest":"//////////////////////////8=","DigestAlg":3},{"Index":12,"Digest":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","DigestAlg":3},{"Index":3,"Digest":"sqg7Dr8vg3Qpmlsr38MeqVWtcjY=","DigestAlg":3},{"Index":9,"Digest":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","DigestAlg":3},{"Index":20,"Digest":"//////////////////////////8=","DigestAlg":3},{"Index":10,"Digest":"RoMGhc7O9bCOMFX7dG5X04Hj4/k=","DigestAlg":3}],"PCRAlg":0,"Raw":"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"}}
1 change: 1 addition & 0 deletions attest/testdata/windows_gcp_shielded_vm.json

Large diffs are not rendered by default.