Skip to content

Commit

Permalink
[launcher] Merge upstream/tdx_rtmr (#513)
Browse files Browse the repository at this point in the history
* Merge upstream/tdx_rtmr

Squashed commit of the following:

commit 896c633
Author: Jessie Liu <[email protected]>
Date:   Wed Oct 30 18:31:27 2024 +0000

    Revert "implement AttestationEvidence for TDX"

    This reverts commit d150246.

commit d150246
Author: Jessie Liu <[email protected]>
Date:   Wed Oct 30 18:26:48 2024 +0000

    implement AttestationEvidence for TDX

commit c38b622
Author: Jiankun Lü <[email protected]>
Date:   Thu Oct 10 18:37:30 2024 -0700

    [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 <[email protected]>

* [launcher] Update attest logging and cloudbuild

Add some logging on support both TDX and SEV attestation.

Update cloudbuild file to make substitution variables
cleaner.

Signed-off-by: Jiankun Lu <[email protected]>

---------

Signed-off-by: Jiankun Lu <[email protected]>
  • Loading branch information
jkl73 authored Dec 11, 2024
1 parent 5bb57bb commit 86a7e85
Show file tree
Hide file tree
Showing 23 changed files with 585 additions and 356 deletions.
20 changes: 15 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,19 @@ on:
- main
pull_request:


jobs:
build:
strategy:
matrix:
go-version: [1.21.x]
# TODO: Get this working on windows-latest
os: [ubuntu-latest]
# ci will build and run tests based on the following matrix
# mac-os linux windows
# x32 [no builds] . ./cmd ./verifier ./launcher [no builds]
# x64 . ./cmd ./verifier . ./cmd ./verifier ./launcher [no builds]
# arm64 . ./cmd ./verifier [no builds] [no builds]
architecture: [x32, x64]
include:
- os: macos-latest
Expand Down Expand Up @@ -71,17 +77,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:
Expand Down
83 changes: 4 additions & 79 deletions client/attest.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
package client

import (
"crypto/x509"
"fmt"
"io"
"net/http"

sabi "github.com/google/go-sev-guest/abi"
sg "github.com/google/go-sev-guest/client"
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 {
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down
5 changes: 2 additions & 3 deletions client/attest_network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
}
Expand Down
113 changes: 3 additions & 110 deletions client/attest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package client

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"math/big"
"net/http"
"net/http/httptest"
"strings"
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions cmd/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,15 +34,15 @@ 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
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/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
Expand Down
14 changes: 8 additions & 6 deletions cmd/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
Loading

0 comments on commit 86a7e85

Please sign in to comment.