mirror of
https://github.com/google/go-attestation.git
synced 2024-12-24 07:06:45 +00:00
420 lines
10 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|