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 }