In [199]:
import boto3    
from Crypto import Random
from Crypto.Cipher import AES
import base64
import gzip
import struct

def get_config(region_name='us-west-2', region_code='us2'):
    
    source_file_path, bucket_name = get_source_file_info(region_code)
    print('[DEBUG] enc_source_file_path {}, {}'.format(source_file_path, bucket_name))
    
    source_file_contents = get_source_file_contents(source_file_path, bucket_name, region_name)
    print('[DEBUG] len(source_file_contents) {}'.format(len(source_file_contents)))
    
    date_string = source_file_path[:-12].split('_')[-1:][0]  # get the date portion of the file name
    
    key, key_length = get_key_from(source_file_contents, date_string, region_name)
    print('[DEBUG] key {}'.format([b for b in key]))
    
    iv = source_file_contents[key_length+1:key_length+17]  # init vector is the 16 bytes after the key
    print('[DEBUG] iv {}'.format([b for b in iv]))
    
    enc_config = source_file_contents[key_length+17:]  # enc_text is the remainder of the blob
    print('[DEBUG] enc_config len({})'.format(len(enc_config)))
    
    config_file_contents = extract_config_file_contents(enc_config, key, iv)
    print('[DEBUG] config_file_contents {}'.format(config_file_contents))
    # return config_file_contents
    

def get_source_file_info(region_code):
    client_config_ref_file = "_shared/client_config_current.txt"
    bucket_name = "qa-cake-entities-{}".format(region_code)                                         
    session = boto3.Session(profile_name=region_name)
    s3 = session.resource('s3')
    config_alias = s3.Object(bucket_name, client_config_ref_file)
    source_file_path = config_alias.get()["Body"].read().decode()
    return source_file_path, bucket_name

def get_source_file_contents(enc_config_file_path, bucket_name, region_name):
    session = boto3.Session(profile_name=region_name)
    s3 = session.resource('s3')
    enc_source_obj = s3.Object(bucket_name, enc_config_file_path)
    enc_source_contents = enc_source_obj.get()["Body"].read()
    return enc_source_contents

def get_key_from(source_file_contents, date_string, region_name):
    key_length = ord(source_file_contents[0:1])  # key length is stored in the first byte
    enc_key_bytes = source_file_contents[1:key_length+1]  # enc key is the the next [key_length] bytes +1, 0 is the length byte
    # enc_key = "".join(map(chr, enc_key_bytes))
    key = kms_decrypt(enc_key_bytes, region_name, date_string)
    return key, key_length

def extract_config_file_contents(enc_config, key, iv):
    config_file_contents_gz = decrypt(enc_config, key, iv)
    print("[DEBUG] config_file_contents_gz [:16] {}".format([b for b in config_file_contents_gz[:16]]))
    print("[DEBUG] config_file_contents_gz [:16] {}".format(config_file_contents_gz[:16]))
    with open("/Users/robert/Downloads/testing.gz", 'wb') as gzfile:
        print(gzfile.write(config_file_contents_gz))  # writes an invalid gzip file
    config_file_contents = gzip.decompress(config_file_contents_gz)
    print("[DEBUG] config_file_contents {}".format([b for b in config_file_contents]))
    print("[DEBUG] config_file_contents {}".format(len([b for b in config_file_contents])))
    return config_file_contents

def kms_decrypt(key, region_name, date_string):
    session = boto3.Session(profile_name=region_name)
    client = session.client('kms')
    context = {
        'file': 'client_config',
        'cake_aws_region': regions_available[region_name],
        'file_timestamp': date_string
    }
    response = client.decrypt(
        CiphertextBlob = key, 
        EncryptionContext = context,
        GrantTokens = [region_name]
    )
    return response['Plaintext']


def decrypt(encrypted, key, iv):
    aes = AES.new(key, AES.MODE_CBC, iv)
    plain_text = base64.b64decode(aes.decrypt(encrypted))
    return plain_text


print(get_config())
# [DEBUG] enc_config b'\xbb\x979\xee\x8f&\xd7\x8eA>\x0bBw\x02
# \x89\x03\xe3\x94\x19\x0f\x86\x9f\x03\x7f['


[DEBUG] enc_source_file_path _shared/client_config/client_config_20160815215016.json.gz.enc, qa-cake-entities-us2
[DEBUG] len(source_file_contents) 8952
[DEBUG] key [238, 178, 35, 103, 233, 113, 252, 221, 182, 126, 120, 203, 69, 10, 19, 97]
[DEBUG] iv [218, 102, 216, 146, 223, 176, 183, 253, 180, 38, 44, 107, 76, 234, 38, 71]
[DEBUG] enc_config len(8784)
[DEBUG] config_file_contents_gz [:16] [162, 228, 50, 69, 199, 231, 1, 213, 27, 116, 98, 159, 94, 227, 101, 141]
[DEBUG] config_file_contents_gz [:16] b'\xa2\xe42E\xc7\xe7\x01\xd5\x1btb\x9f^\xe3e\x8d'
191
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-199-4ac0f0b6cbfe> in <module>()
     86 
     87 
---> 88 print(get_config())
     89 # [DEBUG] enc_config b'\xbb\x979\xee\x8f&\xd7\x8eA>\x0bBw\x02
     90 # \x89\x03\xe3\x94\x19\x0f\x86\x9f\x03\x7f['

<ipython-input-199-4ac0f0b6cbfe> in get_config(region_name, region_code)
     25     print('[DEBUG] enc_config len({})'.format(len(enc_config)))
     26 
---> 27     config_file_contents = extract_config_file_contents(enc_config, key, iv)
     28     print('[DEBUG] config_file_contents {}'.format(config_file_contents))
     29     # return config_file_contents

<ipython-input-199-4ac0f0b6cbfe> in extract_config_file_contents(enc_config, key, iv)
     59     with open("/Users/robert/Downloads/testing.gz", 'wb') as gzfile:
     60         print(gzfile.write(config_file_contents_gz))  # writes an invalid gzip file
---> 61     config_file_contents = gzip.decompress(config_file_contents_gz)
     62     print("[DEBUG] config_file_contents {}".format([b for b in config_file_contents]))
     63     print("[DEBUG] config_file_contents {}".format(len([b for b in config_file_contents])))

/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/gzip.py in decompress(data)
    528     """
    529     with GzipFile(fileobj=io.BytesIO(data)) as f:
--> 530         return f.read()
    531 
    532 

/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/gzip.py in read(self, size)
    272             import errno
    273             raise OSError(errno.EBADF, "read() on write-only GzipFile object")
--> 274         return self._buffer.read(size)
    275 
    276     def read1(self, size=-1):

/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/gzip.py in read(self, size)
    459                 # jump to the next member, if there is one.
    460                 self._init_read()
--> 461                 if not self._read_gzip_header():
    462                     self._size = self._pos
    463                     return b""

/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/gzip.py in _read_gzip_header(self)
    407 
    408         if magic != b'\037\213':
--> 409             raise OSError('Not a gzipped file (%r)' % magic)
    410 
    411         (method, flag,

OSError: Not a gzipped file (b'\xa2\xe4')