mirror of
https://github.com/google/go-attestation.git
synced 2025-04-07 11:26:47 +00:00
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:
parent
c251eb0fbd
commit
33a0bbe4ea
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user