From c38b622528004a8bb3fb50a87fec39ca7c632cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiankun=20L=C3=BC?= Date: Thu, 10 Oct 2024 18:37:30 -0700 Subject: [PATCH 01/21] [launcher] Add TDX/RTMR attestation in launcher (#478) Allow a TDX machine to create a TD quote and request a hardware rooted attestation from the attestation verifier. ./launcher ci will now only run in linux. Upgrade go-sev-guest. Signed-off-by: Jiankun Lu --- .github/workflows/ci.yml | 14 +-- client/attest.go | 83 +---------------- client/attest_network_test.go | 5 +- client/attest_test.go | 113 +---------------------- cmd/go.mod | 8 +- cmd/go.sum | 14 +-- go.mod | 3 +- go.sum | 6 +- go.work.sum | 1 + internal/cert.go | 84 ++++++++++++++++++ internal/cert_test.go | 76 ++++++++++++++++ internal/test/test_cert.go | 47 ++++++++++ launcher/agent/agent.go | 158 ++++++++++++++++++++++++++++----- launcher/cloudbuild.yaml | 70 +++++++-------- launcher/go.mod | 7 +- launcher/go.sum | 10 +-- launcher/image/cloudbuild.yaml | 2 +- launcher/launcher/main.go | 2 +- verifier/client.go | 17 +++- verifier/go.mod | 5 +- verifier/go.sum | 6 +- verifier/rest/rest.go | 125 ++++++++++++++++---------- verifier/rest/rest_test.go | 30 +++++-- 23 files changed, 538 insertions(+), 348 deletions(-) create mode 100644 internal/cert.go create mode 100644 internal/cert_test.go create mode 100644 internal/test/test_cert.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7853bfc32..7cd0d447e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,17 +71,21 @@ jobs: - name: Install Windows packages run: choco install openssl if: runner.os == 'Windows' - - name: Build all modules - run: go build -v ./... ./cmd/... ./launcher/... ./verifier/... + - name: Build all modules except launcher + run: go build -v ./... ./cmd/... ./verifier/... + - name: Build launcher module + run: go build -v ./launcher/... + if: runner.os == 'Linux' - name: Run specific tests under root permission run: | GO_EXECUTABLE_PATH=$(which go) sudo $GO_EXECUTABLE_PATH test -v -run "TestFetchImageSignaturesDockerPublic" ./launcher + if: runner.os == 'Linux' - 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' + if: (runner.os == 'Linux') && matrix.architecture == 'x64' + - name: Test all modules except launcher + run: go test -v ./... ./cmd/... ./verifier/... -skip='TestCacheConcurrentSetGet|TestHwAttestationPass|TestHardwareAttestationPass' lint: strategy: diff --git a/client/attest.go b/client/attest.go index 3ea022917..8e7be72a3 100644 --- a/client/attest.go +++ b/client/attest.go @@ -1,9 +1,7 @@ package client import ( - "crypto/x509" "fmt" - "io" "net/http" sabi "github.com/google/go-sev-guest/abi" @@ -11,14 +9,10 @@ import ( tg "github.com/google/go-tdx-guest/client" tabi "github.com/google/go-tdx-guest/client/linuxabi" tpb "github.com/google/go-tdx-guest/proto/tdx" + "github.com/google/go-tpm-tools/internal" pb "github.com/google/go-tpm-tools/proto/attest" ) -const ( - maxIssuingCertificateURLs = 3 - maxCertChainLength = 4 -) - // TEEDevice is an interface to add an attestation report from a TEE technology's // attestation driver or quote provider. type TEEDevice interface { @@ -49,6 +43,7 @@ type AttestOpts struct { // Currently, we only support PCR replay for PCRs orthogonal to those in the // firmware event log, where PCRs 0-9 and 14 are often measured. If the two // logs overlap, server-side verification using this library may fail. + // Deprecated: Manually populate the pb.Attestation instead. CanonicalEventLog []byte // If non-nil, will be used to fetch the AK certificate chain for validation. // Key.Attest() will construct the certificate chain by making GET requests to @@ -66,77 +61,6 @@ type AttestOpts struct { TEENonce []byte } -// Given a certificate, iterates through its IssuingCertificateURLs and returns -// the certificate that signed it. If the certificate lacks an -// IssuingCertificateURL, return nil. If fetching the certificates fails or the -// cert chain is malformed, return an error. -func fetchIssuingCertificate(client *http.Client, cert *x509.Certificate) (*x509.Certificate, error) { - // Check if we should event attempt fetching. - if cert == nil || len(cert.IssuingCertificateURL) == 0 { - return nil, nil - } - // For each URL, fetch and parse the certificate, then verify whether it signed cert. - // If successful, return the parsed certificate. If any step in this process fails, try the next url. - // If all the URLs fail, return the last error we got. - // TODO(Issue #169): Return a multi-error here - var lastErr error - for i, url := range cert.IssuingCertificateURL { - // Limit the number of attempts. - if i >= maxIssuingCertificateURLs { - break - } - resp, err := client.Get(url) - if err != nil { - lastErr = fmt.Errorf("failed to retrieve certificate at %v: %w", url, err) - continue - } - - if resp.StatusCode != http.StatusOK { - lastErr = fmt.Errorf("certificate retrieval from %s returned non-OK status: %v", url, resp.StatusCode) - continue - } - certBytes, err := io.ReadAll(resp.Body) - resp.Body.Close() - if err != nil { - lastErr = fmt.Errorf("failed to read response body from %s: %w", url, err) - continue - } - - parsedCert, err := x509.ParseCertificate(certBytes) - if err != nil { - lastErr = fmt.Errorf("failed to parse response from %s into a certificate: %w", url, err) - continue - } - - // Check if the parsed certificate signed the current one. - if err = cert.CheckSignatureFrom(parsedCert); err != nil { - lastErr = fmt.Errorf("parent certificate from %s did not sign child: %w", url, err) - continue - } - return parsedCert, nil - } - return nil, lastErr -} - -// Constructs the certificate chain for the key's certificate. -// If an error is encountered in the process, return what has been constructed so far. -func (k *Key) getCertificateChain(client *http.Client) ([][]byte, error) { - var certs [][]byte - currentCert := k.cert - for len(certs) <= maxCertChainLength { - issuingCert, err := fetchIssuingCertificate(client, currentCert) - if err != nil { - return nil, err - } - if issuingCert == nil { - return certs, nil - } - certs = append(certs, issuingCert.Raw) - currentCert = issuingCert - } - return nil, fmt.Errorf("max certificate chain length (%v) exceeded", maxCertChainLength) -} - // SevSnpQuoteProvider encapsulates the SEV-SNP attestation device to add its attestation report // to a pb.Attestation. type SevSnpQuoteProvider struct { @@ -383,12 +307,13 @@ func (k *Key) Attest(opts AttestOpts) (*pb.Attestation, error) { // Attempt to construct certificate chain. fetchIssuingCertificate checks if // AK cert is present and contains intermediate cert URLs. if opts.CertChainFetcher != nil { - attestation.IntermediateCerts, err = k.getCertificateChain(opts.CertChainFetcher) + attestation.IntermediateCerts, err = internal.GetCertificateChain(k.cert, opts.CertChainFetcher) if err != nil { return nil, fmt.Errorf("fetching certificate chain: %w", err) } } + // TODO: issues/504 this should be outside of this function, not related to TPM attestation if err := getTEEAttestationReport(&attestation, opts); err != nil { return nil, fmt.Errorf("collecting TEE attestation report: %w", err) } diff --git a/client/attest_network_test.go b/client/attest_network_test.go index 83a461c19..2f225c2e7 100644 --- a/client/attest_network_test.go +++ b/client/attest_network_test.go @@ -5,6 +5,7 @@ import ( "net/http" "testing" + "github.com/google/go-tpm-tools/internal" "github.com/google/go-tpm-tools/internal/test" pb "github.com/google/go-tpm-tools/proto/attest" "google.golang.org/protobuf/proto" @@ -24,9 +25,7 @@ func TestNetworkFetchIssuingCertificate(t *testing.T) { t.Fatalf("Error parsing AK Cert: %v", err) } - key := &Key{cert: akCert} - - certChain, err := key.getCertificateChain(externalClient) + certChain, err := internal.GetCertificateChain(akCert, externalClient) if err != nil { t.Error(err) } diff --git a/client/attest_test.go b/client/attest_test.go index 84b98ef2c..5da979080 100644 --- a/client/attest_test.go +++ b/client/attest_test.go @@ -2,10 +2,7 @@ package client import ( "bytes" - "crypto/rand" - "crypto/rsa" "crypto/x509" - "math/big" "net/http" "net/http/httptest" "strings" @@ -23,112 +20,8 @@ import ( var localClient = http.DefaultClient -// Returns an x509 Certificate with the provided issuingURL and signed with the provided parent certificate and key. -// If parentCert and parentKey are nil, the certificate will be self-signed. -func getTestCert(t *testing.T, issuingURL []string, parentCert *x509.Certificate, parentKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey) { - t.Helper() - - certKey, _ := rsa.GenerateKey(rand.Reader, 2048) - - template := &x509.Certificate{ - SerialNumber: big.NewInt(1), - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(10, 0, 0), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - IsCA: true, - MaxPathLenZero: true, - IssuingCertificateURL: issuingURL, - } - - if parentCert == nil && parentKey == nil { - parentCert = template - parentKey = certKey - } - - certBytes, err := x509.CreateCertificate(rand.Reader, template, parentCert, certKey.Public(), parentKey) - if err != nil { - t.Fatalf("Unable to create test certificate: %v", err) - } - - cert, err := x509.ParseCertificate(certBytes) - if err != nil { - t.Fatalf("Unable to parse test certificate: %v", err) - } - - return cert, certKey -} - -func TestFetchIssuingCertificateSucceeds(t *testing.T) { - testCA, caKey := getTestCert(t, nil, nil, nil) - - ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { - rw.WriteHeader(http.StatusOK) - rw.Write(testCA.Raw) - })) - defer ts.Close() - - leafCert, _ := getTestCert(t, []string{"invalid.URL", ts.URL}, testCA, caKey) - - cert, err := fetchIssuingCertificate(localClient, leafCert) - if err != nil || cert == nil { - t.Errorf("fetchIssuingCertificate() did not find valid intermediate cert: %v", err) - } -} - -func TestFetchIssuingCertificateReturnsErrorIfMalformedCertificateFound(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { - rw.WriteHeader(http.StatusOK) - rw.Write([]byte("these are some random bytes")) - })) - defer ts.Close() - - testCA, caKey := getTestCert(t, nil, nil, nil) - leafCert, _ := getTestCert(t, []string{ts.URL}, testCA, caKey) - - _, err := fetchIssuingCertificate(localClient, leafCert) - if err == nil { - t.Fatal("expected fetchIssuingCertificate to fail with malformed cert") - } -} - -func TestGetCertificateChainSucceeds(t *testing.T) { - // Create CA and corresponding server. - testCA, caKey := getTestCert(t, nil, nil, nil) - - caServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { - rw.WriteHeader(http.StatusOK) - rw.Write(testCA.Raw) - })) - - defer caServer.Close() - - // Create intermediate cert and corresponding server. - intermediateCert, intermediateKey := getTestCert(t, []string{caServer.URL}, testCA, caKey) - - intermediateServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { - rw.WriteHeader(http.StatusOK) - rw.Write(intermediateCert.Raw) - })) - defer intermediateServer.Close() - - // Create leaf cert. - leafCert, _ := getTestCert(t, []string{intermediateServer.URL}, intermediateCert, intermediateKey) - - key := &Key{cert: leafCert} - - certChain, err := key.getCertificateChain(localClient) - if err != nil { - t.Fatal(err) - } - if len(certChain) != 2 { - t.Fatalf("getCertificateChain did not return the expected number of certificates: got %v, want 2", len(certChain)) - } -} - func TestKeyAttestSucceedsWithCertChainRetrieval(t *testing.T) { - testCA, caKey := getTestCert(t, nil, nil, nil) + testCA, caKey := test.GetTestCert(t, nil, nil, nil) caServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { rw.WriteHeader(http.StatusOK) @@ -137,7 +30,7 @@ func TestKeyAttestSucceedsWithCertChainRetrieval(t *testing.T) { defer caServer.Close() - leafCert, _ := getTestCert(t, []string{caServer.URL}, testCA, caKey) + leafCert, _ := test.GetTestCert(t, []string{caServer.URL}, testCA, caKey) rwc := test.GetTPM(t) defer CheckedClose(t, rwc) @@ -173,7 +66,7 @@ func TestKeyAttestGetCertificateChainConditions(t *testing.T) { t.Fatalf("Failed to generate test AK: %v", err) } - akCert, _ := getTestCert(t, nil, nil, nil) + akCert, _ := test.GetTestCert(t, nil, nil, nil) testcases := []struct { name string diff --git a/cmd/go.mod b/cmd/go.mod index 1674fc3f4..1d5e3bbab 100644 --- a/cmd/go.mod +++ b/cmd/go.mod @@ -8,8 +8,8 @@ require ( github.com/containerd/containerd v1.7.16 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/protobuf v1.5.4 - github.com/google/go-sev-guest v0.11.1 - github.com/google/go-tdx-guest v0.3.1 + github.com/google/go-sev-guest v0.11.2-0.20241009005433-de2ac900e958 + 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/google/go-tpm-tools/verifier v0.4.4 @@ -34,7 +34,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-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 @@ -42,7 +43,6 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/pborman/uuid v1.2.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/cmd/go.sum b/cmd/go.sum index a95fc24a0..9a4d65d17 100644 --- a/cmd/go.sum +++ b/cmd/go.sum @@ -336,17 +336,19 @@ 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= github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= 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-sev-guest v0.11.2-0.20241009005433-de2ac900e958 h1:GfnkFZNr80qFGLR/EY75zwk8puz8+frGj4iwPwnJbSU= +github.com/google/go-sev-guest v0.11.2-0.20241009005433-de2ac900e958/go.mod h1:8+UOtSaqVIZjJJ9DDmgRko3J/kNc6jI5KLHxoeao7cA= +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/go.mod b/go.mod index d80707976..7530075eb 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( 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-sev-guest v0.11.2-0.20241009005433-de2ac900e958 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 @@ -21,7 +21,6 @@ require ( 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 - github.com/pborman/uuid v1.2.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/go.sum b/go.sum index a1fa1a630..4a55b9d2e 100644 --- a/go.sum +++ b/go.sum @@ -315,8 +315,8 @@ github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOm github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= 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-sev-guest v0.11.2-0.20241009005433-de2ac900e958 h1:GfnkFZNr80qFGLR/EY75zwk8puz8+frGj4iwPwnJbSU= +github.com/google/go-sev-guest v0.11.2-0.20241009005433-de2ac900e958/go.mod h1:8+UOtSaqVIZjJJ9DDmgRko3J/kNc6jI5KLHxoeao7cA= 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= @@ -550,8 +550,6 @@ github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= -github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= diff --git a/go.work.sum b/go.work.sum index 73dd10603..b007031c8 100644 --- a/go.work.sum +++ b/go.work.sum @@ -694,6 +694,7 @@ 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.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= 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= diff --git a/internal/cert.go b/internal/cert.go new file mode 100644 index 000000000..a4a2cbb7c --- /dev/null +++ b/internal/cert.go @@ -0,0 +1,84 @@ +package internal + +import ( + "crypto/x509" + "fmt" + "io" + "net/http" +) + +const ( + maxIssuingCertificateURLs = 3 + maxCertChainLength = 4 +) + +// GetCertificateChain constructs the certificate chain for the key's certificate. +// If an error is encountered in the process, return what has been constructed so far. +func GetCertificateChain(cert *x509.Certificate, client *http.Client) ([][]byte, error) { + var certs [][]byte + currentCert := cert + for len(certs) <= maxCertChainLength { + issuingCert, err := fetchIssuingCertificate(client, currentCert) + if err != nil { + return nil, err + } + if issuingCert == nil { + return certs, nil + } + certs = append(certs, issuingCert.Raw) + currentCert = issuingCert + } + return nil, fmt.Errorf("max certificate chain length (%v) exceeded", maxCertChainLength) +} + +// Given a certificate, iterates through its IssuingCertificateURLs and returns +// the certificate that signed it. If the certificate lacks an +// IssuingCertificateURL, return nil. If fetching the certificates fails or the +// cert chain is malformed, return an error. +func fetchIssuingCertificate(client *http.Client, cert *x509.Certificate) (*x509.Certificate, error) { + // Check if we should event attempt fetching. + if cert == nil || len(cert.IssuingCertificateURL) == 0 { + return nil, nil + } + // For each URL, fetch and parse the certificate, then verify whether it signed cert. + // If successful, return the parsed certificate. If any step in this process fails, try the next url. + // If all the URLs fail, return the last error we got. + // TODO(Issue #169): Return a multi-error here + var lastErr error + for i, url := range cert.IssuingCertificateURL { + // Limit the number of attempts. + if i >= maxIssuingCertificateURLs { + break + } + resp, err := client.Get(url) + if err != nil { + lastErr = fmt.Errorf("failed to retrieve certificate at %v: %w", url, err) + continue + } + + if resp.StatusCode != http.StatusOK { + lastErr = fmt.Errorf("certificate retrieval from %s returned non-OK status: %v", url, resp.StatusCode) + continue + } + certBytes, err := io.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + lastErr = fmt.Errorf("failed to read response body from %s: %w", url, err) + continue + } + + parsedCert, err := x509.ParseCertificate(certBytes) + if err != nil { + lastErr = fmt.Errorf("failed to parse response from %s into a certificate: %w", url, err) + continue + } + + // Check if the parsed certificate signed the current one. + if err = cert.CheckSignatureFrom(parsedCert); err != nil { + lastErr = fmt.Errorf("parent certificate from %s did not sign child: %w", url, err) + continue + } + return parsedCert, nil + } + return nil, lastErr +} diff --git a/internal/cert_test.go b/internal/cert_test.go new file mode 100644 index 000000000..9a502b36e --- /dev/null +++ b/internal/cert_test.go @@ -0,0 +1,76 @@ +package internal + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/google/go-tpm-tools/internal/test" +) + +var localClient = http.DefaultClient + +func TestFetchIssuingCertificateSucceeds(t *testing.T) { + testCA, caKey := test.GetTestCert(t, nil, nil, nil) + + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.WriteHeader(http.StatusOK) + rw.Write(testCA.Raw) + })) + defer ts.Close() + + leafCert, _ := test.GetTestCert(t, []string{"invalid.URL", ts.URL}, testCA, caKey) + + cert, err := fetchIssuingCertificate(localClient, leafCert) + if err != nil || cert == nil { + t.Errorf("fetchIssuingCertificate() did not find valid intermediate cert: %v", err) + } +} + +func TestFetchIssuingCertificateReturnsErrorIfMalformedCertificateFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.WriteHeader(http.StatusOK) + rw.Write([]byte("these are some random bytes")) + })) + defer ts.Close() + + testCA, caKey := test.GetTestCert(t, nil, nil, nil) + leafCert, _ := test.GetTestCert(t, []string{ts.URL}, testCA, caKey) + + _, err := fetchIssuingCertificate(localClient, leafCert) + if err == nil { + t.Fatal("expected fetchIssuingCertificate to fail with malformed cert") + } +} + +func TestGetCertificateChainSucceeds(t *testing.T) { + // Create CA and corresponding server. + testCA, caKey := test.GetTestCert(t, nil, nil, nil) + + caServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.WriteHeader(http.StatusOK) + rw.Write(testCA.Raw) + })) + + defer caServer.Close() + + // Create intermediate cert and corresponding server. + intermediateCert, intermediateKey := test.GetTestCert(t, []string{caServer.URL}, testCA, caKey) + + intermediateServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.WriteHeader(http.StatusOK) + rw.Write(intermediateCert.Raw) + })) + defer intermediateServer.Close() + + // Create leaf cert. + leafCert, _ := test.GetTestCert(t, []string{intermediateServer.URL}, intermediateCert, intermediateKey) + + certChain, err := GetCertificateChain(leafCert, localClient) + if err != nil { + t.Fatal(err) + } + if len(certChain) != 2 { + t.Fatalf("GetCertificateChain did not return the expected number of certificates: got %v, want 2", len(certChain)) + } +} diff --git a/internal/test/test_cert.go b/internal/test/test_cert.go new file mode 100644 index 000000000..0f45a277a --- /dev/null +++ b/internal/test/test_cert.go @@ -0,0 +1,47 @@ +package test + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "math/big" + "testing" + "time" +) + +// GetTestCert returns an x509 Certificate with the provided issuingURL and signed with the provided parent certificate and key. +// If parentCert and parentKey are nil, the certificate will be self-signed. +func GetTestCert(t *testing.T, issuingURL []string, parentCert *x509.Certificate, parentKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey) { + t.Helper() + + certKey, _ := rsa.GenerateKey(rand.Reader, 2048) + + template := &x509.Certificate{ + SerialNumber: big.NewInt(1), + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: true, + MaxPathLenZero: true, + IssuingCertificateURL: issuingURL, + } + + if parentCert == nil && parentKey == nil { + parentCert = template + parentKey = certKey + } + + certBytes, err := x509.CreateCertificate(rand.Reader, template, parentCert, certKey.Public(), parentKey) + if err != nil { + t.Fatalf("Unable to create test certificate: %v", err) + } + + cert, err := x509.ParseCertificate(certBytes) + if err != nil { + t.Fatalf("Unable to parse test certificate: %v", err) + } + + return cert, certKey +} diff --git a/launcher/agent/agent.go b/launcher/agent/agent.go index 28aa4b49b..5bfa79485 100644 --- a/launcher/agent/agent.go +++ b/launcher/agent/agent.go @@ -13,12 +13,20 @@ import ( "io" "log" "net/http" + "os" "sync" "time" "github.com/cenkalti/backoff/v4" + "github.com/google/go-configfs-tsm/configfs/configfsi" + + "github.com/google/go-configfs-tsm/configfs/linuxtsm" + tg "github.com/google/go-tdx-guest/client" + tlabi "github.com/google/go-tdx-guest/client/linuxabi" + "github.com/google/go-tpm-tools/cel" "github.com/google/go-tpm-tools/client" + "github.com/google/go-tpm-tools/internal" "github.com/google/go-tpm-tools/launcher/internal/signaturediscovery" "github.com/google/go-tpm-tools/launcher/spec" pb "github.com/google/go-tpm-tools/proto/attest" @@ -41,6 +49,13 @@ type AttestationAgent interface { Close() error } +type attestRoot interface { + // Extend measures the cel content into a measurement register and appends to the CEL. + Extend(cel.Content, *cel.CEL) error + // Attest fetches a technology-specific quote from the root of trust. + Attest(nonce []byte) (any, error) +} + // AttestAgentOpts contains user generated options when calling the // VerifyAttestation API type AttestAgentOpts struct { @@ -50,13 +65,12 @@ type AttestAgentOpts struct { } type agent struct { - tpm io.ReadWriteCloser - tpmMu sync.Mutex + ar attestRoot + cosCel cel.CEL fetchedAK *client.Key client verifier.Client principalFetcher principalIDTokenFetcher sigsFetcher signaturediscovery.Fetcher - cosCel cel.CEL launchSpec spec.LaunchSpec logger *log.Logger sigsCache *sigsCache @@ -75,8 +89,8 @@ func CreateAttestationAgent(tpm io.ReadWriteCloser, akFetcher util.TpmKeyFetcher if err != nil { return nil, fmt.Errorf("failed to create an Attestation Agent: %w", err) } - return &agent{ - tpm: tpm, + + attestAgent := &agent{ client: verifierClient, fetchedAK: ak, principalFetcher: principalFetcher, @@ -84,7 +98,32 @@ func CreateAttestationAgent(tpm io.ReadWriteCloser, akFetcher util.TpmKeyFetcher launchSpec: launchSpec, logger: logger, sigsCache: &sigsCache{}, - }, nil + } + + // check if is a TDX machine + qp, err := tg.GetQuoteProvider() + if err != nil || qp.IsSupported() != nil { + logger.Println("Using TPM PCRs for measurement.") + // by default using TPM + attestAgent.ar = &tpmAttestRoot{ + fetchedAK: ak, + tpm: tpm, + } + } else { + logger.Println("Using TDX RTMRs for measurement.") + // try to create tsm client for tdx rtmr + tsm, err := linuxtsm.MakeClient() + if err != nil { + return nil, fmt.Errorf("failed to create TSM for TDX: %v", err) + } + + attestAgent.ar = &tdxAttestRoot{ + qp: qp, + tsmClient: tsm, + } + } + + return attestAgent, nil } // Close cleans up the agent @@ -96,9 +135,7 @@ func (a *agent) Close() error { // MeasureEvent takes in a cel.Content and appends it to the CEL eventlog // under the attestation agent. func (a *agent) MeasureEvent(event cel.Content) error { - a.tpmMu.Lock() - defer a.tpmMu.Unlock() - return a.cosCel.AppendEvent(a.tpm, cel.CosEventPCR, defaultCELHashAlgo, event) + return a.ar.Extend(event, &a.cosCel) } // Attest fetches the nonce and connection ID from the Attestation Service, @@ -115,20 +152,9 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error return nil, fmt.Errorf("failed to get principal tokens: %w", err) } - var buf bytes.Buffer - if err := a.cosCel.EncodeCEL(&buf); err != nil { - return nil, err - } - - attestation, err := a.attest(challenge.Nonce, buf.Bytes()) - if err != nil { - return nil, fmt.Errorf("failed to attest: %v", err) - } - req := verifier.VerifyAttestationRequest{ Challenge: challenge, GcpCredentials: principalTokens, - Attestation: attestation, TokenOptions: verifier.TokenOptions{ CustomAudience: opts.Aud, CustomNonce: opts.Nonces, @@ -136,6 +162,38 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error }, } + attResult, err := a.ar.Attest(challenge.Nonce) + if err != nil { + return nil, fmt.Errorf("failed to attest: %v", err) + } + + var cosCel bytes.Buffer + if err := a.cosCel.EncodeCEL(&cosCel); err != nil { + return nil, err + } + + switch v := attResult.(type) { + case *pb.Attestation: + a.logger.Println("attestation through TPM quote") + + v.CanonicalEventLog = cosCel.Bytes() + req.Attestation = v + case *verifier.TDCCELAttestation: + a.logger.Println("attestation through TDX quote") + + certChain, err := internal.GetCertificateChain(a.fetchedAK.Cert(), http.DefaultClient) + if err != nil { + return nil, fmt.Errorf("failed when fetching certificate chain: %w", err) + } + + v.CanonicalEventLog = cosCel.Bytes() + v.IntermediateCerts = certChain + v.AkCert = a.fetchedAK.CertDERBytes() + req.TDCCELAttestation = v + default: + return nil, fmt.Errorf("received an unsupported attestation type! %v", v) + } + signatures := a.sigsCache.get() if len(signatures) > 0 { req.ContainerImageSignatures = signatures @@ -152,10 +210,62 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error return resp.ClaimsToken, nil } -func (a *agent) attest(nonce []byte, cel []byte) (*pb.Attestation, error) { - a.tpmMu.Lock() - defer a.tpmMu.Unlock() - return a.fetchedAK.Attest(client.AttestOpts{Nonce: nonce, CanonicalEventLog: cel, CertChainFetcher: http.DefaultClient}) +type tpmAttestRoot struct { + tpmMu sync.Mutex + fetchedAK *client.Key + tpm io.ReadWriteCloser +} + +func (t *tpmAttestRoot) Extend(c cel.Content, l *cel.CEL) error { + return l.AppendEventPCR(t.tpm, cel.CosEventPCR, defaultCELHashAlgo, c) +} + +func (t *tpmAttestRoot) Attest(nonce []byte) (any, error) { + t.tpmMu.Lock() + defer t.tpmMu.Unlock() + + return t.fetchedAK.Attest(client.AttestOpts{ + Nonce: nonce, + CertChainFetcher: http.DefaultClient, + }) +} + +type tdxAttestRoot struct { + tdxMu sync.Mutex + qp *tg.LinuxConfigFsQuoteProvider + tsmClient configfsi.Client +} + +func (t *tdxAttestRoot) Extend(c cel.Content, l *cel.CEL) error { + return l.AppendEventRTMR(t.tsmClient, cel.CosRTMR, c) +} + +func (t *tdxAttestRoot) Attest(nonce []byte) (any, error) { + t.tdxMu.Lock() + defer t.tdxMu.Unlock() + + var tdxNonce [tlabi.TdReportDataSize]byte + copy(tdxNonce[:], nonce) + + rawQuote, err := tg.GetRawQuote(t.qp, tdxNonce) + if err != nil { + return nil, err + } + + ccelData, err := os.ReadFile("/sys/firmware/acpi/tables/data/CCEL") + if err != nil { + return nil, err + } + ccelTable, err := os.ReadFile("/sys/firmware/acpi/tables/CCEL") + if err != nil { + return nil, err + } + + return &verifier.TDCCELAttestation{ + CcelAcpiTable: ccelTable, + CcelData: ccelData, + TdQuote: rawQuote, + }, nil } // Refresh refreshes the internal state of the attestation agent. diff --git a/launcher/cloudbuild.yaml b/launcher/cloudbuild.yaml index ed9b2378d..791a90b29 100644 --- a/launcher/cloudbuild.yaml +++ b/launcher/cloudbuild.yaml @@ -1,7 +1,7 @@ substitutions: '_BASE_IMAGE': '' # an empty base image means the build will use the latest image in '_BASE_IMAGE_FAMILY' - '_BASE_IMAGE_FAMILY': 'cos-113-lts' # base image family - '_OUTPUT_IMAGE_PREFIX': 'confidential-space' + '_BASE_IMAGE_FAMILY': 'cos-tdx-113-lts' # base image family + '_OUTPUT_IMAGE_PREFIX': 'confidential-space' # in order '_OUTPUT_IMAGE_SUFFIX': '' '_OUTPUT_IMAGE_FAMILY': '' '_BUCKET_NAME': '${PROJECT_ID}_cloudbuild' @@ -21,7 +21,7 @@ steps: if [ -z ${base_image} ] then echo "getting the latest COS image" - base_image=$(gcloud compute images describe-from-family ${BASE_IMAGE_FAMILY} --project cos-cloud | grep name | cut -d ' ' -f 2) + base_image=$(gcloud compute images describe-from-family ${BASE_IMAGE_FAMILY} --project confidential-vm-images | grep name | cut -d ' ' -f 2) fi echo ${base_image} > /workspace/base_image.txt @@ -30,9 +30,9 @@ steps: id: DebugImageBuild waitFor: ['BaseImageIdent'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' - - 'OUTPUT_IMAGE_FAMILY=$_OUTPUT_IMAGE_FAMILY' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' + - 'OUTPUT_IMAGE_FAMILY=${_OUTPUT_IMAGE_FAMILY}' - 'BUCKET_NAME=$_BUCKET_NAME' - 'SHORT_SHA=${SHORT_SHA}' script: | @@ -49,9 +49,9 @@ steps: id: HardenedImageBuild waitFor: ['BaseImageIdent'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' - - 'OUTPUT_IMAGE_FAMILY=$_OUTPUT_IMAGE_FAMILY' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' + - 'OUTPUT_IMAGE_FAMILY=${_OUTPUT_IMAGE_FAMILY}' - 'BUCKET_NAME=$_BUCKET_NAME' - 'SHORT_SHA=${SHORT_SHA}' script: | @@ -68,8 +68,8 @@ steps: id: ExperimentsTests waitFor: ['DebugImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -84,8 +84,8 @@ steps: id: HttpServerTests waitFor: ['DebugImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -100,8 +100,8 @@ steps: id: DebugImageTests waitFor: ['DebugImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -116,8 +116,8 @@ steps: id: HardenedImageTests waitFor: ['HardenedImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -131,8 +131,8 @@ steps: id: LaunchPolicyTests waitFor: ['HardenedImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -147,8 +147,8 @@ steps: id: HardenedNetworkIngressTests waitFor: ['HardenedImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -161,8 +161,8 @@ steps: id: DebugNetworkIngressTests waitFor: ['DebugImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -175,8 +175,8 @@ steps: id: LogRedirectionTests waitFor: ['HardenedImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -190,8 +190,8 @@ steps: id: HardenedDiscoverContainerSignatureTests waitFor: ['HardenedImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -204,8 +204,8 @@ steps: id: DebugDiscoverContainerSignatureTests waitFor: ['DebugImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -218,8 +218,8 @@ steps: id: MemoryMonitoringTests waitFor: ['HardenedImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -232,8 +232,8 @@ steps: id: ODAWithSignedContainerTest waitFor: ['HardenedImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash @@ -246,8 +246,8 @@ steps: id: MountTests waitFor: ['HardenedImageBuild'] env: - - 'OUTPUT_IMAGE_PREFIX=$_OUTPUT_IMAGE_PREFIX' - - 'OUTPUT_IMAGE_SUFFIX=$_OUTPUT_IMAGE_SUFFIX' + - 'OUTPUT_IMAGE_PREFIX=${_OUTPUT_IMAGE_PREFIX}' + - 'OUTPUT_IMAGE_SUFFIX=${_OUTPUT_IMAGE_SUFFIX}' - 'PROJECT_ID=$PROJECT_ID' script: | #!/usr/bin/env bash diff --git a/launcher/go.mod b/launcher/go.mod index a7da7f3be..16ba00eb9 100644 --- a/launcher/go.mod +++ b/launcher/go.mod @@ -9,6 +9,8 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-cmp v0.6.0 + github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc + github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843 github.com/google/go-tpm v0.9.0 github.com/google/go-tpm-tools v0.4.4 github.com/google/go-tpm-tools/verifier v0.4.4 @@ -45,10 +47,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.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.2-0.20240902060211-1f7f7b9b42b9 // indirect + github.com/google/go-sev-guest v0.11.2-0.20241009005433-de2ac900e958 // 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 @@ -62,7 +62,6 @@ require ( github.com/moby/sys/signal v0.7.0 // indirect github.com/moby/sys/user v0.1.0 // indirect github.com/opencontainers/selinux v1.11.0 // indirect - github.com/pborman/uuid v1.2.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/launcher/go.sum b/launcher/go.sum index 784880023..1be3055a0 100644 --- a/launcher/go.sum +++ b/launcher/go.sum @@ -363,10 +363,10 @@ github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOm github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= 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.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-sev-guest v0.11.2-0.20241009005433-de2ac900e958 h1:GfnkFZNr80qFGLR/EY75zwk8puz8+frGj4iwPwnJbSU= +github.com/google/go-sev-guest v0.11.2-0.20241009005433-de2ac900e958/go.mod h1:8+UOtSaqVIZjJJ9DDmgRko3J/kNc6jI5KLHxoeao7cA= +github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843 h1:+MoPobRN9HrDhGyn6HnF5NYo4uMBKaiFqAtf/D/OB4A= +github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843/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= @@ -624,8 +624,6 @@ github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= -github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= diff --git a/launcher/image/cloudbuild.yaml b/launcher/image/cloudbuild.yaml index 1ac42a381..fad9f0bbc 100644 --- a/launcher/image/cloudbuild.yaml +++ b/launcher/image/cloudbuild.yaml @@ -2,7 +2,7 @@ substitutions: '_BASE_IMAGE': '' '_OUTPUT_IMAGE_NAME': '' '_OUTPUT_IMAGE_FAMILY': '' - '_BASE_IMAGE_PROJECT': 'cos-cloud' + '_BASE_IMAGE_PROJECT': 'confidential-vm-images' '_IMAGE_ENV': '' '_BUCKET_NAME': '${PROJECT_ID}_cloudbuild' '_CS_LICENSE': '' diff --git a/launcher/launcher/main.go b/launcher/launcher/main.go index 9181d4e61..a9a9899ea 100644 --- a/launcher/launcher/main.go +++ b/launcher/launcher/main.go @@ -161,7 +161,7 @@ func startLauncher(ctx context.Context, launchSpec spec.LaunchSpec, serialConsol } defer tpm.Close() - // check AK (EK signing) cert + // check if TPM AK (EK signing) cert is present gceAk, err := client.GceAttestationKeyECC(tpm) if err != nil { return err diff --git a/verifier/client.go b/verifier/client.go index 0af28ae1a..7b8783391 100644 --- a/verifier/client.go +++ b/verifier/client.go @@ -37,11 +37,24 @@ type TokenOptions struct { // Challenge from CreateChallenge, optional GcpCredentials linked to the // attestation, the Attestation generated from the TPM, and optional container image signatures associated with the workload. type VerifyAttestationRequest struct { - Challenge *Challenge - GcpCredentials [][]byte + Challenge *Challenge + GcpCredentials [][]byte + // Attestation is for TPM attestation Attestation *attestpb.Attestation ContainerImageSignatures []oci.Signature TokenOptions TokenOptions + // TDCCELAttestation is for TDX CCEL RTMR attestation + TDCCELAttestation *TDCCELAttestation +} + +type TDCCELAttestation struct { + CcelAcpiTable []byte + CcelData []byte + CanonicalEventLog []byte + TdQuote []byte + // still needs following two for GCE info + AkCert []byte + IntermediateCerts [][]byte } // VerifyAttestationResponse is the response from a successful diff --git a/verifier/go.mod b/verifier/go.mod index 717c00e0e..5c0f2569c 100644 --- a/verifier/go.mod +++ b/verifier/go.mod @@ -9,13 +9,13 @@ require ( cloud.google.com/go/confidentialcomputing v1.6.0 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-sev-guest v0.11.2-0.20241009005433-de2ac900e958 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/google/uuid v1.6.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 - github.com/pborman/uuid v1.2.1 go.uber.org/multierr v1.11.0 golang.org/x/net v0.27.0 golang.org/x/oauth2 v0.21.0 @@ -41,7 +41,6 @@ require ( 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 - github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/verifier/go.sum b/verifier/go.sum index 6a69d1572..8974f960f 100644 --- a/verifier/go.sum +++ b/verifier/go.sum @@ -332,8 +332,8 @@ github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOm github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= 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-sev-guest v0.11.2-0.20241009005433-de2ac900e958 h1:GfnkFZNr80qFGLR/EY75zwk8puz8+frGj4iwPwnJbSU= +github.com/google/go-sev-guest v0.11.2-0.20241009005433-de2ac900e958/go.mod h1:8+UOtSaqVIZjJJ9DDmgRko3J/kNc6jI5KLHxoeao7cA= 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= @@ -577,8 +577,6 @@ github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= -github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= diff --git a/verifier/rest/rest.go b/verifier/rest/rest.go index 1b20d2764..561a4079e 100644 --- a/verifier/rest/rest.go +++ b/verifier/rest/rest.go @@ -109,11 +109,17 @@ func (c *restClient) CreateChallenge(ctx context.Context) (*verifier.Challenge, // VerifyAttestation implements verifier.Client func (c *restClient) VerifyAttestation(ctx context.Context, request verifier.VerifyAttestationRequest) (*verifier.VerifyAttestationResponse, error) { - if request.Challenge == nil || request.Attestation == nil { + if request.Challenge == nil { return nil, fmt.Errorf("nil value provided in challenge") } + + if request.Attestation == nil && request.TDCCELAttestation == nil { + return nil, fmt.Errorf("neither TPM nor TDX attestation is present") + } + req := convertRequestToREST(request) req.Challenge = request.Challenge.Name + response, err := c.v1Client.VerifyAttestation(ctx, req) if err != nil { return nil, fmt.Errorf("calling v1.VerifyAttestation: %w", err) @@ -140,36 +146,6 @@ func convertRequestToREST(request verifier.VerifyAttestationRequest) *confidenti idTokens[i] = string(token) } - quotes := make([]*confidentialcomputingpb.TpmAttestation_Quote, len(request.Attestation.GetQuotes())) - for i, quote := range request.Attestation.GetQuotes() { - pcrVals := map[int32][]byte{} - for idx, val := range quote.GetPcrs().GetPcrs() { - pcrVals[int32(idx)] = val - } - - quotes[i] = &confidentialcomputingpb.TpmAttestation_Quote{ - RawQuote: quote.GetQuote(), - RawSignature: quote.GetRawSig(), - HashAlgo: int32(quote.GetPcrs().GetHash()), - PcrValues: pcrVals, - } - } - - certs := make([][]byte, len(request.Attestation.GetIntermediateCerts())) - for i, cert := range request.Attestation.GetIntermediateCerts() { - certs[i] = cert - } - - signatures := make([]*confidentialcomputingpb.ContainerImageSignature, len(request.ContainerImageSignatures)) - for i, sig := range request.ContainerImageSignatures { - signature, err := convertOCISignatureToREST(sig) - if err != nil { - log.Printf("failed to convert OCI signature [%v] to ContainerImageSignature proto: %v", sig, err) - continue - } - signatures[i] = signature - } - var tokenType confidentialcomputingpb.TokenType switch request.TokenOptions.TokenType { case "OIDC": @@ -182,17 +158,21 @@ func convertRequestToREST(request verifier.VerifyAttestationRequest) *confidenti tokenType = confidentialcomputingpb.TokenType_TOKEN_TYPE_UNSPECIFIED } + signatures := make([]*confidentialcomputingpb.ContainerImageSignature, len(request.ContainerImageSignatures)) + for i, sig := range request.ContainerImageSignatures { + signature, err := convertOCISignatureToREST(sig) + if err != nil { + log.Printf("failed to convert OCI signature [%v] to ContainerImageSignature proto: %v", sig, err) + continue + } + signatures[i] = signature + } + verifyReq := &confidentialcomputingpb.VerifyAttestationRequest{ GcpCredentials: &confidentialcomputingpb.GcpCredentials{ ServiceAccountIdTokens: idTokens, }, - TpmAttestation: &confidentialcomputingpb.TpmAttestation{ - Quotes: quotes, - TcgEventLog: request.Attestation.GetEventLog(), - CanonicalEventLog: request.Attestation.GetCanonicalEventLog(), - AkCert: request.Attestation.GetAkCert(), - CertChain: certs, - }, + TpmAttestation: nil, ConfidentialSpaceInfo: &confidentialcomputingpb.ConfidentialSpaceInfo{ SignedEntities: []*confidentialcomputingpb.SignedEntity{{ContainerImageSignatures: signatures}}, }, @@ -203,20 +183,67 @@ func convertRequestToREST(request verifier.VerifyAttestationRequest) *confidenti }, } - if request.Attestation.GetSevSnpAttestation() != nil { - sevsnp, err := convertSEVSNPProtoToREST(request.Attestation.GetSevSnpAttestation()) - if err != nil { - log.Fatalf("Failed to convert SEVSNP proto to API proto: %v", err) + if request.Attestation != nil { + // TPM attestation route + quotes := make([]*confidentialcomputingpb.TpmAttestation_Quote, len(request.Attestation.GetQuotes())) + for i, quote := range request.Attestation.GetQuotes() { + pcrVals := map[int32][]byte{} + for idx, val := range quote.GetPcrs().GetPcrs() { + pcrVals[int32(idx)] = val + } + + quotes[i] = &confidentialcomputingpb.TpmAttestation_Quote{ + RawQuote: quote.GetQuote(), + RawSignature: quote.GetRawSig(), + HashAlgo: int32(quote.GetPcrs().GetHash()), + PcrValues: pcrVals, + } } - verifyReq.TeeAttestation = sevsnp - } - if request.Attestation.GetTdxAttestation() != nil { - tdx, err := convertTDXProtoToREST(request.Attestation.GetTdxAttestation()) - if err != nil { - log.Fatalf("Failed to convert TD quote proto to API proto: %v", err) + certs := make([][]byte, len(request.Attestation.GetIntermediateCerts())) + for i, cert := range request.Attestation.GetIntermediateCerts() { + certs[i] = cert + } + + verifyReq.TpmAttestation = &confidentialcomputingpb.TpmAttestation{ + Quotes: quotes, + TcgEventLog: request.Attestation.GetEventLog(), + CanonicalEventLog: request.Attestation.GetCanonicalEventLog(), + AkCert: request.Attestation.GetAkCert(), + CertChain: certs, + } + + if request.Attestation.GetSevSnpAttestation() != nil { + sevsnp, err := convertSEVSNPProtoToREST(request.Attestation.GetSevSnpAttestation()) + if err != nil { + log.Fatalf("Failed to convert SEVSNP proto to API proto: %v", err) + } + verifyReq.TeeAttestation = sevsnp + } + + if request.Attestation.GetTdxAttestation() != nil { + tdx, err := convertTDXProtoToREST(request.Attestation.GetTdxAttestation()) + if err != nil { + log.Fatalf("Failed to convert TD quote proto to API proto: %v", err) + } + verifyReq.TeeAttestation = tdx + } + } else if request.TDCCELAttestation != nil { + // TDX attestation route + // still need AK for GCE info! + verifyReq.TpmAttestation = &confidentialcomputingpb.TpmAttestation{ + AkCert: request.TDCCELAttestation.AkCert, + CertChain: request.TDCCELAttestation.IntermediateCerts, + } + + verifyReq.TeeAttestation = &confidentialcomputingpb.VerifyAttestationRequest_TdCcel{ + TdCcel: &confidentialcomputingpb.TdxCcelAttestation{ + TdQuote: request.TDCCELAttestation.TdQuote, + CcelAcpiTable: request.TDCCELAttestation.CcelAcpiTable, + CcelData: request.TDCCELAttestation.CcelData, + CanonicalEventLog: request.TDCCELAttestation.CanonicalEventLog, + }, } - verifyReq.TeeAttestation = tdx } return verifyReq diff --git a/verifier/rest/rest_test.go b/verifier/rest/rest_test.go index eaf6946fa..02464f4c0 100644 --- a/verifier/rest/rest_test.go +++ b/verifier/rest/rest_test.go @@ -11,7 +11,7 @@ import ( tpb "github.com/google/go-tdx-guest/proto/tdx" tgtestdata "github.com/google/go-tdx-guest/testing/testdata" "github.com/google/go-tpm-tools/verifier" - "github.com/pborman/uuid" + "github.com/google/uuid" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/testing/protocmp" ) @@ -95,23 +95,41 @@ func testRawCertTable(t testing.TB) *testCertTable { vcekraw := []byte("vcek") vlekraw := []byte("vlek") extraraw := []byte("extra") - headers[0].GUID = uuid.Parse(sabi.ArkGUID) + + var err error + headers[0].GUID, err = uuid.Parse(sabi.ArkGUID) + if err != nil { + t.Fatalf("cannot parse uuid: %v", err) + } headers[0].Offset = uint32(len(headers) * sabi.CertTableEntrySize) headers[0].Length = uint32(len(arkraw)) - headers[1].GUID = uuid.Parse(sabi.AskGUID) + headers[1].GUID, err = uuid.Parse(sabi.AskGUID) + + if err != nil { + t.Fatalf("cannot parse uuid: %v", err) + } headers[1].Offset = headers[0].Offset + headers[0].Length headers[1].Length = uint32(len(askraw)) - headers[2].GUID = uuid.Parse(sabi.VcekGUID) + headers[2].GUID, err = uuid.Parse(sabi.VcekGUID) + if err != nil { + t.Fatalf("cannot parse uuid: %v", err) + } headers[2].Offset = headers[1].Offset + headers[1].Length headers[2].Length = uint32(len(vcekraw)) - headers[3].GUID = uuid.Parse(sabi.VlekGUID) + headers[3].GUID, err = uuid.Parse(sabi.VlekGUID) + if err != nil { + t.Fatalf("cannot parse uuid: %v", err) + } headers[3].Offset = headers[2].Offset + headers[2].Length headers[3].Length = uint32(len(vlekraw)) - headers[4].GUID = uuid.Parse(extraGUID) + headers[4].GUID, err = uuid.Parse(extraGUID) + if err != nil { + t.Fatalf("cannot parse uuid: %v", err) + } headers[4].Offset = headers[3].Offset + headers[3].Length headers[4].Length = uint32(len(extraraw)) From d15024692582d4fbbf1722c669dd89ce5d62804a Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Wed, 30 Oct 2024 18:26:48 +0000 Subject: [PATCH 02/21] implement AttestationEvidence for TDX --- launcher/agent/agent.go | 69 ++++++++++++------- launcher/teeserver/tee_server.go | 110 +++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 23 deletions(-) diff --git a/launcher/agent/agent.go b/launcher/agent/agent.go index 5bfa79485..6a9c14ef1 100644 --- a/launcher/agent/agent.go +++ b/launcher/agent/agent.go @@ -45,6 +45,7 @@ type principalIDTokenFetcher func(audience string) ([][]byte, error) type AttestationAgent interface { MeasureEvent(cel.Content) error Attest(context.Context, AttestAgentOpts) ([]byte, error) + AttestationEvidence([]byte, string) (*evidence, error) Refresh(context.Context) error Close() error } @@ -138,31 +139,23 @@ func (a *agent) MeasureEvent(event cel.Content) error { return a.ar.Extend(event, &a.cosCel) } -// Attest fetches the nonce and connection ID from the Attestation Service, -// creates an attestation message, and returns the resultant -// principalIDTokens and Metadata Server-generated ID tokens for the instance. -func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error) { - challenge, err := a.client.CreateChallenge(ctx) - if err != nil { - return nil, err - } +type evidence struct { + TPMAttestation *pb.Attestation + TDXAttestation *verifier.TDCCELAttestation + PrincipalTokens [][]byte + ContainerSignatures []oci.Signature +} - principalTokens, err := a.principalFetcher(challenge.Name) +func (a *agent) AttestationEvidence(nonce []byte, principalAud string) (*evidence, error) { + attEvidence := &evidence{} + + var err error + attEvidence.PrincipalTokens, err = a.principalFetcher(principalAud) if err != nil { return nil, fmt.Errorf("failed to get principal tokens: %w", err) } - req := verifier.VerifyAttestationRequest{ - Challenge: challenge, - GcpCredentials: principalTokens, - TokenOptions: verifier.TokenOptions{ - CustomAudience: opts.Aud, - CustomNonce: opts.Nonces, - TokenType: opts.TokenType, - }, - } - - attResult, err := a.ar.Attest(challenge.Nonce) + attResult, err := a.ar.Attest(nonce) if err != nil { return nil, fmt.Errorf("failed to attest: %v", err) } @@ -177,7 +170,7 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error a.logger.Println("attestation through TPM quote") v.CanonicalEventLog = cosCel.Bytes() - req.Attestation = v + attEvidence.TPMAttestation = v case *verifier.TDCCELAttestation: a.logger.Println("attestation through TDX quote") @@ -189,17 +182,47 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error v.CanonicalEventLog = cosCel.Bytes() v.IntermediateCerts = certChain v.AkCert = a.fetchedAK.CertDERBytes() - req.TDCCELAttestation = v + attEvidence.TDXAttestation = v default: return nil, fmt.Errorf("received an unsupported attestation type! %v", v) } signatures := a.sigsCache.get() if len(signatures) > 0 { - req.ContainerImageSignatures = signatures + attEvidence.ContainerSignatures = signatures a.logger.Printf("Found container image signatures: %v\n", signatures) } + return attEvidence, nil +} + +// Attest fetches the nonce and connection ID from the Attestation Service, +// creates an attestation message, and returns the resultant +// principalIDTokens and Metadata Server-generated ID tokens for the instance. +func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error) { + challenge, err := a.client.CreateChallenge(ctx) + if err != nil { + return nil, err + } + + evidence, err := a.AttestationEvidence(challenge.Nonce, challenge.Name) + if err != nil { + return nil, err + } + + req := verifier.VerifyAttestationRequest{ + Challenge: challenge, + GcpCredentials: evidence.PrincipalTokens, + Attestation: evidence.TPMAttestation, + TDCCELAttestation: evidence.TDXAttestation, + ContainerImageSignatures: evidence.ContainerSignatures, + TokenOptions: verifier.TokenOptions{ + CustomAudience: opts.Aud, + CustomNonce: opts.Nonces, + TokenType: opts.TokenType, + }, + } + resp, err := a.client.VerifyAttestation(ctx, req) if err != nil { return nil, err diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 05f415ec5..7f5ee20ea 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -14,6 +14,7 @@ import ( "github.com/google/go-tpm-tools/launcher/agent" "github.com/google/go-tpm-tools/launcher/launcherfile" + "github.com/google/go-tpm-tools/verifier/oci" ) type attestHandler struct { @@ -67,6 +68,7 @@ func (a *attestHandler) Handler() http.Handler { // --unix-socket /tmp/container_launcher/teeserver.sock http://localhost/v1/token mux.HandleFunc("/v1/token", a.getToken) + mux.HandleFunc("/v1/evidence", a.getEvidence) return mux } @@ -138,6 +140,114 @@ func (a *attestHandler) getToken(w http.ResponseWriter, r *http.Request) { w.Write([]byte("TEE server received invalid request")) } +type evidenceRequest struct { + PrincipalAudience string `json:"gcp_credentials_aud"` + Nonce []byte `json:"nonce"` +} + +type confidentialSpaceInfo struct { + SignedEntities []oci.Signature `json:"signed_entities,omitempty"` + CosEventLog []byte `json:"cos_event_log,omitempty"` +} + +type gcpEvidence struct { + GcpCredentials [][]byte `json:"gcp_credentials,omitempty"` + ConfidentialSpaceInfo *confidentialSpaceInfo `json:"confidential_space_info,omitempty"` + AkCert []byte `json:"ak_cert,omitempty"` + IntermediateCerts [][]byte `json:"intermediate_certs,omitempty"` +} + +type tdxEvidence struct { + EventLogTable []byte `json:"ccel_table,omitempty"` + EventLogData []byte `json:"ccel_data,omitempty"` + TdxQuote []byte `json:"quote"` + GcpData *gcpEvidence `json:"gcp_data,omitempty"` +} + +func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + + switch r.Method { + case "GET": + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("use POST request for evidence")) + return + case "POST": + var evidenceReq evidenceRequest + decoder := json.NewDecoder(r.Body) + decoder.DisallowUnknownFields() + + err := decoder.Decode(&evidenceReq) + if err != nil { + a.logger.Print(err.Error()) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + if len(evidenceReq.Nonce) == 0 { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("nonce is a required parameter")) + return + } + + if evidenceReq.PrincipalAudience == "" { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("gcp_credentials_aud is a required parameter")) + return + } + + evidence, err := a.attestAgent.AttestationEvidence(evidenceReq.Nonce, evidenceReq.PrincipalAudience) + if err != nil { + a.logger.Print(err.Error()) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + // Check for TDX Attestation. + if evidence.TDXAttestation == nil { + err_msg := "getEvidence is only supported for TDX Attestation" + a.logger.Print(err_msg) + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err_msg)) + return + } + + tdxEvi := &tdxEvidence{ + TdxQuote: evidence.TDXAttestation.TdQuote, + EventLogTable: evidence.TDXAttestation.CcelAcpiTable, + EventLogData: evidence.TDXAttestation.CcelData, + GcpData: &gcpEvidence{ + GcpCredentials: evidence.PrincipalTokens, + AkCert: evidence.TDXAttestation.AkCert, + IntermediateCerts: evidence.TDXAttestation.IntermediateCerts, + ConfidentialSpaceInfo: &confidentialSpaceInfo{ + SignedEntities: evidence.ContainerSignatures, + CosEventLog: evidence.TDXAttestation.CanonicalEventLog, + }, + }, + } + + jsonData, err := json.Marshal(tdxEvi) + if err != nil { + err_msg := "error marshalling response" + a.logger.Print(err_msg) + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err_msg)) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(jsonData) + return + } + + w.WriteHeader(http.StatusBadRequest) + // TODO: add an url pointing to the REST API document + w.Write([]byte("TEE server received invalid request")) +} + // Serve starts the server, will block until the server shutdown. func (s *TeeServer) Serve() error { return s.server.Serve(s.netListener) From 896c633d3087e2cca331cf4e709802d13a54de74 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Wed, 30 Oct 2024 18:31:27 +0000 Subject: [PATCH 03/21] Revert "implement AttestationEvidence for TDX" This reverts commit d15024692582d4fbbf1722c669dd89ce5d62804a. --- launcher/agent/agent.go | 69 +++++++------------ launcher/teeserver/tee_server.go | 110 ------------------------------- 2 files changed, 23 insertions(+), 156 deletions(-) diff --git a/launcher/agent/agent.go b/launcher/agent/agent.go index 6a9c14ef1..5bfa79485 100644 --- a/launcher/agent/agent.go +++ b/launcher/agent/agent.go @@ -45,7 +45,6 @@ type principalIDTokenFetcher func(audience string) ([][]byte, error) type AttestationAgent interface { MeasureEvent(cel.Content) error Attest(context.Context, AttestAgentOpts) ([]byte, error) - AttestationEvidence([]byte, string) (*evidence, error) Refresh(context.Context) error Close() error } @@ -139,23 +138,31 @@ func (a *agent) MeasureEvent(event cel.Content) error { return a.ar.Extend(event, &a.cosCel) } -type evidence struct { - TPMAttestation *pb.Attestation - TDXAttestation *verifier.TDCCELAttestation - PrincipalTokens [][]byte - ContainerSignatures []oci.Signature -} - -func (a *agent) AttestationEvidence(nonce []byte, principalAud string) (*evidence, error) { - attEvidence := &evidence{} +// Attest fetches the nonce and connection ID from the Attestation Service, +// creates an attestation message, and returns the resultant +// principalIDTokens and Metadata Server-generated ID tokens for the instance. +func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error) { + challenge, err := a.client.CreateChallenge(ctx) + if err != nil { + return nil, err + } - var err error - attEvidence.PrincipalTokens, err = a.principalFetcher(principalAud) + principalTokens, err := a.principalFetcher(challenge.Name) if err != nil { return nil, fmt.Errorf("failed to get principal tokens: %w", err) } - attResult, err := a.ar.Attest(nonce) + req := verifier.VerifyAttestationRequest{ + Challenge: challenge, + GcpCredentials: principalTokens, + TokenOptions: verifier.TokenOptions{ + CustomAudience: opts.Aud, + CustomNonce: opts.Nonces, + TokenType: opts.TokenType, + }, + } + + attResult, err := a.ar.Attest(challenge.Nonce) if err != nil { return nil, fmt.Errorf("failed to attest: %v", err) } @@ -170,7 +177,7 @@ func (a *agent) AttestationEvidence(nonce []byte, principalAud string) (*evidenc a.logger.Println("attestation through TPM quote") v.CanonicalEventLog = cosCel.Bytes() - attEvidence.TPMAttestation = v + req.Attestation = v case *verifier.TDCCELAttestation: a.logger.Println("attestation through TDX quote") @@ -182,47 +189,17 @@ func (a *agent) AttestationEvidence(nonce []byte, principalAud string) (*evidenc v.CanonicalEventLog = cosCel.Bytes() v.IntermediateCerts = certChain v.AkCert = a.fetchedAK.CertDERBytes() - attEvidence.TDXAttestation = v + req.TDCCELAttestation = v default: return nil, fmt.Errorf("received an unsupported attestation type! %v", v) } signatures := a.sigsCache.get() if len(signatures) > 0 { - attEvidence.ContainerSignatures = signatures + req.ContainerImageSignatures = signatures a.logger.Printf("Found container image signatures: %v\n", signatures) } - return attEvidence, nil -} - -// Attest fetches the nonce and connection ID from the Attestation Service, -// creates an attestation message, and returns the resultant -// principalIDTokens and Metadata Server-generated ID tokens for the instance. -func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error) { - challenge, err := a.client.CreateChallenge(ctx) - if err != nil { - return nil, err - } - - evidence, err := a.AttestationEvidence(challenge.Nonce, challenge.Name) - if err != nil { - return nil, err - } - - req := verifier.VerifyAttestationRequest{ - Challenge: challenge, - GcpCredentials: evidence.PrincipalTokens, - Attestation: evidence.TPMAttestation, - TDCCELAttestation: evidence.TDXAttestation, - ContainerImageSignatures: evidence.ContainerSignatures, - TokenOptions: verifier.TokenOptions{ - CustomAudience: opts.Aud, - CustomNonce: opts.Nonces, - TokenType: opts.TokenType, - }, - } - resp, err := a.client.VerifyAttestation(ctx, req) if err != nil { return nil, err diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 7f5ee20ea..05f415ec5 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -14,7 +14,6 @@ import ( "github.com/google/go-tpm-tools/launcher/agent" "github.com/google/go-tpm-tools/launcher/launcherfile" - "github.com/google/go-tpm-tools/verifier/oci" ) type attestHandler struct { @@ -68,7 +67,6 @@ func (a *attestHandler) Handler() http.Handler { // --unix-socket /tmp/container_launcher/teeserver.sock http://localhost/v1/token mux.HandleFunc("/v1/token", a.getToken) - mux.HandleFunc("/v1/evidence", a.getEvidence) return mux } @@ -140,114 +138,6 @@ func (a *attestHandler) getToken(w http.ResponseWriter, r *http.Request) { w.Write([]byte("TEE server received invalid request")) } -type evidenceRequest struct { - PrincipalAudience string `json:"gcp_credentials_aud"` - Nonce []byte `json:"nonce"` -} - -type confidentialSpaceInfo struct { - SignedEntities []oci.Signature `json:"signed_entities,omitempty"` - CosEventLog []byte `json:"cos_event_log,omitempty"` -} - -type gcpEvidence struct { - GcpCredentials [][]byte `json:"gcp_credentials,omitempty"` - ConfidentialSpaceInfo *confidentialSpaceInfo `json:"confidential_space_info,omitempty"` - AkCert []byte `json:"ak_cert,omitempty"` - IntermediateCerts [][]byte `json:"intermediate_certs,omitempty"` -} - -type tdxEvidence struct { - EventLogTable []byte `json:"ccel_table,omitempty"` - EventLogData []byte `json:"ccel_data,omitempty"` - TdxQuote []byte `json:"quote"` - GcpData *gcpEvidence `json:"gcp_data,omitempty"` -} - -func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/html") - - switch r.Method { - case "GET": - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("use POST request for evidence")) - return - case "POST": - var evidenceReq evidenceRequest - decoder := json.NewDecoder(r.Body) - decoder.DisallowUnknownFields() - - err := decoder.Decode(&evidenceReq) - if err != nil { - a.logger.Print(err.Error()) - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - - if len(evidenceReq.Nonce) == 0 { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("nonce is a required parameter")) - return - } - - if evidenceReq.PrincipalAudience == "" { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("gcp_credentials_aud is a required parameter")) - return - } - - evidence, err := a.attestAgent.AttestationEvidence(evidenceReq.Nonce, evidenceReq.PrincipalAudience) - if err != nil { - a.logger.Print(err.Error()) - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - - // Check for TDX Attestation. - if evidence.TDXAttestation == nil { - err_msg := "getEvidence is only supported for TDX Attestation" - a.logger.Print(err_msg) - w.WriteHeader(http.StatusPreconditionFailed) - w.Write([]byte(err_msg)) - return - } - - tdxEvi := &tdxEvidence{ - TdxQuote: evidence.TDXAttestation.TdQuote, - EventLogTable: evidence.TDXAttestation.CcelAcpiTable, - EventLogData: evidence.TDXAttestation.CcelData, - GcpData: &gcpEvidence{ - GcpCredentials: evidence.PrincipalTokens, - AkCert: evidence.TDXAttestation.AkCert, - IntermediateCerts: evidence.TDXAttestation.IntermediateCerts, - ConfidentialSpaceInfo: &confidentialSpaceInfo{ - SignedEntities: evidence.ContainerSignatures, - CosEventLog: evidence.TDXAttestation.CanonicalEventLog, - }, - }, - } - - jsonData, err := json.Marshal(tdxEvi) - if err != nil { - err_msg := "error marshalling response" - a.logger.Print(err_msg) - w.WriteHeader(http.StatusPreconditionFailed) - w.Write([]byte(err_msg)) - return - } - - w.WriteHeader(http.StatusOK) - w.Write(jsonData) - return - } - - w.WriteHeader(http.StatusBadRequest) - // TODO: add an url pointing to the REST API document - w.Write([]byte("TEE server received invalid request")) -} - // Serve starts the server, will block until the server shutdown. func (s *TeeServer) Serve() error { return s.server.Serve(s.netListener) From a88eca8b1aa6fe01306358c147bf74cb7f61d3ec Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Wed, 30 Oct 2024 18:40:09 +0000 Subject: [PATCH 04/21] restore changes on branch --- launcher/agent/agent.go | 69 ++++++++++++------- launcher/teeserver/tee_server.go | 110 +++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 23 deletions(-) diff --git a/launcher/agent/agent.go b/launcher/agent/agent.go index 5bfa79485..6a9c14ef1 100644 --- a/launcher/agent/agent.go +++ b/launcher/agent/agent.go @@ -45,6 +45,7 @@ type principalIDTokenFetcher func(audience string) ([][]byte, error) type AttestationAgent interface { MeasureEvent(cel.Content) error Attest(context.Context, AttestAgentOpts) ([]byte, error) + AttestationEvidence([]byte, string) (*evidence, error) Refresh(context.Context) error Close() error } @@ -138,31 +139,23 @@ func (a *agent) MeasureEvent(event cel.Content) error { return a.ar.Extend(event, &a.cosCel) } -// Attest fetches the nonce and connection ID from the Attestation Service, -// creates an attestation message, and returns the resultant -// principalIDTokens and Metadata Server-generated ID tokens for the instance. -func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error) { - challenge, err := a.client.CreateChallenge(ctx) - if err != nil { - return nil, err - } +type evidence struct { + TPMAttestation *pb.Attestation + TDXAttestation *verifier.TDCCELAttestation + PrincipalTokens [][]byte + ContainerSignatures []oci.Signature +} - principalTokens, err := a.principalFetcher(challenge.Name) +func (a *agent) AttestationEvidence(nonce []byte, principalAud string) (*evidence, error) { + attEvidence := &evidence{} + + var err error + attEvidence.PrincipalTokens, err = a.principalFetcher(principalAud) if err != nil { return nil, fmt.Errorf("failed to get principal tokens: %w", err) } - req := verifier.VerifyAttestationRequest{ - Challenge: challenge, - GcpCredentials: principalTokens, - TokenOptions: verifier.TokenOptions{ - CustomAudience: opts.Aud, - CustomNonce: opts.Nonces, - TokenType: opts.TokenType, - }, - } - - attResult, err := a.ar.Attest(challenge.Nonce) + attResult, err := a.ar.Attest(nonce) if err != nil { return nil, fmt.Errorf("failed to attest: %v", err) } @@ -177,7 +170,7 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error a.logger.Println("attestation through TPM quote") v.CanonicalEventLog = cosCel.Bytes() - req.Attestation = v + attEvidence.TPMAttestation = v case *verifier.TDCCELAttestation: a.logger.Println("attestation through TDX quote") @@ -189,17 +182,47 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error v.CanonicalEventLog = cosCel.Bytes() v.IntermediateCerts = certChain v.AkCert = a.fetchedAK.CertDERBytes() - req.TDCCELAttestation = v + attEvidence.TDXAttestation = v default: return nil, fmt.Errorf("received an unsupported attestation type! %v", v) } signatures := a.sigsCache.get() if len(signatures) > 0 { - req.ContainerImageSignatures = signatures + attEvidence.ContainerSignatures = signatures a.logger.Printf("Found container image signatures: %v\n", signatures) } + return attEvidence, nil +} + +// Attest fetches the nonce and connection ID from the Attestation Service, +// creates an attestation message, and returns the resultant +// principalIDTokens and Metadata Server-generated ID tokens for the instance. +func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error) { + challenge, err := a.client.CreateChallenge(ctx) + if err != nil { + return nil, err + } + + evidence, err := a.AttestationEvidence(challenge.Nonce, challenge.Name) + if err != nil { + return nil, err + } + + req := verifier.VerifyAttestationRequest{ + Challenge: challenge, + GcpCredentials: evidence.PrincipalTokens, + Attestation: evidence.TPMAttestation, + TDCCELAttestation: evidence.TDXAttestation, + ContainerImageSignatures: evidence.ContainerSignatures, + TokenOptions: verifier.TokenOptions{ + CustomAudience: opts.Aud, + CustomNonce: opts.Nonces, + TokenType: opts.TokenType, + }, + } + resp, err := a.client.VerifyAttestation(ctx, req) if err != nil { return nil, err diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 05f415ec5..7f5ee20ea 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -14,6 +14,7 @@ import ( "github.com/google/go-tpm-tools/launcher/agent" "github.com/google/go-tpm-tools/launcher/launcherfile" + "github.com/google/go-tpm-tools/verifier/oci" ) type attestHandler struct { @@ -67,6 +68,7 @@ func (a *attestHandler) Handler() http.Handler { // --unix-socket /tmp/container_launcher/teeserver.sock http://localhost/v1/token mux.HandleFunc("/v1/token", a.getToken) + mux.HandleFunc("/v1/evidence", a.getEvidence) return mux } @@ -138,6 +140,114 @@ func (a *attestHandler) getToken(w http.ResponseWriter, r *http.Request) { w.Write([]byte("TEE server received invalid request")) } +type evidenceRequest struct { + PrincipalAudience string `json:"gcp_credentials_aud"` + Nonce []byte `json:"nonce"` +} + +type confidentialSpaceInfo struct { + SignedEntities []oci.Signature `json:"signed_entities,omitempty"` + CosEventLog []byte `json:"cos_event_log,omitempty"` +} + +type gcpEvidence struct { + GcpCredentials [][]byte `json:"gcp_credentials,omitempty"` + ConfidentialSpaceInfo *confidentialSpaceInfo `json:"confidential_space_info,omitempty"` + AkCert []byte `json:"ak_cert,omitempty"` + IntermediateCerts [][]byte `json:"intermediate_certs,omitempty"` +} + +type tdxEvidence struct { + EventLogTable []byte `json:"ccel_table,omitempty"` + EventLogData []byte `json:"ccel_data,omitempty"` + TdxQuote []byte `json:"quote"` + GcpData *gcpEvidence `json:"gcp_data,omitempty"` +} + +func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + + switch r.Method { + case "GET": + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("use POST request for evidence")) + return + case "POST": + var evidenceReq evidenceRequest + decoder := json.NewDecoder(r.Body) + decoder.DisallowUnknownFields() + + err := decoder.Decode(&evidenceReq) + if err != nil { + a.logger.Print(err.Error()) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + if len(evidenceReq.Nonce) == 0 { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("nonce is a required parameter")) + return + } + + if evidenceReq.PrincipalAudience == "" { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("gcp_credentials_aud is a required parameter")) + return + } + + evidence, err := a.attestAgent.AttestationEvidence(evidenceReq.Nonce, evidenceReq.PrincipalAudience) + if err != nil { + a.logger.Print(err.Error()) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + // Check for TDX Attestation. + if evidence.TDXAttestation == nil { + err_msg := "getEvidence is only supported for TDX Attestation" + a.logger.Print(err_msg) + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err_msg)) + return + } + + tdxEvi := &tdxEvidence{ + TdxQuote: evidence.TDXAttestation.TdQuote, + EventLogTable: evidence.TDXAttestation.CcelAcpiTable, + EventLogData: evidence.TDXAttestation.CcelData, + GcpData: &gcpEvidence{ + GcpCredentials: evidence.PrincipalTokens, + AkCert: evidence.TDXAttestation.AkCert, + IntermediateCerts: evidence.TDXAttestation.IntermediateCerts, + ConfidentialSpaceInfo: &confidentialSpaceInfo{ + SignedEntities: evidence.ContainerSignatures, + CosEventLog: evidence.TDXAttestation.CanonicalEventLog, + }, + }, + } + + jsonData, err := json.Marshal(tdxEvi) + if err != nil { + err_msg := "error marshalling response" + a.logger.Print(err_msg) + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err_msg)) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(jsonData) + return + } + + w.WriteHeader(http.StatusBadRequest) + // TODO: add an url pointing to the REST API document + w.Write([]byte("TEE server received invalid request")) +} + // Serve starts the server, will block until the server shutdown. func (s *TeeServer) Serve() error { return s.server.Serve(s.netListener) From a6b3a654275398ecef95844851ea9eac5fc4f318 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Thu, 31 Oct 2024 18:14:13 +0000 Subject: [PATCH 05/21] set content type to json --- launcher/teeserver/tee_server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 7f5ee20ea..e57985e0b 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -165,7 +165,7 @@ type tdxEvidence struct { } func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/html") + w.Header().Set("Content-Type", "application/json") switch r.Method { case "GET": From c27ae96dae002a076a68a48df418711502cfa1b8 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Thu, 31 Oct 2024 19:42:46 +0000 Subject: [PATCH 06/21] make gcpdata value --- launcher/teeserver/tee_server.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index e57985e0b..f1ad8151e 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -151,17 +151,17 @@ type confidentialSpaceInfo struct { } type gcpEvidence struct { - GcpCredentials [][]byte `json:"gcp_credentials,omitempty"` - ConfidentialSpaceInfo *confidentialSpaceInfo `json:"confidential_space_info,omitempty"` - AkCert []byte `json:"ak_cert,omitempty"` - IntermediateCerts [][]byte `json:"intermediate_certs,omitempty"` + GcpCredentials [][]byte `json:"gcp_credentials,omitempty"` + ConfidentialSpaceInfo confidentialSpaceInfo `json:"confidential_space_info,omitempty"` + AkCert []byte `json:"ak_cert,omitempty"` + IntermediateCerts [][]byte `json:"intermediate_certs,omitempty"` } type tdxEvidence struct { - EventLogTable []byte `json:"ccel_table,omitempty"` - EventLogData []byte `json:"ccel_data,omitempty"` - TdxQuote []byte `json:"quote"` - GcpData *gcpEvidence `json:"gcp_data,omitempty"` + EventLogTable []byte `json:"ccel_table,omitempty"` + EventLogData []byte `json:"ccel_data,omitempty"` + TdxQuote []byte `json:"quote"` + GcpData gcpEvidence `json:"gcp_data,omitempty"` } func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { @@ -218,11 +218,11 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { TdxQuote: evidence.TDXAttestation.TdQuote, EventLogTable: evidence.TDXAttestation.CcelAcpiTable, EventLogData: evidence.TDXAttestation.CcelData, - GcpData: &gcpEvidence{ + GcpData: gcpEvidence{ GcpCredentials: evidence.PrincipalTokens, AkCert: evidence.TDXAttestation.AkCert, IntermediateCerts: evidence.TDXAttestation.IntermediateCerts, - ConfidentialSpaceInfo: &confidentialSpaceInfo{ + ConfidentialSpaceInfo: confidentialSpaceInfo{ SignedEntities: evidence.ContainerSignatures, CosEventLog: evidence.TDXAttestation.CanonicalEventLog, }, From 1efb862459217d8b3febb52c650c48454f9ec805 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Thu, 14 Nov 2024 18:50:47 +0000 Subject: [PATCH 07/21] update evidence format --- launcher/teeserver/tee_server.go | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index f1ad8151e..c958cb2d4 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -147,7 +147,6 @@ type evidenceRequest struct { type confidentialSpaceInfo struct { SignedEntities []oci.Signature `json:"signed_entities,omitempty"` - CosEventLog []byte `json:"cos_event_log,omitempty"` } type gcpEvidence struct { @@ -157,11 +156,16 @@ type gcpEvidence struct { IntermediateCerts [][]byte `json:"intermediate_certs,omitempty"` } +type tdxAttestation struct { + CcelAcpiTable []byte `json:"ccel_table,omitempty"` + CcelData []byte `json:"ccel_data,omitempty"` + TdQuote []byte `json:"quote"` + CanonicalEventLog []byte `json:"canonical_event_log,omitempty"` +} + type tdxEvidence struct { - EventLogTable []byte `json:"ccel_table,omitempty"` - EventLogData []byte `json:"ccel_data,omitempty"` - TdxQuote []byte `json:"quote"` - GcpData gcpEvidence `json:"gcp_data,omitempty"` + Attestation tdxAttestation `json:"attestation,omitempty"` + GcpData gcpEvidence `json:"gcp_data,omitempty"` } func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { @@ -215,16 +219,18 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { } tdxEvi := &tdxEvidence{ - TdxQuote: evidence.TDXAttestation.TdQuote, - EventLogTable: evidence.TDXAttestation.CcelAcpiTable, - EventLogData: evidence.TDXAttestation.CcelData, + Attestation: tdxAttestation{ + TdQuote: evidence.TDXAttestation.TdQuote, + CcelAcpiTable: evidence.TDXAttestation.CcelAcpiTable, + CcelData: evidence.TDXAttestation.CcelData, + CanonicalEventLog: evidence.TDXAttestation.CanonicalEventLog, + }, GcpData: gcpEvidence{ GcpCredentials: evidence.PrincipalTokens, AkCert: evidence.TDXAttestation.AkCert, IntermediateCerts: evidence.TDXAttestation.IntermediateCerts, ConfidentialSpaceInfo: confidentialSpaceInfo{ SignedEntities: evidence.ContainerSignatures, - CosEventLog: evidence.TDXAttestation.CanonicalEventLog, }, }, } From e191ddd77e1b14da270a5b90d64c7b5b83273aca Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Thu, 5 Dec 2024 01:23:54 +0000 Subject: [PATCH 08/21] log evidence before sending --- launcher/teeserver/tee_server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index c958cb2d4..58abc651e 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -235,6 +235,8 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { }, } + a.logger.Printf("%+v\n", tdxEvi) + jsonData, err := json.Marshal(tdxEvi) if err != nil { err_msg := "error marshalling response" From ac3c7be0f2d2b8e9ebf80da5dd82111bd20646cf Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Thu, 5 Dec 2024 17:16:59 +0000 Subject: [PATCH 09/21] use ita-style nonce --- launcher/teeserver/tee_server.go | 44 ++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 58abc651e..b7c60e8cf 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -4,6 +4,7 @@ package teeserver import ( "context" + "crypto/sha512" "encoding/json" "fmt" "log" @@ -140,9 +141,14 @@ func (a *attestHandler) getToken(w http.ResponseWriter, r *http.Request) { w.Write([]byte("TEE server received invalid request")) } +type itaNonce struct { + Val []byte `json:"val"` + Iat []byte `json:"iat"` + Signature []byte `json:"signature"` +} + type evidenceRequest struct { - PrincipalAudience string `json:"gcp_credentials_aud"` - Nonce []byte `json:"nonce"` + Nonce itaNonce `json:"nonce"` } type confidentialSpaceInfo struct { @@ -168,6 +174,26 @@ type tdxEvidence struct { GcpData gcpEvidence `json:"gcp_data,omitempty"` } +func processITANonce(input itaNonce) ([]byte, error) { + if len(input.Val) == 0 { + return nil, fmt.Errorf("no value in nonce") + } + + if len(input.Iat) == 0 { + return nil, fmt.Errorf("no iat in nonce") + } + + nonce := append(input.Val, input.Iat...) + + hash := sha512.New() + _, err := hash.Write(nonce) + if err != nil { + return nil, err + } + + return hash.Sum(nil), nil +} + func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -189,19 +215,15 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { return } - if len(evidenceReq.Nonce) == 0 { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("nonce is a required parameter")) - return - } - - if evidenceReq.PrincipalAudience == "" { + nonce, err := processITANonce(evidenceReq.Nonce) + if err != nil { + a.logger.Print(err.Error()) w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("gcp_credentials_aud is a required parameter")) + w.Write([]byte(err.Error())) return } - evidence, err := a.attestAgent.AttestationEvidence(evidenceReq.Nonce, evidenceReq.PrincipalAudience) + evidence, err := a.attestAgent.AttestationEvidence(nonce, "ita://"+string(evidenceReq.Nonce.Val)) if err != nil { a.logger.Print(err.Error()) w.WriteHeader(http.StatusBadRequest) From fe201f5e75461d4337acd19ad9bb461bed0973b7 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Tue, 10 Dec 2024 23:38:28 +0000 Subject: [PATCH 10/21] use structured log for evidence --- launcher/teeserver/tee_server.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index b7c60e8cf..75031fe4b 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -13,6 +13,8 @@ import ( "os" "path/filepath" + "cloud.google.com/go/compute/metadata" + "cloud.google.com/go/logging" "github.com/google/go-tpm-tools/launcher/agent" "github.com/google/go-tpm-tools/launcher/launcherfile" "github.com/google/go-tpm-tools/verifier/oci" @@ -23,6 +25,7 @@ type attestHandler struct { attestAgent agent.AttestationAgent defaultTokenFile string logger *log.Logger + cloudLogger *logging.Logger } type customTokenRequest struct { @@ -46,6 +49,20 @@ func New(ctx context.Context, unixSock string, a agent.AttestationAgent, logger return nil, fmt.Errorf("cannot listen to the socket [%s]: %v", unixSock, err) } + // Configure Cloud Logging client. + mdsClient := metadata.NewClient(nil) + + projectID, err := mdsClient.ProjectIDWithContext(ctx) + if err != nil { + return nil, err + } + + // Configure Cloud Logging client/logger. + cloudLogger, err := logging.NewClient(ctx, projectID) + if err != nil { + return nil, err + } + teeServer := TeeServer{ netListener: nl, server: &http.Server{ @@ -54,6 +71,7 @@ func New(ctx context.Context, unixSock string, a agent.AttestationAgent, logger attestAgent: a, defaultTokenFile: filepath.Join(launcherfile.HostTmpPath, launcherfile.AttestationVerifierTokenFilename), logger: logger, + cloudLogger: cloudLogger.Logger("ita-prototype-image"), }).Handler(), }, } @@ -209,7 +227,7 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { err := decoder.Decode(&evidenceReq) if err != nil { - a.logger.Print(err.Error()) + a.logger.Printf(err.Error()) w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return @@ -257,7 +275,13 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { }, } - a.logger.Printf("%+v\n", tdxEvi) + logEntry := logging.Entry{ + Severity: logging.Info, + Payload: tdxEvi, + } + + a.cloudLogger.Log(logEntry) + a.cloudLogger.Flush() jsonData, err := json.Marshal(tdxEvi) if err != nil { From ecc58ee9febb5852f6519f107208aebaf10be4b7 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Tue, 10 Dec 2024 23:44:14 +0000 Subject: [PATCH 11/21] go mod tidy --- launcher/go.mod | 3 +++ launcher/go.sum | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/launcher/go.mod b/launcher/go.mod index 16ba00eb9..48ec21ca7 100644 --- a/launcher/go.mod +++ b/launcher/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( cloud.google.com/go/compute/metadata v0.5.0 + cloud.google.com/go/logging v1.10.0 github.com/cenkalti/backoff/v4 v4.2.1 github.com/containerd/containerd v1.7.16 github.com/coreos/go-systemd/v22 v22.5.0 @@ -23,9 +24,11 @@ require ( ) require ( + cloud.google.com/go v0.115.0 // indirect cloud.google.com/go/auth v0.7.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect cloud.google.com/go/confidentialcomputing v1.6.0 // indirect + cloud.google.com/go/longrunning v0.5.9 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect diff --git a/launcher/go.sum b/launcher/go.sum index 1be3055a0..2f946a815 100644 --- a/launcher/go.sum +++ b/launcher/go.sum @@ -30,6 +30,8 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= +cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE= cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs= cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI= @@ -47,6 +49,12 @@ cloud.google.com/go/confidentialcomputing v1.6.0/go.mod h1:0Y5aQEtvVIUIkFYDwqdc/ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v1.1.10 h1:ZSAr64oEhQSClwBL670MsJAW5/RLiC6kfw3Bqmd5ZDI= +cloud.google.com/go/iam v1.1.10/go.mod h1:iEgMq62sg8zx446GCaijmA2Miwg5o3UbO+nI47WHJps= +cloud.google.com/go/logging v1.10.0 h1:f+ZXMqyrSJ5vZ5pE/zr0xC8y/M9BLNzQeLBwfeZ+wY4= +cloud.google.com/go/logging v1.10.0/go.mod h1:EHOwcxlltJrYGqMGfghSet736KR3hX1MAj614mrMk9I= +cloud.google.com/go/longrunning v0.5.9 h1:haH9pAuXdPAMqHvzX0zlWQigXT7B0+CL4/2nXXdBo5k= +cloud.google.com/go/longrunning v0.5.9/go.mod h1:HD+0l9/OOW0za6UWdKJtXoFAX/BGg/3Wj8p10NeWF7c= cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= @@ -829,6 +837,8 @@ go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGX go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= From c3df3ddbf1083a85e7a286fc0d771d47a60ec127 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Wed, 11 Dec 2024 01:14:14 +0000 Subject: [PATCH 12/21] remove CCEL table --- launcher/teeserver/tee_server.go | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 75031fe4b..e3a33f642 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -261,7 +261,6 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { tdxEvi := &tdxEvidence{ Attestation: tdxAttestation{ TdQuote: evidence.TDXAttestation.TdQuote, - CcelAcpiTable: evidence.TDXAttestation.CcelAcpiTable, CcelData: evidence.TDXAttestation.CcelData, CanonicalEventLog: evidence.TDXAttestation.CanonicalEventLog, }, From c01d0276afd2bf14dc38469db1ca2412ecb8c9fe Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Wed, 18 Dec 2024 01:34:30 +0000 Subject: [PATCH 13/21] write evidence to file --- launcher/teeserver/tee_server.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index e3a33f642..7f8277fac 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -6,6 +6,7 @@ import ( "context" "crypto/sha512" "encoding/json" + "errors" "fmt" "log" "net" @@ -289,6 +290,31 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusPreconditionFailed) w.Write([]byte(err_msg)) return + + // Check if output file exists. + filename := "/tmp/container_launcher/ita_evidence" + _, err = os.Stat(filename) + if err != nil && !errors.Is(err, os.ErrNotExist) { + os.Exit(1) + } else if err == nil { + os.Remove(filename) + } + + // Create output file. + f, err := os.Create(filename) + if err != nil { + fmt.Printf("failed to create output file: %v", err) + os.Exit(1) + } + defer f.Close() + + // Write to output file. + _, err = f.WriteString(string(jsonData)) + if err != nil { + fmt.Printf("failed to write to output file: %v", err) + os.Exit(1) + } + } w.WriteHeader(http.StatusOK) From f8e55aa757831ee0bde766e3d90ba9f36f87bea0 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Wed, 18 Dec 2024 01:35:35 +0000 Subject: [PATCH 14/21] fix error returns --- launcher/teeserver/tee_server.go | 51 ++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 7f8277fac..4cddffb72 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -290,31 +290,36 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusPreconditionFailed) w.Write([]byte(err_msg)) return + } - // Check if output file exists. - filename := "/tmp/container_launcher/ita_evidence" - _, err = os.Stat(filename) - if err != nil && !errors.Is(err, os.ErrNotExist) { - os.Exit(1) - } else if err == nil { - os.Remove(filename) - } - - // Create output file. - f, err := os.Create(filename) - if err != nil { - fmt.Printf("failed to create output file: %v", err) - os.Exit(1) - } - defer f.Close() - - // Write to output file. - _, err = f.WriteString(string(jsonData)) - if err != nil { - fmt.Printf("failed to write to output file: %v", err) - os.Exit(1) - } + // Check if output file exists. + filename := "/tmp/container_launcher/ita_evidence" + _, err = os.Stat(filename) + if err != nil && !errors.Is(err, os.ErrNotExist) { + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err.Error())) + return + } else if err == nil { + os.Remove(filename) + } + // Create output file. + f, err := os.Create(filename) + if err != nil { + fmt.Printf("failed to create output file: %v", err) + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err.Error())) + return + } + defer f.Close() + + // Write to output file. + _, err = f.WriteString(string(jsonData)) + if err != nil { + fmt.Printf("failed to write to output file: %v", err) + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err.Error())) + return } w.WriteHeader(http.StatusOK) From 2d99431c63fb9500711322dc9530e776186679f2 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Fri, 20 Dec 2024 17:55:15 +0000 Subject: [PATCH 15/21] remove cloud logging --- launcher/teeserver/tee_server.go | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 4cddffb72..62ff96e28 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -14,8 +14,6 @@ import ( "os" "path/filepath" - "cloud.google.com/go/compute/metadata" - "cloud.google.com/go/logging" "github.com/google/go-tpm-tools/launcher/agent" "github.com/google/go-tpm-tools/launcher/launcherfile" "github.com/google/go-tpm-tools/verifier/oci" @@ -26,7 +24,6 @@ type attestHandler struct { attestAgent agent.AttestationAgent defaultTokenFile string logger *log.Logger - cloudLogger *logging.Logger } type customTokenRequest struct { @@ -50,20 +47,6 @@ func New(ctx context.Context, unixSock string, a agent.AttestationAgent, logger return nil, fmt.Errorf("cannot listen to the socket [%s]: %v", unixSock, err) } - // Configure Cloud Logging client. - mdsClient := metadata.NewClient(nil) - - projectID, err := mdsClient.ProjectIDWithContext(ctx) - if err != nil { - return nil, err - } - - // Configure Cloud Logging client/logger. - cloudLogger, err := logging.NewClient(ctx, projectID) - if err != nil { - return nil, err - } - teeServer := TeeServer{ netListener: nl, server: &http.Server{ @@ -72,7 +55,6 @@ func New(ctx context.Context, unixSock string, a agent.AttestationAgent, logger attestAgent: a, defaultTokenFile: filepath.Join(launcherfile.HostTmpPath, launcherfile.AttestationVerifierTokenFilename), logger: logger, - cloudLogger: cloudLogger.Logger("ita-prototype-image"), }).Handler(), }, } @@ -275,13 +257,7 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { }, } - logEntry := logging.Entry{ - Severity: logging.Info, - Payload: tdxEvi, - } - - a.cloudLogger.Log(logEntry) - a.cloudLogger.Flush() + a.logger.Printf("%+v\n", tdxEvi) jsonData, err := json.Marshal(tdxEvi) if err != nil { From 013b491793531f7244c5110cf02dd249fa498ad2 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Fri, 3 Jan 2025 00:48:18 +0000 Subject: [PATCH 16/21] convert signedentities before sending --- launcher/teeserver/tee_server.go | 39 +++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 62ff96e28..0acf29043 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -5,6 +5,7 @@ package teeserver import ( "context" "crypto/sha512" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -16,7 +17,6 @@ import ( "github.com/google/go-tpm-tools/launcher/agent" "github.com/google/go-tpm-tools/launcher/launcherfile" - "github.com/google/go-tpm-tools/verifier/oci" ) type attestHandler struct { @@ -152,8 +152,13 @@ type evidenceRequest struct { Nonce itaNonce `json:"nonce"` } +type containerSignature struct { + Payload []byte `json:"payload,omitempty"` + Signature []byte `json:"signature,omitempty"` +} + type confidentialSpaceInfo struct { - SignedEntities []oci.Signature `json:"signed_entities,omitempty"` + SignedEntities []containerSignature `json:"signed_entities,omitempty"` } type gcpEvidence struct { @@ -252,11 +257,39 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { AkCert: evidence.TDXAttestation.AkCert, IntermediateCerts: evidence.TDXAttestation.IntermediateCerts, ConfidentialSpaceInfo: confidentialSpaceInfo{ - SignedEntities: evidence.ContainerSignatures, + SignedEntities: make([]containerSignature, len(evidence.ContainerSignatures)), }, }, } + for i, sig := range evidence.ContainerSignatures { + sigPayload, err := sig.Payload() + if err != nil { + a.logger.Print(err.Error()) + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err.Error())) + return + } + + b64Sig, err := sig.Base64Encoded() + if err != nil { + a.logger.Print(err.Error()) + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err.Error())) + return + } + + sigBytes, err := base64.StdEncoding.DecodeString(b64Sig) + if err != nil { + a.logger.Print(err.Error()) + w.WriteHeader(http.StatusPreconditionFailed) + w.Write([]byte(err.Error())) + return + } + + tdxEvi.GcpData.ConfidentialSpaceInfo.SignedEntities[i] = containerSignature{sigPayload, sigBytes} + } + a.logger.Printf("%+v\n", tdxEvi) jsonData, err := json.Marshal(tdxEvi) From a9d495bbd7883e0d16f5cd10d92c62697e0195dd Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Wed, 8 Jan 2025 18:26:34 +0000 Subject: [PATCH 17/21] rename evidence fields --- launcher/teeserver/tee_server.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 0acf29043..d6e44b707 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -148,6 +148,12 @@ type itaNonce struct { Signature []byte `json:"signature"` } +type tokenOptions struct { + Audience string `json:"audience"` + Nonce []string `json:"nonce"` + TokenType string `json:"tokenType"` +} + type evidenceRequest struct { Nonce itaNonce `json:"nonce"` } @@ -176,8 +182,8 @@ type tdxAttestation struct { } type tdxEvidence struct { - Attestation tdxAttestation `json:"attestation,omitempty"` - GcpData gcpEvidence `json:"gcp_data,omitempty"` + Attestation tdxAttestation `json:"tdx,omitempty"` + GcpData gcpEvidence `json:"gcpcs,omitempty"` } func processITANonce(input itaNonce) ([]byte, error) { From 1fc7888eb2d72b535c93cb8209d9095ee87f5fe1 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Thu, 9 Jan 2025 00:41:35 +0000 Subject: [PATCH 18/21] fix API --- launcher/teeserver/evidence.go | 51 ++++++++++++++++ launcher/teeserver/tee_server.go | 100 ++++++++++++++++++------------- 2 files changed, 111 insertions(+), 40 deletions(-) create mode 100644 launcher/teeserver/evidence.go diff --git a/launcher/teeserver/evidence.go b/launcher/teeserver/evidence.go new file mode 100644 index 000000000..2bea2b5bf --- /dev/null +++ b/launcher/teeserver/evidence.go @@ -0,0 +1,51 @@ +package teeserver + +type tdxEvidence struct { + CcelAcpiTable []byte `json:"ccel_table,omitempty"` + CcelData []byte `json:"ccel_data,omitempty"` + CanonicalEventLog []byte `json:"canonical_event_log"` + Quote []byte `json:"quote"` +} + +type containerSignature struct { + Payload []byte `json:"payload"` + Signature []byte `json:"signature"` +} + +type keyIDs struct { + IDs map[string][]string `json:"key_ids"` +} + +type principalTags struct { + ContainerSignatureKIDs keyIDs `json:"container_image_signatures"` +} + +type tokenTypeOptions struct { + AllowedPrincipalTags principalTags `json:"allowed_principal_tags"` +} + +type tokenOptions struct { + Audience string `json:"audience"` + Nonces []string `json:"nonce"` + TokenType string `json:"tokenType"` + TokenTypeOpts tokenTypeOptions `json:"tokenTypeOptions"` +} + +type confidentialSpaceInfo struct { + SignedEntities []containerSignature `json:"signed_entities"` + TokenOpts tokenOptions `json:"token_options"` +} + +type gcpData struct { + GcpCredentials []string `json:"gcp_credentials"` + AKCert []byte `json:"ak_cert"` + IntermediateCerts [][]byte `json:"intermediate_certs"` + CSInfo confidentialSpaceInfo `json:"confidential_space_info"` +} + +type tokenRequest struct { + PolicyMatch bool `json:"policy_must_match"` + TDX tdxEvidence `json:"tdx"` + SigAlg string `json:"token_signing_alg"` + GCP gcpData `json:"gcpcs"` +} diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index d6e44b707..643fd02d9 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -148,43 +148,43 @@ type itaNonce struct { Signature []byte `json:"signature"` } -type tokenOptions struct { - Audience string `json:"audience"` - Nonce []string `json:"nonce"` - TokenType string `json:"tokenType"` -} +// type tokenOptions struct { +// Audience string `json:"audience"` +// Nonce []string `json:"nonce"` +// TokenType string `json:"tokenType"` +// } type evidenceRequest struct { Nonce itaNonce `json:"nonce"` } -type containerSignature struct { - Payload []byte `json:"payload,omitempty"` - Signature []byte `json:"signature,omitempty"` -} - -type confidentialSpaceInfo struct { - SignedEntities []containerSignature `json:"signed_entities,omitempty"` -} - -type gcpEvidence struct { - GcpCredentials [][]byte `json:"gcp_credentials,omitempty"` - ConfidentialSpaceInfo confidentialSpaceInfo `json:"confidential_space_info,omitempty"` - AkCert []byte `json:"ak_cert,omitempty"` - IntermediateCerts [][]byte `json:"intermediate_certs,omitempty"` -} - -type tdxAttestation struct { - CcelAcpiTable []byte `json:"ccel_table,omitempty"` - CcelData []byte `json:"ccel_data,omitempty"` - TdQuote []byte `json:"quote"` - CanonicalEventLog []byte `json:"canonical_event_log,omitempty"` -} - -type tdxEvidence struct { - Attestation tdxAttestation `json:"tdx,omitempty"` - GcpData gcpEvidence `json:"gcpcs,omitempty"` -} +// type containerSignature struct { +// Payload []byte `json:"payload,omitempty"` +// Signature []byte `json:"signature,omitempty"` +// } + +// type confidentialSpaceInfo struct { +// SignedEntities []containerSignature `json:"signed_entities,omitempty"` +// } + +// type gcpEvidence struct { +// GcpCredentials [][]byte `json:"gcp_credentials,omitempty"` +// ConfidentialSpaceInfo confidentialSpaceInfo `json:"confidential_space_info,omitempty"` +// AkCert []byte `json:"ak_cert,omitempty"` +// IntermediateCerts [][]byte `json:"intermediate_certs,omitempty"` +// } + +// type tdxAttestation struct { +// CcelAcpiTable []byte `json:"ccel_table,omitempty"` +// CcelData []byte `json:"ccel_data,omitempty"` +// TdQuote []byte `json:"quote"` +// CanonicalEventLog []byte `json:"canonical_event_log,omitempty"` +// } + +// type tdxEvidence struct { +// Attestation tdxAttestation `json:"tdx,omitempty"` +// GcpData gcpEvidence `json:"gcpcs,omitempty"` +// } func processITANonce(input itaNonce) ([]byte, error) { if len(input.Val) == 0 { @@ -252,22 +252,42 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { return } - tdxEvi := &tdxEvidence{ - Attestation: tdxAttestation{ - TdQuote: evidence.TDXAttestation.TdQuote, + tdxEvi := &tokenRequest{ + PolicyMatch: true, + TDX: tdxEvidence{ + Quote: evidence.TDXAttestation.TdQuote, CcelData: evidence.TDXAttestation.CcelData, CanonicalEventLog: evidence.TDXAttestation.CanonicalEventLog, }, - GcpData: gcpEvidence{ - GcpCredentials: evidence.PrincipalTokens, - AkCert: evidence.TDXAttestation.AkCert, + SigAlg: "RS256", + GCP: gcpData{ + GcpCredentials: []string{}, + AKCert: evidence.TDXAttestation.AkCert, IntermediateCerts: evidence.TDXAttestation.IntermediateCerts, - ConfidentialSpaceInfo: confidentialSpaceInfo{ + CSInfo: confidentialSpaceInfo{ SignedEntities: make([]containerSignature, len(evidence.ContainerSignatures)), + TokenOpts: tokenOptions{ + Audience: "custom-audience", + Nonces: []string{"nonce1", "nonce2"}, + TokenType: "OIDC", + TokenTypeOpts: tokenTypeOptions{ + AllowedPrincipalTags: principalTags{ + ContainerSignatureKIDs: keyIDs{ + map[string][]string{ + "key_ids": []string{"kid1", "kid2"}, + }, + }, + }, + }, + }, }, }, } + for _, token := range evidence.PrincipalTokens { + tdxEvi.GCP.GcpCredentials = append(tdxEvi.GCP.GcpCredentials, string(token)) + } + for i, sig := range evidence.ContainerSignatures { sigPayload, err := sig.Payload() if err != nil { @@ -293,7 +313,7 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { return } - tdxEvi.GcpData.ConfidentialSpaceInfo.SignedEntities[i] = containerSignature{sigPayload, sigBytes} + tdxEvi.GCP.CSInfo.SignedEntities[i] = containerSignature{sigPayload, sigBytes} } a.logger.Printf("%+v\n", tdxEvi) From 73b8be46ae9ab454791de8528feb7ca50fecc155 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Thu, 9 Jan 2025 22:52:23 +0000 Subject: [PATCH 19/21] rename JSON fields --- launcher/teeserver/evidence.go | 6 +++--- launcher/teeserver/tee_server.go | 34 -------------------------------- 2 files changed, 3 insertions(+), 37 deletions(-) diff --git a/launcher/teeserver/evidence.go b/launcher/teeserver/evidence.go index 2bea2b5bf..446ae8295 100644 --- a/launcher/teeserver/evidence.go +++ b/launcher/teeserver/evidence.go @@ -26,9 +26,9 @@ type tokenTypeOptions struct { type tokenOptions struct { Audience string `json:"audience"` - Nonces []string `json:"nonce"` - TokenType string `json:"tokenType"` - TokenTypeOpts tokenTypeOptions `json:"tokenTypeOptions"` + Nonces []string `json:"nonces"` + TokenType string `json:"token_type"` + TokenTypeOpts tokenTypeOptions `json:"token_type_options"` } type confidentialSpaceInfo struct { diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 643fd02d9..aed1b625c 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -148,44 +148,10 @@ type itaNonce struct { Signature []byte `json:"signature"` } -// type tokenOptions struct { -// Audience string `json:"audience"` -// Nonce []string `json:"nonce"` -// TokenType string `json:"tokenType"` -// } - type evidenceRequest struct { Nonce itaNonce `json:"nonce"` } -// type containerSignature struct { -// Payload []byte `json:"payload,omitempty"` -// Signature []byte `json:"signature,omitempty"` -// } - -// type confidentialSpaceInfo struct { -// SignedEntities []containerSignature `json:"signed_entities,omitempty"` -// } - -// type gcpEvidence struct { -// GcpCredentials [][]byte `json:"gcp_credentials,omitempty"` -// ConfidentialSpaceInfo confidentialSpaceInfo `json:"confidential_space_info,omitempty"` -// AkCert []byte `json:"ak_cert,omitempty"` -// IntermediateCerts [][]byte `json:"intermediate_certs,omitempty"` -// } - -// type tdxAttestation struct { -// CcelAcpiTable []byte `json:"ccel_table,omitempty"` -// CcelData []byte `json:"ccel_data,omitempty"` -// TdQuote []byte `json:"quote"` -// CanonicalEventLog []byte `json:"canonical_event_log,omitempty"` -// } - -// type tdxEvidence struct { -// Attestation tdxAttestation `json:"tdx,omitempty"` -// GcpData gcpEvidence `json:"gcpcs,omitempty"` -// } - func processITANonce(input itaNonce) ([]byte, error) { if len(input.Val) == 0 { return nil, fmt.Errorf("no value in nonce") From 6eb9c4cf537e21a5bc4978e4fe03b1b39a17d8c5 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Fri, 10 Jan 2025 22:28:35 +0000 Subject: [PATCH 20/21] trim 0xFF from end of CCEL data --- launcher/teeserver/tee_server.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index aed1b625c..0fd528b75 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -172,6 +172,19 @@ func processITANonce(input itaNonce) ([]byte, error) { return hash.Sum(nil), nil } +func trimCCELData(data []byte) []byte { + trimIndex := len(data) + for ; trimIndex >= 0; trimIndex-- { + c := data[trimIndex-1] + // Proceed until 0xFF padding ends. + if c != byte(255) { + break + } + } + + return data[:trimIndex] +} + func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -222,7 +235,7 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { PolicyMatch: true, TDX: tdxEvidence{ Quote: evidence.TDXAttestation.TdQuote, - CcelData: evidence.TDXAttestation.CcelData, + CcelData: trimCCELData(evidence.TDXAttestation.CcelData), CanonicalEventLog: evidence.TDXAttestation.CanonicalEventLog, }, SigAlg: "RS256", @@ -240,7 +253,7 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { AllowedPrincipalTags: principalTags{ ContainerSignatureKIDs: keyIDs{ map[string][]string{ - "key_ids": []string{"kid1", "kid2"}, + "key_ids": {"kid1", "kid2"}, }, }, }, From c68c6e6ad77fba0da61f8d916717c5776185ada6 Mon Sep 17 00:00:00 2001 From: Jessie Liu Date: Mon, 13 Jan 2025 18:18:18 +0000 Subject: [PATCH 21/21] fix deviations from API def --- launcher/teeserver/evidence.go | 8 ++++---- launcher/teeserver/tee_server.go | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/launcher/teeserver/evidence.go b/launcher/teeserver/evidence.go index 446ae8295..a1adea33e 100644 --- a/launcher/teeserver/evidence.go +++ b/launcher/teeserver/evidence.go @@ -2,7 +2,7 @@ package teeserver type tdxEvidence struct { CcelAcpiTable []byte `json:"ccel_table,omitempty"` - CcelData []byte `json:"ccel_data,omitempty"` + CcelData []byte `json:"event_log,omitempty"` CanonicalEventLog []byte `json:"canonical_event_log"` Quote []byte `json:"quote"` } @@ -12,12 +12,12 @@ type containerSignature struct { Signature []byte `json:"signature"` } -type keyIDs struct { - IDs map[string][]string `json:"key_ids"` +type containerImageSignatures struct { + KeyIDs []string `json:"key_ids"` } type principalTags struct { - ContainerSignatureKIDs keyIDs `json:"container_image_signatures"` + ContainerImageSigs containerImageSignatures `json:"container_image_signatures"` } type tokenTypeOptions struct { diff --git a/launcher/teeserver/tee_server.go b/launcher/teeserver/tee_server.go index 0fd528b75..1c5b7228b 100644 --- a/launcher/teeserver/tee_server.go +++ b/launcher/teeserver/tee_server.go @@ -251,10 +251,8 @@ func (a *attestHandler) getEvidence(w http.ResponseWriter, r *http.Request) { TokenType: "OIDC", TokenTypeOpts: tokenTypeOptions{ AllowedPrincipalTags: principalTags{ - ContainerSignatureKIDs: keyIDs{ - map[string][]string{ - "key_ids": {"kid1", "kid2"}, - }, + ContainerImageSigs: containerImageSignatures{ + KeyIDs: []string{"kid1", "kid2"}, }, }, },