From 02aca361c114827af836bd97a4f7f4031cd1aaec Mon Sep 17 00:00:00 2001 From: Josh Krstic Date: Tue, 26 Jul 2022 16:40:14 -0700 Subject: [PATCH] Add fake client for launcher/verifier/client interface Fix test with new Fake Client --- launcher/agent/agent.go | 2 +- launcher/agent/agent_test.go | 52 ++++++++-------- launcher/verifier/fake/fakeverifier.go | 59 +++++++++++++++++++ .../service/fake_attestationverifier.go | 6 +- .../service/fake_tokens/fake_rsa_token.txt | 1 - 5 files changed, 89 insertions(+), 31 deletions(-) create mode 100644 launcher/verifier/fake/fakeverifier.go delete mode 100644 launcher/verifier/grpcclient/service/fake_tokens/fake_rsa_token.txt diff --git a/launcher/agent/agent.go b/launcher/agent/agent.go index 7b799fb0e..fa8b9a977 100644 --- a/launcher/agent/agent.go +++ b/launcher/agent/agent.go @@ -65,7 +65,7 @@ func (a *agent) MeasureEvent(event cel.Content) error { // Attest fetches the nonce and connection ID from the Attestation Service, // creates an attestation message, and returns the resultant -// principalIDTokens are Metadata Server-generated ID tokens for the instance. +// principalIDTokens and Metadata Server-generated ID tokens for the instance. func (a *agent) Attest(ctx context.Context) ([]byte, error) { challenge, err := a.client.CreateChallenge(ctx) if err != nil { diff --git a/launcher/agent/agent_test.go b/launcher/agent/agent_test.go index ea3756234..a00145679 100644 --- a/launcher/agent/agent_test.go +++ b/launcher/agent/agent_test.go @@ -1,19 +1,18 @@ package agent import ( - "bytes" "context" - "log" - "net" + "crypto/rand" + "crypto/rsa" + "fmt" "testing" + "github.com/golang-jwt/jwt/v4" "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/internal/test" - "github.com/google/go-tpm-tools/launcher/verifier/grpcclient" + "github.com/google/go-tpm-tools/launcher/verifier/fake" "github.com/google/go-tpm-tools/launcher/verifier/grpcclient/service" "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/test/bufconn" servgrpc "github.com/google/go-tpm-tools/launcher/verifier/grpcclient/proto/attestation_verifier/v0" ) @@ -27,32 +26,37 @@ func TestAttest(t *testing.T) { fakeServer := service.New() servgrpc.RegisterAttestationVerifierServer(server, &fakeServer) - lis := bufconn.Listen(1024 * 1024) - go func() { - if err := server.Serve(lis); err != nil { - log.Fatalf("Server exited with error: %v", err) - } - }() - bufDialer := func(context.Context, string) (net.Conn, error) { - return lis.Dial() - } + fakeSigner, _ := rsa.GenerateKey(rand.Reader, 2048) + verifierClient := fake.NewClient(fakeSigner) + agent := CreateAttestationAgent(tpm, client.AttestationKeyECC, verifierClient, placeholderFetcher) - conn, err := grpc.DialContext(context.Background(), "bufconn", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials())) + tokenBytes, err := agent.Attest(context.Background()) if err != nil { - t.Fatalf("failed to connect to attestation service: %v", err) + t.Errorf("failed to attest to Attestation Service: %v", err) } - verifierClient := grpcclient.NewClient(conn, log.Default()) - // Cannot test a GCE key on the simulator. - agent := CreateAttestationAgent(tpm, client.AttestationKeyECC, verifierClient, placeholderFetcher) - token, err := agent.Attest(context.Background()) + registeredClaims := &jwt.RegisteredClaims{} + keyFunc := func(token *jwt.Token) (interface{}, error) { return fakeSigner.Public(), nil } + token, err := jwt.ParseWithClaims(string(tokenBytes), registeredClaims, keyFunc) + + err = registeredClaims.Valid() if err != nil { - t.Errorf("failed to attest to Attestation Service: %v", err) + t.Errorf("Invalid exp, iat, or nbf: %s", err) } - if !bytes.Equal(token, service.FakeToken) { - t.Errorf("received unexpected token: %v, expected: %v", token, service.FakeToken) + if !registeredClaims.VerifyAudience("TestingAudience", true) { + t.Errorf("Invalid aud") } + + if !registeredClaims.VerifyIssuer("TestingIssuer", true) { + t.Errorf("Invalid iss") + } + + if registeredClaims.Subject != "TestingSubject" { + t.Errorf("Invalid sub") + } + + fmt.Printf("token.Claims: %v\n", token.Claims) } func placeholderFetcher(audience string) ([][]byte, error) { diff --git a/launcher/verifier/fake/fakeverifier.go b/launcher/verifier/fake/fakeverifier.go new file mode 100644 index 000000000..a37987af5 --- /dev/null +++ b/launcher/verifier/fake/fakeverifier.go @@ -0,0 +1,59 @@ +// Package fakeverifier is a fake implementation of the AttestationVerifier for testing. +package fake + +import ( + "context" + "crypto" + + "github.com/golang-jwt/jwt/v4" + "github.com/google/go-tpm-tools/launcher/verifier" +) + +type fakeClient struct { + //pbClient servpb.AttestationVerifierClient + signer crypto.Signer +} + +func NewClient(signer crypto.Signer) verifier.Client { + return &fakeClient{signer} +} + +// CreateChallenge returns a hard coded, basic challenge. +// +// If you have found this method is insufficient for your tests, this class must be updated to +// allow for better testing. +func (fc *fakeClient) CreateChallenge(ctx context.Context) (*verifier.Challenge, error) { + return &verifier.Challenge{ + Name: "FakeName", + Nonce: []byte{0x0}, + }, nil +} + +// VerifyAttestation does basic checks and returns a hard coded attestation response. +// +// If you have found this method is insufficient for your tests, this class must be updated to +// allow for better testing. +func (fc *fakeClient) VerifyAttestation(ctx context.Context, request verifier.VerifyAttestationRequest) (*verifier.VerifyAttestationResponse, error) { + // Determine signing algorithm. + signingMethod := jwt.SigningMethodRS256 + now := jwt.TimeFunc() + claims := jwt.RegisteredClaims{ + IssuedAt: &jwt.NumericDate{Time: now}, + NotBefore: &jwt.NumericDate{Time: now}, + ExpiresAt: &jwt.NumericDate{Time: now.Add(60 * 60 * 1e9)}, // Add takes nanoseconds + Audience: []string{"TestingAudience"}, + Issuer: "TestingIssuer", + Subject: "TestingSubject", + } + + token := jwt.NewWithClaims(signingMethod, claims) + + // Instead of a private key, provide the signer. + signed, _ := token.SignedString(fc.signer) + + response := verifier.VerifyAttestationResponse{ + ClaimsToken: []byte(signed), + } + + return &response, nil +} diff --git a/launcher/verifier/grpcclient/service/fake_attestationverifier.go b/launcher/verifier/grpcclient/service/fake_attestationverifier.go index 8e6ae8c98..a14e8191f 100644 --- a/launcher/verifier/grpcclient/service/fake_attestationverifier.go +++ b/launcher/verifier/grpcclient/service/fake_attestationverifier.go @@ -13,10 +13,6 @@ import ( _ "embed" ) -// FakeToken is generated by fake_tokens/fake_rsa_token.txt. -//go:embed fake_tokens/fake_rsa_token.txt -var FakeToken []byte - // FakeServer implements the AttestationVerifier service methods. The initial // connection ID produced by the server will be "0", incrementing with every // subsequent request to GetParams. @@ -74,7 +70,7 @@ func (s *FakeServer) Verify(ctx context.Context, req *servpb.VerifyRequest) (*se // TODO(b/210015375): Return a more realistic fake OIDC token with fake signing key and claims. resp := &servpb.VerifyResponse{ - ClaimsToken: FakeToken, + ClaimsToken: []byte{1}, } return resp, nil diff --git a/launcher/verifier/grpcclient/service/fake_tokens/fake_rsa_token.txt b/launcher/verifier/grpcclient/service/fake_tokens/fake_rsa_token.txt deleted file mode 100644 index 054550dac..000000000 --- a/launcher/verifier/grpcclient/service/fake_tokens/fake_rsa_token.txt +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJSUzI1NiIsImtpZCI6InNPeEhJdzNVa2djVEs1RW4xKy8zeFBMUUJRRCtSVnNVdnRrTTBLdzh5U1E9IiwidHlwIjoiSldUIn0.eyJhdWQiOiJodHRwczovL3N0cy5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTY0MDk4MTg0OSwiaWF0IjoxNjQwODk1NDQ5LCJpc3MiOiJodHRwczovL3N0b3JhZ2UuZ29vZ2xlYXBpcy5jb20vYXR0ZXN0YXRpb24tdmVyaWZpZXItZGV2IiwibmJmIjoxNjQwODk1NDQ5LCJzdWIiOiJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9jb21wdXRlL3YxL3Byb2plY3RzL2JpdGxvY2tlci1kYW5jZS1kZW1vL3pvbmVzL3VzLXdlc3QyLWMvaW5zdGFuY2VzL3VidTIwIiwidGVlIjp7InZlcnNpb24iOnsibWFqb3IiOjAsIm1pbm9yIjoxfSwicGxhdGZvcm0iOnsiaGFyZHdhcmVfdGVjaG5vbG9neSI6IkFNRF9TRVYifSwiY29udGFpbmVyIjp7ImltYWdlX3JlZmVyZW5jZSI6ImxhdW5jaGVyLmdjci5pby9nb29nbGUvZGViaWFuMTA6bGF0ZXN0IiwiaW1hZ2VfZGlnZXN0Ijoic2hhMjU2OmZmMTY0YTY3NjZiZDhiZDJkN2Q1ZTk0ZGI0MTg2NDY0MjZlNTg3MmM2NjRjNmEyNzBjNjg5ZjBiMWQyOGY4NmYiLCJyZXN0YXJ0X3BvbGljeSI6Ik5ldmVyIiwiaW1hZ2VfaWQiOiJzaGEyNTY6MzBlMzgwNTI0MmU1MDIyMzIzYTk3ZjhiNmRlZWM4NGZlNTgzMjBjM2NjMWU3MTc0MjlkZjdmNDdiNWIzMTdiZSIsImVudl9vdmVycmlkZSI6ZmFsc2UsImNtZF9vdmVycmlkZSI6ZmFsc2UsImVudHJ5cG9pbnRfb3ZlcnJpZGUiOmZhbHNlLCJlbnYiOnsiRk9PIjoiQkFSIiwiUEFUSCI6Ii91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL2JpbiIsIlNPTUVfTkFNRSI6IlNPTUVfVkFMVUUifSwiY21kIjpbIi0tZm9vIiwiYmFyIiwiYmF6Il19LCJnY2UiOnsiem9uZSI6InVzLXdlc3QyLWMiLCJwcm9qZWN0X2lkIjoiYml0bG9ja2VyLWRhbmNlLWRlbW8iLCJwcm9qZWN0X251bWJlciI6NTY3MDQ1MDUzODc4LCJpbnN0YW5jZV9uYW1lIjoidWJ1MjAiLCJpbnN0YW5jZV9pZCI6Mjk5NDAxNTUzNzY4MjU1NDcwOX0sImVtYWlscyI6WyJjeWhhbmlzaEBnb29nbGUuY29tIiwiamVzc2llcWxpdUBnb29nbGUuY29tIl19fQ.LQDMX2Q3cAQn6ATQIdWyzK-w6PfjSJ9Xb2VbTpRgHtqgQMr4tmU6N27pnmPnkeVkUubhOvf5g3c2FOxNcZvU7pROJNo_2JJaJK2q70PskwoMcZyQLtY0NUV3FgSAZHQfi6HxCwDIDQGGN1PZ9CfQi6hP8xZBfso0__M-eTuMOcy8Zq3_w0o7JIkgL7NZcKr61dFDTGLwzNPGpPnJsoAVXWbMRz3oUxfXPHXwfOiLeSEEh4R7Gh6bshkms4Trxy0H20yMsVq69YZ4vNba3Vv147PlJXvFqcTTXKlzZTWYdLswtZaeDTremh-B6koEoQYyIJ14Jdz9fIDtErSE6okQ-XNH7PesbbaJFZBvULJwIP0izy3JHucNqdXwzr0PCCIY_8e_mTo9PCdXgbtaIsiFRlBweUq2jkWPxVs63diFNULaBZRN8He45jl6evCAl15bEEnt9dm-U__3DigXjh6D9N6qtg5B0aNg28544RhPbhr2shu_l2vTxQlaltSrlFJS