Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[do not publish] ITA Prototype #510

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
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
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading
Loading