From 78eb710fea4fc981c63304ddf2ec23a7746b2965 Mon Sep 17 00:00:00 2001 From: yawangwang Date: Wed, 25 Sep 2024 12:53:16 -0700 Subject: [PATCH 1/4] Instantiate backoff per goroutine (#496) --- .github/workflows/ci.yml | 4 +-- launcher/agent/agent.go | 8 ++--- launcher/agent/agent_test.go | 14 +++++--- launcher/container_runner.go | 6 ++-- launcher/container_runner_test.go | 57 ++++++++++++++++++------------- verifier/rest/rest_test.go | 14 ++++---- 6 files changed, 59 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 194d6dbf2..7853bfc32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,8 +77,8 @@ jobs: run: | GO_EXECUTABLE_PATH=$(which go) sudo $GO_EXECUTABLE_PATH test -v -run "TestFetchImageSignaturesDockerPublic" ./launcher - - name: Run specific tests to capture potential data race - run: go test ./launcher/agent -race -run TestCacheConcurrentSetGet + - name: Run all tests in launcher to capture potential data race + run: go test -v -race ./launcher/... if: (runner.os == 'Linux' || runner.os == 'macOS') && matrix.architecture == 'x64' - name: Test all modules run: go test -v ./... ./cmd/... ./launcher/... ./verifier/... -skip='TestCacheConcurrentSetGet|TestHwAttestationPass|TestHardwareAttestationPass' diff --git a/launcher/agent/agent.go b/launcher/agent/agent.go index 06878d7f2..efca958ec 100644 --- a/launcher/agent/agent.go +++ b/launcher/agent/agent.go @@ -140,7 +140,7 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error if a.launchSpec.Experiments.EnableSignedContainerCache { signatures = a.sigsCache.get() } else { - signatures = fetchContainerImageSignatures(ctx, a.sigsFetcher, a.launchSpec.SignedImageRepos, defaultRetryPolicy(), a.logger) + signatures = fetchContainerImageSignatures(ctx, a.sigsFetcher, a.launchSpec.SignedImageRepos, defaultRetryPolicy, a.logger) } if len(signatures) > 0 { req.ContainerImageSignatures = signatures @@ -167,14 +167,14 @@ func (a *agent) attest(nonce []byte, cel []byte) (*pb.Attestation, error) { // It will reset the container image signatures for now. func (a *agent) Refresh(ctx context.Context) error { if a.launchSpec.Experiments.EnableSignedContainerCache { - signatures := fetchContainerImageSignatures(ctx, a.sigsFetcher, a.launchSpec.SignedImageRepos, defaultRetryPolicy(), a.logger) + signatures := fetchContainerImageSignatures(ctx, a.sigsFetcher, a.launchSpec.SignedImageRepos, defaultRetryPolicy, a.logger) a.sigsCache.set(signatures) a.logger.Printf("Refreshed container image signature cache: %v\n", signatures) } return nil } -func fetchContainerImageSignatures(ctx context.Context, fetcher signaturediscovery.Fetcher, targetRepos []string, retry backoff.BackOff, logger *log.Logger) []oci.Signature { +func fetchContainerImageSignatures(ctx context.Context, fetcher signaturediscovery.Fetcher, targetRepos []string, retry func() backoff.BackOff, logger *log.Logger) []oci.Signature { signatures := make([][]oci.Signature, len(targetRepos)) var wg sync.WaitGroup @@ -191,7 +191,7 @@ func fetchContainerImageSignatures(ctx context.Context, fetcher signaturediscove sigs = s return err }, - retry, + retry(), func(err error, _ time.Duration) { logger.Printf("Failed to fetch container image signatures from repo %q: %v", targetRepo, err) }) diff --git a/launcher/agent/agent_test.go b/launcher/agent/agent_test.go index 4cc05b9c6..9cfe7c3ca 100644 --- a/launcher/agent/agent_test.go +++ b/launcher/agent/agent_test.go @@ -292,8 +292,11 @@ func TestFetchContainerImageSignatures(t *testing.T) { t.Fatalf("failed to create AK: %v", err) } - testRetryPolicy := backoff.NewExponentialBackOff() - testRetryPolicy.MaxElapsedTime = time.Millisecond + testRetryPolicy := func() backoff.BackOff { + b := backoff.NewExponentialBackOff() + b.MaxElapsedTime = time.Millisecond + return b + } sdClient := signaturediscovery.NewFakeClient() gotSigs := fetchContainerImageSignatures(ctx, sdClient, tc.targetRepos, testRetryPolicy, log.Default()) @@ -487,7 +490,10 @@ func TestFetchContainerImageSignatures_RetriesOnFailure(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { sdClient := NewFailingClient(tc.resultmap) - b := backoff.NewConstantBackOff(time.Millisecond) + retryPolicy := func() backoff.BackOff { + b := backoff.NewExponentialBackOff() + return backoff.WithMaxRetries(b, 2) + } repos := []string{} wantSigs := []oci.Signature{} @@ -500,7 +506,7 @@ func TestFetchContainerImageSignatures_RetriesOnFailure(t *testing.T) { } } - gotSigs := fetchContainerImageSignatures(ctx, sdClient, repos, backoff.WithMaxRetries(b, 2), log.Default()) + gotSigs := fetchContainerImageSignatures(ctx, sdClient, repos, retryPolicy, log.Default()) if len(gotSigs) != len(wantSigs) { t.Errorf("fetchContainerImageSignatures did not return expected signatures for test case %s, got signatures length %d, but want %d", tc.name, len(gotSigs), len(wantSigs)) diff --git a/launcher/container_runner.go b/launcher/container_runner.go index a44cf5395..f966bab20 100644 --- a/launcher/container_runner.go +++ b/launcher/container_runner.go @@ -407,13 +407,13 @@ func (r *ContainerRunner) refreshToken(ctx context.Context) (time.Duration, erro // ctx must be a cancellable context. func (r *ContainerRunner) fetchAndWriteToken(ctx context.Context) error { - return r.fetchAndWriteTokenWithRetry(ctx, defaultRetryPolicy()) + return r.fetchAndWriteTokenWithRetry(ctx, defaultRetryPolicy) } // ctx must be a cancellable context. // retry specifies the refresher goroutine's retry policy. func (r *ContainerRunner) fetchAndWriteTokenWithRetry(ctx context.Context, - retry *backoff.ExponentialBackOff) error { + retry func() *backoff.ExponentialBackOff) error { if err := os.MkdirAll(launcherfile.HostTmpPath, 0744); err != nil { return err } @@ -439,7 +439,7 @@ func (r *ContainerRunner) fetchAndWriteTokenWithRetry(ctx context.Context, duration, err = r.refreshToken(ctx) return err }, - retry, + retry(), func(err error, t time.Duration) { r.logger.Printf("failed to refresh attestation service token at time %v: %v", t, err) }) diff --git a/launcher/container_runner_test.go b/launcher/container_runner_test.go index 648d468ba..179a1ffc4 100644 --- a/launcher/container_runner_test.go +++ b/launcher/container_runner_test.go @@ -11,6 +11,7 @@ import ( "os" "path" "strconv" + "sync" "testing" "time" @@ -43,6 +44,10 @@ type fakeAttestationAgent struct { sigsCache []string sigsFetcherFunc func(context.Context) []string launchSpec spec.LaunchSpec + + // attMu sits on top of attempts field and protects attempts. + attMu sync.Mutex + attempts int } func (f *fakeAttestationAgent) MeasureEvent(event cel.Content) error { @@ -69,7 +74,7 @@ func (f *fakeAttestationAgent) Refresh(ctx context.Context) error { return nil } -func (f fakeAttestationAgent) Close() error { +func (f *fakeAttestationAgent) Close() error { return nil } @@ -315,16 +320,22 @@ func TestTokenIsNotChangedIfRefreshFails(t *testing.T) { expectedToken := createJWT(t, 5*time.Second) ttl := 5 * time.Second - successfulAttestFunc := func(context.Context, agent.AttestAgentOpts) ([]byte, error) { - return expectedToken, nil - } - errorAttestFunc := func(context.Context, agent.AttestAgentOpts) ([]byte, error) { + attestAgent := &fakeAttestationAgent{} + attestAgent.attestFunc = func(context.Context, agent.AttestAgentOpts) ([]byte, error) { + attestAgent.attMu.Lock() + defer func() { + attestAgent.attempts = attestAgent.attempts + 1 + attestAgent.attMu.Unlock() + }() + if attestAgent.attempts%2 == 0 { + return expectedToken, nil + } return nil, errors.New("attest unsuccessful") } runner := ContainerRunner{ - attestAgent: &fakeAttestationAgent{attestFunc: successfulAttestFunc}, + attestAgent: attestAgent, logger: log.Default(), } @@ -342,9 +353,6 @@ func TestTokenIsNotChangedIfRefreshFails(t *testing.T) { t.Errorf("Initial token written to file does not match expected token: got %v, want %v", data, expectedToken) } - // Change attest agent to return error. - runner.attestAgent = &fakeAttestationAgent{attestFunc: errorAttestFunc} - time.Sleep(ttl) data, err = os.ReadFile(filepath) @@ -407,7 +415,7 @@ func testRetryPolicyWithNTries(t *testing.T, numTries int, expectRefresh bool) { attestAgent: &fakeAttestationAgent{attestFunc: attestFunc}, logger: log.Default(), } - if err := runner.fetchAndWriteTokenWithRetry(ctx, testRetryPolicyThreeTimes()); err != nil { + if err := runner.fetchAndWriteTokenWithRetry(ctx, testRetryPolicyThreeTimes); err != nil { t.Fatalf("fetchAndWriteTokenWithRetry failed: %v", err) } filepath := path.Join(launcherfile.HostTmpPath, launcherfile.AttestationVerifierTokenFilename) @@ -448,16 +456,25 @@ func TestFetchAndWriteTokenWithTokenRefresh(t *testing.T) { defer cancel() expectedToken := createJWT(t, 5*time.Second) + expectedRefreshedToken := createJWT(t, 10*time.Second) ttl := 5 * time.Second + attestAgent := &fakeAttestationAgent{} + attestAgent.attestFunc = func(context.Context, agent.AttestAgentOpts) ([]byte, error) { + attestAgent.attMu.Lock() + defer func() { + attestAgent.attempts = attestAgent.attempts + 1 + attestAgent.attMu.Unlock() + }() + if attestAgent.attempts%2 == 0 { + return expectedToken, nil + } + return expectedRefreshedToken, nil + } runner := ContainerRunner{ - attestAgent: &fakeAttestationAgent{ - attestFunc: func(context.Context, agent.AttestAgentOpts) ([]byte, error) { - return expectedToken, nil - }, - }, - logger: log.Default(), + attestAgent: attestAgent, + logger: log.Default(), } if err := runner.fetchAndWriteToken(ctx); err != nil { @@ -474,14 +491,6 @@ func TestFetchAndWriteTokenWithTokenRefresh(t *testing.T) { t.Errorf("Initial token written to file does not match expected token: got %v, want %v", data, expectedToken) } - // Change attest agent to return new token. - expectedRefreshedToken := createJWT(t, 10*time.Second) - runner.attestAgent = &fakeAttestationAgent{ - attestFunc: func(context.Context, agent.AttestAgentOpts) ([]byte, error) { - return expectedRefreshedToken, nil - }, - } - // Check that token has not been refreshed yet. data, err = os.ReadFile(filepath) if err != nil { diff --git a/verifier/rest/rest_test.go b/verifier/rest/rest_test.go index 0fff7ef99..eaf6946fa 100644 --- a/verifier/rest/rest_test.go +++ b/verifier/rest/rest_test.go @@ -130,10 +130,10 @@ func testRawCertTable(t testing.TB) *testCertTable { func TestConvertTDXProtoToREST(t *testing.T) { testCases := []struct { - name string - quote func() *tpb.QuoteV4 + name string + quote func() *tpb.QuoteV4 wantPass bool - } { + }{ { name: "successful TD quote conversion", quote: func() *tpb.QuoteV4 { @@ -141,7 +141,7 @@ func TestConvertTDXProtoToREST(t *testing.T) { if err != nil { t.Fatalf("Unable to convert Raw TD Quote to TDX V4 quote: %v", err) } - + quote, ok := tdx.(*tpb.QuoteV4) if !ok { t.Fatal("Quote format not supported, want QuoteV4 format") @@ -151,8 +151,8 @@ func TestConvertTDXProtoToREST(t *testing.T) { wantPass: true, }, { - name: "nil TD quote conversion", - quote: func() *tpb.QuoteV4 { return nil }, + name: "nil TD quote conversion", + quote: func() *tpb.QuoteV4 { return nil }, wantPass: false, }, } @@ -169,7 +169,7 @@ func TestConvertTDXProtoToREST(t *testing.T) { TdQuote: tgtestdata.RawQuote, }, } - + if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" { t.Errorf("TDX API proto mismatch: %s", diff) } From b0877ff72db8954ce444792ea40addd3462d534a Mon Sep 17 00:00:00 2001 From: yawangwang Date: Tue, 1 Oct 2024 15:37:40 -0700 Subject: [PATCH 2/4] Remove EnableSignedContainerCache + EnableMeasureMemoryMonitor from container launcher (#498) --- launcher/agent/agent.go | 15 ++---- launcher/agent/agent_test.go | 48 ++++++++------------ launcher/container_runner.go | 6 +-- launcher/container_runner_test.go | 7 +-- launcher/internal/experiments/experiments.go | 6 +-- 5 files changed, 30 insertions(+), 52 deletions(-) diff --git a/launcher/agent/agent.go b/launcher/agent/agent.go index efca958ec..28aa4b49b 100644 --- a/launcher/agent/agent.go +++ b/launcher/agent/agent.go @@ -136,12 +136,7 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error }, } - var signatures []oci.Signature - if a.launchSpec.Experiments.EnableSignedContainerCache { - signatures = a.sigsCache.get() - } else { - signatures = fetchContainerImageSignatures(ctx, a.sigsFetcher, a.launchSpec.SignedImageRepos, defaultRetryPolicy, a.logger) - } + signatures := a.sigsCache.get() if len(signatures) > 0 { req.ContainerImageSignatures = signatures a.logger.Printf("Found container image signatures: %v\n", signatures) @@ -166,11 +161,9 @@ func (a *agent) attest(nonce []byte, cel []byte) (*pb.Attestation, error) { // Refresh refreshes the internal state of the attestation agent. // It will reset the container image signatures for now. func (a *agent) Refresh(ctx context.Context) error { - if a.launchSpec.Experiments.EnableSignedContainerCache { - signatures := fetchContainerImageSignatures(ctx, a.sigsFetcher, a.launchSpec.SignedImageRepos, defaultRetryPolicy, a.logger) - a.sigsCache.set(signatures) - a.logger.Printf("Refreshed container image signature cache: %v\n", signatures) - } + signatures := fetchContainerImageSignatures(ctx, a.sigsFetcher, a.launchSpec.SignedImageRepos, defaultRetryPolicy, a.logger) + a.sigsCache.set(signatures) + a.logger.Printf("Refreshed container image signature cache: %v\n", signatures) return nil } diff --git a/launcher/agent/agent_test.go b/launcher/agent/agent_test.go index 9cfe7c3ca..7eaee0c62 100644 --- a/launcher/agent/agent_test.go +++ b/launcher/agent/agent_test.go @@ -19,7 +19,6 @@ import ( "github.com/google/go-tpm-tools/cel" "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/internal/test" - "github.com/google/go-tpm-tools/launcher/internal/experiments" "github.com/google/go-tpm-tools/launcher/internal/signaturediscovery" "github.com/google/go-tpm-tools/launcher/spec" attestpb "github.com/google/go-tpm-tools/proto/attest" @@ -88,16 +87,9 @@ func TestAttest(t *testing.T) { containerSignaturesFetcher signaturediscovery.Fetcher }{ { - name: "all experiment flags disabled", - launchSpec: spec.LaunchSpec{}, - principalIDTokenFetcher: placeholderPrincipalFetcher, - containerSignaturesFetcher: signaturediscovery.NewFakeClient(), - }, - { - name: "enable signed container", + name: "Happy path with container signatures", launchSpec: spec.LaunchSpec{ SignedImageRepos: []string{signaturediscovery.FakeRepoWithSignatures}, - Experiments: experiments.Experiments{EnableSignedContainerCache: true}, }, principalIDTokenFetcher: placeholderPrincipalFetcher, containerSignaturesFetcher: signaturediscovery.NewFakeClient(), @@ -158,26 +150,26 @@ func TestAttest(t *testing.T) { if claims.Subject != "https://www.googleapis.com/compute/v1/projects/fakeProject/zones/fakeZone/instances/fakeInstance" { t.Errorf("Invalid sub") } - if tc.launchSpec.Experiments.EnableSignedContainerCache { - got := claims.ContainerImageSignatures - want := []fake.ContainerImageSignatureClaims{ - { - Payload: "test data", - Signature: base64.StdEncoding.EncodeToString([]byte("test data")), - PubKey: "test data", - SigAlg: "ECDSA_P256_SHA256", - }, - { - Payload: "hello world", - Signature: base64.StdEncoding.EncodeToString([]byte("hello world")), - PubKey: "hello world", - SigAlg: "RSASSA_PKCS1V15_SHA256", - }, - } - if !cmp.Equal(got, want) { - t.Errorf("ContainerImageSignatureClaims does not match expected value: got %v, want %v", got, want) - } + + got := claims.ContainerImageSignatures + want := []fake.ContainerImageSignatureClaims{ + { + Payload: "test data", + Signature: base64.StdEncoding.EncodeToString([]byte("test data")), + PubKey: "test data", + SigAlg: "ECDSA_P256_SHA256", + }, + { + Payload: "hello world", + Signature: base64.StdEncoding.EncodeToString([]byte("hello world")), + PubKey: "hello world", + SigAlg: "RSASSA_PKCS1V15_SHA256", + }, } + if !cmp.Equal(got, want) { + t.Errorf("ContainerImageSignatureClaims does not match expected value: got %v, want %v", got, want) + } + ms := &attestpb.MachineState{} err = protojson.Unmarshal([]byte(claims.MachineStateMarshaled), ms) if err != nil { diff --git a/launcher/container_runner.go b/launcher/container_runner.go index f966bab20..e7ce6d6a1 100644 --- a/launcher/container_runner.go +++ b/launcher/container_runner.go @@ -270,10 +270,8 @@ func (r *ContainerRunner) measureCELEvents(ctx context.Context) error { if err := r.measureContainerClaims(ctx); err != nil { return fmt.Errorf("failed to measure container claims: %v", err) } - if r.launchSpec.Experiments.EnableMeasureMemoryMonitor { - if err := r.measureMemoryMonitor(); err != nil { - return fmt.Errorf("failed to measure memory monitoring state: %v", err) - } + if err := r.measureMemoryMonitor(); err != nil { + return fmt.Errorf("failed to measure memory monitoring state: %v", err) } separator := cel.CosTlv{ diff --git a/launcher/container_runner_test.go b/launcher/container_runner_test.go index 179a1ffc4..8cae33b71 100644 --- a/launcher/container_runner_test.go +++ b/launcher/container_runner_test.go @@ -24,7 +24,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-tpm-tools/cel" "github.com/google/go-tpm-tools/launcher/agent" - "github.com/google/go-tpm-tools/launcher/internal/experiments" "github.com/google/go-tpm-tools/launcher/launcherfile" "github.com/google/go-tpm-tools/launcher/spec" "github.com/opencontainers/go-digest" @@ -43,7 +42,6 @@ type fakeAttestationAgent struct { attestFunc func(context.Context, agent.AttestAgentOpts) ([]byte, error) sigsCache []string sigsFetcherFunc func(context.Context) []string - launchSpec spec.LaunchSpec // attMu sits on top of attempts field and protects attempts. attMu sync.Mutex @@ -68,7 +66,7 @@ func (f *fakeAttestationAgent) Attest(ctx context.Context, _ agent.AttestAgentOp // Refresh simulates the behavior of an actual agent. func (f *fakeAttestationAgent) Refresh(ctx context.Context) error { - if f.launchSpec.Experiments.EnableSignedContainerCache { + if f.sigsFetcherFunc != nil { f.sigsCache = f.sigsFetcherFunc(ctx) } return nil @@ -192,7 +190,6 @@ func TestRefreshTokenWithSignedContainerCacheEnabled(t *testing.T) { sigsFetcherFunc: func(context.Context) []string { return oldCache }, - launchSpec: spec.LaunchSpec{Experiments: experiments.Experiments{EnableSignedContainerCache: true}}, } fakeAgent.attestFunc = func(context.Context, agent.AttestAgentOpts) ([]byte, error) { return createJWTWithSignatures(t, fakeAgent.sigsCache), nil @@ -586,6 +583,7 @@ func TestMeasureCELEvents(t *testing.T) { cel.EnvVarType, cel.OverrideEnvType, cel.OverrideArgType, + cel.MemoryMonitorType, cel.LaunchSeparatorType, }, launchSpec: spec.LaunchSpec{ @@ -605,7 +603,6 @@ func TestMeasureCELEvents(t *testing.T) { cel.MemoryMonitorType, cel.LaunchSeparatorType, }, - launchSpec: spec.LaunchSpec{Experiments: experiments.Experiments{EnableMeasureMemoryMonitor: true}}, }, } diff --git a/launcher/internal/experiments/experiments.go b/launcher/internal/experiments/experiments.go index cef84e849..6547b0f50 100644 --- a/launcher/internal/experiments/experiments.go +++ b/launcher/internal/experiments/experiments.go @@ -11,10 +11,8 @@ import ( // Failure to unmarshal the experiment JSON data will result in an empty object being returned // to treat experiment flags as their default value. The error should still be checked. type Experiments struct { - EnableTestFeatureForImage bool - EnableSignedContainerCache bool - EnableMeasureMemoryMonitor bool - EnableTempFSMount bool + EnableTestFeatureForImage bool + EnableTempFSMount bool } // New takes a filepath, opens the file, and calls ReadJsonInput with the contents From 3332c30f17b7f360d4e88bc0780478cbc707f86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiankun=20L=C3=BC?= Date: Thu, 3 Oct 2024 13:27:41 -0700 Subject: [PATCH 3/4] Support RTMR CEL eventlog replay (#486) Add new RTMR CEL AppendEventRTMR Redirect AppendEvent to AppendEventPCR Signed-off-by: Jiankun Lu --- cel/canonical_eventlog.go | 174 ++++++++++++++++++++++----------- cel/canonical_eventlog_test.go | 109 ++++++++++++++++----- cel/cos_tlv.go | 12 +++ cel/cos_tlv_test.go | 8 +- go.mod | 7 +- go.sum | 14 +-- go.work.sum | 6 ++ launcher/go.mod | 5 +- launcher/go.sum | 10 +- proto/attest.proto | 2 +- proto/attest/attest.pb.go | 2 +- server/eventlog.go | 43 ++++++-- server/eventlog_test.go | 158 ++++++++++++++++++++++++++++-- server/verify.go | 23 ++++- server/verify_test.go | 10 +- verifier/go.mod | 5 +- verifier/go.sum | 10 +- 17 files changed, 464 insertions(+), 134 deletions(-) diff --git a/cel/canonical_eventlog.go b/cel/canonical_eventlog.go index 3cfcfdb7a..1b9d89302 100644 --- a/cel/canonical_eventlog.go +++ b/cel/canonical_eventlog.go @@ -9,23 +9,28 @@ import ( "fmt" "io" - pb "github.com/google/go-tpm-tools/proto/tpm" + "github.com/google/go-configfs-tsm/configfs/configfsi" + "github.com/google/go-eventlog/register" + "github.com/google/go-tdx-guest/rtmr" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" ) const ( // CEL spec 5.1 - recnumTypeValue uint8 = 0 - pcrTypeValue uint8 = 1 + recnumTypeValue uint8 = 0 + // PCRTypeValue indicates a PCR event index + PCRTypeValue uint8 = 1 _ uint8 = 2 // nvindex field is not supported yet digestsTypeValue uint8 = 3 + // CCMRTypeValue indicates a RTMR event index + CCMRTypeValue uint8 = 108 // not in the CEL spec tlvTypeFieldLength int = 1 tlvLengthFieldLength int = 4 - recnumValueLength uint32 = 8 // support up to 2^64 records - pcrValueLength uint32 = 1 // support up to 256 PCRs + recnumValueLength uint32 = 8 // support up to 2^64 records + regIndexValueLength uint32 = 1 // support up to 256 registers ) // TLV definition according to CEL spec TCG_IWG_CEL_v1_r0p37, page 16. @@ -100,10 +105,13 @@ func UnmarshalFirstTLV(buf *bytes.Buffer) (tlv TLV, err error) { // Record represents a Canonical Eventlog Record. type Record struct { - RecNum uint64 - PCR uint8 - Digests map[crypto.Hash][]byte - Content TLV + RecNum uint64 + // Generic Measurement Register index number, register type + // is determined by IndexType + Index uint8 + IndexType uint8 + Digests map[crypto.Hash][]byte + Content TLV } // Content is a interface for the content in CELR. @@ -117,25 +125,69 @@ type CEL struct { Records []Record } -// AppendEvent appends a new record to the CEL. -func (c *CEL) AppendEvent(tpm io.ReadWriteCloser, pcr int, hashAlgos []crypto.Hash, event Content) error { - if len(hashAlgos) == 0 { - return fmt.Errorf("need to specify at least one hash algorithm") - } +// generateDigestMap computes hashes with the given hash algos and the given event +func generateDigestMap(hashAlgos []crypto.Hash, event Content) (map[crypto.Hash][]byte, error) { digestsMap := make(map[crypto.Hash][]byte) - for _, hashAlgo := range hashAlgos { digest, err := event.GenerateDigest(hashAlgo) if err != nil { - return err + return digestsMap, err } digestsMap[hashAlgo] = digest + } + return digestsMap, nil +} + +// AppendEventRTMR appends a new RTMR record to the CEL. rtmrIndex indicates the RTMR to extend. +// The index showing up in the record will be rtmrIndex + 1. +func (c *CEL) AppendEventRTMR(client configfsi.Client, rtmrIndex int, event Content) error { + digestsMap, err := generateDigestMap([]crypto.Hash{crypto.SHA384}, event) + if err != nil { + return err + } + + eventTlv, err := event.GetTLV() + if err != nil { + return err + } + + err = rtmr.ExtendDigestClient(client, rtmrIndex, digestsMap[crypto.SHA384]) + if err != nil { + return err + } + + celrRTMR := Record{ + RecNum: uint64(len(c.Records)), + Index: uint8(rtmrIndex) + 1, // CCMR conversion from RTMR + Digests: digestsMap, + Content: eventTlv, + IndexType: CCMRTypeValue, + } + + c.Records = append(c.Records, celrRTMR) + return nil +} + +// AppendEvent appends a new PCR record to the CEL. +// This function is a wrapper of AppendEventPCR, for backward +// compatibility. +func (c *CEL) AppendEvent(tpm io.ReadWriteCloser, pcr int, hashAlgos []crypto.Hash, event Content) error { + return c.AppendEventPCR(tpm, pcr, hashAlgos, event) +} + +// AppendEventPCR appends a new PCR record to the CEL. +func (c *CEL) AppendEventPCR(tpm io.ReadWriteCloser, pcr int, hashAlgos []crypto.Hash, event Content) error { + digestsMap, err := generateDigestMap(hashAlgos, event) + if err != nil { + return err + } - tpm2Alg, err := tpm2.HashToAlgorithm(hashAlgo) + for hs, dgst := range digestsMap { + tpm2Alg, err := tpm2.HashToAlgorithm(hs) if err != nil { return err } - if err := tpm2.PCRExtend(tpm, tpmutil.Handle(pcr), tpm2Alg, digest, ""); err != nil { + if err := tpm2.PCRExtend(tpm, tpmutil.Handle(pcr), tpm2Alg, dgst, ""); err != nil { return fmt.Errorf("failed to extend event to PCR%d: %v", pcr, err) } } @@ -145,14 +197,15 @@ func (c *CEL) AppendEvent(tpm io.ReadWriteCloser, pcr int, hashAlgos []crypto.Ha return err } - celr := Record{ - RecNum: uint64(len(c.Records)), - PCR: uint8(pcr), - Digests: digestsMap, - Content: eventTlv, + celrPCR := Record{ + RecNum: uint64(len(c.Records)), + Index: uint8(pcr), + Digests: digestsMap, + Content: eventTlv, + IndexType: PCRTypeValue, } - c.Records = append(c.Records, celr) + c.Records = append(c.Records, celrPCR) return nil } @@ -177,24 +230,24 @@ func unmarshalRecNum(tlv TLV) (uint64, error) { return binary.BigEndian.Uint64(tlv.Value), nil } -func createPCRField(pcrNum uint8) TLV { - return TLV{pcrTypeValue, []byte{pcrNum}} +func createIndexField(indexType uint8, indexNum uint8) TLV { + return TLV{indexType, []byte{indexNum}} } -// UnmarshalPCR takes in a TLV with its type equals to the PCR type value (1), and -// return its PCR number. -func unmarshalPCR(tlv TLV) (pcrNum uint8, err error) { - if tlv.Type != pcrTypeValue { - return 0, fmt.Errorf("type of the TLV [%d] indicates it is not a PCR field [%d]", - tlv.Type, pcrTypeValue) +// unmarshalIndex takes in a TLV with its type equals to the PCR or CCMR type value, and +// return its index number. +func unmarshalIndex(tlv TLV) (indexType uint8, pcrNum uint8, err error) { + if tlv.Type != PCRTypeValue && tlv.Type != CCMRTypeValue { + return 0, 0, fmt.Errorf("type of the TLV [%d] indicates it is not a PCR [%d] or a CCMR [%d] field ", + tlv.Type, PCRTypeValue, CCMRTypeValue) } - if uint32(len(tlv.Value)) != pcrValueLength { - return 0, fmt.Errorf( - "length of the value of the TLV [%d] doesn't match the defined length [%d] of value for a PCR field", - len(tlv.Value), pcrValueLength) + if uint32(len(tlv.Value)) != regIndexValueLength { + return 0, 0, fmt.Errorf( + "length of the value of the TLV [%d] doesn't match the defined length [%d] of value for a register index field", + len(tlv.Value), regIndexValueLength) } - return tlv.Value[0], nil + return tlv.Type, tlv.Value[0], nil } func createDigestField(digestMap map[crypto.Hash][]byte) (TLV, error) { @@ -254,7 +307,8 @@ func (r *Record) EncodeCELR(buf *bytes.Buffer) error { if err != nil { return err } - pcrField, err := createPCRField(r.PCR).MarshalBinary() + + indexField, err := createIndexField(r.IndexType, r.Index).MarshalBinary() if err != nil { return err } @@ -274,7 +328,7 @@ func (r *Record) EncodeCELR(buf *bytes.Buffer) error { if err != nil { return err } - _, err = buf.Write(pcrField) + _, err = buf.Write(indexField) if err != nil { return err } @@ -329,11 +383,11 @@ func DecodeToCELR(buf *bytes.Buffer) (r Record, err error) { return Record{}, err } - pcr, err := UnmarshalFirstTLV(buf) + regIndex, err := UnmarshalFirstTLV(buf) if err != nil { return Record{}, err } - r.PCR, err = unmarshalPCR(pcr) + r.IndexType, r.Index, err = unmarshalIndex(regIndex) if err != nil { return Record{}, err } @@ -355,18 +409,18 @@ func DecodeToCELR(buf *bytes.Buffer) (r Record, err error) { } // Replay takes the digests from a Canonical Event Log and carries out the -// extend sequence for each PCR in the log. It then compares the final digests -// against a bank of PCR values to see if they match. -func (c *CEL) Replay(bank *pb.PCRs) error { - tpm2Alg := tpm2.Algorithm(bank.GetHash()) - cryptoHash, err := tpm2Alg.Hash() +// extend sequence for each register (PCR, RTMR) in the log. It then compares +// the final digests against a bank of register values to see if they match. +// make sure CEL has only one indexType event +func (c *CEL) Replay(regs register.MRBank) error { + cryptoHash, err := regs.CryptoHash() if err != nil { return err } replayed := make(map[uint8][]byte) for _, record := range c.Records { - if _, ok := replayed[record.PCR]; !ok { - replayed[record.PCR] = make([]byte, cryptoHash.Size()) + if _, ok := replayed[record.Index]; !ok { + replayed[record.Index] = make([]byte, cryptoHash.Size()) } hasher := cryptoHash.New() digestsMap := record.Digests @@ -374,27 +428,33 @@ func (c *CEL) Replay(bank *pb.PCRs) error { if !ok { return fmt.Errorf("the CEL record did not contain a %v digest", cryptoHash) } - hasher.Write(replayed[record.PCR]) + hasher.Write(replayed[record.Index]) hasher.Write(digest) - replayed[record.PCR] = hasher.Sum(nil) + replayed[record.Index] = hasher.Sum(nil) + } + + // to a map for easy matching + registers := make(map[int][]byte) + for _, r := range regs.MRs() { + registers[r.Idx()] = r.Dgst() } - var failedReplayPcrs []uint8 - for replayPcr, replayDigest := range replayed { - bankDigest, ok := bank.Pcrs[uint32(replayPcr)] + var failedReplayRegs []uint8 + for replayReg, replayDigest := range replayed { + bankDigest, ok := registers[int(replayReg)] if !ok { - return fmt.Errorf("the CEL contained record(s) for PCR%d without a matching PCR in the bank to verify", replayPcr) + return fmt.Errorf("the CEL contains record(s) for register %d without a matching register in the given bank to verify", replayReg) } if !bytes.Equal(bankDigest, replayDigest) { - failedReplayPcrs = append(failedReplayPcrs, replayPcr) + failedReplayRegs = append(failedReplayRegs, replayReg) } } - if len(failedReplayPcrs) == 0 { + if len(failedReplayRegs) == 0 { return nil } - return fmt.Errorf("CEL replay failed for these PCRs in bank %v: %v", cryptoHash, failedReplayPcrs) + return fmt.Errorf("CEL replay failed for these registers in bank %v: %v", cryptoHash, failedReplayRegs) } // VerifyDigests checks the digest generated by the given record's content to make sure they are equal to diff --git a/cel/canonical_eventlog_test.go b/cel/canonical_eventlog_test.go index 82779bb32..af31fb984 100644 --- a/cel/canonical_eventlog_test.go +++ b/cel/canonical_eventlog_test.go @@ -8,9 +8,13 @@ import ( "reflect" "testing" + "github.com/google/go-configfs-tsm/configfs/configfsi" + "github.com/google/go-configfs-tsm/configfs/fakertmr" + configfstsmrtmr "github.com/google/go-configfs-tsm/rtmr" + "github.com/google/go-eventlog/proto/state" + "github.com/google/go-eventlog/register" "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/internal/test" - pb "github.com/google/go-tpm-tools/proto/tpm" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" ) @@ -24,10 +28,10 @@ func TestCELEncodingDecoding(t *testing.T) { cel := &CEL{} cosEvent := CosTlv{ImageDigestType, []byte("sha256:781d8dfdd92118436bd914442c8339e653b83f6bf3c1a7a98efcfb7c4fed7483")} - appendOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent) + appendPcrEventOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent) cosEvent2 := CosTlv{ImageRefType, []byte("docker.io/bazel/experimental/test:latest")} - appendOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent2) + appendPcrEventOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent2) var buf bytes.Buffer if err := cel.EncodeCEL(&buf); err != nil { @@ -46,10 +50,16 @@ func TestCELEncodingDecoding(t *testing.T) { if decodedcel.Records[1].RecNum != 1 { t.Errorf("recnum mismatch") } - if decodedcel.Records[0].PCR != uint8(test.DebugPCR) { + if decodedcel.Records[0].IndexType != PCRTypeValue { + t.Errorf("index type mismatch") + } + if decodedcel.Records[0].Index != uint8(test.DebugPCR) { t.Errorf("pcr value mismatch") } - if decodedcel.Records[1].PCR != uint8(test.ApplicationPCR) { + if decodedcel.Records[1].IndexType != PCRTypeValue { + t.Errorf("index type mismatch") + } + if decodedcel.Records[1].Index != uint8(test.ApplicationPCR) { t.Errorf("pcr value mismatch") } @@ -62,6 +72,8 @@ func TestCELMeasureAndReplay(t *testing.T) { tpm := test.GetTPM(t) defer client.CheckedClose(t, tpm) + fakeRTMR := fakertmr.CreateRtmrSubsystem(t.TempDir()) + err := tpm2.PCRReset(tpm, tpmutil.Handle(test.DebugPCR)) if err != nil { t.Fatal(err) @@ -72,23 +84,36 @@ func TestCELMeasureAndReplay(t *testing.T) { } cel := &CEL{} + celRTMR := &CEL{} cosEvent := CosTlv{ImageRefType, []byte("docker.io/bazel/experimental/test:latest")} + someEvent2 := make([]byte, 10) rand.Read(someEvent2) cosEvent2 := CosTlv{ImageDigestType, someEvent2} - appendOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent) - appendOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent2) - appendOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent2) - appendOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent) - appendOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent) + appendPcrEventOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent) + appendRtmrEventOrFatal(t, celRTMR, fakeRTMR, CosRTMR, cosEvent) + + appendPcrEventOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent2) + appendRtmrEventOrFatal(t, celRTMR, fakeRTMR, CosRTMR, cosEvent) + + appendPcrEventOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent2) + appendRtmrEventOrFatal(t, celRTMR, fakeRTMR, CosRTMR, cosEvent2) + + appendPcrEventOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent) + appendRtmrEventOrFatal(t, celRTMR, fakeRTMR, CosRTMR, cosEvent) + + appendPcrEventOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent) + appendRtmrEventOrFatal(t, celRTMR, fakeRTMR, CosRTMR, cosEvent) replay(t, cel, tpm, measuredHashes, []int{test.DebugPCR, test.ApplicationPCR}, true /*shouldSucceed*/) // Supersets should pass. replay(t, cel, tpm, measuredHashes, []int{0, 13, 14, test.DebugPCR, 22, test.ApplicationPCR}, true /*shouldSucceed*/) + + replayRTMR(t, celRTMR, fakeRTMR, []int{0, 1, 2, 3}, true /*shouldSucceed*/) } func TestCELReplayFailTamperedDigest(t *testing.T) { @@ -99,15 +124,14 @@ func TestCELReplayFailTamperedDigest(t *testing.T) { cosEvent := CosTlv{ImageRefType, []byte("docker.io/bazel/experimental/test:latest")} someEvent2 := make([]byte, 10) - rand.Read(someEvent2) cosEvent2 := CosTlv{ImageDigestType, someEvent2} - appendOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent) - appendOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent2) - appendOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent2) - appendOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent) - appendOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent) + appendPcrEventOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent) + appendPcrEventOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, cosEvent2) + appendPcrEventOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent2) + appendPcrEventOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent) + appendPcrEventOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, cosEvent) modifiedRecord := cel.Records[3] for hash := range modifiedRecord.Digests { @@ -137,8 +161,10 @@ func TestCELReplayFailMissingPCRsInBank(t *testing.T) { someEvent := make([]byte, 10) someEvent2 := make([]byte, 10) rand.Read(someEvent2) - appendOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, CosTlv{ImageRefType, someEvent}) - appendOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, CosTlv{ImageDigestType, someEvent2}) + + appendPcrEventOrFatal(t, cel, tpm, test.DebugPCR, measuredHashes, CosTlv{ImageRefType, someEvent}) + appendPcrEventOrFatal(t, cel, tpm, test.ApplicationPCR, measuredHashes, CosTlv{ImageDigestType, someEvent2}) + replay(t, cel, tpm, measuredHashes, []int{test.DebugPCR}, false /*shouldSucceed*/) replay(t, cel, tpm, measuredHashes, @@ -155,21 +181,50 @@ func replay(t *testing.T, cel *CEL, tpm io.ReadWriteCloser, measuredHashes []cry if err != nil { t.Fatal(err) } - pbPcr := &pb.PCRs{Hash: pb.HashAlgo(tpm2Hash), - Pcrs: map[uint32][]byte{}, - } + + pcrBank := register.PCRBank{TCGHashAlgo: state.HashAlgo(tpm2Hash)} for index, val := range pcrMap { - pbPcr.Pcrs[uint32(index)] = val + pcrBank.PCRs = append(pcrBank.PCRs, register.PCR{ + Index: index, + Digest: val, + DigestAlg: hash}) } - if err := cel.Replay(pbPcr); shouldSucceed && err != nil { + + if err := cel.Replay(pcrBank); shouldSucceed && err != nil { t.Errorf("failed to replay CEL on %v bank: %v", - pb.HashAlgo_name[int32(pbPcr.Hash)], err) + hash, err) + } + } +} + +func replayRTMR(t *testing.T, cel *CEL, rtmr *fakertmr.RtmrSubsystem, rtmrs []int, shouldSucceed bool) { + rtmrBank := register.RTMRBank{} + + // RTMR 0 to 3 + for _, rtmrIndex := range rtmrs { + digest, err := configfstsmrtmr.GetDigest(rtmr, rtmrIndex) + if err != nil { + t.Fatal(err) } + + rtmrBank.RTMRs = append(rtmrBank.RTMRs, register.RTMR{ + Index: rtmrIndex, + Digest: digest.Digest}) + } + + if err := cel.Replay(rtmrBank); shouldSucceed && err != nil { + t.Errorf("failed to replay RTMR: %v", err) + } +} + +func appendPcrEventOrFatal(t *testing.T, cel *CEL, tpm io.ReadWriteCloser, pcr int, hashAlgos []crypto.Hash, event Content) { + if err := cel.AppendEventPCR(tpm, pcr, hashAlgos, event); err != nil { + t.Fatalf("failed to append PCR event: %v", err) } } -func appendOrFatal(t *testing.T, cel *CEL, tpm io.ReadWriteCloser, pcr int, hashAlgos []crypto.Hash, event Content) { - if err := cel.AppendEvent(tpm, pcr, hashAlgos, event); err != nil { - t.Fatalf("failed to append event: %v", err) +func appendRtmrEventOrFatal(t *testing.T, cel *CEL, rtmrClient configfsi.Client, rtmr int, event Content) { + if err := cel.AppendEventRTMR(rtmrClient, rtmr, event); err != nil { + t.Fatalf("failed to append RTMR event: %v", err) } } diff --git a/cel/cos_tlv.go b/cel/cos_tlv.go index 5cbd2ccd8..0cff062dc 100644 --- a/cel/cos_tlv.go +++ b/cel/cos_tlv.go @@ -14,6 +14,18 @@ const ( CosEventType uint8 = 80 // CosEventPCR is the PCR which should be used for CosEventType events. CosEventPCR = 13 + // CosRTMR is the RTMR to be extended for COS events + // According to https://uefi.org/specs/UEFI/2.10/38_Confidential_Computing.html + // CCELMRIndex TDX Register + // 0 MRTD + // 1 RTMR[0] + // 2 RTMR[1] + // 3 RTMR[2] + // So: + // 4 RTMR[3] + CosRTMR = 3 + // CosCCELMRIndex is the CCMR index to use in eventlog for COS events. + CosCCELMRIndex = 4 ) // CosType represent a COS content type in a CEL record content. diff --git a/cel/cos_tlv_test.go b/cel/cos_tlv_test.go index 73934bcd4..c7486b4a1 100644 --- a/cel/cos_tlv_test.go +++ b/cel/cos_tlv_test.go @@ -14,7 +14,6 @@ import ( func TestCosEventlog(t *testing.T) { tpm := test.GetTPM(t) defer client.CheckedClose(t, tpm) - cel := &CEL{} testEvents := []struct { @@ -41,9 +40,10 @@ func TestCosEventlog(t *testing.T) { } for _, testEvent := range testEvents { - cos := CosTlv{testEvent.cosNestedEventType, testEvent.eventPayload} - if err := cel.AppendEvent(tpm, testEvent.pcr, measuredHashes, cos); err != nil { - t.Fatal(err.Error()) + cosEvent := CosTlv{testEvent.cosNestedEventType, testEvent.eventPayload} + + if err := cel.AppendEventPCR(tpm, testEvent.pcr, measuredHashes, cosEvent); err != nil { + t.Fatal(err) } } diff --git a/go.mod b/go.mod index 185093f61..d80707976 100644 --- a/go.mod +++ b/go.mod @@ -6,17 +6,18 @@ require ( github.com/google/gce-tcb-verifier v0.2.3-0.20240905212129-12f728a62786 github.com/google/go-attestation v0.5.1 github.com/google/go-cmp v0.6.0 + github.com/google/go-configfs-tsm v0.3.3-0.20240910040719-1cc7e25d9272 github.com/google/go-sev-guest v0.11.1 - github.com/google/go-tdx-guest v0.3.1 + github.com/google/go-tdx-guest v0.3.2-0.20240902060211-1f7f7b9b42b9 github.com/google/go-tpm v0.9.0 github.com/google/logger v1.1.1 - google.golang.org/protobuf v1.34.1 + google.golang.org/protobuf v1.34.2 ) require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/certificate-transparency-go v1.1.2 // indirect - github.com/google/go-configfs-tsm v0.2.2 // indirect + github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba github.com/google/go-tspi v0.3.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/go.sum b/go.sum index 7829ceca9..a1fa1a630 100644 --- a/go.sum +++ b/go.sum @@ -306,8 +306,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98= -github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= +github.com/google/go-configfs-tsm v0.3.3-0.20240910040719-1cc7e25d9272 h1:ut0AQwF/hqo+dY3FiGtTJ8E8TMCg4PgRdj/6Z79hY9k= +github.com/google/go-configfs-tsm v0.3.3-0.20240910040719-1cc7e25d9272/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= +github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba h1:05m5+kgZjxYUZrx3bZfkKHl6wkch+Khao6N21rFHInk= +github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba/go.mod h1:7huE5P8w2NTObSwSJjboHmB7ioBNblkijdzoVa2skfQ= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -315,8 +317,8 @@ github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVgg github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= github.com/google/go-sev-guest v0.11.1 h1:gnww4U8fHV5DCPz4gykr1s8SEX1fFNcxCBy+vvXN24k= github.com/google/go-sev-guest v0.11.1/go.mod h1:qBOfb+JmgsUI3aUyzQoGC13Kpp9zwLeWvuyXmA9q77w= -github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw= -github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE= +github.com/google/go-tdx-guest v0.3.2-0.20240902060211-1f7f7b9b42b9 h1:hk7vjuJgvYnHMZYI0cIDSXiC5XBmOlzRotA5bJ7nb+c= +github.com/google/go-tdx-guest v0.3.2-0.20240902060211-1f7f7b9b42b9/go.mod h1:g/n8sKITIT9xRivBUbizo34DTsUm2nN2uU3A662h09g= github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= @@ -1222,8 +1224,8 @@ google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX7 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/go.work.sum b/go.work.sum index 31a3480c8..73dd10603 100644 --- a/go.work.sum +++ b/go.work.sum @@ -686,6 +686,7 @@ github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -693,7 +694,9 @@ github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZat github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-configfs-tsm v0.3.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= github.com/google/go-containerregistry v0.14.0/go.mod h1:aiJ2fp/SXvkWgmYHioXnbMdlgB8eXiiYOY55gfN91Wk= +github.com/google/go-eventlog v0.0.1/go.mod h1:7huE5P8w2NTObSwSJjboHmB7ioBNblkijdzoVa2skfQ= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-sev-guest v0.8.0/go.mod h1:hc1R4R6f8+NcJwITs0L90fYWTsBpd1Ix+Gur15sqHDs= github.com/google/go-tpm-tools v0.4.2/go.mod h1:fGUDZu4tw3V4hUVuFHmiYgRd0c58/IXivn9v3Ea/ck4= @@ -703,6 +706,7 @@ github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww= github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= @@ -876,6 +880,7 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= @@ -944,6 +949,7 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/launcher/go.mod b/launcher/go.mod index cd012c0f8..a7da7f3be 100644 --- a/launcher/go.mod +++ b/launcher/go.mod @@ -45,9 +45,10 @@ require ( github.com/google/certificate-transparency-go v1.1.2 // indirect github.com/google/gce-tcb-verifier v0.2.3-0.20240905212129-12f728a62786 // indirect github.com/google/go-attestation v0.5.1 // indirect - github.com/google/go-configfs-tsm v0.2.2 // indirect + github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc // indirect + github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba // indirect github.com/google/go-sev-guest v0.11.1 // indirect - github.com/google/go-tdx-guest v0.3.1 // indirect + github.com/google/go-tdx-guest v0.3.2-0.20240902060211-1f7f7b9b42b9 // indirect github.com/google/go-tspi v0.3.0 // indirect github.com/google/logger v1.1.1 // indirect github.com/google/s2a-go v0.1.7 // indirect diff --git a/launcher/go.sum b/launcher/go.sum index f81ac130c..784880023 100644 --- a/launcher/go.sum +++ b/launcher/go.sum @@ -354,8 +354,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98= -github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= +github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc h1:SG12DWUUM5igxm+//YX5Yq4vhdoRnOG9HkCodkOn+YU= +github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= +github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba h1:05m5+kgZjxYUZrx3bZfkKHl6wkch+Khao6N21rFHInk= +github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba/go.mod h1:7huE5P8w2NTObSwSJjboHmB7ioBNblkijdzoVa2skfQ= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -363,8 +365,8 @@ github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVgg github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= github.com/google/go-sev-guest v0.11.1 h1:gnww4U8fHV5DCPz4gykr1s8SEX1fFNcxCBy+vvXN24k= github.com/google/go-sev-guest v0.11.1/go.mod h1:qBOfb+JmgsUI3aUyzQoGC13Kpp9zwLeWvuyXmA9q77w= -github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw= -github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE= +github.com/google/go-tdx-guest v0.3.2-0.20240902060211-1f7f7b9b42b9 h1:hk7vjuJgvYnHMZYI0cIDSXiC5XBmOlzRotA5bJ7nb+c= +github.com/google/go-tdx-guest v0.3.2-0.20240902060211-1f7f7b9b42b9/go.mod h1:g/n8sKITIT9xRivBUbizo34DTsUm2nN2uU3A662h09g= github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= diff --git a/proto/attest.proto b/proto/attest.proto index 23420a053..3385408c0 100644 --- a/proto/attest.proto +++ b/proto/attest.proto @@ -29,7 +29,7 @@ message Attestation { bytes event_log = 3; // Optional information about a GCE instance, unused outside of GCE GCEInstanceInfo instance_info = 4; - // A TCG Canonical Event Log. + // A COS event log using the TCG Canonical Event Log format bytes canonical_event_log = 5; // Attestation Key (AK) Certificate, encoded as ASN.1 DER. // Optional. diff --git a/proto/attest/attest.pb.go b/proto/attest/attest.pb.go index 77484c11a..8be2672ed 100644 --- a/proto/attest/attest.pb.go +++ b/proto/attest/attest.pb.go @@ -284,7 +284,7 @@ type Attestation struct { EventLog []byte `protobuf:"bytes,3,opt,name=event_log,json=eventLog,proto3" json:"event_log,omitempty"` // Optional information about a GCE instance, unused outside of GCE InstanceInfo *GCEInstanceInfo `protobuf:"bytes,4,opt,name=instance_info,json=instanceInfo,proto3" json:"instance_info,omitempty"` - // A TCG Canonical Event Log. + // A COS event log using the TCG Canonical Event Log format CanonicalEventLog []byte `protobuf:"bytes,5,opt,name=canonical_event_log,json=canonicalEventLog,proto3" json:"canonical_event_log,omitempty"` // Attestation Key (AK) Certificate, encoded as ASN.1 DER. // Optional. diff --git a/server/eventlog.go b/server/eventlog.go index 0518ef4ef..97f4cb8e6 100644 --- a/server/eventlog.go +++ b/server/eventlog.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/google/go-attestation/attest" + "github.com/google/go-eventlog/register" "github.com/google/go-tpm-tools/cel" pb "github.com/google/go-tpm-tools/proto/attest" tpmpb "github.com/google/go-tpm-tools/proto/tpm" @@ -88,17 +89,29 @@ func parsePCClientEventLog(rawEventLog []byte, pcrs *tpmpb.PCRs, loader Bootload }, createGroupedError("failed to fully parse MachineState:", errors) } -func parseCanonicalEventLog(rawCanonicalEventLog []byte, pcrs *tpmpb.PCRs) (*pb.MachineState, error) { +// ParseCosCELPCR takes an encoded COS CEL and PCR bank, replays the CEL against the PCRs, +// and returns the AttestedCosState +func ParseCosCELPCR(cosEventLog []byte, p register.PCRBank) (*pb.MachineState, error) { + return getCosStateFromCEL(cosEventLog, p, cel.PCRTypeValue) +} + +// ParseCosCELRTMR takes in a raw COS CEL and a RTMR bank, validates and returns it's +// COS states as parts of the MachineState. +func ParseCosCELRTMR(cosEventLog []byte, r register.RTMRBank) (*pb.MachineState, error) { + return getCosStateFromCEL(cosEventLog, r, cel.CCMRTypeValue) +} + +func getCosStateFromCEL(rawCanonicalEventLog []byte, register register.MRBank, trustingRegisterType uint8) (*pb.MachineState, error) { decodedCEL, err := cel.DecodeToCEL(bytes.NewBuffer(rawCanonicalEventLog)) if err != nil { return nil, err } // Validate the COS event log first. - if err := decodedCEL.Replay(pcrs); err != nil { + if err := decodedCEL.Replay(register); err != nil { return nil, err } - cosState, err := getVerifiedCosState(decodedCEL) + cosState, err := getVerifiedCosState(decodedCEL, trustingRegisterType) if err != nil { return nil, err } @@ -117,7 +130,9 @@ func contains(set [][]byte, value []byte) bool { return false } -func getVerifiedCosState(coscel cel.CEL) (*pb.AttestedCosState, error) { +// getVerifiedCosState takes in CEL and a register type (can be PCR or CCELMR), and returns the state +// in the CEL. It will only include events using the correct registerType. +func getVerifiedCosState(coscel cel.CEL, registerType uint8) (*pb.AttestedCosState, error) { cosState := &pb.AttestedCosState{} cosState.Container = &pb.ContainerState{} cosState.HealthMonitoring = &pb.HealthMonitoringState{} @@ -127,9 +142,21 @@ func getVerifiedCosState(coscel cel.CEL) (*pb.AttestedCosState, error) { seenSeparator := false for _, record := range coscel.Records { - // COS State only comes from the CosEventPCR - if record.PCR != cel.CosEventPCR { - return nil, fmt.Errorf("found unexpected PCR %d in CEL log", record.PCR) + if record.IndexType != registerType { + return nil, fmt.Errorf("expect registerType: %d, but get %d in a CEL record", registerType, record.IndexType) + } + + switch record.IndexType { + case cel.PCRTypeValue: + if record.Index != cel.CosEventPCR { + return nil, fmt.Errorf("found unexpected PCR %d in COS CEL log", record.Index) + } + case cel.CCMRTypeValue: + if record.Index != cel.CosCCELMRIndex { + return nil, fmt.Errorf("found unexpected CCELMR %d in COS CEL log", record.Index) + } + default: + return nil, fmt.Errorf("unknown COS CEL log index type %d", record.IndexType) } // The Content.Type is not verified at this point, so we have to fail @@ -316,7 +343,7 @@ func getPlatformState(hash crypto.Hash, events []*pb.Event) (*pb.PlatformState, // Separate helper function so we can use attest.ParseSecurebootState without // needing to reparse the entire event log. func parseReplayHelper(rawEventLog []byte, pcrs *tpmpb.PCRs) ([]attest.Event, error) { - // Similar to parseCanonicalEventLog, just return an empty array of events for an empty log + // Similar to ParseCosCanonicalEventLogPCR, just return an empty array of events for an empty log if len(rawEventLog) == 0 { return nil, nil } diff --git a/server/eventlog_test.go b/server/eventlog_test.go index 18c8112c0..59002f377 100644 --- a/server/eventlog_test.go +++ b/server/eventlog_test.go @@ -10,6 +10,10 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/google/go-configfs-tsm/configfs/fakertmr" + configfstsmrtmr "github.com/google/go-configfs-tsm/rtmr" + "github.com/google/go-eventlog/proto/state" + "github.com/google/go-eventlog/register" "github.com/google/go-tpm-tools/cel" "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/internal/test" @@ -689,6 +693,139 @@ func TestParseSecureBootState(t *testing.T) { } } +func convertToPCRBank(t *testing.T, pcrs *pb.PCRs) register.PCRBank { + pcrBank := register.PCRBank{TCGHashAlgo: state.HashAlgo(pcrs.Hash)} + digestAlg, err := pcrBank.TCGHashAlgo.CryptoHash() + if err != nil { + t.Fatal(err) + } + for ind, dgst := range pcrs.GetPcrs() { + pcrBank.PCRs = append(pcrBank.PCRs, register.PCR{ + Index: int(ind), + Digest: dgst, + DigestAlg: digestAlg}, + ) + } + return pcrBank +} + +func getRTMRBank(t *testing.T, fakeRTMR *fakertmr.RtmrSubsystem) register.RTMRBank { + rtmrBank := register.RTMRBank{} + // RTMR 0 to 3 + for i := 0; i < 4; i++ { + digest, err := configfstsmrtmr.GetDigest(fakeRTMR, i) + if err != nil { + t.Fatal(err) + } + rtmrBank.RTMRs = append(rtmrBank.RTMRs, register.RTMR{Index: i, Digest: digest.Digest}) + } + return rtmrBank +} + +func TestParsingRTMREventlog(t *testing.T) { + coscel := &cel.CEL{} + emptyCosState := attestpb.ContainerState{} + emptyHealthMonitoringState := attestpb.HealthMonitoringState{} + + var buf bytes.Buffer + // First, encode an empty CEL and try to parse it. + if err := coscel.EncodeCEL(&buf); err != nil { + t.Fatal(err) + } + + fakeRTMR := fakertmr.CreateRtmrSubsystem(t.TempDir()) + rtmrBank := getRTMRBank(t, fakeRTMR) + + msState, err := ParseCosCELRTMR(buf.Bytes(), rtmrBank) + if err != nil { + t.Errorf("expecting no error from ParseCosCELRTMR(), but get %v", err) + } + if diff := cmp.Diff(msState.Cos.Container, &emptyCosState, protocmp.Transform()); diff != "" { + t.Errorf("unexpected container state difference:\n%v", diff) + } + if diff := cmp.Diff(msState.Cos.HealthMonitoring, &emptyHealthMonitoringState, protocmp.Transform()); diff != "" { + t.Errorf("unexpected health monitoring difference:\n%v", diff) + } + if msState.Cos.HealthMonitoring.MemoryEnabled != nil { + t.Errorf("unexpected MemoryEnabled state, want nil, but got %v", *msState.Cos.HealthMonitoring.MemoryEnabled) + } + + // add events + testCELEvents := []struct { + cosNestedEventType cel.CosType + register int + eventPayload []byte + }{ + {cel.ImageRefType, cel.CosRTMR, []byte("docker.io/bazel/experimental/test:latest")}, + {cel.ImageDigestType, cel.CosRTMR, []byte("sha256:781d8dfdd92118436bd914442c8339e653b83f6bf3c1a7a98efcfb7c4fed7483")}, + {cel.RestartPolicyType, cel.CosRTMR, []byte(attestpb.RestartPolicy_Always.String())}, + {cel.ImageIDType, cel.CosRTMR, []byte("sha256:5DF4A1AC347DCF8CF5E9D0ABC04B04DB847D1B88D3B1CC1006F0ACB68E5A1F4B")}, + {cel.EnvVarType, cel.CosRTMR, []byte("foo=bar")}, + {cel.EnvVarType, cel.CosRTMR, []byte("bar=baz")}, + {cel.EnvVarType, cel.CosRTMR, []byte("baz=foo=bar")}, + {cel.EnvVarType, cel.CosRTMR, []byte("empty=")}, + {cel.ArgType, cel.CosRTMR, []byte("--x")}, + {cel.ArgType, cel.CosRTMR, []byte("--y")}, + {cel.ArgType, cel.CosRTMR, []byte("")}, + {cel.MemoryMonitorType, cel.CosRTMR, []byte{1}}, + } + + expectedEnvVars := make(map[string]string) + expectedEnvVars["foo"] = "bar" + expectedEnvVars["bar"] = "baz" + expectedEnvVars["baz"] = "foo=bar" + expectedEnvVars["empty"] = "" + + wantContainerState := attestpb.ContainerState{ + ImageReference: string(testCELEvents[0].eventPayload), + ImageDigest: string(testCELEvents[1].eventPayload), + RestartPolicy: attestpb.RestartPolicy_Always, + ImageId: string(testCELEvents[3].eventPayload), + EnvVars: expectedEnvVars, + Args: []string{string(testCELEvents[8].eventPayload), string(testCELEvents[9].eventPayload), string(testCELEvents[10].eventPayload)}, + } + enabled := true + wantHealthMonitoringState := attestpb.HealthMonitoringState{ + MemoryEnabled: &enabled, + } + + for _, testEvent := range testCELEvents { + cosEvent := cel.CosTlv{EventType: testEvent.cosNestedEventType, EventContent: testEvent.eventPayload} + if err := coscel.AppendEventRTMR(fakeRTMR, testEvent.register, cosEvent); err != nil { + t.Fatal(err) + } + } + buf = bytes.Buffer{} + if err := coscel.EncodeCEL(&buf); err != nil { + t.Fatal(err) + } + + rtmrBank = getRTMRBank(t, fakeRTMR) + + if msState, err := ParseCosCELRTMR(buf.Bytes(), rtmrBank); err != nil { + t.Errorf("expecting no error from ParseCosCELRTMR(), but get %v", err) + } else { + if diff := cmp.Diff(msState.Cos.Container, &wantContainerState, protocmp.Transform()); diff != "" { + t.Errorf("unexpected container state difference:\n%v", diff) + } + if diff := cmp.Diff(msState.Cos.HealthMonitoring, &wantHealthMonitoringState, protocmp.Transform()); diff != "" { + t.Errorf("unexpected health monitoring state difference:\n%v", diff) + } + } + + // Faking PCR with RTMR should fail + imposterPcrBank := map[uint32][]byte{} + imposterPcrBank[1] = rtmrBank.RTMRs[0].Digest + imposterPcrBank[2] = rtmrBank.RTMRs[1].Digest + imposterPcrBank[3] = rtmrBank.RTMRs[2].Digest + imposterPcrBank[4] = rtmrBank.RTMRs[3].Digest + imposterPcrs := &pb.PCRs{Hash: pb.HashAlgo_SHA384, Pcrs: imposterPcrBank} + hackedPCRBank := convertToPCRBank(t, imposterPcrs) + if _, err = ParseCosCELPCR(buf.Bytes(), hackedPCRBank); err == nil { + t.Errorf("expecting error from ParseCosCELPCR() when using RTMR CEL Log, but get nil") + } +} + func TestParsingCELEventLog(t *testing.T) { test.SkipForRealTPM(t) tpm := test.GetTPM(t) @@ -719,10 +856,11 @@ func TestParsingCELEventLog(t *testing.T) { } for _, bank := range banks { + pcrBank := convertToPCRBank(t, bank) // pcrs can have any value here, since the coscel has no records, the replay should always success. - msState, err := parseCanonicalEventLog(buf.Bytes(), bank) + msState, err := ParseCosCELPCR(buf.Bytes(), pcrBank) if err != nil { - t.Errorf("expecting no error from parseCanonicalEventLog(), but get %v", err) + t.Errorf("expecting no error from ParseCosCELPCR(), but get %v", err) } if diff := cmp.Diff(msState.Cos.Container, &emptyCosState, protocmp.Transform()); diff != "" { t.Errorf("unexpected container state difference:\n%v", diff) @@ -774,8 +912,9 @@ func TestParsingCELEventLog(t *testing.T) { MemoryEnabled: &enabled, } for _, testEvent := range testCELEvents { - cos := cel.CosTlv{EventType: testEvent.cosNestedEventType, EventContent: testEvent.eventPayload} - if err := coscel.AppendEvent(tpm, testEvent.pcr, implementedHashes, cos); err != nil { + cosEvent := cel.CosTlv{EventType: testEvent.cosNestedEventType, EventContent: testEvent.eventPayload} + + if err := coscel.AppendEventPCR(tpm, testEvent.pcr, implementedHashes, cosEvent); err != nil { t.Fatal(err) } } @@ -788,8 +927,10 @@ func TestParsingCELEventLog(t *testing.T) { t.Fatal(err) } for _, bank := range banks { - if msState, err := parseCanonicalEventLog(buf.Bytes(), bank); err != nil { - t.Errorf("expecting no error from parseCanonicalEventLog(), but get %v", err) + pcrBank := convertToPCRBank(t, bank) + + if msState, err := ParseCosCELPCR(buf.Bytes(), pcrBank); err != nil { + t.Errorf("expecting no error from ParseCosCELPCR(), but get %v", err) } else { if diff := cmp.Diff(msState.Cos.Container, &wantContainerState, protocmp.Transform()); diff != "" { t.Errorf("unexpected container state difference:\n%v", diff) @@ -827,7 +968,8 @@ func TestParsingCELEventLog(t *testing.T) { t.Fatal(err) } for _, bank := range banks { - _, err := parseCanonicalEventLog(buf.Bytes(), bank) + pcrBank := convertToPCRBank(t, bank) + _, err := ParseCosCELPCR(buf.Bytes(), pcrBank) if err == nil { t.Errorf("expected error when parsing event log with unknown content type") } @@ -837,7 +979,7 @@ func TestParsingCELEventLog(t *testing.T) { func generateNonCosCelEvent(hashAlgoList []crypto.Hash) (cel.Record, error) { randRecord := cel.Record{} randRecord.RecNum = 0 - randRecord.PCR = cel.CosEventPCR + randRecord.Index = cel.CosEventPCR contentValue := make([]byte, 10) rand.Read(contentValue) randRecord.Content = cel.TLV{Type: 250, Value: contentValue} diff --git a/server/verify.go b/server/verify.go index b0fa77f25..f8e41b5f6 100644 --- a/server/verify.go +++ b/server/verify.go @@ -8,6 +8,8 @@ import ( "errors" "fmt" + "github.com/google/go-eventlog/proto/state" + "github.com/google/go-eventlog/register" "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/go-tdx-guest/proto/tdx" "github.com/google/go-tpm-tools/internal" @@ -124,13 +126,26 @@ func VerifyAttestation(attestation *pb.Attestation, opts VerifyOpts) (*pb.Machin // Parse event logs and replay the events against the provided PCRs pcrs := quote.GetPcrs() - state, err := parseMachineStateFromTPM(attestation, pcrs, opts) + tpmMachineState, err := parseMachineStateFromTPM(attestation, pcrs, opts) if err != nil { lastErr = fmt.Errorf("failed to parse machine state from TCG event log: %w", err) continue } - celState, err := parseCanonicalEventLog(attestation.GetCanonicalEventLog(), pcrs) + pcrBank := register.PCRBank{TCGHashAlgo: state.HashAlgo(pcrs.Hash)} + digestAlg, err := pcrBank.TCGHashAlgo.CryptoHash() + if err != nil { + return nil, fmt.Errorf("invalid digest algorithm") + } + + for pcrIndex, digest := range pcrs.GetPcrs() { + pcrBank.PCRs = append(pcrBank.PCRs, register.PCR{ + Index: int(pcrIndex), + Digest: digest, + DigestAlg: digestAlg}) + } + + celState, err := ParseCosCELPCR(attestation.GetCanonicalEventLog(), pcrBank) if err != nil { lastErr = fmt.Errorf("failed to validate the Canonical event log: %w", err) continue @@ -146,7 +161,7 @@ func VerifyAttestation(attestation *pb.Attestation, opts VerifyOpts) (*pb.Machin } proto.Merge(machineState, celState) - proto.Merge(machineState, state) + proto.Merge(machineState, tpmMachineState) return machineState, nil } @@ -397,6 +412,8 @@ func parseMachineStateFromTPM(attestation *pb.Attestation, pcrs *tpmpb.PCRs, opt return nil, fmt.Errorf("failed to validate the PCClient event log: %w", err) } + // TODO #500 remove verifyGceTechnology from parseMachineStateFromTPM/VerifyAttestation, + // as it's an application speicified operation. tech := ms.GetPlatform().Technology if err := verifyGceTechnology(attestation, tech, &opts); err != nil { return nil, fmt.Errorf("failed to verify memory encryption technology: %w", err) diff --git a/server/verify_test.go b/server/verify_test.go index 0d0508b30..573a592b2 100644 --- a/server/verify_test.go +++ b/server/verify_test.go @@ -459,8 +459,8 @@ func TestVerifyAttestationWithCEL(t *testing.T) { {cel.MemoryMonitorType, cel.CosEventPCR, []byte{1}}, } for _, testEvent := range testEvents { - cos := cel.CosTlv{EventType: testEvent.cosNestedEventType, EventContent: testEvent.eventPayload} - if err := coscel.AppendEvent(rwc, testEvent.pcr, measuredHashes, cos); err != nil { + cosEvent := cel.CosTlv{EventType: testEvent.cosNestedEventType, EventContent: testEvent.eventPayload} + if err := coscel.AppendEventPCR(rwc, testEvent.pcr, measuredHashes, cosEvent); err != nil { t.Fatal(err) } } @@ -531,10 +531,12 @@ func TestVerifyFailWithTamperedCELContent(t *testing.T) { cosEvent := cel.CosTlv{EventType: cel.ImageRefType, EventContent: []byte("docker.io/bazel/experimental/test:latest")} cosEvent2 := cel.CosTlv{EventType: cel.ImageDigestType, EventContent: []byte("sha256:781d8dfdd92118436bd914442c8339e653b83f6bf3c1a7a98efcfb7c4fed7483")} - if err := c.AppendEvent(rwc, cel.CosEventPCR, measuredHashes, cosEvent); err != nil { + + if err := c.AppendEventPCR(rwc, cel.CosEventPCR, measuredHashes, cosEvent); err != nil { t.Fatalf("failed to append event: %v", err) } - if err := c.AppendEvent(rwc, cel.CosEventPCR, measuredHashes, cosEvent2); err != nil { + + if err := c.AppendEventPCR(rwc, cel.CosEventPCR, measuredHashes, cosEvent2); err != nil { t.Fatalf("failed to append event: %v", err) } diff --git a/verifier/go.mod b/verifier/go.mod index 10ce8fae5..717c00e0e 100644 --- a/verifier/go.mod +++ b/verifier/go.mod @@ -10,6 +10,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-cmp v0.6.0 github.com/google/go-sev-guest v0.11.1 + github.com/google/go-tdx-guest v0.3.2-0.20240902060211-1f7f7b9b42b9 github.com/google/go-tpm v0.9.0 github.com/google/go-tpm-tools v0.4.4 github.com/opencontainers/go-digest v1.0.0 @@ -35,8 +36,8 @@ require ( github.com/google/certificate-transparency-go v1.1.2 // indirect github.com/google/gce-tcb-verifier v0.2.3-0.20240905212129-12f728a62786 // indirect github.com/google/go-attestation v0.5.1 // indirect - github.com/google/go-configfs-tsm v0.2.2 // indirect - github.com/google/go-tdx-guest v0.3.1 // indirect + github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc // indirect + github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba // indirect github.com/google/go-tspi v0.3.0 // indirect github.com/google/logger v1.1.1 // indirect github.com/google/s2a-go v0.1.7 // indirect diff --git a/verifier/go.sum b/verifier/go.sum index be929d161..6a69d1572 100644 --- a/verifier/go.sum +++ b/verifier/go.sum @@ -323,8 +323,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98= -github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= +github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc h1:SG12DWUUM5igxm+//YX5Yq4vhdoRnOG9HkCodkOn+YU= +github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= +github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba h1:05m5+kgZjxYUZrx3bZfkKHl6wkch+Khao6N21rFHInk= +github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba/go.mod h1:7huE5P8w2NTObSwSJjboHmB7ioBNblkijdzoVa2skfQ= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -332,8 +334,8 @@ github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVgg github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= github.com/google/go-sev-guest v0.11.1 h1:gnww4U8fHV5DCPz4gykr1s8SEX1fFNcxCBy+vvXN24k= github.com/google/go-sev-guest v0.11.1/go.mod h1:qBOfb+JmgsUI3aUyzQoGC13Kpp9zwLeWvuyXmA9q77w= -github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw= -github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE= +github.com/google/go-tdx-guest v0.3.2-0.20240902060211-1f7f7b9b42b9 h1:hk7vjuJgvYnHMZYI0cIDSXiC5XBmOlzRotA5bJ7nb+c= +github.com/google/go-tdx-guest v0.3.2-0.20240902060211-1f7f7b9b42b9/go.mod h1:g/n8sKITIT9xRivBUbizo34DTsUm2nN2uU3A662h09g= github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= From 6405dbb2414fb6c1306bcab50b57958af4b55e23 Mon Sep 17 00:00:00 2001 From: Alex Wu Date: Thu, 3 Oct 2024 13:58:13 -0700 Subject: [PATCH 4/4] Change ParseCosCEL* to return an AttestedCosState (#501) --- server/eventlog.go | 10 ++++------ server/eventlog_test.go | 32 ++++++++++++++++---------------- server/verify.go | 2 +- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/server/eventlog.go b/server/eventlog.go index 97f4cb8e6..6056a5fa0 100644 --- a/server/eventlog.go +++ b/server/eventlog.go @@ -91,17 +91,17 @@ func parsePCClientEventLog(rawEventLog []byte, pcrs *tpmpb.PCRs, loader Bootload // ParseCosCELPCR takes an encoded COS CEL and PCR bank, replays the CEL against the PCRs, // and returns the AttestedCosState -func ParseCosCELPCR(cosEventLog []byte, p register.PCRBank) (*pb.MachineState, error) { +func ParseCosCELPCR(cosEventLog []byte, p register.PCRBank) (*pb.AttestedCosState, error) { return getCosStateFromCEL(cosEventLog, p, cel.PCRTypeValue) } // ParseCosCELRTMR takes in a raw COS CEL and a RTMR bank, validates and returns it's // COS states as parts of the MachineState. -func ParseCosCELRTMR(cosEventLog []byte, r register.RTMRBank) (*pb.MachineState, error) { +func ParseCosCELRTMR(cosEventLog []byte, r register.RTMRBank) (*pb.AttestedCosState, error) { return getCosStateFromCEL(cosEventLog, r, cel.CCMRTypeValue) } -func getCosStateFromCEL(rawCanonicalEventLog []byte, register register.MRBank, trustingRegisterType uint8) (*pb.MachineState, error) { +func getCosStateFromCEL(rawCanonicalEventLog []byte, register register.MRBank, trustingRegisterType uint8) (*pb.AttestedCosState, error) { decodedCEL, err := cel.DecodeToCEL(bytes.NewBuffer(rawCanonicalEventLog)) if err != nil { return nil, err @@ -116,9 +116,7 @@ func getCosStateFromCEL(rawCanonicalEventLog []byte, register register.MRBank, t return nil, err } - return &pb.MachineState{ - Cos: cosState, - }, err + return cosState, err } func contains(set [][]byte, value []byte) bool { diff --git a/server/eventlog_test.go b/server/eventlog_test.go index 59002f377..c232296aa 100644 --- a/server/eventlog_test.go +++ b/server/eventlog_test.go @@ -736,18 +736,18 @@ func TestParsingRTMREventlog(t *testing.T) { fakeRTMR := fakertmr.CreateRtmrSubsystem(t.TempDir()) rtmrBank := getRTMRBank(t, fakeRTMR) - msState, err := ParseCosCELRTMR(buf.Bytes(), rtmrBank) + acosState, err := ParseCosCELRTMR(buf.Bytes(), rtmrBank) if err != nil { t.Errorf("expecting no error from ParseCosCELRTMR(), but get %v", err) } - if diff := cmp.Diff(msState.Cos.Container, &emptyCosState, protocmp.Transform()); diff != "" { + if diff := cmp.Diff(acosState.Container, &emptyCosState, protocmp.Transform()); diff != "" { t.Errorf("unexpected container state difference:\n%v", diff) } - if diff := cmp.Diff(msState.Cos.HealthMonitoring, &emptyHealthMonitoringState, protocmp.Transform()); diff != "" { + if diff := cmp.Diff(acosState.HealthMonitoring, &emptyHealthMonitoringState, protocmp.Transform()); diff != "" { t.Errorf("unexpected health monitoring difference:\n%v", diff) } - if msState.Cos.HealthMonitoring.MemoryEnabled != nil { - t.Errorf("unexpected MemoryEnabled state, want nil, but got %v", *msState.Cos.HealthMonitoring.MemoryEnabled) + if acosState.HealthMonitoring.MemoryEnabled != nil { + t.Errorf("unexpected MemoryEnabled state, want nil, but got %v", *acosState.HealthMonitoring.MemoryEnabled) } // add events @@ -802,13 +802,13 @@ func TestParsingRTMREventlog(t *testing.T) { rtmrBank = getRTMRBank(t, fakeRTMR) - if msState, err := ParseCosCELRTMR(buf.Bytes(), rtmrBank); err != nil { + if acosState, err := ParseCosCELRTMR(buf.Bytes(), rtmrBank); err != nil { t.Errorf("expecting no error from ParseCosCELRTMR(), but get %v", err) } else { - if diff := cmp.Diff(msState.Cos.Container, &wantContainerState, protocmp.Transform()); diff != "" { + if diff := cmp.Diff(acosState.Container, &wantContainerState, protocmp.Transform()); diff != "" { t.Errorf("unexpected container state difference:\n%v", diff) } - if diff := cmp.Diff(msState.Cos.HealthMonitoring, &wantHealthMonitoringState, protocmp.Transform()); diff != "" { + if diff := cmp.Diff(acosState.HealthMonitoring, &wantHealthMonitoringState, protocmp.Transform()); diff != "" { t.Errorf("unexpected health monitoring state difference:\n%v", diff) } } @@ -858,18 +858,18 @@ func TestParsingCELEventLog(t *testing.T) { for _, bank := range banks { pcrBank := convertToPCRBank(t, bank) // pcrs can have any value here, since the coscel has no records, the replay should always success. - msState, err := ParseCosCELPCR(buf.Bytes(), pcrBank) + acosState, err := ParseCosCELPCR(buf.Bytes(), pcrBank) if err != nil { t.Errorf("expecting no error from ParseCosCELPCR(), but get %v", err) } - if diff := cmp.Diff(msState.Cos.Container, &emptyCosState, protocmp.Transform()); diff != "" { + if diff := cmp.Diff(acosState.Container, &emptyCosState, protocmp.Transform()); diff != "" { t.Errorf("unexpected container state difference:\n%v", diff) } - if diff := cmp.Diff(msState.Cos.HealthMonitoring, &emptyHealthMonitoringState, protocmp.Transform()); diff != "" { + if diff := cmp.Diff(acosState.HealthMonitoring, &emptyHealthMonitoringState, protocmp.Transform()); diff != "" { t.Errorf("unexpected health monitoring difference:\n%v", diff) } - if msState.Cos.HealthMonitoring.MemoryEnabled != nil { - t.Errorf("unexpected MemoryEnabled state, want nil, but got %v", *msState.Cos.HealthMonitoring.MemoryEnabled) + if acosState.HealthMonitoring.MemoryEnabled != nil { + t.Errorf("unexpected MemoryEnabled state, want nil, but got %v", *acosState.HealthMonitoring.MemoryEnabled) } } @@ -929,13 +929,13 @@ func TestParsingCELEventLog(t *testing.T) { for _, bank := range banks { pcrBank := convertToPCRBank(t, bank) - if msState, err := ParseCosCELPCR(buf.Bytes(), pcrBank); err != nil { + if acosState, err := ParseCosCELPCR(buf.Bytes(), pcrBank); err != nil { t.Errorf("expecting no error from ParseCosCELPCR(), but get %v", err) } else { - if diff := cmp.Diff(msState.Cos.Container, &wantContainerState, protocmp.Transform()); diff != "" { + if diff := cmp.Diff(acosState.Container, &wantContainerState, protocmp.Transform()); diff != "" { t.Errorf("unexpected container state difference:\n%v", diff) } - if diff := cmp.Diff(msState.Cos.HealthMonitoring, &wantHealthMonitoringState, protocmp.Transform()); diff != "" { + if diff := cmp.Diff(acosState.HealthMonitoring, &wantHealthMonitoringState, protocmp.Transform()); diff != "" { t.Errorf("unexpected health monitoring state difference:\n%v", diff) } } diff --git a/server/verify.go b/server/verify.go index f8e41b5f6..e3f86ffd0 100644 --- a/server/verify.go +++ b/server/verify.go @@ -150,6 +150,7 @@ func VerifyAttestation(attestation *pb.Attestation, opts VerifyOpts) (*pb.Machin lastErr = fmt.Errorf("failed to validate the Canonical event log: %w", err) continue } + machineState.Cos = celState // Verify the PCR hash algorithm. We have this check here (instead of at // the start of the loop) so that the user gets a "SHA-1 not supported" @@ -160,7 +161,6 @@ func VerifyAttestation(attestation *pb.Attestation, opts VerifyOpts) (*pb.Machin continue } - proto.Merge(machineState, celState) proto.Merge(machineState, tpmMachineState) return machineState, nil