src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

cache.go (2822B)


      1 package v4
      2 
      3 import (
      4 	"strings"
      5 	"sync"
      6 	"time"
      7 
      8 	"github.com/aws/aws-sdk-go-v2/aws"
      9 )
     10 
     11 func lookupKey(service, region string) string {
     12 	var s strings.Builder
     13 	s.Grow(len(region) + len(service) + 3)
     14 	s.WriteString(region)
     15 	s.WriteRune('/')
     16 	s.WriteString(service)
     17 	return s.String()
     18 }
     19 
     20 type derivedKey struct {
     21 	AccessKey  string
     22 	Date       time.Time
     23 	Credential []byte
     24 }
     25 
     26 type derivedKeyCache struct {
     27 	values map[string]derivedKey
     28 	mutex  sync.RWMutex
     29 }
     30 
     31 func newDerivedKeyCache() derivedKeyCache {
     32 	return derivedKeyCache{
     33 		values: make(map[string]derivedKey),
     34 	}
     35 }
     36 
     37 func (s *derivedKeyCache) Get(credentials aws.Credentials, service, region string, signingTime SigningTime) []byte {
     38 	key := lookupKey(service, region)
     39 	s.mutex.RLock()
     40 	if cred, ok := s.get(key, credentials, signingTime.Time); ok {
     41 		s.mutex.RUnlock()
     42 		return cred
     43 	}
     44 	s.mutex.RUnlock()
     45 
     46 	s.mutex.Lock()
     47 	if cred, ok := s.get(key, credentials, signingTime.Time); ok {
     48 		s.mutex.Unlock()
     49 		return cred
     50 	}
     51 	cred := deriveKey(credentials.SecretAccessKey, service, region, signingTime)
     52 	entry := derivedKey{
     53 		AccessKey:  credentials.AccessKeyID,
     54 		Date:       signingTime.Time,
     55 		Credential: cred,
     56 	}
     57 	s.values[key] = entry
     58 	s.mutex.Unlock()
     59 
     60 	return cred
     61 }
     62 
     63 func (s *derivedKeyCache) get(key string, credentials aws.Credentials, signingTime time.Time) ([]byte, bool) {
     64 	cacheEntry, ok := s.retrieveFromCache(key)
     65 	if ok && cacheEntry.AccessKey == credentials.AccessKeyID && isSameDay(signingTime, cacheEntry.Date) {
     66 		return cacheEntry.Credential, true
     67 	}
     68 	return nil, false
     69 }
     70 
     71 func (s *derivedKeyCache) retrieveFromCache(key string) (derivedKey, bool) {
     72 	if v, ok := s.values[key]; ok {
     73 		return v, true
     74 	}
     75 	return derivedKey{}, false
     76 }
     77 
     78 // SigningKeyDeriver derives a signing key from a set of credentials
     79 type SigningKeyDeriver struct {
     80 	cache derivedKeyCache
     81 }
     82 
     83 // NewSigningKeyDeriver returns a new SigningKeyDeriver
     84 func NewSigningKeyDeriver() *SigningKeyDeriver {
     85 	return &SigningKeyDeriver{
     86 		cache: newDerivedKeyCache(),
     87 	}
     88 }
     89 
     90 // DeriveKey returns a derived signing key from the given credentials to be used with SigV4 signing.
     91 func (k *SigningKeyDeriver) DeriveKey(credential aws.Credentials, service, region string, signingTime SigningTime) []byte {
     92 	return k.cache.Get(credential, service, region, signingTime)
     93 }
     94 
     95 func deriveKey(secret, service, region string, t SigningTime) []byte {
     96 	hmacDate := HMACSHA256([]byte("AWS4"+secret), []byte(t.ShortTimeFormat()))
     97 	hmacRegion := HMACSHA256(hmacDate, []byte(region))
     98 	hmacService := HMACSHA256(hmacRegion, []byte(service))
     99 	return HMACSHA256(hmacService, []byte("aws4_request"))
    100 }
    101 
    102 func isSameDay(x, y time.Time) bool {
    103 	xYear, xMonth, xDay := x.Date()
    104 	yYear, yMonth, yDay := y.Date()
    105 
    106 	if xYear != yYear {
    107 		return false
    108 	}
    109 
    110 	if xMonth != yMonth {
    111 		return false
    112 	}
    113 
    114 	return xDay == yDay
    115 }