...

Source file src/github.com/NCR-Corporation/ncr-bsp-hmac/go/sign/access_key_signer.go

Documentation: github.com/NCR-Corporation/ncr-bsp-hmac/go/sign

     1  package sign
     2  
     3  import (
     4  	"crypto/hmac"
     5  	"crypto/sha512"
     6  	"encoding/base64"
     7  	"fmt"
     8  	"net/http"
     9  	"strings"
    10  )
    11  
    12  const (
    13  	AccessKeyPrefix = "AccessKey"
    14  	DateHeader      = "Date"
    15  	DateTimeFormat  = "2006-01-02T15:04:05.000Z"
    16  )
    17  
    18  // accessKeyHTTPSigner implements the HTTPSigner interface
    19  type accessKeyHTTPSigner struct {
    20  	sharedKey string
    21  	secretKey string
    22  }
    23  
    24  // NewAccessKeyHTTPSigner is a function that returns an implementation that conforms to the sign.Signer interface
    25  func NewAccessKeyHTTPSigner(sharedKey, secretKey string) (HTTPSigner, error) {
    26  	if len(sharedKey) == 0 || len(secretKey) == 0 {
    27  		return nil, fmt.Errorf("sharedKey or secretKey cannot be an empty string")
    28  	}
    29  	return &accessKeyHTTPSigner{sharedKey, secretKey}, nil
    30  }
    31  
    32  func (s *accessKeyHTTPSigner) Sign(req *http.Request) (*http.Request, error) {
    33  	auth := req.Header.Get("Authorization")
    34  	// If the Authorization header already exists then don't do anything
    35  	// TODO(sai): make sure this is what you want to do.
    36  	if len(auth) > 0 {
    37  		return req, nil
    38  	}
    39  	// 1. get a unique key
    40  	uniqueKey, err := getUniqueKey(s.secretKey, req)
    41  	if err != nil {
    42  		return nil, fmt.Errorf("sign: error while generating new unique key for HMAC signing: %v", err)
    43  	}
    44  	// 2. get signable content
    45  	signableContent := getSignableContent(req)
    46  
    47  	// 3. HMAC content
    48  
    49  	hmacContent, err := calculateHMAC(uniqueKey, signableContent)
    50  	if err != nil {
    51  		return nil, fmt.Errorf("sign: error while calculating hmac: %v", err)
    52  	}
    53  
    54  	accessKey := s.sharedKey + ":" + hmacContent
    55  
    56  	req.Header.Add("Authorization", fmt.Sprintf("%s %s", AccessKeyPrefix, accessKey))
    57  
    58  	return req, nil
    59  }
    60  
    61  func getUniqueKey(secretKey string, req *http.Request) (string, error) {
    62  	date := req.Header.Get(DateHeader)
    63  	if len(date) == 0 {
    64  		return "", fmt.Errorf("sign: error while access header %s from the request", DateHeader)
    65  	}
    66  	parsedDate, err := http.ParseTime(date)
    67  	if err != nil {
    68  		return "", fmt.Errorf("sign: error while parsing date to RFC specification: %v", err)
    69  	}
    70  	return strings.TrimSpace(secretKey) + parsedDate.Format(DateTimeFormat), nil
    71  }
    72  
    73  func getSignableContent(req *http.Request) string {
    74  	query := req.URL.RawQuery
    75  	pathAndQuery := ""
    76  	if len(query) > 0 {
    77  		pathAndQuery = req.URL.EscapedPath() + "?" + query
    78  	} else {
    79  		pathAndQuery = req.URL.EscapedPath()
    80  	}
    81  	headers := req.Header
    82  	contentType := strings.TrimSpace(headers.Get("Content-Type"))
    83  	contentMD5 := headers.Get("Content-MD5")
    84  	nepApplicationKey := headers.Get("nep-application-key")
    85  	nepCorrelationId := headers.Get("nep-correlation-id")
    86  	nepOrganization := headers.Get("nep-organization")
    87  	nepServiceVersion := headers.Get("nep-service-version")
    88  
    89  	signableContent := []string{
    90  		req.Method,
    91  		pathAndQuery,
    92  		contentType,
    93  		contentMD5,
    94  		nepApplicationKey,
    95  		nepCorrelationId,
    96  		nepOrganization,
    97  		nepServiceVersion,
    98  	}
    99  
   100  	signableContent = filterSignableContent(signableContent, func(c string) bool {
   101  		return len(c) > 0
   102  	})
   103  	return strings.Join(signableContent, "\n")
   104  }
   105  
   106  func filterSignableContent(content []string, f func(string) bool) []string {
   107  	cf := make([]string, 0)
   108  	for _, c := range content {
   109  		if f(c) {
   110  			cf = append(cf, c)
   111  		}
   112  	}
   113  	return cf
   114  }
   115  
   116  func calculateHMAC(uniqueKey, signableContent string) (string, error) {
   117  	h := hmac.New(sha512.New, []byte(uniqueKey))
   118  	_, err := h.Write([]byte(signableContent))
   119  	if err != nil {
   120  		return "", fmt.Errorf("sign: error while writing to HMAC writer: %v", err)
   121  	}
   122  	sum := h.Sum(nil)
   123  	return base64.StdEncoding.EncodeToString(sum), nil
   124  }
   125  

View as plain text