diff --git a/cmd/attest_test.go b/cmd/attest_test.go index f3772eef..b2c45d5c 100644 --- a/cmd/attest_test.go +++ b/cmd/attest_test.go @@ -14,6 +14,7 @@ import ( tgtestclient "github.com/google/go-tdx-guest/testing/client" "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/internal/test" + "github.com/google/go-tpm-tools/internal/util" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" ) @@ -205,8 +206,8 @@ func TestFormatFlagFail(t *testing.T) { } func TestMetadataPass(t *testing.T) { - var dummyInstance = Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} - mock, err := NewMetadataServer(dummyInstance) + var dummyInstance = util.Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} + mock, err := util.NewMetadataServer(dummyInstance) if err != nil { t.Error(err) } @@ -270,8 +271,8 @@ func TestAttestWithGCEAK(t *testing.T) { } defer tpm2.NVUndefineSpace(rwc, "", tpm2.HandlePlatform, tpmutil.Handle(getIndex[op.keyAlgo])) - var dummyInstance = Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} - mock, err := NewMetadataServer(dummyInstance) + var dummyInstance = util.Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} + mock, err := util.NewMetadataServer(dummyInstance) if err != nil { t.Error(err) } diff --git a/cmd/fake_cloudlogging_server.go b/cmd/fake_cloudlogging_server.go index 25d2b1d8..51cb139b 100644 --- a/cmd/fake_cloudlogging_server.go +++ b/cmd/fake_cloudlogging_server.go @@ -14,6 +14,7 @@ import ( logpb "cloud.google.com/go/logging/apiv2/loggingpb" tspb "github.com/golang/protobuf/ptypes/timestamp" + "github.com/google/go-tpm-tools/internal/util" "google.golang.org/grpc" ) @@ -125,7 +126,7 @@ func (h *loggingHandler) WriteLogEntries(_ context.Context, req *logpb.WriteLogE var logEntryPayload []map[string]interface{} logEntryPayload = append(logEntryPayload, map[string]interface{}{"aud": "test", "iat": float64(1709752525), "exp": float64(1919752525)}) logEntryPayload = append(logEntryPayload, map[string]interface{}{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0IiwiaWF0IjoxNzA5NzUyNTI1LCJleHAiOjE5MTk3NTI1MjV9.EBLA2zX3c-Fu0l--J9Gey6LIXMO1TFRCoe3bzuPGc1k"}) - logEntryPayload = append(logEntryPayload, map[string]interface{}{"Name": "projects/test-project/locations/us-central-1/challenges/" + fakeChallengeUUID, "Nonce": fakeTpmNonce, "ConnID": ""}) + logEntryPayload = append(logEntryPayload, map[string]interface{}{"Name": "projects/test-project/locations/us-central-1/challenges/" + util.FakeChallengeUUID, "Nonce": util.FakeTpmNonce, "ConnID": ""}) attestationMapFields := []string{"TeeAttestation", "ak_pub", "quotes", "event_log", "ak_cert"} for _, entry := range h.logs["projects/"+TestProjectID+"/logs/"+toolName] { payload := entry.GetJsonPayload().AsMap() diff --git a/cmd/fake_oauth2_server.go b/cmd/fake_oauth2_server.go deleted file mode 100644 index d409b613..00000000 --- a/cmd/fake_oauth2_server.go +++ /dev/null @@ -1,32 +0,0 @@ -package cmd - -import ( - "net/http" - "net/http/httptest" -) - -type oauth2Server struct { - server *httptest.Server -} - -func newMockOauth2Server() *oauth2Server { - mux := http.NewServeMux() - mux.HandleFunc("/o/oauth2/auth", func(_ http.ResponseWriter, _ *http.Request) { - // Unimplemented: Should return authorization code back to the user - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, _ *http.Request) { - // Should return acccess token back to the user - w.Header().Set("Content-Type", "application/x-www-form-urlencoded") - w.Write([]byte("access_token=mocktoken&scope=user&token_type=bearer")) - }) - - server := httptest.NewServer(mux) - - return &oauth2Server{server: server} -} - -// Stop shuts down the server. -func (s *oauth2Server) Stop() { - s.server.Close() -} diff --git a/cmd/testdata/credentials b/cmd/testdata/credentials deleted file mode 100644 index 229c3227..00000000 --- a/cmd/testdata/credentials +++ /dev/null @@ -1,6 +0,0 @@ -{ - "client_id": "id", - "client_secret": "testdata", - "refresh_token": "testdata", - "type": "authorized_user" -} \ No newline at end of file diff --git a/cmd/token.go b/cmd/token.go index 95e88bfb..a136743e 100644 --- a/cmd/token.go +++ b/cmd/token.go @@ -6,8 +6,6 @@ import ( "errors" "fmt" "log" - "net/url" - "strings" "time" "cloud.google.com/go/compute/metadata" @@ -15,19 +13,15 @@ import ( "github.com/containerd/containerd/namespaces" "github.com/golang-jwt/jwt/v4" "github.com/google/go-tpm-tools/client" - "github.com/google/go-tpm-tools/launcher/agent" - "github.com/google/go-tpm-tools/launcher/spec" - "github.com/google/go-tpm-tools/launcher/verifier" - "github.com/google/go-tpm-tools/launcher/verifier/rest" + "github.com/google/go-tpm-tools/internal/util" + "github.com/google/go-tpm-tools/verifier" "github.com/google/go-tpm/legacy/tpm2" "github.com/spf13/cobra" - "golang.org/x/oauth2/google" "google.golang.org/api/option" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) -var mdsClient *metadata.Client var mockCloudLoggingServerAddress string const toolName = "gotpm" @@ -37,7 +31,7 @@ var tokenCmd = &cobra.Command{ Use: "token", Short: "Attest and fetch an OIDC token from Google Attestation Verification Service.", Long: `Gather attestation report and send it to Google Attestation Verification Service for an OIDC token. -The OIDC token includes claims regarding the GCE VM, which is verified by Attestation Verification Service. Note that Confidential Computing API needs to be enabled for your account to access Google Attestation Verification Service https://pantheon.corp.google.com/apis/api/confidentialcomputing.googleapis.com. +The OIDC token includes claims regarding the GCE VM, which is verified by Attestation Verification Service. Note that Confidential Computing API needs to be enabled for your account to access Google Attestation Verification Service https://console.cloud.google.com/apis/api/confidentialcomputing.googleapis.com. --algo flag overrides the public key algorithm for the GCE TPM attestation key. If not provided then by default rsa is used. `, Args: cobra.NoArgs, @@ -49,32 +43,13 @@ The OIDC token includes claims regarding the GCE VM, which is verified by Attest defer rwc.Close() // Metadata Server (MDS). A GCP specific client. - mdsClient = metadata.NewClient(nil) + mdsClient := metadata.NewClient(nil) ctx := namespaces.WithNamespace(context.Background(), namespaces.Default) - // TODO: principalFetcher is copied from go-tpm-tools/launcher/container_runner.go, to be refactored - // Fetch GCP specific ID token with specific audience. - // See https://cloud.google.com/functions/docs/securing/authenticating#functions-bearer-token-example-go. - principalFetcher := func(audience string) ([][]byte, error) { - u := url.URL{ - Path: "instance/service-accounts/default/identity", - RawQuery: url.Values{ - "audience": {audience}, - "format": {"full"}, - }.Encode(), - } - idToken, err := mdsClient.Get(u.String()) - if err != nil { - return nil, fmt.Errorf("failed to get principal tokens: %w", err) - } - fmt.Fprintf(debugOutput(), "GCP ID token fetched is: %s\n", idToken) - tokens := [][]byte{[]byte(idToken)} - return tokens, nil - } fmt.Fprintf(debugOutput(), "Attestation Address is set to %s\n", asAddress) - region, err := getRegion(mdsClient) + region, err := util.GetRegion(mdsClient) if err != nil { return fmt.Errorf("failed to fetch Region from MDS, the tool is probably not running in a GCE VM: %v", err) } @@ -84,7 +59,7 @@ The OIDC token includes claims regarding the GCE VM, which is verified by Attest return fmt.Errorf("failed to retrieve ProjectID from MDS: %v", err) } - verifierClient, err := getRESTClient(ctx, asAddress, projectID, region) + verifierClient, err := util.NewRESTClient(ctx, asAddress, projectID, region) if err != nil { return fmt.Errorf("failed to create REST verifier client: %v", err) } @@ -104,7 +79,7 @@ The OIDC token includes claims regarding the GCE VM, which is verified by Attest return err } if gceAK.Cert() == nil { - return errors.New("failed to find gceAKCert on this VM: try creating a new VM or verifying the VM has an EK cert using get-shielded-identity gcloud command. The used key algorithm is: " + usedKeyAlgo) + return errors.New("failed to find GCE AK Certificate on this VM: try creating a new VM or verifying the VM has an EK cert using get-shielded-identity gcloud command. The used key algorithm is: " + usedKeyAlgo) } gceAK.Close() @@ -135,14 +110,41 @@ The OIDC token includes claims regarding the GCE VM, which is verified by Attest } key = "gceAK" - attestAgent := agent.CreateAttestationAgent(rwc, attestationKeys[key][keyAlgo], verifierClient, principalFetcher, nil, spec.LaunchSpec{}, nil, cloudLogger) fmt.Fprintf(debugOutput(), "Fetching attestation verifier OIDC token\n") - token, err := attestAgent.Attest(ctx, agent.AttestAgentOpts{Aud: audience, TokenType: "OIDC"}) + + challenge, err := verifierClient.CreateChallenge(ctx) if err != nil { - return fmt.Errorf("failed to retrieve attestation service token: %v", err) + return err + } + + principalTokens, err := util.PrincipalFetcher(challenge.Name, mdsClient) + if err != nil { + return fmt.Errorf("failed to get principal tokens: %w", err) + } + + attestation, err := util.FetchAttestation(rwc, attestationKeys[key][keyAlgo], challenge.Nonce) + if err != nil { + return err } + req := verifier.VerifyAttestationRequest{ + Challenge: challenge, + GcpCredentials: principalTokens, + Attestation: attestation, + TokenOptions: verifier.TokenOptions{CustomAudience: audience, TokenType: "OIDC"}, + } + + resp, err := verifierClient.VerifyAttestation(ctx, req) + if err != nil { + return err + } + if len(resp.PartialErrs) > 0 { + fmt.Fprintf(debugOutput(), "partial errors from VerifyAttestation: %v", resp.PartialErrs) + } + + token := resp.ClaimsToken + // Get token expiration. claims := &jwt.RegisteredClaims{} _, _, err = jwt.NewParser().ParseUnverified(string(token), claims) @@ -176,6 +178,8 @@ The OIDC token includes claims regarding the GCE VM, which is verified by Attest } if cloudLog { + cloudLogger.Log(logging.Entry{Payload: challenge}) + cloudLogger.Log(logging.Entry{Payload: attestation}) cloudLogger.Log(logging.Entry{Payload: map[string]string{"token": string(token)}}) cloudLogger.Log(logging.Entry{Payload: mapClaims}) cloudLogClient.Close() @@ -190,40 +194,6 @@ The OIDC token includes claims regarding the GCE VM, which is verified by Attest }, } -// TODO: getRESTClient is copied from go-tpm-tools/launcher/container_runner.go, to be refactored. -// getRESTClient returns a REST verifier.Client that points to the given address. -// It defaults to the Attestation Verifier instance at -// https://confidentialcomputing.googleapis.com. -func getRESTClient(ctx context.Context, asAddr string, ProjectID string, Region string) (verifier.Client, error) { - httpClient, err := google.DefaultClient(ctx) - if err != nil { - return nil, fmt.Errorf("failed to create HTTP client: %v", err) - } - - opts := []option.ClientOption{option.WithHTTPClient(httpClient)} - if asAddr != "" { - opts = append(opts, option.WithEndpoint(asAddr)) - } - - restClient, err := rest.NewClient(ctx, ProjectID, Region, opts...) - if err != nil { - return nil, err - } - return restClient, nil -} - -func getRegion(client *metadata.Client) (string, error) { - zone, err := client.Zone() - if err != nil { - return "", fmt.Errorf("failed to retrieve zone from MDS: %v", err) - } - lastDash := strings.LastIndex(zone, "-") - if lastDash == -1 { - return "", fmt.Errorf("got malformed zone from MDS: %v", zone) - } - return zone[:lastDash], nil -} - func init() { RootCmd.AddCommand(tokenCmd) addOutputFlag(tokenCmd) diff --git a/cmd/token_test.go b/cmd/token_test.go index 31e171f9..fb26db0e 100644 --- a/cmd/token_test.go +++ b/cmd/token_test.go @@ -13,6 +13,7 @@ import ( "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/internal/test" + "github.com/google/go-tpm-tools/internal/util" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" "golang.org/x/oauth2" @@ -49,24 +50,27 @@ func TestTokenWithGCEAK(t *testing.T) { defer tpm2.NVUndefineSpace(rwc, "", tpm2.HandlePlatform, tpmutil.Handle(getIndex[op.algo])) defer tpm2.NVUndefineSpace(rwc, "", tpm2.HandlePlatform, tpmutil.Handle(getCertIndex[op.algo])) - var dummyMetaInstance = Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} - mockMdsServer, err := NewMetadataServer(dummyMetaInstance) + var dummyMetaInstance = util.Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} + mockMdsServer, err := util.NewMetadataServer(dummyMetaInstance) if err != nil { t.Error(err) } defer mockMdsServer.Stop() - mockOauth2Server := newMockOauth2Server() + mockOauth2Server, err := util.NewMockOauth2Server() + if err != nil { + t.Error(err) + } defer mockOauth2Server.Stop() // Endpoint is Google's OAuth 2.0 default endpoint. Change to mock server. google.Endpoint = oauth2.Endpoint{ - AuthURL: mockOauth2Server.server.URL + "/o/oauth2/auth", - TokenURL: mockOauth2Server.server.URL + "/token", + AuthURL: mockOauth2Server.Server.URL + "/o/oauth2/auth", + TokenURL: mockOauth2Server.Server.URL + "/token", AuthStyle: oauth2.AuthStyleInParams, } - mockAttestationServer, err := newMockAttestationServer() + mockAttestationServer, err := util.NewMockAttestationServer() if err != nil { t.Error(err) } @@ -77,7 +81,7 @@ func TestTokenWithGCEAK(t *testing.T) { t.Error(err) } - RootCmd.SetArgs([]string{"token", "--algo", op.algo, "--output", secretFile1, "--verifier-endpoint", mockAttestationServer.server.URL, "--cloud-log", "--audience", "https://api.test.com"}) + RootCmd.SetArgs([]string{"token", "--algo", op.algo, "--output", secretFile1, "--verifier-endpoint", mockAttestationServer.Server.URL, "--cloud-log", "--audience", "https://api.test.com"}) if err := RootCmd.Execute(); err != nil { t.Error(err) } diff --git a/cmd/verify_test.go b/cmd/verify_test.go index ec41a40c..a43e4be1 100644 --- a/cmd/verify_test.go +++ b/cmd/verify_test.go @@ -12,6 +12,7 @@ import ( tgtestdata "github.com/google/go-tdx-guest/testing/testdata" "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/internal/test" + "github.com/google/go-tpm-tools/internal/util" pb "github.com/google/go-tpm-tools/proto/attest" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" @@ -93,8 +94,8 @@ func TestVerifyWithGCEAK(t *testing.T) { } defer tpm2.NVUndefineSpace(rwc, "", tpm2.HandlePlatform, tpmutil.Handle(getIndex[op.keyAlgo])) - var dummyInstance = Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} - mock, err := NewMetadataServer(dummyInstance) + var dummyInstance = util.Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} + mock, err := util.NewMetadataServer(dummyInstance) if err != nil { t.Error(err) } diff --git a/cmd/fake_attestation_server.go b/internal/util/fake_attestation_server.go similarity index 58% rename from cmd/fake_attestation_server.go rename to internal/util/fake_attestation_server.go index 6b550142..33e8e9dc 100644 --- a/cmd/fake_attestation_server.go +++ b/internal/util/fake_attestation_server.go @@ -1,23 +1,23 @@ -package cmd +package util import ( "fmt" "net/http" "net/http/httptest" - "os" "github.com/golang-jwt/jwt/v4" "golang.org/x/net/http2" ) -const fakeAsHostEnv = "GOOGLE_APPLICATION_CREDENTIALS" -const fakeChallengeUUID = "947b4f7b-e6d4-4cfe-971c-39ffe00268ba" -const fakeTpmNonce = "R29vZ0F0dGVzdFYxeGtJUGlRejFPOFRfTzg4QTRjdjRpQQ==" +// FakeChallengeUUID is the challenge for fake attestation server +const FakeChallengeUUID = "947b4f7b-e6d4-4cfe-971c-39ffe00268ba" -// attestationServer provides fake implementation for the GCE attestation server. -type attestationServer struct { - server *httptest.Server - oldFakeAsHostEnv string +// FakeTpmNonce is the tpm nonce for fake attestation server +const FakeTpmNonce = "R29vZ0F0dGVzdFYxeGtJUGlRejFPOFRfTzg4QTRjdjRpQQ==" + +// MockAttestationServer provides fake implementation for the GCE attestation server. +type MockAttestationServer struct { + Server *httptest.Server } type fakeOidcTokenPayload struct { @@ -30,7 +30,8 @@ func (payload *fakeOidcTokenPayload) Valid() error { return nil } -func newMockAttestationServer() (*attestationServer, error) { +// NewMockAttestationServer creates a mock verifier +func NewMockAttestationServer() (*MockAttestationServer, error) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { locationPath := "/v1/projects/test-project/locations/us-central" if r.URL.Path == locationPath { @@ -39,11 +40,10 @@ func newMockAttestationServer() (*attestationServer, error) { } challengePath := locationPath + "-1/challenges" if r.URL.Path == challengePath { - challenge := "{\n \"name\": \"projects/test-project/locations/us-central-1/challenges/947b4f7b-e6d4-4cfe-971c-39ffe00268ba\",\n \"createTime\": \"2023-09-21T01:04:48.230111757Z\",\n \"expireTime\": \"2023-09-21T02:04:48.230111757Z\",\n \"tpmNonce\": \"" + fakeTpmNonce + "\"\n}\n" + challenge := "{\n \"name\": \"projects/test-project/locations/us-central-1/challenges/" + FakeChallengeUUID + "\",\n \"createTime\": \"2023-09-21T01:04:48.230111757Z\",\n \"expireTime\": \"2023-09-21T02:04:48.230111757Z\",\n \"tpmNonce\": \"" + FakeTpmNonce + "\"\n}\n" w.Write([]byte(challenge)) } - challengeNonce := "/947b4f7b-e6d4-4cfe-971c-39ffe00268ba" - verifyAttestationPath := challengePath + challengeNonce + ":verifyAttestation" + verifyAttestationPath := challengePath + "/" + FakeChallengeUUID + ":verifyAttestation" if r.URL.Path == verifyAttestationPath { payload := &fakeOidcTokenPayload{ Audience: "test", @@ -64,18 +64,10 @@ func newMockAttestationServer() (*attestationServer, error) { } httpServer.Start() - old := os.Getenv(fakeAsHostEnv) - cwd, err := os.Getwd() - if err != nil { - return nil, err - } - os.Setenv(fakeAsHostEnv, cwd+"/testdata/credentials") - - return &attestationServer{oldFakeAsHostEnv: old, server: httpServer}, nil + return &MockAttestationServer{Server: httpServer}, nil } // Stop shuts down the server. -func (s *attestationServer) Stop() { - os.Setenv(fakeAsHostEnv, s.oldFakeAsHostEnv) - s.server.Close() +func (s *MockAttestationServer) Stop() { + s.Server.Close() } diff --git a/cmd/fake_metadata.go b/internal/util/fake_metadata.go similarity index 99% rename from cmd/fake_metadata.go rename to internal/util/fake_metadata.go index f1b378a6..aa5e1e9b 100644 --- a/cmd/fake_metadata.go +++ b/internal/util/fake_metadata.go @@ -1,4 +1,4 @@ -package cmd +package util import ( "fmt" diff --git a/internal/util/fake_oauth2_server.go b/internal/util/fake_oauth2_server.go new file mode 100644 index 00000000..cd8a53e0 --- /dev/null +++ b/internal/util/fake_oauth2_server.go @@ -0,0 +1,71 @@ +package util + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "os" +) + +// Application Default Credentials (ADC) is a strategy used by the Google authentication libraries to automatically find credentials based on the application environment. +// ADC searches for credentials in GOOGLE_APPLICATION_CREDENTIALS environment variable first (https://cloud.google.com/docs/authentication/application-default-credentials) +// We use fakeAsHostEnv to let ADC find fake credential. +const oauth2CredentialHostEnv = "GOOGLE_APPLICATION_CREDENTIALS" + +// MockOauth2Server is a struct for mocking Oauth2Server +type MockOauth2Server struct { + Server *httptest.Server + OriginalCred string +} + +// NewMockOauth2Server creates a mock Oauth2 server for testing purpose +func NewMockOauth2Server() (*MockOauth2Server, error) { + mux := http.NewServeMux() + mux.HandleFunc("/o/oauth2/auth", func(_ http.ResponseWriter, _ *http.Request) { + // Unimplemented: Should return authorization code back to the user + }) + + mux.HandleFunc("/token", func(w http.ResponseWriter, _ *http.Request) { + // Should return acccess token back to the user + w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.Write([]byte("access_token=mocktoken&scope=user&token_type=bearer")) + }) + + server := httptest.NewServer(mux) + + // create test oauth2 credentials + testCredentials := map[string]string{ + "client_id": "id", + "client_secret": "testdata", + "refresh_token": "testdata", + "type": "authorized_user", + } + + fakeOauthCredentialData, err := json.MarshalIndent(testCredentials, "", " ") // Indent for readability + if err != nil { + return nil, err + } + + file, err := os.CreateTemp("", "fake_oauth2_test_credentials") + if err != nil { + return nil, err + } + defer file.Close() + + _, err = file.Write(fakeOauthCredentialData) + if err != nil { + return nil, err + } + + old := os.Getenv(oauth2CredentialHostEnv) + os.Setenv(oauth2CredentialHostEnv, file.Name()) + + return &MockOauth2Server{Server: server, OriginalCred: old}, nil +} + +// Stop cleans up the fake credential, reset the original one, and shuts down the server. +func (s *MockOauth2Server) Stop() { + os.Remove(os.Getenv(oauth2CredentialHostEnv)) + os.Setenv(oauth2CredentialHostEnv, s.OriginalCred) + s.Server.Close() +} diff --git a/internal/util/util.go b/internal/util/util.go new file mode 100644 index 00000000..edbba0fb --- /dev/null +++ b/internal/util/util.go @@ -0,0 +1,98 @@ +// Package util provides helper funtions to prepare materials for talking to attestation verifiers. +package util + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "cloud.google.com/go/compute/metadata" + "github.com/google/go-tpm-tools/cel" + "github.com/google/go-tpm-tools/client" + attestpb "github.com/google/go-tpm-tools/proto/attest" + "github.com/google/go-tpm-tools/verifier" + "github.com/google/go-tpm-tools/verifier/rest" + "golang.org/x/oauth2/google" + "google.golang.org/api/option" +) + +// TpmKeyFetcher abstracts the fetching of various types of Attestation Key from TPM +type TpmKeyFetcher func(rw io.ReadWriter) (*client.Key, error) + +// PrincipalFetcher fetch ID token with specific audience from Metadata server. +// See https://cloud.google.com/functions/docs/securing/authenticating#functions-bearer-token-example-go. +func PrincipalFetcher(audience string, mdsClient *metadata.Client) ([][]byte, error) { + u := url.URL{ + Path: "instance/service-accounts/default/identity", + RawQuery: url.Values{ + "audience": {audience}, + "format": {"full"}, + }.Encode(), + } + idToken, err := mdsClient.Get(u.String()) + if err != nil { + return nil, fmt.Errorf("failed to get principal tokens: %w", err) + } + + tokens := [][]byte{[]byte(idToken)} + return tokens, nil +} + +// FetchAttestation gathers the materials required for remote attestation from TPM +func FetchAttestation(tpm io.ReadWriteCloser, akFetcher TpmKeyFetcher, nonce []byte) (*attestpb.Attestation, error) { + ak, err := akFetcher(tpm) + if err != nil { + return nil, fmt.Errorf("failed to get AK: %v", err) + } + defer ak.Close() + + var buf bytes.Buffer + coscel := &cel.CEL{} + if err := coscel.EncodeCEL(&buf); err != nil { + return nil, err + } + + attestation, err := ak.Attest(client.AttestOpts{Nonce: nonce, CanonicalEventLog: buf.Bytes(), CertChainFetcher: http.DefaultClient}) + if err != nil { + return nil, fmt.Errorf("failed to attest: %v", err) + } + return attestation, nil +} + +// NewRESTClient returns a REST verifier.Client that points to the given address. +// It defaults to the Attestation Verifier instance at +// https://confidentialcomputing.googleapis.com. +func NewRESTClient(ctx context.Context, asAddr string, ProjectID string, Region string) (verifier.Client, error) { + httpClient, err := google.DefaultClient(ctx) + if err != nil { + return nil, fmt.Errorf("failed to create HTTP client: %v", err) + } + + opts := []option.ClientOption{option.WithHTTPClient(httpClient)} + if asAddr != "" { + opts = append(opts, option.WithEndpoint(asAddr)) + } + + restClient, err := rest.NewClient(ctx, ProjectID, Region, opts...) + if err != nil { + return nil, err + } + return restClient, nil +} + +// GetRegion retrieves region information from GCE metadata server +func GetRegion(client *metadata.Client) (string, error) { + zone, err := client.Zone() + if err != nil { + return "", fmt.Errorf("failed to retrieve zone from MDS: %v", err) + } + lastDash := strings.LastIndex(zone, "-") + if lastDash == -1 { + return "", fmt.Errorf("got malformed zone from MDS: %v", zone) + } + return zone[:lastDash], nil +} diff --git a/internal/util/util_test.go b/internal/util/util_test.go new file mode 100644 index 00000000..5e16c1f1 --- /dev/null +++ b/internal/util/util_test.go @@ -0,0 +1,131 @@ +package util + +import ( + "bytes" + "context" + "encoding/base64" + "reflect" + "testing" + + "cloud.google.com/go/compute/metadata" + "github.com/containerd/containerd/namespaces" + "github.com/google/go-cmp/cmp" + "github.com/google/go-tpm-tools/client" + "github.com/google/go-tpm-tools/internal/test" + "github.com/google/go-tpm-tools/proto/attest" + "github.com/google/go-tpm-tools/verifier" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" +) + +func TestPrincipleFetcher(t *testing.T) { + var dummyMetaInstance = Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} + mockMdsServer, err := NewMetadataServer(dummyMetaInstance) + if err != nil { + t.Error(err) + } + defer mockMdsServer.Stop() + mdsClient := metadata.NewClient(nil) + gotTokens, err := PrincipalFetcher("test_audience", mdsClient) + if err != nil { + t.Error(err) + } + wantTokens := [][]byte{[]byte("test_jwt_token")} + if !cmp.Equal(wantTokens, gotTokens) { + t.Error("ID Token Mismatch") + } +} + +func TestFetchAttestation(t *testing.T) { + rwc := test.GetTPM(t) + defer client.CheckedClose(t, rwc) + tests := []struct { + name string + keyFetcher TpmKeyFetcher + }{ + {"RSA", client.AttestationKeyRSA}, + {"ECC", client.AttestationKeyECC}, + } + for _, op := range tests { + t.Run(op.name, func(t *testing.T) { + attestation, err := FetchAttestation(rwc, op.keyFetcher, []byte("test")) + if err != nil { + t.Errorf("Failed to get attestation %s", err) + } + if !bytes.Equal(attestation.EventLog, test.Rhel8EventLog) { + t.Errorf("attestation event log mismatch %s", err) + } + }) + } +} + +func TestNewRESTClient(t *testing.T) { + ctx := namespaces.WithNamespace(context.Background(), namespaces.Default) + + mockOauth2Server, err := NewMockOauth2Server() + if err != nil { + t.Error(err) + } + defer mockOauth2Server.Stop() + + // Endpoint is Google's OAuth 2.0 default endpoint. Change to mock server. + google.Endpoint = oauth2.Endpoint{ + AuthURL: mockOauth2Server.Server.URL + "/o/oauth2/auth", + TokenURL: mockOauth2Server.Server.URL + "/token", + AuthStyle: oauth2.AuthStyleInParams, + } + + mockAttestationServer, err := NewMockAttestationServer() + if err != nil { + t.Error(err) + } + defer mockAttestationServer.Stop() + + restClient, err := NewRESTClient(ctx, mockAttestationServer.Server.URL, "test-project", "us-central") + if err != nil { + t.Errorf("Failed to create rest client %s", err) + } + gotChallenge, err := restClient.CreateChallenge(ctx) + if err != nil { + t.Errorf("Failed to call CreateChallenge %s", err) + } + gotTokenResponse, err := restClient.VerifyAttestation(ctx, verifier.VerifyAttestationRequest{ + Challenge: gotChallenge, + Attestation: &attest.Attestation{}, + }) + if err != nil { + t.Errorf("Failed to call VerifyAttestation %s", err) + } + + wantNonce, _ := base64.StdEncoding.DecodeString(FakeTpmNonce) + wantChallenge := &verifier.Challenge{ + Name: "projects/test-project/locations/us-central-1/challenges/" + FakeChallengeUUID, + Nonce: []byte(wantNonce), + ConnID: ""} + wantToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0IiwiaWF0IjoxNzA5NzUyNTI1LCJleHAiOjE5MTk3NTI1MjV9.EBLA2zX3c-Fu0l--J9Gey6LIXMO1TFRCoe3bzuPGc1k" + if !reflect.DeepEqual(gotChallenge, wantChallenge) { + t.Error("Challenge Mismatch") + } + if !bytes.Equal(gotTokenResponse.ClaimsToken, []byte(wantToken)) { + t.Error("Token Mismatch") + } + +} + +func TestGetRegion(t *testing.T) { + var dummyMetaInstance = Instance{ProjectID: "test-project", ProjectNumber: "1922337278274", Zone: "us-central-1a", InstanceID: "12345678", InstanceName: "default"} + mockMdsServer, err := NewMetadataServer(dummyMetaInstance) + if err != nil { + t.Error(err) + } + defer mockMdsServer.Stop() + // Metadata Server (MDS). A GCP specific client. + mdsClient := metadata.NewClient(nil) + region, err := GetRegion(mdsClient) + if err != nil { + t.Errorf("Failed to GetRegion %s", err) + } + if region != "us-central" { + t.Error("Region Mismatch") + } +} diff --git a/launcher/agent/agent.go b/launcher/agent/agent.go index 44651a0d..0dbe26db 100644 --- a/launcher/agent/agent.go +++ b/launcher/agent/agent.go @@ -6,28 +6,23 @@ package agent import ( - "bytes" "context" "crypto" "fmt" "io" "log" - "net/http" "sync" - "cloud.google.com/go/logging" "github.com/google/go-tpm-tools/cel" - "github.com/google/go-tpm-tools/client" - "github.com/google/go-tpm-tools/launcher/internal/oci" + "github.com/google/go-tpm-tools/internal/util" "github.com/google/go-tpm-tools/launcher/internal/signaturediscovery" "github.com/google/go-tpm-tools/launcher/spec" - "github.com/google/go-tpm-tools/launcher/verifier" - pb "github.com/google/go-tpm-tools/proto/attest" + "github.com/google/go-tpm-tools/verifier" + "github.com/google/go-tpm-tools/verifier/oci" ) var defaultCELHashAlgo = []crypto.Hash{crypto.SHA256, crypto.SHA1} -type tpmKeyFetcher func(rw io.ReadWriter) (*client.Key, error) type principalIDTokenFetcher func(audience string) ([][]byte, error) // AttestationAgent is an agent that interacts with GCE's Attestation Service @@ -49,14 +44,13 @@ type AttestAgentOpts struct { type agent struct { tpm io.ReadWriteCloser - akFetcher tpmKeyFetcher + akFetcher util.TpmKeyFetcher client verifier.Client principalFetcher principalIDTokenFetcher sigsFetcher signaturediscovery.Fetcher cosCel cel.CEL launchSpec spec.LaunchSpec logger *log.Logger - cloudLogger *logging.Logger sigsCache *sigsCache } @@ -67,7 +61,7 @@ type agent struct { // - principalFetcher is a func to fetch GCE principal tokens for a given audience. // - signaturesFetcher is a func to fetch container image signatures associated with the running workload. // - logger will log any partial errors returned by VerifyAttestation. -func CreateAttestationAgent(tpm io.ReadWriteCloser, akFetcher tpmKeyFetcher, verifierClient verifier.Client, principalFetcher principalIDTokenFetcher, sigsFetcher signaturediscovery.Fetcher, launchSpec spec.LaunchSpec, logger *log.Logger, cloudLogger *logging.Logger) AttestationAgent { +func CreateAttestationAgent(tpm io.ReadWriteCloser, akFetcher util.TpmKeyFetcher, verifierClient verifier.Client, principalFetcher principalIDTokenFetcher, sigsFetcher signaturediscovery.Fetcher, launchSpec spec.LaunchSpec, logger *log.Logger) AttestationAgent { return &agent{ tpm: tpm, client: verifierClient, @@ -76,7 +70,6 @@ func CreateAttestationAgent(tpm io.ReadWriteCloser, akFetcher tpmKeyFetcher, ver sigsFetcher: sigsFetcher, launchSpec: launchSpec, logger: logger, - cloudLogger: cloudLogger, sigsCache: &sigsCache{}, } } @@ -101,7 +94,7 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error return nil, fmt.Errorf("failed to get principal tokens: %w", err) } - attestation, err := a.getAttestation(challenge.Nonce) + attestation, err := util.FetchAttestation(a.tpm, a.akFetcher, challenge.Nonce) if err != nil { return nil, err } @@ -117,11 +110,6 @@ func (a *agent) Attest(ctx context.Context, opts AttestAgentOpts) ([]byte, error }, } - if a.cloudLogger != nil { - a.cloudLogger.Log(logging.Entry{Payload: challenge}) - a.cloudLogger.Log(logging.Entry{Payload: attestation}) - } - var signatures []oci.Signature if a.launchSpec.Experiments.EnableSignedContainerCache { signatures = a.sigsCache.get() @@ -154,25 +142,6 @@ func (a *agent) Refresh(ctx context.Context) error { return nil } -func (a *agent) getAttestation(nonce []byte) (*pb.Attestation, error) { - ak, err := a.akFetcher(a.tpm) - if err != nil { - return nil, fmt.Errorf("failed to get AK: %v", err) - } - defer ak.Close() - - var buf bytes.Buffer - if err := a.cosCel.EncodeCEL(&buf); err != nil { - return nil, err - } - - attestation, err := ak.Attest(client.AttestOpts{Nonce: nonce, CanonicalEventLog: buf.Bytes(), CertChainFetcher: http.DefaultClient}) - if err != nil { - return nil, fmt.Errorf("failed to attest: %v", err) - } - return attestation, nil -} - func fetchContainerImageSignatures(ctx context.Context, fetcher signaturediscovery.Fetcher, targetRepos []string, logger *log.Logger) []oci.Signature { signatures := make([][]oci.Signature, len(targetRepos)) diff --git a/launcher/agent/agent_test.go b/launcher/agent/agent_test.go index 3280034c..fc2d0cb3 100644 --- a/launcher/agent/agent_test.go +++ b/launcher/agent/agent_test.go @@ -16,12 +16,20 @@ import ( "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/internal/test" "github.com/google/go-tpm-tools/launcher/internal/experiments" - "github.com/google/go-tpm-tools/launcher/internal/oci" - "github.com/google/go-tpm-tools/launcher/internal/oci/cosign" "github.com/google/go-tpm-tools/launcher/internal/signaturediscovery" "github.com/google/go-tpm-tools/launcher/spec" - "github.com/google/go-tpm-tools/launcher/verifier" - "github.com/google/go-tpm-tools/launcher/verifier/fake" + "github.com/google/go-tpm-tools/verifier" + "github.com/google/go-tpm-tools/verifier/fake" + "github.com/google/go-tpm-tools/verifier/oci" + "github.com/google/go-tpm-tools/verifier/oci/cosign" + "github.com/google/go-tpm-tools/verifier/rest" + "golang.org/x/oauth2/google" + "google.golang.org/api/option" +) + +var ( + fakeProject = "confidentialcomputing-e2e" + fakeRegion = "us-central1" ) func TestAttest(t *testing.T) { @@ -64,8 +72,7 @@ func TestAttest(t *testing.T) { verifierClient := fake.NewClient(fakeSigner) - agent := CreateAttestationAgent(tpm, client.AttestationKeyECC, verifierClient, tc.principalIDTokenFetcher, tc.containerSignaturesFetcher, tc.launchSpec, log.Default(), nil) - + agent := CreateAttestationAgent(tpm, client.AttestationKeyECC, verifierClient, tc.principalIDTokenFetcher, tc.containerSignaturesFetcher, tc.launchSpec, log.Default()) if err := agent.Refresh(ctx); err != nil { t.Errorf("failed to fresh attestation agent: %v", err) } @@ -308,3 +315,40 @@ func convertOCISignatureToBase64(t *testing.T, sigs []oci.Signature) []string { return base64Sigs } + +// Skip the test if we are not running in an environment with Google API +func testClient(t *testing.T) verifier.Client { + // TODO: Connect to the autopush endpoint by default. + hClient, err := google.DefaultClient(context.Background()) + if err != nil { + t.Skipf("Getting HTTP Client: %v", err) + } + + vClient, err := rest.NewClient(context.Background(), + fakeProject, + fakeRegion, + option.WithHTTPClient(hClient), + ) + if err != nil { + t.Fatalf("Creating Verifier Client: %v", err) + } + return vClient +} + +func testPrincipalIDTokenFetcher(_ string) ([][]byte, error) { + return [][]byte{}, nil +} + +func TestWithAgent(t *testing.T) { + vClient := testClient(t) + + tpm := test.GetTPM(t) + defer client.CheckedClose(t, tpm) + + a := CreateAttestationAgent(tpm, client.AttestationKeyECC, vClient, testPrincipalIDTokenFetcher, signaturediscovery.NewFakeClient(), spec.LaunchSpec{}, log.Default()) + token, err := a.Attest(context.Background(), AttestAgentOpts{}) + if err != nil { + t.Errorf("failed to attest to Attestation Service: %v", err) + } + t.Logf("Got Token: |%v|", string(token)) +} diff --git a/launcher/container_runner.go b/launcher/container_runner.go index 56988a0d..c8ad36ef 100644 --- a/launcher/container_runner.go +++ b/launcher/container_runner.go @@ -9,7 +9,6 @@ import ( "io" "log" "math/rand" - "net/url" "os" "os/exec" "path" @@ -28,19 +27,16 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/google/go-tpm-tools/cel" "github.com/google/go-tpm-tools/client" + "github.com/google/go-tpm-tools/internal/util" "github.com/google/go-tpm-tools/launcher/agent" "github.com/google/go-tpm-tools/launcher/internal/signaturediscovery" "github.com/google/go-tpm-tools/launcher/internal/systemctl" "github.com/google/go-tpm-tools/launcher/launcherfile" "github.com/google/go-tpm-tools/launcher/spec" "github.com/google/go-tpm-tools/launcher/teeserver" - "github.com/google/go-tpm-tools/launcher/verifier" - "github.com/google/go-tpm-tools/launcher/verifier/rest" v1 "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/oauth2" - "golang.org/x/oauth2/google" - "google.golang.org/api/option" ) // ContainerRunner contains information about the container settings @@ -181,23 +177,12 @@ func NewRunner(ctx context.Context, cdClient *containerd.Client, token oauth2.To len(containerSpec.Process.Args), len(launchSpec.Cmd)) } - // Fetch ID token with specific audience. - // See https://cloud.google.com/functions/docs/securing/authenticating#functions-bearer-token-example-go. - principalFetcher := func(audience string) ([][]byte, error) { - u := url.URL{ - Path: "instance/service-accounts/default/identity", - RawQuery: url.Values{ - "audience": {audience}, - "format": {"full"}, - }.Encode(), - } - idToken, err := mdsClient.Get(u.String()) + principalFetcherWithImpersonate := func(audience string) ([][]byte, error) { + tokens, err := util.PrincipalFetcher(audience, mdsClient) if err != nil { - return nil, fmt.Errorf("failed to get principal tokens: %w", err) + return nil, err } - tokens := [][]byte{[]byte(idToken)} - // Fetch impersonated ID tokens. for _, sa := range launchSpec.ImpersonateServiceAccounts { idToken, err := FetchImpersonatedToken(ctx, sa, audience) @@ -212,7 +197,7 @@ func NewRunner(ctx context.Context, cdClient *containerd.Client, token oauth2.To asAddr := launchSpec.AttestationServiceAddr - verifierClient, err := getRESTClient(ctx, asAddr, launchSpec) + verifierClient, err := util.NewRESTClient(ctx, asAddr, launchSpec.ProjectID, launchSpec.Region) if err != nil { return nil, fmt.Errorf("failed to create REST verifier client: %v", err) } @@ -222,7 +207,7 @@ func NewRunner(ctx context.Context, cdClient *containerd.Client, token oauth2.To return &ContainerRunner{ container, launchSpec, - agent.CreateAttestationAgent(tpm, client.GceAttestationKeyECC, verifierClient, principalFetcher, sdClient, launchSpec, logger, nil), + agent.CreateAttestationAgent(tpm, client.GceAttestationKeyECC, verifierClient, principalFetcherWithImpersonate, sdClient, launchSpec, logger), logger, serialConsole, }, nil @@ -236,27 +221,6 @@ func getSignatureDiscoveryClient(cdClient *containerd.Client, token oauth2.Token return signaturediscovery.New(cdClient, imageDesc, remoteOpt) } -// getRESTClient returns a REST verifier.Client that points to the given address. -// It defaults to the Attestation Verifier instance at -// https://confidentialcomputing.googleapis.com. -func getRESTClient(ctx context.Context, asAddr string, spec spec.LaunchSpec) (verifier.Client, error) { - httpClient, err := google.DefaultClient(ctx) - if err != nil { - return nil, fmt.Errorf("failed to create HTTP client: %v", err) - } - - opts := []option.ClientOption{option.WithHTTPClient(httpClient)} - if asAddr != "" { - opts = append(opts, option.WithEndpoint(asAddr)) - } - - restClient, err := rest.NewClient(ctx, spec.ProjectID, spec.Region, opts...) - if err != nil { - return nil, err - } - return restClient, nil -} - // formatEnvVars formats the environment variables to the oci format func formatEnvVars(envVars []spec.EnvVar) ([]string, error) { var result []string diff --git a/launcher/internal/signaturediscovery/client.go b/launcher/internal/signaturediscovery/client.go index 0e9cdb8e..0ccf7e79 100644 --- a/launcher/internal/signaturediscovery/client.go +++ b/launcher/internal/signaturediscovery/client.go @@ -8,8 +8,8 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" - "github.com/google/go-tpm-tools/launcher/internal/oci" - "github.com/google/go-tpm-tools/launcher/internal/oci/cosign" + "github.com/google/go-tpm-tools/verifier/oci" + "github.com/google/go-tpm-tools/verifier/oci/cosign" v1 "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/launcher/internal/signaturediscovery/fakeclient.go b/launcher/internal/signaturediscovery/fakeclient.go index 6d9b4da8..f2deffe6 100644 --- a/launcher/internal/signaturediscovery/fakeclient.go +++ b/launcher/internal/signaturediscovery/fakeclient.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - "github.com/google/go-tpm-tools/launcher/internal/oci" - "github.com/google/go-tpm-tools/launcher/internal/oci/cosign" + "github.com/google/go-tpm-tools/verifier/oci" + "github.com/google/go-tpm-tools/verifier/oci/cosign" ) const ( diff --git a/launcher/spec/launch_spec.go b/launcher/spec/launch_spec.go index d5f46bd3..71ace342 100644 --- a/launcher/spec/launch_spec.go +++ b/launcher/spec/launch_spec.go @@ -10,6 +10,7 @@ import ( "strings" "cloud.google.com/go/compute/metadata" + "github.com/google/go-tpm-tools/internal/util" "github.com/google/go-tpm-tools/launcher/internal/experiments" ) @@ -165,18 +166,6 @@ func (s *LaunchSpec) UnmarshalJSON(b []byte) error { return nil } -func getRegion(client *metadata.Client) (string, error) { - zone, err := client.Zone() - if err != nil { - return "", fmt.Errorf("failed to retrieve zone from MDS: %v", err) - } - lastDash := strings.LastIndex(zone, "-") - if lastDash == -1 { - return "", fmt.Errorf("got malformed zone from MDS: %v", zone) - } - return zone[:lastDash], nil -} - // GetLaunchSpec takes in a metadata server client, reads and parse operator's // input to the GCE instance custom metadata and return a LaunchSpec. // ImageRef (tee-image-reference) is required, will return an error if @@ -197,7 +186,7 @@ func GetLaunchSpec(client *metadata.Client) (LaunchSpec, error) { return LaunchSpec{}, fmt.Errorf("failed to retrieve projectID from MDS: %v", err) } - spec.Region, err = getRegion(client) + spec.Region, err = util.GetRegion(client) if err != nil { return LaunchSpec{}, err } diff --git a/launcher/verifier/rest/rest_network_test.go b/launcher/verifier/rest/rest_network_test.go deleted file mode 100644 index e306af49..00000000 --- a/launcher/verifier/rest/rest_network_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package rest - -import ( - "context" - "log" - "testing" - - "github.com/google/go-tpm-tools/client" - "github.com/google/go-tpm-tools/internal/test" - "github.com/google/go-tpm-tools/launcher/agent" - "github.com/google/go-tpm-tools/launcher/internal/signaturediscovery" - "github.com/google/go-tpm-tools/launcher/spec" - "github.com/google/go-tpm-tools/launcher/verifier" - "golang.org/x/oauth2/google" - "google.golang.org/api/option" -) - -var ( - fakeProject = "confidentialcomputing-e2e" - fakeRegion = "us-central1" -) - -// Skip the test if we are not running in an environment with Google API -func testClient(t *testing.T) verifier.Client { - // TODO: Connect to the autopush endpoint by default. - hClient, err := google.DefaultClient(context.Background()) - if err != nil { - t.Skipf("Getting HTTP Client: %v", err) - } - - vClient, err := NewClient(context.Background(), - fakeProject, - fakeRegion, - option.WithHTTPClient(hClient), - ) - if err != nil { - t.Fatalf("Creating Verifier Client: %v", err) - } - return vClient -} - -func testPrincipalIDTokenFetcher(_ string) ([][]byte, error) { - return [][]byte{}, nil -} - -func TestWithAgent(t *testing.T) { - vClient := testClient(t) - - tpm := test.GetTPM(t) - defer client.CheckedClose(t, tpm) - - a := agent.CreateAttestationAgent(tpm, client.AttestationKeyECC, vClient, testPrincipalIDTokenFetcher, signaturediscovery.NewFakeClient(), spec.LaunchSpec{}, log.Default(), nil) - token, err := a.Attest(context.Background(), agent.AttestAgentOpts{}) - if err != nil { - t.Errorf("failed to attest to Attestation Service: %v", err) - } - t.Logf("Got Token: |%v|", string(token)) -} diff --git a/launcher/verifier/client.go b/verifier/client.go similarity index 96% rename from launcher/verifier/client.go rename to verifier/client.go index ae5fa7e0..0af28ae1 100644 --- a/launcher/verifier/client.go +++ b/verifier/client.go @@ -5,8 +5,8 @@ package verifier import ( "context" - "github.com/google/go-tpm-tools/launcher/internal/oci" attestpb "github.com/google/go-tpm-tools/proto/attest" + "github.com/google/go-tpm-tools/verifier/oci" "google.golang.org/genproto/googleapis/rpc/status" ) diff --git a/launcher/verifier/fake/fakeclaims.go b/verifier/fake/fakeclaims.go similarity index 100% rename from launcher/verifier/fake/fakeclaims.go rename to verifier/fake/fakeclaims.go diff --git a/launcher/verifier/fake/fakeverifier.go b/verifier/fake/fakeverifier.go similarity index 96% rename from launcher/verifier/fake/fakeverifier.go rename to verifier/fake/fakeverifier.go index ee28c3ca..19eb6045 100644 --- a/launcher/verifier/fake/fakeverifier.go +++ b/verifier/fake/fakeverifier.go @@ -8,8 +8,8 @@ import ( "time" "github.com/golang-jwt/jwt/v4" - "github.com/google/go-tpm-tools/launcher/internal/oci" - "github.com/google/go-tpm-tools/launcher/verifier" + "github.com/google/go-tpm-tools/verifier" + "github.com/google/go-tpm-tools/verifier/oci" "go.uber.org/multierr" "google.golang.org/genproto/googleapis/rpc/code" "google.golang.org/genproto/googleapis/rpc/status" diff --git a/launcher/internal/oci/cosign/fakesignature.go b/verifier/oci/cosign/fakesignature.go similarity index 95% rename from launcher/internal/oci/cosign/fakesignature.go rename to verifier/oci/cosign/fakesignature.go index db16a4ec..d51005ca 100644 --- a/launcher/internal/oci/cosign/fakesignature.go +++ b/verifier/oci/cosign/fakesignature.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "fmt" - "github.com/google/go-tpm-tools/launcher/internal/oci" + "github.com/google/go-tpm-tools/verifier/oci" ) type fakeSig struct { diff --git a/launcher/internal/oci/cosign/signature.go b/verifier/oci/cosign/signature.go similarity index 98% rename from launcher/internal/oci/cosign/signature.go rename to verifier/oci/cosign/signature.go index 91f59dd7..2d519e07 100644 --- a/launcher/internal/oci/cosign/signature.go +++ b/verifier/oci/cosign/signature.go @@ -7,7 +7,7 @@ import ( "errors" "fmt" - "github.com/google/go-tpm-tools/launcher/internal/oci" + "github.com/google/go-tpm-tools/verifier/oci" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/launcher/internal/oci/cosign/signature_test.go b/verifier/oci/cosign/signature_test.go similarity index 100% rename from launcher/internal/oci/cosign/signature_test.go rename to verifier/oci/cosign/signature_test.go diff --git a/launcher/internal/oci/interface.go b/verifier/oci/interface.go similarity index 100% rename from launcher/internal/oci/interface.go rename to verifier/oci/interface.go diff --git a/launcher/verifier/rest/rest.go b/verifier/rest/rest.go similarity index 98% rename from launcher/verifier/rest/rest.go rename to verifier/rest/rest.go index a41455e3..4d63ad4d 100644 --- a/launcher/verifier/rest/rest.go +++ b/verifier/rest/rest.go @@ -8,8 +8,8 @@ import ( "log" "strings" - "github.com/google/go-tpm-tools/launcher/internal/oci" - "github.com/google/go-tpm-tools/launcher/verifier" + "github.com/google/go-tpm-tools/verifier" + "github.com/google/go-tpm-tools/verifier/oci" v1 "cloud.google.com/go/confidentialcomputing/apiv1" confidentialcomputingpb "cloud.google.com/go/confidentialcomputing/apiv1/confidentialcomputingpb" diff --git a/launcher/verifier/rest/rest_test.go b/verifier/rest/rest_test.go similarity index 92% rename from launcher/verifier/rest/rest_test.go rename to verifier/rest/rest_test.go index 8b0f97a9..6c24c506 100644 --- a/launcher/verifier/rest/rest_test.go +++ b/verifier/rest/rest_test.go @@ -4,7 +4,7 @@ import ( "testing" confidentialcomputingpb "cloud.google.com/go/confidentialcomputing/apiv1/confidentialcomputingpb" - "github.com/google/go-tpm-tools/launcher/verifier" + "github.com/google/go-tpm-tools/verifier" ) // Make sure our conversion function can handle empty values.