attest: expose algorithms used in measurement log

Expose the algorithms that are used in the measurement log. This lets
clients generate PCR measurements that match their log digests.
This commit is contained in:
Eric Chiang 2019-09-19 15:23:53 -07:00
parent c251eb0fbd
commit 33a0bbe4ea
3 changed files with 110 additions and 25 deletions

View File

@ -62,18 +62,12 @@ type Event struct {
// match their data to their digest.
}
// ParseEventLog parses an unverified measurement log.
func ParseEventLog(measurementLog []byte) (*EventLog, error) {
rawEvents, err := parseEventLog(measurementLog)
if err != nil {
return nil, fmt.Errorf("parsing measurement log: %v", err)
}
return &EventLog{rawEvents}, nil
}
// EventLog is a parsed measurement log. This contains unverified data representing
// boot events that must be replayed against PCR values to determine authenticity.
type EventLog struct {
// Algs holds the set of algorithms that the event log uses.
Algs []HashAlg
rawEvents []rawEvent
}
@ -281,36 +275,122 @@ func replayEvents(rawEvents []rawEvent, pcrs []PCR) ([]Event, error) {
// 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)
// ParseEventLog parses an unverified measurement log.
func ParseEventLog(measurementLog []byte) (*EventLog, error) {
r := bytes.NewBuffer(measurementLog)
parseFn := parseRawEvent
var el EventLog
e, err := parseFn(r)
if err != nil {
return nil, fmt.Errorf("parse first event: %v", err)
}
var events []rawEvent
if e.typ == eventTypeNoAction {
specID, err := parseSpecIDEvent(e.data)
if err != nil {
return nil, fmt.Errorf("failed to parse spec ID event: %v", err)
}
for _, alg := range specID.algs {
switch tpm2.Algorithm(alg) {
case tpm2.AlgSHA1:
el.Algs = append(el.Algs, HashSHA1)
case tpm2.AlgSHA256:
el.Algs = append(el.Algs, HashSHA256)
}
}
if len(el.Algs) == 0 {
return nil, fmt.Errorf("measurement log didn't use sha1 or sha256 digests")
}
// 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.
// replayed events since it intentionally doesn't extend the PCRs.
//
// 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
// Note that this doesn't actually guarentee that events have SHA256
// digests.
parseFn = parseRawEvent2
} else {
events = append(events, e)
el.Algs = []HashAlg{HashSHA1}
el.rawEvents = append(el.rawEvents, e)
}
for r.Len() != 0 {
e, err := parseFn(r)
if err != nil {
return nil, err
}
events = append(events, e)
el.rawEvents = append(el.rawEvents, e)
}
return events, nil
return &el, nil
}
type specIDEvent struct {
algs []uint16
}
type specAlgSize struct {
ID uint16
Size uint16
}
var (
// Expected values for various Spec ID Event fields.
// https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=19
wantSignature = [16]byte{0x53, 0x70,
0x65, 0x63, 0x20, 0x49,
0x44, 0x20, 0x45, 0x76,
0x65, 0x6e, 0x74, 0x30,
0x33, 0x00} // "Spec ID Event03\0"
wantMajor uint8 = 2
wantMinor uint8 = 0
wantErrata = 0
)
// parseSpecIDEvent parses a TCG_EfiSpecIDEventStruct structure from the reader.
//
// https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=18
func parseSpecIDEvent(b []byte) (*specIDEvent, error) {
r := bytes.NewReader(b)
var header struct {
Signature [16]byte
PlatformClass uint32
VersionMinor uint8
VersionMajor uint8
Errata uint8
UintnSize uint8
NumAlgs uint32
}
if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
return nil, fmt.Errorf("reading event header: %v", err)
}
if header.Signature != wantSignature {
return nil, fmt.Errorf("invalid spec id signature: %x", header.Signature)
}
if header.VersionMajor != wantMajor {
return nil, fmt.Errorf("invalid spec major version, got %02x, wanted %02x",
header.VersionMajor, wantMajor)
}
if header.VersionMinor != wantMinor {
return nil, fmt.Errorf("invalid spec minor version, got %02x, wanted %02x",
header.VersionMajor, wantMinor)
}
// TODO(ericchiang): Check errata? Or do we expect that to change in ways
// we're okay with?
algs := make([]specAlgSize, header.NumAlgs)
if err := binary.Read(r, binary.LittleEndian, &algs); err != nil {
return nil, fmt.Errorf("reading algorithms: %v", err)
}
var vendorInfoSize uint8
if err := binary.Read(r, binary.LittleEndian, &vendorInfoSize); err != nil {
return nil, fmt.Errorf("reading vender info size: %v", err)
}
if r.Len() != int(vendorInfoSize) {
return nil, fmt.Errorf("reading vendor info, expected %d remaining bytes, got %d", vendorInfoSize, r.Len())
}
var e specIDEvent
for _, alg := range algs {
e.algs = append(e.algs, alg.ID)
}
return &e, nil
}
type rawEvent struct {

View File

@ -62,7 +62,7 @@ func testParseEventLog(t *testing.T, testdata string) {
if err := json.Unmarshal(data, &dump); err != nil {
t.Fatalf("parsing test data: %v", err)
}
if _, err := parseEventLog(dump.Log.Raw); err != nil {
if _, err := ParseEventLog(dump.Log.Raw); err != nil {
t.Fatalf("parsing event log: %v", err)
}
}
@ -72,7 +72,7 @@ func TestParseCryptoAgileEventLog(t *testing.T) {
if err != nil {
t.Fatalf("reading test data: %v", err)
}
if _, err := parseEventLog(data); err != nil {
if _, err := ParseEventLog(data); err != nil {
t.Fatalf("parsing event log: %v", err)
}
}

View File

@ -307,7 +307,12 @@ func (t *TPM) NewAIK(opts *AIKConfig) (*AIK, error) {
return t.tpm.newAIK(opts)
}
// PCRs returns the present value of Platform Configuration Registers with the given digest algorithm.
// PCRs returns the present value of Platform Configuration Registers with
// the given digest algorithm.
//
// Use ParseEventLog to determine which algorithm to use to match the values
// present in the event log. It's not always guarenteed that a system with TPM
// 2.0 will extend PCRs with SHA256 digests.
func (t *TPM) PCRs(alg HashAlg) ([]PCR, error) {
return t.tpm.pcrs(alg)
}