github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/mw_http_signature_validation_test.go (about)

     1  package gateway
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/hmac"
     6  	"crypto/rand"
     7  	"crypto/rsa"
     8  	"crypto/sha1"
     9  	"crypto/sha256"
    10  	"crypto/sha512"
    11  	"crypto/x509"
    12  	"encoding/base64"
    13  	"encoding/pem"
    14  	"fmt"
    15  	"hash"
    16  	"net/http"
    17  	"net/http/httptest"
    18  	"net/url"
    19  	"regexp"
    20  	"strings"
    21  	"sync"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/justinas/alice"
    26  	"github.com/lonelycode/go-uuid/uuid"
    27  
    28  	"github.com/TykTechnologies/tyk/apidef"
    29  	"github.com/TykTechnologies/tyk/config"
    30  	"github.com/TykTechnologies/tyk/user"
    31  )
    32  
    33  const hmacAuthDef = `{
    34  	"api_id": "1",
    35  	"org_id": "default",
    36  	"enable_signature_checking": true,
    37  	"hmac_allowed_clock_skew": 5000,
    38  	"auth": {"auth_header_name": "authorization"},
    39  	"version_data": {
    40  		"not_versioned": true,
    41  		"versions": {
    42  			"v1": {"name": "v1"}
    43  		}
    44  	},
    45  	"proxy": {
    46  		"listen_path": "/v1",
    47  		"target_url": "` + TestHttpAny + `"
    48  	}
    49  }`
    50  
    51  func createHMACAuthSession() *user.SessionState {
    52  	session := new(user.SessionState)
    53  	session.Rate = 8.0
    54  	session.Allowance = session.Rate
    55  	session.LastCheck = time.Now().Unix()
    56  	session.Per = 1.0
    57  	session.QuotaRenewalRate = 300 // 5 minutes
    58  	session.QuotaRenews = time.Now().Unix() + 20
    59  	session.QuotaRemaining = 1
    60  	session.QuotaMax = -1
    61  	session.HMACEnabled = true
    62  	session.HmacSecret = "9879879878787878"
    63  	session.Mutex = &sync.RWMutex{}
    64  	return session
    65  }
    66  
    67  func createRSAAuthSession(pubCertId string) *user.SessionState {
    68  	session := new(user.SessionState)
    69  	session.Rate = 8.0
    70  	session.Allowance = session.Rate
    71  	session.LastCheck = time.Now().Unix()
    72  	session.Per = 1.0
    73  	session.QuotaRenewalRate = 300 // 5 minutes
    74  	session.QuotaRenews = time.Now().Unix() + 20
    75  	session.QuotaRemaining = 1
    76  	session.QuotaMax = -1
    77  	session.EnableHTTPSignatureValidation = true
    78  	session.RSACertificateId = pubCertId
    79  	session.Mutex = &sync.RWMutex{}
    80  	return session
    81  }
    82  
    83  func getHMACAuthChain(spec *APISpec) http.Handler {
    84  	remote, _ := url.Parse(TestHttpAny)
    85  	proxy := TykNewSingleHostReverseProxy(remote, spec, nil)
    86  	proxyHandler := ProxyHandler(proxy, spec)
    87  	baseMid := BaseMiddleware{Spec: spec, Proxy: proxy}
    88  	chain := alice.New(mwList(
    89  		&IPWhiteListMiddleware{baseMid},
    90  		&IPBlackListMiddleware{BaseMiddleware: baseMid},
    91  		&HTTPSignatureValidationMiddleware{BaseMiddleware: baseMid},
    92  		&VersionCheck{BaseMiddleware: baseMid},
    93  		&KeyExpired{baseMid},
    94  		&AccessRightsCheck{baseMid},
    95  		&RateLimitAndQuotaCheck{baseMid},
    96  	)...).Then(proxyHandler)
    97  	return chain
    98  }
    99  
   100  type testAuthFailEventHandler struct {
   101  	cb func(config.EventMessage)
   102  }
   103  
   104  func (w *testAuthFailEventHandler) Init(handlerConf interface{}) error {
   105  	return nil
   106  }
   107  
   108  func (w *testAuthFailEventHandler) HandleEvent(em config.EventMessage) {
   109  	w.cb(em)
   110  }
   111  
   112  func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
   113  	c := make(chan struct{})
   114  	go func() {
   115  		defer close(c)
   116  		wg.Wait()
   117  	}()
   118  	select {
   119  	case <-c:
   120  		return false // completed normally
   121  	case <-time.After(timeout):
   122  		return true // timed out
   123  	}
   124  }
   125  
   126  func testPrepareHMACAuthSessionPass(tb testing.TB, hashFn func() hash.Hash, eventWG *sync.WaitGroup, withHeader bool, isBench bool) (string, *APISpec, *http.Request, string) {
   127  	spec := LoadSampleAPI(hmacAuthDef)
   128  
   129  	session := createHMACAuthSession()
   130  
   131  	// Should not receive an AuthFailure event
   132  	cb := func(em config.EventMessage) {
   133  		eventWG.Done()
   134  	}
   135  	spec.EventPaths = map[apidef.TykEvent][]config.TykEventHandler{
   136  		"AuthFailure": {&testAuthFailEventHandler{cb}},
   137  	}
   138  
   139  	sessionKey := ""
   140  	if isBench {
   141  		sessionKey = uuid.New()
   142  	} else {
   143  		sessionKey = "9876"
   144  	}
   145  
   146  	spec.SessionManager.UpdateSession(sessionKey, session, 60, false)
   147  
   148  	req := TestReq(tb, "GET", "/", nil)
   149  
   150  	refDate := "Mon, 02 Jan 2006 15:04:05 MST"
   151  
   152  	// Signature needs to be: Authorization: Signature keyId="hmac-key-1",algorithm="hmac-sha1",signature="Base64(HMAC-SHA1(signing string))"
   153  
   154  	// Prep the signature string
   155  	tim := time.Now().Format(refDate)
   156  	req.Header.Set("Date", tim)
   157  	signatureString := ""
   158  	if withHeader {
   159  		req.Header.Set("X-Test-1", "hello")
   160  		req.Header.Set("X-Test-2", "world")
   161  		signatureString = strings.ToLower("(request-target): ") + "get /\n"
   162  		signatureString += strings.ToLower("Date") + ": " + tim + "\n"
   163  		signatureString += strings.ToLower("X-Test-1") + ": " + "hello" + "\n"
   164  		signatureString += strings.ToLower("X-Test-2") + ": " + "world"
   165  	} else {
   166  		signatureString = strings.ToLower("Date") + ": " + tim
   167  	}
   168  
   169  	// Encode it
   170  	key := []byte(session.HmacSecret)
   171  	h := hmac.New(hashFn, key)
   172  	h.Write([]byte(signatureString))
   173  
   174  	sigString := base64.StdEncoding.EncodeToString(h.Sum(nil))
   175  	encodedString := url.QueryEscape(sigString)
   176  
   177  	return encodedString, spec, req, sessionKey
   178  }
   179  
   180  func testPrepareRSAAuthSessionPass(tb testing.TB, eventWG *sync.WaitGroup, privateKey *rsa.PrivateKey, pubCertId string, withHeader bool, isBench bool) (string, *APISpec, *http.Request, string) {
   181  	spec := LoadSampleAPI(hmacAuthDef)
   182  	session := createRSAAuthSession(pubCertId)
   183  
   184  	// Should not receive an AuthFailure event
   185  	cb := func(em config.EventMessage) {
   186  		eventWG.Done()
   187  	}
   188  	spec.EventPaths = map[apidef.TykEvent][]config.TykEventHandler{
   189  		"AuthFailure": {&testAuthFailEventHandler{cb}},
   190  	}
   191  
   192  	sessionKey := ""
   193  	if isBench {
   194  		sessionKey = uuid.New()
   195  	} else {
   196  		sessionKey = "9876"
   197  	}
   198  
   199  	spec.SessionManager.UpdateSession(sessionKey, session, 60, false)
   200  
   201  	req := TestReq(tb, "GET", "/", nil)
   202  
   203  	refDate := "Mon, 02 Jan 2006 15:04:05 MST"
   204  
   205  	// Signature needs to be: Authorization: Signature keyId="hmac-key-1",algorithm="hmac-sha1",signature="Base64(HMAC-SHA1(signing string))"
   206  
   207  	// Prep the signature string
   208  	tim := time.Now().Format(refDate)
   209  	req.Header.Set("Date", tim)
   210  	signatureString := ""
   211  	if withHeader {
   212  		req.Header.Set("X-Test-1", "hello")
   213  		req.Header.Set("X-Test-2", "world")
   214  		signatureString = strings.ToLower("(request-target): ") + "get /\n"
   215  		signatureString += strings.ToLower("Date") + ": " + tim + "\n"
   216  		signatureString += strings.ToLower("X-Test-1") + ": " + "hello" + "\n"
   217  		signatureString += strings.ToLower("X-Test-2") + ": " + "world"
   218  	} else {
   219  		signatureString = strings.ToLower("Date") + ": " + tim
   220  	}
   221  
   222  	h := sha256.New()
   223  	h.Write([]byte(signatureString))
   224  	hashed := h.Sum(nil)
   225  
   226  	signature, _ := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed)
   227  
   228  	sigString := base64.StdEncoding.EncodeToString(signature)
   229  
   230  	return sigString, spec, req, sessionKey
   231  }
   232  
   233  func TestHMACAuthSessionPass(t *testing.T) {
   234  	// Should not receive an AuthFailure event
   235  	var eventWG sync.WaitGroup
   236  	eventWG.Add(1)
   237  	encodedString, spec, req, sessionKey := testPrepareHMACAuthSessionPass(t, sha1.New, &eventWG, false, false)
   238  
   239  	recorder := httptest.NewRecorder()
   240  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"%s\",algorithm=\"hmac-sha1\",signature=\"%s\"", sessionKey, encodedString))
   241  
   242  	chain := getHMACAuthChain(spec)
   243  	chain.ServeHTTP(recorder, req)
   244  
   245  	if recorder.Code != 200 {
   246  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code, recorder.Body.String())
   247  	}
   248  
   249  	// Check we did not get our AuthFailure event
   250  	if !waitTimeout(&eventWG, 20*time.Millisecond) {
   251  		t.Error("Request should not have generated an AuthFailure event!: \n")
   252  	}
   253  }
   254  
   255  func TestHMACAuthSessionSHA512Pass(t *testing.T) {
   256  	// Should not receive an AuthFailure event
   257  	var eventWG sync.WaitGroup
   258  	eventWG.Add(1)
   259  	encodedString, spec, req, sessionKey := testPrepareHMACAuthSessionPass(t, sha512.New, &eventWG, false, false)
   260  
   261  	recorder := httptest.NewRecorder()
   262  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"%s\",algorithm=\"hmac-sha512\",signature=\"%s\"", sessionKey, encodedString))
   263  
   264  	spec.HmacAllowedAlgorithms = []string{"hmac-sha512"}
   265  	chain := getHMACAuthChain(spec)
   266  	chain.ServeHTTP(recorder, req)
   267  
   268  	if recorder.Code != 200 {
   269  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code, recorder.Body.String())
   270  	}
   271  
   272  	// Check we did not get our AuthFailure event
   273  	if !waitTimeout(&eventWG, 20*time.Millisecond) {
   274  		t.Error("Request should not have generated an AuthFailure event!: \n")
   275  	}
   276  }
   277  
   278  func BenchmarkHMACAuthSessionPass(b *testing.B) {
   279  	b.ReportAllocs()
   280  
   281  	var eventWG sync.WaitGroup
   282  	eventWG.Add(b.N)
   283  	encodedString, spec, req, sessionKey := testPrepareHMACAuthSessionPass(b, sha1.New, &eventWG, false, true)
   284  
   285  	recorder := httptest.NewRecorder()
   286  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"%s\",algorithm=\"hmac-sha1\",signature=\"%s\"", sessionKey, encodedString))
   287  
   288  	chain := getHMACAuthChain(spec)
   289  
   290  	for i := 0; i < b.N; i++ {
   291  		chain.ServeHTTP(recorder, req)
   292  		if recorder.Code != 200 {
   293  			b.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code, recorder.Body.String())
   294  		}
   295  	}
   296  }
   297  
   298  func TestHMACAuthSessionAuxDateHeader(t *testing.T) {
   299  	spec := LoadSampleAPI(hmacAuthDef)
   300  
   301  	session := createHMACAuthSession()
   302  
   303  	// Should not receive an AuthFailure event
   304  	var eventWG sync.WaitGroup
   305  	eventWG.Add(1)
   306  	cb := func(em config.EventMessage) {
   307  		eventWG.Done()
   308  	}
   309  	spec.EventPaths = map[apidef.TykEvent][]config.TykEventHandler{
   310  		"AuthFailure": {&testAuthFailEventHandler{cb}},
   311  	}
   312  
   313  	// Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session.
   314  	spec.SessionManager.UpdateSession("9876", session, 60, false)
   315  
   316  	recorder := httptest.NewRecorder()
   317  	req := TestReq(t, "GET", "/", nil)
   318  
   319  	refDate := "Mon, 02 Jan 2006 15:04:05 MST"
   320  
   321  	// Signature needs to be: Authorization: Signature keyId="hmac-key-1",algorithm="hmac-sha1",signature="Base64(HMAC-SHA1(signing string))"
   322  
   323  	// Prep the signature string
   324  	tim := time.Now().Format(refDate)
   325  	req.Header.Set("x-aux-date", tim)
   326  	signatureString := strings.ToLower("x-aux-date") + ": " + tim
   327  
   328  	// Encode it
   329  	key := []byte(session.HmacSecret)
   330  	h := hmac.New(sha1.New, key)
   331  	h.Write([]byte(signatureString))
   332  
   333  	sigString := base64.StdEncoding.EncodeToString(h.Sum(nil))
   334  	encodedString := url.QueryEscape(sigString)
   335  
   336  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"9876\",algorithm=\"hmac-sha1\",signature=\"%s\"", encodedString))
   337  
   338  	chain := getHMACAuthChain(spec)
   339  	chain.ServeHTTP(recorder, req)
   340  
   341  	if recorder.Code != 200 {
   342  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   343  	}
   344  
   345  	// Check we did not get our AuthFailure event
   346  	if !waitTimeout(&eventWG, 20*time.Millisecond) {
   347  		t.Error("Request should not have generated an AuthFailure event!: \n")
   348  	}
   349  }
   350  
   351  func TestHMACAuthSessionFailureDateExpired(t *testing.T) {
   352  	spec := LoadSampleAPI(hmacAuthDef)
   353  	session := createHMACAuthSession()
   354  
   355  	// Should receive an AuthFailure event
   356  	var eventWG sync.WaitGroup
   357  	eventWG.Add(1)
   358  	cb := func(em config.EventMessage) {
   359  		eventWG.Done()
   360  	}
   361  	spec.EventPaths = map[apidef.TykEvent][]config.TykEventHandler{
   362  		"AuthFailure": {&testAuthFailEventHandler{cb}},
   363  	}
   364  
   365  	// Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session.
   366  	spec.SessionManager.UpdateSession("9876", session, 60, false)
   367  
   368  	recorder := httptest.NewRecorder()
   369  	req := TestReq(t, "GET", "/", nil)
   370  
   371  	refDate := "Mon, 02 Jan 2006 15:04:05 MST"
   372  
   373  	// Signature needs to be: Authorization: Signature keyId="hmac-key-1",algorithm="hmac-sha1",signature="Base64(HMAC-SHA1(signing string))"
   374  
   375  	// Prep the signature string
   376  	tim := time.Now().Format(refDate)
   377  	req.Header.Set("Date", tim)
   378  	signatureString := strings.ToLower("Date") + ":" + tim
   379  
   380  	// Encode it
   381  	key := []byte(session.HmacSecret)
   382  	h := hmac.New(sha1.New, key)
   383  	h.Write([]byte(signatureString))
   384  
   385  	sigString := base64.StdEncoding.EncodeToString(h.Sum(nil))
   386  	encodedString := url.QueryEscape(sigString)
   387  
   388  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"9876\",algorithm=\"hmac-sha1\",signature=\"%s\"", encodedString))
   389  
   390  	chain := getHMACAuthChain(spec)
   391  	chain.ServeHTTP(recorder, req)
   392  
   393  	if recorder.Code != 400 {
   394  		t.Error("Request should have failed with out of date error!: \n", recorder.Code)
   395  	}
   396  
   397  	// Check we did get our AuthFailure event
   398  	if waitTimeout(&eventWG, 20*time.Millisecond) {
   399  		t.Error("Request should have generated an AuthFailure event!: \n")
   400  	}
   401  }
   402  
   403  func TestHMACAuthSessionKeyMissing(t *testing.T) {
   404  	spec := LoadSampleAPI(hmacAuthDef)
   405  	session := createHMACAuthSession()
   406  
   407  	// Should receive an AuthFailure event
   408  	var eventWG sync.WaitGroup
   409  	eventWG.Add(1)
   410  	cb := func(em config.EventMessage) {
   411  		eventWG.Done()
   412  	}
   413  	spec.EventPaths = map[apidef.TykEvent][]config.TykEventHandler{
   414  		"AuthFailure": {&testAuthFailEventHandler{cb}},
   415  	}
   416  
   417  	// Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session.
   418  	spec.SessionManager.UpdateSession("9876", session, 60, false)
   419  
   420  	recorder := httptest.NewRecorder()
   421  	req := TestReq(t, "GET", "/", nil)
   422  
   423  	refDate := "Mon, 02 Jan 2006 15:04:05 MST"
   424  
   425  	// Signature needs to be: Authorization: Signature keyId="hmac-key-1",algorithm="hmac-sha1",signature="Base64(HMAC-SHA1(signing string))"
   426  
   427  	// Prep the signature string
   428  	tim := time.Now().Format(refDate)
   429  	req.Header.Set("Date", tim)
   430  	signatureString := strings.ToLower("Date") + ":" + tim
   431  
   432  	// Encode it
   433  	key := []byte(session.HmacSecret)
   434  	h := hmac.New(sha1.New, key)
   435  	h.Write([]byte(signatureString))
   436  
   437  	sigString := base64.StdEncoding.EncodeToString(h.Sum(nil))
   438  	encodedString := url.QueryEscape(sigString)
   439  
   440  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"98765\",algorithm=\"hmac-sha1\",signature=\"%s\"", encodedString))
   441  
   442  	chain := getHMACAuthChain(spec)
   443  	chain.ServeHTTP(recorder, req)
   444  
   445  	if recorder.Code != 400 {
   446  		t.Error("Request should have failed with key not found error!: \n", recorder.Code)
   447  	}
   448  
   449  	// Check we did get our AuthFailure event
   450  	if waitTimeout(&eventWG, 20*time.Millisecond) {
   451  		t.Error("Request should have generated an AuthFailure event!: \n")
   452  	}
   453  }
   454  
   455  func TestHMACAuthSessionMalformedHeader(t *testing.T) {
   456  	spec := LoadSampleAPI(hmacAuthDef)
   457  	session := createHMACAuthSession()
   458  
   459  	// Should receive an AuthFailure event
   460  	var eventWG sync.WaitGroup
   461  	eventWG.Add(1)
   462  	cb := func(em config.EventMessage) {
   463  		eventWG.Done()
   464  	}
   465  	spec.EventPaths = map[apidef.TykEvent][]config.TykEventHandler{
   466  		"AuthFailure": {&testAuthFailEventHandler{cb}},
   467  	}
   468  
   469  	// Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session.
   470  	spec.SessionManager.UpdateSession("9876", session, 60, false)
   471  
   472  	recorder := httptest.NewRecorder()
   473  	req := TestReq(t, "GET", "/", nil)
   474  
   475  	refDate := "Mon, 02 Jan 2006 15:04:05 MST"
   476  
   477  	// Signature needs to be: Authorization: Signature keyId="hmac-key-1",algorithm="hmac-sha1",signature="Base64(HMAC-SHA1(signing string))"
   478  
   479  	// Prep the signature string
   480  	tim := time.Now().Format(refDate)
   481  	req.Header.Set("Date", tim)
   482  	signatureString := strings.ToLower("Date") + ":" + tim
   483  
   484  	// Encode it
   485  	key := []byte(session.HmacSecret)
   486  	h := hmac.New(sha1.New, key)
   487  	h.Write([]byte(signatureString))
   488  
   489  	sigString := base64.StdEncoding.EncodeToString(h.Sum(nil))
   490  	encodedString := url.QueryEscape(sigString)
   491  
   492  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyID=\"98765\", algorithm=\"hmac-sha256\", signature=\"%s\"", encodedString))
   493  
   494  	chain := getHMACAuthChain(spec)
   495  	chain.ServeHTTP(recorder, req)
   496  
   497  	if recorder.Code != 400 {
   498  		t.Error("Request should have failed with key not found error!: \n", recorder.Code)
   499  	}
   500  
   501  	// Check we did get our AuthFailure event
   502  	if waitTimeout(&eventWG, 20*time.Millisecond) {
   503  		t.Error("Request should have generated an AuthFailure event!: \n")
   504  	}
   505  }
   506  
   507  func TestHMACAuthSessionPassWithHeaderField(t *testing.T) {
   508  	// Should not receive an AuthFailure event
   509  	var eventWG sync.WaitGroup
   510  	eventWG.Add(1)
   511  	encodedString, spec, req, sessionKey := testPrepareHMACAuthSessionPass(t, sha1.New, &eventWG, true, false)
   512  
   513  	recorder := httptest.NewRecorder()
   514  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"%s\",algorithm=\"hmac-sha1\",headers=\"(request-target) date x-test-1 x-test-2\",signature=\"%s\"", sessionKey, encodedString))
   515  
   516  	chain := getHMACAuthChain(spec)
   517  	chain.ServeHTTP(recorder, req)
   518  
   519  	if recorder.Code != 200 {
   520  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   521  	}
   522  
   523  	// Check we did not get our AuthFailure event
   524  	if !waitTimeout(&eventWG, 20*time.Millisecond) {
   525  		t.Error("Request should not have generated an AuthFailure event!: \n")
   526  	}
   527  }
   528  
   529  func BenchmarkHMACAuthSessionPassWithHeaderField(b *testing.B) {
   530  	b.ReportAllocs()
   531  
   532  	var eventWG sync.WaitGroup
   533  	eventWG.Add(b.N)
   534  	encodedString, spec, req, sessionKey := testPrepareHMACAuthSessionPass(b, sha1.New, &eventWG, true, true)
   535  
   536  	recorder := httptest.NewRecorder()
   537  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"%s\",algorithm=\"hmac-sha1\",headers=\"(request-target) date x-test-1 x-test-2\",signature=\"%s\"", sessionKey, encodedString))
   538  
   539  	chain := getHMACAuthChain(spec)
   540  
   541  	for i := 0; i < b.N; i++ {
   542  		chain.ServeHTTP(recorder, req)
   543  		if recorder.Code != 200 {
   544  			b.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   545  		}
   546  	}
   547  }
   548  
   549  func getUpperCaseEscaped(signature string) []string {
   550  	r := regexp.MustCompile(`%[A-F0-9][A-F0-9]`)
   551  	foundList := r.FindAllString(signature, -1)
   552  	return foundList
   553  }
   554  
   555  func replaceUpperCase(originalSignature string, lowercaseList []string) string {
   556  	newSignature := originalSignature
   557  	for _, lStr := range lowercaseList {
   558  		asUpper := strings.ToLower(lStr)
   559  		newSignature = strings.Replace(newSignature, lStr, asUpper, -1)
   560  	}
   561  
   562  	return newSignature
   563  }
   564  
   565  func TestHMACAuthSessionPassWithHeaderFieldLowerCase(t *testing.T) {
   566  	spec := LoadSampleAPI(hmacAuthDef)
   567  	session := createHMACAuthSession()
   568  
   569  	// Should not receive an AuthFailure event
   570  	var eventWG sync.WaitGroup
   571  	eventWG.Add(1)
   572  	cb := func(em config.EventMessage) {
   573  		eventWG.Done()
   574  	}
   575  	spec.EventPaths = map[apidef.TykEvent][]config.TykEventHandler{
   576  		"AuthFailure": {&testAuthFailEventHandler{cb}},
   577  	}
   578  
   579  	// Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session.
   580  	spec.SessionManager.UpdateSession("9876", session, 60, false)
   581  
   582  	recorder := httptest.NewRecorder()
   583  	req := TestReq(t, "GET", "/", nil)
   584  
   585  	refDate := "Mon, 02 Jan 2006 15:04:05 MST"
   586  
   587  	// Signature needs to be: Authorization: Signature keyId="hmac-key-1",algorithm="hmac-sha1",signature="Base64(HMAC-SHA1(signing string))"
   588  
   589  	// Prep the signature string
   590  	tim := time.Now().Format(refDate)
   591  	req.Header.Set("Date", tim)
   592  	req.Header.Set("X-Test-1", "hello?")
   593  	req.Header.Set("X-Test-2", "world£")
   594  	signatureString := strings.ToLower("(request-target): ") + "get /\n"
   595  	signatureString += strings.ToLower("Date") + ": " + tim + "\n"
   596  	signatureString += strings.ToLower("X-Test-1") + ": " + "hello?" + "\n"
   597  	signatureString += strings.ToLower("X-Test-2") + ": " + "world£"
   598  
   599  	// Encode it
   600  	key := []byte(session.HmacSecret)
   601  	h := hmac.New(sha1.New, key)
   602  	h.Write([]byte(signatureString))
   603  
   604  	sigString := base64.StdEncoding.EncodeToString(h.Sum(nil))
   605  	encodedString := url.QueryEscape(sigString)
   606  
   607  	upperCaseList := getUpperCaseEscaped(encodedString)
   608  	newEncodedSignature := replaceUpperCase(encodedString, upperCaseList)
   609  
   610  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"9876\",algorithm=\"hmac-sha1\",headers=\"(request-target) date x-test-1 x-test-2\",signature=\"%s\"", newEncodedSignature))
   611  
   612  	chain := getHMACAuthChain(spec)
   613  	chain.ServeHTTP(recorder, req)
   614  
   615  	if recorder.Code != 200 {
   616  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   617  	}
   618  
   619  	// Check we did not get our AuthFailure event
   620  	if !waitTimeout(&eventWG, 20*time.Millisecond) {
   621  		t.Error("Request should not have generated an AuthFailure event!: \n")
   622  	}
   623  }
   624  
   625  func TestGetFieldValues(t *testing.T) {
   626  	key := `eyJvcmciOiI1ZDgzOTczNDk4NThkYzEwYWU3NjA2ZjQiLCJpZCI6ImU2M2M2MTg4ZjFlYzQ2NzU4N2VlMTA1MzZkYmFjMzk0IiwiaCI6Im11cm11cjY0In0=`
   627  	algo := `hmac-sha1`
   628  	sign := `j27%2FQtZHmlQuWmnQT%2BxLjHcgPl8%3D`
   629  	s := `KeyId="eyJvcmciOiI1ZDgzOTczNDk4NThkYzEwYWU3NjA2ZjQiLCJpZCI6ImU2M2M2MTg4ZjFlYzQ2NzU4N2VlMTA1MzZkYmFjMzk0IiwiaCI6Im11cm11cjY0In0=",algorithm="hmac-sha1",signature="j27%2FQtZHmlQuWmnQT%2BxLjHcgPl8%3D"`
   630  	h, err := getFieldValues(s)
   631  	if err != nil {
   632  		t.Fatal(err)
   633  	}
   634  	if h.KeyID != key {
   635  		t.Errorf("expected keyID:%s got %s", key, h.KeyID)
   636  	}
   637  	if h.Algorthm != algo {
   638  		t.Errorf("expected Algorithm:%s got %s", algo, h.Algorthm)
   639  	}
   640  	if h.Signature != sign {
   641  		t.Errorf("expected Signature:%s got %s", sign, h.Signature)
   642  	}
   643  }
   644  
   645  func TestRSAAuthSessionPass(t *testing.T) {
   646  	_, _, _, serverCert := genServerCertificate()
   647  	privateKey := serverCert.PrivateKey.(*rsa.PrivateKey)
   648  	x509Cert, _ := x509.ParseCertificate(serverCert.Certificate[0])
   649  	pubDer, _ := x509.MarshalPKIXPublicKey(x509Cert.PublicKey)
   650  	pubPem := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubDer})
   651  	pubID, _ := CertificateManager.Add(pubPem, "")
   652  	defer CertificateManager.Delete(pubID, "")
   653  
   654  	// Should not receive an AuthFailure event
   655  	var eventWG sync.WaitGroup
   656  	eventWG.Add(1)
   657  	encodedString, spec, req, sessionKey := testPrepareRSAAuthSessionPass(t, &eventWG, privateKey, pubID, false, false)
   658  
   659  	recorder := httptest.NewRecorder()
   660  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"%s\",algorithm=\"rsa-sha256\",signature=\"%s\"", sessionKey, encodedString))
   661  
   662  	chain := getHMACAuthChain(spec)
   663  	chain.ServeHTTP(recorder, req)
   664  
   665  	if recorder.Code != 200 {
   666  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code, recorder.Body.String())
   667  	}
   668  
   669  	// Check we did not get our AuthFailure event
   670  	if !waitTimeout(&eventWG, 20*time.Millisecond) {
   671  		t.Error("Request should not have generated an AuthFailure event!: \n")
   672  	}
   673  }
   674  
   675  func BenchmarkRSAAuthSessionPass(b *testing.B) {
   676  	b.ReportAllocs()
   677  
   678  	_, _, _, serverCert := genServerCertificate()
   679  	privateKey := serverCert.PrivateKey.(*rsa.PrivateKey)
   680  	x509Cert, _ := x509.ParseCertificate(serverCert.Certificate[0])
   681  	pubDer, _ := x509.MarshalPKIXPublicKey(x509Cert.PublicKey)
   682  	pubPem := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubDer})
   683  	pubID, _ := CertificateManager.Add(pubPem, "")
   684  	defer CertificateManager.Delete(pubID, "")
   685  
   686  	var eventWG sync.WaitGroup
   687  	eventWG.Add(b.N)
   688  	encodedString, spec, req, sessionKey := testPrepareRSAAuthSessionPass(b, &eventWG, privateKey, pubID, false, true)
   689  
   690  	recorder := httptest.NewRecorder()
   691  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"%s\",algorithm=\"rsa-sha256\",signature=\"%s\"", sessionKey, encodedString))
   692  
   693  	chain := getHMACAuthChain(spec)
   694  
   695  	for i := 0; i < b.N; i++ {
   696  		chain.ServeHTTP(recorder, req)
   697  		if recorder.Code != 200 {
   698  			b.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code, recorder.Body.String())
   699  		}
   700  	}
   701  }
   702  
   703  func TestRSAAuthSessionKeyMissing(t *testing.T) {
   704  	_, _, _, serverCert := genServerCertificate()
   705  	privateKey := serverCert.PrivateKey.(*rsa.PrivateKey)
   706  	x509Cert, _ := x509.ParseCertificate(serverCert.Certificate[0])
   707  	pubDer, _ := x509.MarshalPKIXPublicKey(x509Cert.PublicKey)
   708  	pubPem := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubDer})
   709  	pubID, _ := CertificateManager.Add(pubPem, "")
   710  	defer CertificateManager.Delete(pubID, "")
   711  
   712  	spec := LoadSampleAPI(hmacAuthDef)
   713  
   714  	// Should receive an AuthFailure event
   715  	var eventWG sync.WaitGroup
   716  	eventWG.Add(1)
   717  	cb := func(em config.EventMessage) {
   718  		eventWG.Done()
   719  	}
   720  	spec.EventPaths = map[apidef.TykEvent][]config.TykEventHandler{
   721  		"AuthFailure": {&testAuthFailEventHandler{cb}},
   722  	}
   723  
   724  	recorder := httptest.NewRecorder()
   725  	encodedString, spec, req, _ := testPrepareRSAAuthSessionPass(t, &eventWG, privateKey, pubID, false, false)
   726  
   727  	req.Header.Set("Authorization", fmt.Sprintf("Signature keyId=\"98765\",algorithm=\"rsa-sha256\",signature=\"%s\"", encodedString))
   728  
   729  	chain := getHMACAuthChain(spec)
   730  	chain.ServeHTTP(recorder, req)
   731  
   732  	if recorder.Code != 400 {
   733  		t.Error("Request should have failed with key not found error!: \n", recorder.Code)
   734  	}
   735  
   736  	// Check we did get our AuthFailure event
   737  	if waitTimeout(&eventWG, 20*time.Millisecond) {
   738  		t.Error("Request should have generated an AuthFailure event!: \n")
   739  	}
   740  }