Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,7 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.31.2 h1:CxeO3Up9XLNFgHeJfgUfgSKVqPGp7n00wVTJWf6ahTM=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.31.2/go.mod h1:qR6jVnZTKDCW3j+fC9mOEPHm++1nKDMkqbbkD6KNsfo=
github.com/GoogleCloudPlatform/confidential-space/server v0.0.0-20260227215608-998bdbe721d1/go.mod h1:5SswqTFF1P+mhlxN5SH3d/1xq3bCeNDBkN3i31sH/uA=
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0=
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0=
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0=
Expand Down Expand Up @@ -1573,6 +1574,7 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
Expand Down Expand Up @@ -1617,6 +1619,7 @@ github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
Expand Down Expand Up @@ -1649,6 +1652,7 @@ github.com/google/go-containerregistry v0.20.1 h1:eTgx9QNYugV4DN5mz4U8hiAGTi1ybX
github.com/google/go-containerregistry v0.20.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
github.com/google/go-eventlog v0.0.1/go.mod h1:7huE5P8w2NTObSwSJjboHmB7ioBNblkijdzoVa2skfQ=
github.com/google/go-eventlog v0.0.2-0.20241213203620-f921bdc3aeb0/go.mod h1:7huE5P8w2NTObSwSJjboHmB7ioBNblkijdzoVa2skfQ=
github.com/google/go-eventlog v0.0.3-0.20250319220925-32d08db9b6f9/go.mod h1:7huE5P8w2NTObSwSJjboHmB7ioBNblkijdzoVa2skfQ=
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d h1:JtmsUf+m+KdwCOgLG578T0Mvd0+l+dezPrJh5KYnXZg=
github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
Expand All @@ -1658,6 +1662,8 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu
github.com/google/go-replayers/grpcreplay v0.1.0 h1:eNb1y9rZFmY4ax45uEEECSa8fsxGRU+8Bil52ASAwic=
github.com/google/go-replayers/httpreplay v0.1.0 h1:AX7FUb4BjrrzNvblr/OlgwrmFiep6soj5K2QSDW7BGk=
github.com/google/go-sev-guest v0.8.0/go.mod h1:hc1R4R6f8+NcJwITs0L90fYWTsBpd1Ix+Gur15sqHDs=
github.com/google/go-sev-guest v0.13.0/go.mod h1:SK9vW+uyfuzYdVN0m8BShL3OQCtXZe/JPF7ZkpD3760=
github.com/google/go-tdx-guest v0.3.2-0.20241217050406-c121d9d550ac/go.mod h1:uHy3VaNXNXhl0fiPxKqTxieeouqQmW6A0EfLcaeCYBk=
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc=
github.com/google/go-tpm-tools v0.4.2/go.mod h1:fGUDZu4tw3V4hUVuFHmiYgRd0c58/IXivn9v3Ea/ck4=
Expand All @@ -1676,6 +1682,7 @@ github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0 h1:BW6OvS3kpT5UEPbCZ+KyX/OB4Ks9/MNMhWjqPPkZxsE=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/trillian v1.4.0 h1:Wa7XHCVzl8RLsUOr2SzoHUZHYjv0G8KMO1xZGamYkbA=
Expand All @@ -1690,6 +1697,7 @@ github.com/googleapis/cloud-bigtable-clients-test v0.0.4/go.mod h1:NNHPqSxC2OBSL
github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/enterprise-certificate-proxy v0.3.5/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww=
Expand Down Expand Up @@ -2153,6 +2161,7 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/tink-crypto/tink-go/v2 v2.2.1-0.20241120130117-c41ea0ed393b/go.mod h1:8qt2du2JzY6pUCRZ4cVz/f+gEmznKkvzd1KScaN5Zqk=
github.com/tj/assert v0.0.0-20171129193455-018094318fb0 h1:Rw8kxzWo1mr6FSaYXjQELRe88y2KdfynXdnK72rdjtA=
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2 h1:eGaGNxrtoZf/mBURsnNQKDR7u50Klgcf2eFDQEnc8Bc=
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b h1:m74UWYy+HBs+jMFR9mdZU6shPewugMyH5+GV6LNgW8w=
Expand Down Expand Up @@ -2461,6 +2470,7 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
Expand Down Expand Up @@ -2498,6 +2508,7 @@ golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
Expand Down Expand Up @@ -2680,6 +2691,7 @@ google.golang.org/api v0.187.0/go.mod h1:KIHlTc4x7N7gKKuVsdmfBXN13yEEWXWFURWY6SB
google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag=
google.golang.org/api v0.196.0/go.mod h1:g9IL21uGkYgvQ5BZg6BAtoGJQIm8r6EgaAbpNey5wBE=
google.golang.org/api v0.197.0/go.mod h1:AuOuo20GoQ331nq7DquGHlU6d+2wN2fZ8O0ta60nRNw=
google.golang.org/api v0.213.0/go.mod h1:V0T5ZhNUUNpYAlL306gFZPFt5F5D/IeyLoktduYYnvQ=
google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE=
google.golang.org/api v0.229.0/go.mod h1:wyDfmq5g1wYJWn29O22FDWN48P7Xcz0xz+LBpptYvB0=
google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=
Expand Down Expand Up @@ -2822,6 +2834,7 @@ google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjr
google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
Expand Down
1 change: 1 addition & 0 deletions launcher/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ func (a *agent) AttestationEvidence(_ context.Context, challenge []byte, extraDa

switch v := attResult.(type) {
case *pb.Attestation:
v.CanonicalEventLog = cosCel.Bytes()
attestation.Quote.Quote = &attestationpb.VmAttestationQuote_TpmQuote{
TpmQuote: convertToTPMQuote(v),
}
Expand Down
176 changes: 168 additions & 8 deletions launcher/agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
_ "embed"
"encoding/base64"
"errors"
"fmt"
Expand All @@ -16,6 +18,7 @@ import (
"testing"
"time"

"github.com/GoogleCloudPlatform/confidential-space/server/extract"
"github.com/GoogleCloudPlatform/confidential-space/server/labels"
attestationpb "github.com/GoogleCloudPlatform/confidential-space/server/proto/gen/attestation"
"github.com/cenkalti/backoff/v4"
Expand All @@ -31,6 +34,7 @@ import (
"github.com/google/go-tpm-tools/launcher/spec"
attestpb "github.com/google/go-tpm-tools/proto/attest"
tpmpb "github.com/google/go-tpm-tools/proto/tpm"
"github.com/google/go-tpm-tools/server"
"github.com/google/go-tpm-tools/verifier"
"github.com/google/go-tpm-tools/verifier/fake"
"github.com/google/go-tpm-tools/verifier/models"
Expand Down Expand Up @@ -653,6 +657,7 @@ func measureFakeEvents(attestAgent AttestationAgent) error {
type fakeTdxAttestRoot struct {
cel gecel.CEL
receivedNonce []byte
tdxQuote []byte
deviceRoTS []DeviceROT
}

Expand Down Expand Up @@ -683,7 +688,7 @@ func (f *fakeTdxAttestRoot) Attest(nonce []byte) (any, error) {
}

return &verifier.TDCCELAttestation{
TdQuote: []byte("fake-tdx-quote"),
TdQuote: f.tdxQuote,
NvidiaAttestation: nvAtt,
}, nil
}
Expand All @@ -699,6 +704,12 @@ func (f *fakeTdxAttestRoot) ComputeNonce(challenge []byte, extraData []byte) []b
return finalNonce[:]
}

//go:embed tdx_quote.b64
var tdxQuoteB64 string

//go:embed cel.b64
var celB64 string

func (f *fakeTdxAttestRoot) AddDeviceROTs(deviceRoTS []DeviceROT) {
f.deviceRoTS = append(f.deviceRoTS, deviceRoTS...)
}
Expand Down Expand Up @@ -779,8 +790,21 @@ func TestAttestationEvidence_TDX_Success(t *testing.T) {
t.Fatal(err)
}

testTDXQuote, err := base64.StdEncoding.DecodeString(tdxQuoteB64)
if err != nil {
t.Fatalf("failed to decode tdx_quote.b64: %v", err)
}
testCEL, err := base64.StdEncoding.DecodeString(celB64)
if err != nil {
t.Fatalf("failed to decode cel.b64: %v", err)
}
decodedCEL, err := gecel.DecodeToCEL(bytes.NewBuffer(testCEL))
if err != nil {
t.Fatalf("failed to decode test CEL: %v", err)
}
fakeRoot := &fakeTdxAttestRoot{
cel: gecel.NewConfComputeMR(),
cel: decodedCEL,
tdxQuote: testTDXQuote,
}
attestAgent := &agent{
avRot: fakeRoot,
Expand All @@ -803,17 +827,30 @@ func TestAttestationEvidence_TDX_Success(t *testing.T) {
t.Fatalf("AttestationEvidence failed: %v", err)
}

// Verify the nonce passed to Attest was derived from challenge+extraData.
expectedNonce := fakeRoot.ComputeNonce(challenge, extraData)
if !bytes.Equal(fakeRoot.receivedNonce, expectedNonce) {
t.Errorf("got nonce %x, want %x", fakeRoot.receivedNonce, expectedNonce)
}

if att.GetQuote().GetTdxCcelQuote() == nil {
t.Fatal("expected TDCCELAttestation to be populated for TDX")
}

if string(att.GetQuote().GetTdxCcelQuote().GetTdQuote()) != "fake-tdx-quote" {
t.Errorf("got quote %s, want fake-tdx-quote", string(att.GetQuote().GetTdxCcelQuote().GetTdQuote()))
if !bytes.Equal(att.GetQuote().GetTdxCcelQuote().GetTdQuote(), testTDXQuote) {
t.Errorf("TDQuote mismatch: got %x, want %x", att.GetQuote().GetTdxCcelQuote().GetTdQuote(), testTDXQuote)
}

expectedHash := fakeRoot.ComputeNonce(challenge, extraData)
if !bytes.Equal(fakeRoot.receivedNonce, expectedHash) {
t.Errorf("got nonce %x, want %x", fakeRoot.receivedNonce, expectedHash)
if att.GetQuote().GetTpmQuote() != nil {
t.Error("expected TPMQuote to be nil for TDX attestation")
}
if len(att.GetQuote().GetTdxCcelQuote().GetCelLaunchEventLog()) == 0 {
t.Error("expected CELLaunchEventLog to be non-empty after MeasureEvent calls")
}
if !bytes.Equal(att.Challenge, challenge) {
t.Errorf("challenge mismatch: got %x, want %x", att.Challenge, challenge)
}
if !bytes.Equal(att.ExtraData, extraData) {
t.Errorf("extraData mismatch: got %x, want %x", att.ExtraData, extraData)
}
}

Expand All @@ -828,6 +865,12 @@ func TestAttestationEvidence_TPM_Success(t *testing.T) {
}
verifierClient := fake.NewClient(fakeSigner)

ak, err := client.AttestationKeyECC(tpm)
if err != nil {
t.Fatalf("failed to create AK: %v", err)
}
defer ak.Close()

agent, err := CreateAttestationAgent(tpm, client.AttestationKeyECC, verifierClient, placeholderPrincipalFetcher, signaturediscovery.NewFakeClient(), spec.LaunchSpec{
Experiments: experiments.Experiments{
EnableAttestationEvidence: true,
Expand Down Expand Up @@ -855,6 +898,54 @@ func TestAttestationEvidence_TPM_Success(t *testing.T) {
if att.GetQuote().GetTdxCcelQuote() != nil {
t.Fatal("expected TDCCELAttestation to be nil for TPM")
}

// Test the evidence can be verified by the server by re-computing the nonce and calling server.VerifyAttestation.
extraDataDigest := sha256.Sum256(extraData)
challengeData := append(challenge, extraDataDigest[:]...)
challengeDigest := sha256.Sum256(challengeData)
tpmNonce := sha256.Sum256(append([]byte(labels.WorkloadAttestation), challengeDigest[:]...))

pbAttestation, err := ak.Attest(client.AttestOpts{Nonce: tpmNonce[:]})
if err != nil {
t.Fatalf("ak.Attest() failed: %v", err)
}

_, err = server.VerifyAttestation(pbAttestation, server.VerifyOpts{
Nonce: tpmNonce[:],
TrustedAKs: []crypto.PublicKey{ak.PublicKey()},
})
if err != nil {
t.Errorf("server.VerifyAttestation failed: %v", err)
}

// Test that the COS state can be extracted from the CEL and contains the expected values.
decodedCEL, err := gecel.DecodeToCEL(bytes.NewBuffer(att.GetQuote().GetTpmQuote().GetCelLaunchEventLog()))
if err != nil {
t.Fatalf("gecel.DecodeToCEL failed: %v", err)
}

cosState, err := extract.VerifiedCOSState(decodedCEL, uint8(gecel.PCRType))
if err != nil {
t.Errorf("extract.VerifiedCOSState failed: %v", err)
} else {
ctr := cosState.GetContainer()
if ctr.GetImageReference() != imageRef {
t.Errorf("ImageReference: got %q, want %q", ctr.GetImageReference(), imageRef)
}
if ctr.GetImageDigest() != imageDigest {
t.Errorf("ImageDigest: got %q, want %q", ctr.GetImageDigest(), imageDigest)
}
if ctr.GetImageId() != imageID {
t.Errorf("ImageId: got %q, want %q", ctr.GetImageId(), imageID)
}
if len(ctr.GetArgs()) == 0 || ctr.GetArgs()[0] != arg {
t.Errorf("Args[0]: got %v, want %q", ctr.GetArgs(), arg)
}
if ctr.GetEnvVars()[envK] != envV {
t.Errorf("EnvVars[%q]: got %q, want %q", envK, ctr.GetEnvVars()[envK], envV)
}
t.Logf("extract.VerifiedCOSState succeeded: ImageRef=%s", ctr.GetImageReference())
}
}

type testClient struct {
Expand Down Expand Up @@ -959,3 +1050,72 @@ func TestConvertPBToTPMQuote(t *testing.T) {
t.Errorf("convertPBToTPMQuote() mismatch (-got +want):\n%s", diff)
}
}

// TestAttestationEvidence_ExperimentDisabled verifies that AttestationEvidence
// returns an error when the EnableAttestationEvidence experiment flag is off.
func TestAttestationEvidence_ExperimentDisabled(t *testing.T) {
ctx := context.Background()
tpm := test.GetTPM(t)
defer client.CheckedClose(t, tpm)

fakeSigner, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("failed to generate signing key: %v", err)
}
agent, err := CreateAttestationAgent(tpm, client.AttestationKeyECC, fake.NewClient(fakeSigner),
placeholderPrincipalFetcher, signaturediscovery.NewFakeClient(),
spec.LaunchSpec{ /* EnableAttestationEvidence defaults to false */ },
logging.SimpleLogger(), nil)
if err != nil {
t.Fatalf("failed to create agent: %v", err)
}
defer agent.Close()

_, err = agent.AttestationEvidence(ctx, []byte("challenge"), nil)
if err == nil {
t.Error("expected error when EnableAttestationEvidence is disabled, got nil")
}
}

// TestAttestationEvidence_TDX_NilExtraData verifies that AttestationEvidence
// handles nil extraData correctly, deriving the nonce from the challenge alone.
func TestAttestationEvidence_TDX_NilExtraData(t *testing.T) {
ctx := context.Background()
tpm := test.GetTPM(t)
defer client.CheckedClose(t, tpm)

ak, err := client.AttestationKeyECC(tpm)
if err != nil {
t.Fatalf("failed to create AK: %v", err)
}
defer ak.Close()

fakeRoot := &fakeTdxAttestRoot{
cel: gecel.NewConfComputeMR(),
tdxQuote: []byte("fake-tdx-quote"),
}
attestatAgent := &agent{
avRot: fakeRoot,
fetchedAK: ak,
launchSpec: spec.LaunchSpec{
Experiments: experiments.Experiments{
EnableAttestationEvidence: true,
},
},
}

challenge := []byte("test-challenge")
att, err := attestatAgent.AttestationEvidence(ctx, challenge, nil)
if err != nil {
t.Fatalf("AttestationEvidence failed with nil extraData: %v", err)
}

expectedNonce := fakeRoot.ComputeNonce(challenge, nil)
if !bytes.Equal(fakeRoot.receivedNonce, expectedNonce) {
t.Errorf("nonce mismatch with nil extraData: got %x, want %x",
fakeRoot.receivedNonce, expectedNonce)
}
if att.ExtraData != nil {
t.Errorf("expected nil ExtraData in attestation, got %x", att.ExtraData)
}
}
Loading
Loading