1# -*- coding: utf-8 -*- 2# Copyright 2015 Google Inc. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Helper functions for customer-supplied encryption functionality.""" 16 17import base64 18import binascii 19from hashlib import sha256 20 21import boto 22 23from gslib.cloud_api import CryptoTuple 24from gslib.exception import CommandException 25 26 27_MAX_DECRYPTION_KEYS = 100 28 29 30def CryptoTupleFromKey(crypto_key): 31 """Returns a CryptoTuple matching the crypto key, or None for no key.""" 32 return CryptoTuple(crypto_key) if crypto_key else None 33 34 35def FindMatchingCryptoKey(key_sha256): 36 """Searches .boto config for an encryption key matching the SHA256 hash. 37 38 Args: 39 key_sha256: Base64-encoded string SHA256 hash of the AES256 encryption key. 40 41 Returns: 42 Base64-encoded encryption key string if a match is found, None otherwise. 43 """ 44 encryption_key = boto.config.get('GSUtil', 'encryption_key', None) 45 if encryption_key is not None: 46 if key_sha256 == Base64Sha256FromBase64EncryptionKey(encryption_key): 47 return encryption_key 48 for i in range(_MAX_DECRYPTION_KEYS): 49 key_number = i + 1 50 decryption_key = boto.config.get( 51 'GSUtil', 'decryption_key%s' % str(key_number), None) 52 if decryption_key is None: 53 # Reading 100 config values can take ~1ms in testing. To avoid adding 54 # this tax, stop reading keys as soon as we encounter a non-existent 55 # entry (in lexicographic order). 56 break 57 elif key_sha256 == Base64Sha256FromBase64EncryptionKey(decryption_key): 58 return decryption_key 59 60 61def GetEncryptionTuple(): 62 """Returns the encryption tuple from .boto configuration.""" 63 encryption_key = _GetBase64EncryptionKey() 64 return CryptoTuple(encryption_key) if encryption_key else None 65 66 67def GetEncryptionTupleAndSha256Hash(): 68 """Returns encryption tuple and SHA256 key hash from .boto configuration.""" 69 encryption_key_sha256 = None 70 encryption_tuple = GetEncryptionTuple() 71 if encryption_tuple: 72 encryption_key_sha256 = Base64Sha256FromBase64EncryptionKey( 73 encryption_tuple.crypto_key) 74 return (encryption_tuple, encryption_key_sha256) 75 76 77def Base64Sha256FromBase64EncryptionKey(encryption_key): 78 return base64.encodestring(binascii.unhexlify( 79 _CalculateSha256FromString( 80 base64.decodestring(encryption_key)))).replace('\n', '') 81 82 83def _CalculateSha256FromString(input_string): 84 sha256_hash = sha256() 85 sha256_hash.update(input_string) 86 return sha256_hash.hexdigest() 87 88 89def _GetBase64EncryptionKey(): 90 """Reads the encryption key from .boto configuration. 91 92 Returns: 93 Base64-encoded encryption key string, or None if no encryption key exists 94 in configuration. 95 """ 96 encryption_key = boto.config.get('GSUtil', 'encryption_key', None) 97 if encryption_key: 98 # Ensure the key has a valid encoding. 99 try: 100 base64.decodestring(encryption_key) 101 except: 102 raise CommandException( 103 'Configured encryption_key is not a valid base64 string. Please ' 104 'double-check your configuration and ensure the key is valid and in ' 105 'base64 format.') 106 return encryption_key 107