go-attestation/attest/eventlog_test.go
2023-06-13 07:43:38 -07:00

420 lines
10 KiB
Go

// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
package attest
import (
"bytes"
"encoding/base64"
"encoding/json"
"os"
"testing"
"github.com/google/go-tpm/legacy/tpm2"
)
// Dump describes the layout of serialized information from the dump command.
type Dump struct {
Static struct {
TPMVersion TPMVersion
EKPem []byte
}
AK 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 := os.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 TestParseCryptoAgileEventLog(t *testing.T) {
data, err := os.ReadFile("testdata/crypto_agile_eventlog")
if err != nil {
t.Fatalf("reading test data: %v", err)
}
if _, err := ParseEventLog(data); 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 := os.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)
}
ak, err := ParseAKPublic(dump.Static.TPMVersion, dump.AK.Public)
if err != nil {
t.Fatalf("parsing AK: %v", err)
}
if err := ak.Verify(Quote{
Version: dump.Static.TPMVersion,
Quote: dump.Quote.Quote,
Signature: dump.Quote.Signature,
}, dump.Log.PCRs, dump.Quote.Nonce); err != nil {
t.Fatalf("verifying quote: %v", err)
}
el, err := ParseEventLog(dump.Log.Raw)
if err != nil {
t.Fatalf("parsing event log: %v", err)
}
events, err := el.Verify(dump.Log.PCRs)
if err != nil {
t.Fatalf("validating event log: %v", err)
}
for i, e := range events {
if e.sequence != i {
t.Errorf("event out of order: events[%d].sequence = %d, want %d", i, e.sequence, i)
}
}
}
func TestParseEventLogEventSizeTooLarge(t *testing.T) {
data := []byte{
// PCR index
0x30, 0x34, 0x39, 0x33,
// type
0x36, 0x30, 0x30, 0x32,
// Digest
0x31, 0x39, 0x36, 0x33, 0x39, 0x34, 0x34, 0x37, 0x39, 0x32,
0x31, 0x32, 0x32, 0x37, 0x39, 0x30, 0x34, 0x30, 0x31, 0x6d,
// Event size (3.183 GB)
0xbd, 0xbf, 0xef, 0x47,
// "event data"
0x00, 0x00, 0x00, 0x00,
}
// If this doesn't panic, the test passed
// TODO(ericchiang): use errors.As once go-attestation switches to Go 1.13.
_, err := ParseEventLog(data)
if err == nil {
t.Fatalf("expected parsing invalid event log to fail")
}
}
func TestParseEventLogEventSizeZero(t *testing.T) {
data := []byte{
// PCR index
0x4, 0x0, 0x0, 0x0,
// type
0xd, 0x0, 0x0, 0x0,
// Digest
0x94, 0x2d, 0xb7, 0x4a, 0xa7, 0x37, 0x5b, 0x23, 0xea, 0x23,
0x58, 0xeb, 0x3b, 0x31, 0x59, 0x88, 0x60, 0xf6, 0x90, 0x59,
// Event size (0 B)
0x0, 0x0, 0x0, 0x0,
// no "event data"
}
if _, err := parseRawEvent(bytes.NewBuffer(data), nil); err != nil {
t.Fatalf("parsing event log: %v", err)
}
}
func TestParseEventLog2EventSizeZero(t *testing.T) {
data := []byte{
// PCR index
0x0, 0x0, 0x0, 0x0,
// type
0x7, 0x0, 0x0, 0x0,
// number of digests
0x1, 0x0, 0x0, 0x0,
// algorithm
0xb, 0x0,
// Digest
0xc8, 0xe3, 0x88, 0xb4, 0x79, 0x12, 0x86, 0x0c,
0x66, 0xa1, 0x5d, 0xad, 0xc4, 0x34, 0xf5, 0xdf,
0x73, 0x6c, 0x3a, 0xb4, 0xbe, 0x52, 0x07, 0x08,
0xdf, 0xac, 0x48, 0x2d, 0x71, 0xce, 0xa0, 0x73,
// Event size (0 B)
0x0, 0x0, 0x0, 0x0,
// no "event data"
}
specID := &specIDEvent{
algs: []specAlgSize{
{ID: uint16(tpm2.AlgSHA256), Size: 32},
},
}
if _, err := parseRawEvent2(bytes.NewBuffer(data), specID); err != nil {
t.Fatalf("parsing event log: %v", err)
}
}
func TestParseShortNoAction(t *testing.T) {
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf#page=110
// says: "For EV_NO_ACTION events other than the EFI Specification ID event
// (Section 9.4.5.1) the log will ...". Thus it is concluded other
// than "EFI Specification ID" events are also valid as NO_ACTION events.
//
// Currently we just assume that such events will have Data shorter than
// "EFI Specification ID" field.
data, err := os.ReadFile("testdata/short_no_action_eventlog")
if err != nil {
t.Fatalf("reading test data: %v", err)
}
if _, err := ParseEventLog(data); err != nil {
t.Fatalf("parsing event log: %v", err)
}
}
func TestParseSpecIDEvent(t *testing.T) {
tests := []struct {
name string
data []byte
want []uint16
wantErr bool
}{
{
name: "sha1",
data: append(
[]byte("Spec ID Event03"), 0x0,
0x0, 0x0, 0x0, 0x0, // platform class
0x0, // version minor
0x2, // version major
0x0, // errata
0x8, // uintn size
0x1, 0x0, 0x0, 0x0, // num algs
0x04, 0x0, // SHA1
0x14, 0x0, // size
0x2, // vendor info size
0x0, 0x0,
),
want: []uint16{0x0004},
},
{
name: "sha1_and_sha256",
data: append(
[]byte("Spec ID Event03"), 0x0,
0x0, 0x0, 0x0, 0x0, // platform class
0x0, // version minor
0x2, // version major
0x0, // errata
0x8, // uintn size
0x2, 0x0, 0x0, 0x0, // num algs
0x04, 0x0, // SHA1
0x14, 0x0, // size
0x0B, 0x0, // SHA256
0x20, 0x0, // size
0x2, // vendor info size
0x0, 0x0,
),
want: []uint16{0x0004, 0x000B},
},
{
name: "invalid_version",
data: append(
[]byte("Spec ID Event03"), 0x0,
0x0, 0x0, 0x0, 0x0, // platform class
0x2, // version minor
0x1, // version major
0x0, // errata
0x8, // uintn size
0x2, 0x0, 0x0, 0x0, // num algs
0x04, 0x0, // SHA1
0x14, 0x0, // size
0x0B, 0x0, // SHA256
0x20, 0x0, // size
0x2, // vendor info size
0x0, 0x0,
),
wantErr: true,
},
{
name: "malicious_number_of_algs",
data: append(
[]byte("Spec ID Event03"), 0x0,
0x0, 0x0, 0x0, 0x0, // platform class
0x0, // version minor
0x2, // version major
0x0, // errata
0x8, // uintn size
0xff, 0xff, 0xff, 0xff, // num algs
0x04, 0x0, // SHA1
0x14, 0x0, // size
0x2, // vendor info size
0x0, 0x0,
),
wantErr: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
spec, err := parseSpecIDEvent(test.data)
var algs []uint16
if (err != nil) != test.wantErr {
t.Fatalf("parsing spec, wantErr=%t, got=%v", test.wantErr, err)
}
if err != nil {
return
}
algsEq := func(want, got []uint16) bool {
if len(got) != len(want) {
return false
}
for i, alg := range got {
if want[i] != alg {
return false
}
}
return true
}
for _, alg := range spec.algs {
algs = append(algs, alg.ID)
}
if !algsEq(test.want, algs) {
t.Errorf("algorithms, got=%x, want=%x", spec.algs, test.want)
}
})
}
}
func TestEBSVerifyWorkaround(t *testing.T) {
pcr5 := []PCR{
{
Index: 5,
Digest: []byte{
0x31, 0x24, 0x58, 0x08, 0xd6, 0xd3, 0x58, 0x49, 0xbc, 0x39,
0x4f, 0x63, 0x43, 0xf2, 0xb3, 0xff, 0x90, 0x8e, 0xd5, 0xe3,
},
DigestAlg: HashSHA1.cryptoHash(),
},
{
Index: 5,
Digest: []byte{
0x6c, 0xae, 0xa1, 0x23, 0xfa, 0x61, 0x11, 0x30, 0x5e, 0xe6, 0x24,
0xe4, 0x52, 0xe2, 0x69, 0xad, 0x14, 0xac, 0x52, 0x2a, 0xb8, 0xbf,
0x0c, 0x88, 0xe1, 0x16, 0x16, 0xde, 0x4c, 0x22, 0x2f, 0x7d,
},
DigestAlg: HashSHA256.cryptoHash(),
},
}
elr, err := os.ReadFile("testdata/ebs_event_missing_eventlog")
if err != nil {
t.Fatal(err)
}
el, err := ParseEventLog(elr)
if err != nil {
t.Fatalf("ParseEventLog() failed: %v", err)
}
if _, err := el.Verify(pcr5); err != nil {
t.Errorf("Verify() failed: %v", err)
}
}
func TestAppendEvents(t *testing.T) {
base, err := os.ReadFile("testdata/ubuntu_2104_shielded_vm_no_secure_boot_eventlog")
if err != nil {
t.Fatalf("reading test data: %v", err)
}
extraLog, err := base64.StdEncoding.DecodeString(`AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUAAABTcGVjIElEIEV2ZW50MDMAAAAAAAACAAEC
AAAABAAUAAsAIAAACAAAAAYAAAACAAAABACX3UqVWDMNeg2Hkxyy6Q35wO4yBwsAVXbW4fKD8+xm
Kv75L4ecBpvSR4d6bz+A7z1prUcKPuMrAQAACAISpgJpbWFfaGFzaD1zaGEyNTYgYXBwYXJtb3I9
MSBwY2k9bm9hZXIsbm9hdHMgcHJpbnRrLmRldmttc2c9b24gc2xhYl9ub21lcmdlIGNvbnNvbGU9
dHR5UzAsMTE1MjAwbjggY29uc29sZT10dHkwIGdsaW51eC1ib290LWltYWdlPTIwMjExMDI3LjAy
LjAzIHF1aWV0IHNwbGFzaCBwbHltb3V0aC5pZ25vcmUtc2VyaWFsLWNvbnNvbGVzIGxzbT1sb2Nr
ZG93bix5YW1hLGxvYWRwaW4sc2FmZXNldGlkLGludGVncml0eSxhcHBhcm1vcixzZWxpbnV4LHNt
YWNrLHRvbW95byxicGYgcGFuaWM9MzAgaTkxNS5lbmFibGVfcHNyPTA=`)
if err != nil {
t.Fatal(err)
}
combined, err := AppendEvents(base, extraLog)
if err != nil {
t.Fatalf("CombineEventLogs() failed: %v", err)
}
// Make sure the combined log parses successfully and has one more
// event than the base log.
parsedBase, err := ParseEventLog(base)
if err != nil {
t.Fatal(err)
}
parsed, err := ParseEventLog(combined)
if err != nil {
t.Fatalf("ParseEventLog(combined_log) failed: %v", err)
}
if got, want := len(parsed.rawEvents), len(parsedBase.rawEvents)+1; got != want {
t.Errorf("unexpected number of events in combined log: got %d, want %d", got, want)
for i, e := range parsed.rawEvents {
t.Logf("logs[%d] = %+v", i, e)
}
}
}