1""" 2Provides AWS4SigningKey class for generating Amazon Web Services 3authentication version 4 signing keys. 4 5""" 6 7# Licensed under the MIT License: 8# http://opensource.org/licenses/MIT 9 10 11from __future__ import unicode_literals 12 13import hmac 14import hashlib 15from warnings import warn 16from datetime import datetime 17from six import text_type 18 19 20class AWS4SigningKey: 21 """ 22 AWS signing key. Used to sign AWS authentication strings. 23 24 The secret key is stored in the instance after instantiation, this can be 25 changed via the store_secret_key argument, see below for details. 26 27 Methods: 28 generate_key() -- Generate AWS4 Signing Key string 29 sign_sha256() -- Generate SHA256 HMAC signature, encoding message to bytes 30 first if required 31 32 Attributes: 33 region -- AWS region the key is scoped for 34 service -- AWS service the key is scoped for 35 date -- Date the key is scoped for 36 scope -- The AWS scope string for this key, calculated from the above 37 attributes 38 key -- The signing key string itself 39 40 amz_date -- Deprecated name for 'date'. Use the 'date' attribute instead. 41 amz_date will be removed in a future version. 42 43 """ 44 45 def __init__(self, secret_key, region, service, date=None, 46 store_secret_key=True): 47 """ 48 >>> AWS4SigningKey(secret_key, region, service[, date] 49 ... [, store_secret_key]) 50 51 secret_key -- This is your AWS secret access key 52 region -- The region you're connecting to, as per list at 53 http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region 54 e.g. us-east-1. For services which don't require a 55 region (e.g. IAM), use us-east-1. 56 service -- The name of the service you're connecting to, as per 57 endpoints at: 58 http://docs.aws.amazon.com/general/latest/gr/rande.html 59 e.g. elasticbeanstalk 60 date -- 8-digit date of the form YYYYMMDD. Key is only valid for 61 requests with a Date or X-Amz-Date header matching this 62 date. If date is not supplied the current date is 63 used. 64 store_secret_key 65 -- Whether the secret key is stored in the instance. By 66 default this is True, meaning the key is stored in 67 the secret_key property and is available to any 68 code the instance is passed to. Having the secret 69 key retained makes it easier to regenerate the key 70 if a scope parameter changes (usually the date). 71 This is used by the AWS4Auth class to perform its 72 automatic key updates when a request date/scope date 73 mismatch is encountered. 74 75 If you are passing instances to untrusted code you can 76 set this to False. This will cause the secret key to be 77 discarded as soon as the signing key has been generated. 78 Note though that you will need to manually regenerate 79 keys when needed (or if you use the regenerate_key() 80 method on an AWS4Auth instance you will need to pass it 81 the secret key). 82 83 All arguments should be supplied as strings. 84 85 """ 86 87 self.region = region 88 self.service = service 89 self.date = date or datetime.utcnow().strftime('%Y%m%d') 90 self.scope = '{}/{}/{}/aws4_request'.format(self.date, self.region, self.service) 91 self.store_secret_key = store_secret_key 92 self.secret_key = secret_key if self.store_secret_key else None 93 self.key = self.generate_key(secret_key, self.region, self.service, self.date) 94 95 @classmethod 96 def generate_key(cls, secret_key, region, service, date, 97 intermediates=False): 98 """ 99 Generate the signing key string as bytes. 100 101 If intermediate is set to True, returns a 4-tuple containing the key 102 and the intermediate keys: 103 104 ( signing_key, date_key, region_key, service_key ) 105 106 The intermediate keys can be used for testing against examples from 107 Amazon. 108 109 """ 110 init_key = ('AWS4' + secret_key).encode('utf-8') 111 date_key = cls.sign_sha256(init_key, date) 112 region_key = cls.sign_sha256(date_key, region) 113 service_key = cls.sign_sha256(region_key, service) 114 key = cls.sign_sha256(service_key, 'aws4_request') 115 if intermediates: 116 return (key, date_key, region_key, service_key) 117 else: 118 return key 119 120 @staticmethod 121 def sign_sha256(key, msg): 122 """ 123 Generate an SHA256 HMAC, encoding msg to UTF-8 if not 124 already encoded. 125 126 key -- signing key. bytes. 127 msg -- message to sign. unicode or bytes. 128 129 """ 130 if isinstance(msg, text_type): 131 msg = msg.encode('utf-8') 132 return hmac.new(key, msg, hashlib.sha256).digest() 133 134 @property 135 def amz_date(self): 136 msg = ("This attribute has been renamed to 'date'. 'amz_date' is " 137 "deprecated and will be removed in a future version.") 138 warn(msg, DeprecationWarning) 139 return self.date 140