diff --git a/verifier/ita/client.go b/verifier/ita/client.go index a47bbd9f..8102ff48 100644 --- a/verifier/ita/client.go +++ b/verifier/ita/client.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "net/http" + "strings" "github.com/google/go-tpm-tools/verifier" ) @@ -18,6 +19,7 @@ const ( itaURL = "https://api.trustauthority.intel.com" nonceEndpoint = "/appraisal/v2/nonce" + // TODO: update one Intel provides Confidential Space endpoint. tokenEndpoint = "/appraisal/v2/attest" apiKeyHeader = "x-api-key" @@ -28,26 +30,63 @@ const ( challengeNamePrefix = "ita://" ) +var regionalURLs map[string]string = map[string]string{ + "US": "https://api.trustauthority.intel.com", + "EU": "https://api.eu.trustauthority.intel.com", +} + type client struct { inner *http.Client apiURL string apiKey string } -func NewClient(apiKey string) (verifier.Client, error) { - if apiKey == "" { - return nil, errors.New("API Key required to initialize ITA connector") +func urlAndKey(regionAndKey string) (string, string, error) { + if regionAndKey == "" { + return "", "", errors.New("API region and key required to initialize ITA client") + } + + // Expect format :. + split := strings.SplitN(regionAndKey, ":", 2) + if len(split) != 2 { + return "", "", errors.New("API region and key not in expected format :") + } + region := strings.ToUpper(split[0]) + url, ok := regionalURLs[region] + if !ok { + // Create list of allowed regions. + keys := []string{} + for k := range regionalURLs { + keys = append(keys, k) + } + return "", "", fmt.Errorf("unsupported region %v, expect one of %v", region, keys) + } + + return url, split[1], nil +} + +func NewClient(regionAndKey string) (verifier.Client, error) { + url, apiKey, err := urlAndKey(regionAndKey) + if err != nil { + return nil, err } return &client{ inner: &http.Client{ Transport: &http.Transport{ - // TODO: See how this should be configured. - TLSClientConfig: &tls.Config{}, - Proxy: http.ProxyFromEnvironment, + // https://github.com/intel/trustauthority-client-for-go/blob/main/go-connector/token.go#L130. + TLSClientConfig: &tls.Config{ + CipherSuites: []uint16{ + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + }, + InsecureSkipVerify: false, + MinVersion: tls.VersionTLS12, + }, + Proxy: http.ProxyFromEnvironment, }, }, - apiURL: itaURL, + apiURL: url, apiKey: apiKey, }, nil } diff --git a/verifier/ita/client_test.go b/verifier/ita/client_test.go index e296205c..8cbd6b18 100644 --- a/verifier/ita/client_test.go +++ b/verifier/ita/client_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -277,3 +278,63 @@ func TestConvertRequestToTokenRequest(t *testing.T) { t.Errorf("convertRequestToTokenRequest did not return expected tokenRequest: %v", diff) } } + +func TestURLAndKey(t *testing.T) { + testAPIKey := "testAPIKey" + + for region, expectedURL := range regionalURLs { + t.Run(region+" region", func(t *testing.T) { + regionAndKey := region + ":" + testAPIKey + + url, key, err := urlAndKey(regionAndKey) + if err != nil { + t.Fatalf("urlAndKey returned error: %v", err) + } + + if url != expectedURL { + t.Errorf("urlAndKey did not return expected URL: got %v, want %v", url, expectedURL) + } + + if key != testAPIKey { + t.Errorf("urlAndKey did not return expected API key: got %v, want %v", url, expectedURL) + } + }) + } +} + +func TestURLAndKeyError(t *testing.T) { + testcases := []struct { + name string + regionAndKey string + expectedSubstr string + }{ + { + name: "No colon separator", + regionAndKey: "notAValidInput", + expectedSubstr: "not in expected format", + }, + { + name: "Unsupported region", + regionAndKey: "Narnia:test-api-key", + expectedSubstr: "unsupported region", + }, + { + name: "Empty input", + regionAndKey: "", + expectedSubstr: "region and key required", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + _, _, err := urlAndKey(tc.regionAndKey) + if err == nil { + t.Fatal("urlAndKey returned successfully, expected error") + } + + if !strings.Contains(err.Error(), tc.expectedSubstr) { + t.Errorf("urlAndKey did not return expected error: got %v, want %v", err.Error(), tc.expectedSubstr) + } + }) + } +} diff --git a/verifier/ita/fake/connector.go b/verifier/ita/fake/connector.go deleted file mode 100644 index 9585a8e4..00000000 --- a/verifier/ita/fake/connector.go +++ /dev/null @@ -1,41 +0,0 @@ -package fake - -import ( - "crypto/x509" - "errors" - - jwt "github.com/golang-jwt/jwt/v4" - itaconnector "github.com/intel/trustauthority-client/go-connector/connector" -) - -// Implements the ITA Connector interface. - -type fakeConnector struct { -} - -// Confirm that Connector implements ITA's connector interface. -var _ itaconnector.Connector = (*fakeConnector)(nil) - -func (c *fakeConnector) GetTokenSigningCertificates() ([]byte, error) { - return nil, errors.New("unimplemented") -} -func (c *fakeConnector) GetNonce(itaconnector.GetNonceArgs) (itaconnector.GetNonceResponse, error) { - return itaconnector.GetNonceResponse{}, errors.New("unimplemented") -} -func (c *fakeConnector) GetToken(itaconnector.GetTokenArgs) (itaconnector.GetTokenResponse, error) { - return itaconnector.GetTokenResponse{}, errors.New("unimplemented") -} -func (c *fakeConnector) Attest(itaconnector.AttestArgs) (itaconnector.AttestResponse, error) { - return itaconnector.AttestResponse{}, errors.New("unimplemented") -} -func (c *fakeConnector) VerifyToken(string) (*jwt.Token, error) { - return nil, errors.New("unimplemented") -} - -func (c *fakeConnector) AttestEvidence(evidence interface{}, cloudProvider string, reqId string) (itaconnector.AttestResponse, error) { - return itaconnector.AttestResponse{}, errors.New("unimplemented") -} - -func (c *fakeConnector) GetAKCertificate(ekCert *x509.Certificate, akTpmtPublic []byte) ([]byte, []byte, []byte, error) { - return nil, nil, nil, errors.New("unimplemented") -}