def __init__(self, keys_path): """ :param keys_path: Path to the directory containg public keys. :type keys_path: ``str`` """ if not os.path.exists(keys_path): raise ValueError('Directory with public keys doesnt exist') self._keys_path = keys_path self._keyczar = Encrypter.Read(self._keys_path) self._aes = AESCrypto()
def test_aes_decrypt(self): aes = AESCrypto() # Test data is not padded so we assume pad is a no-op aes._unpad_data aes._unpad_data = lambda data: data for item in AES_CBC_TEST_VECTORS: key = item['key'].decode('hex') iv = item['iv'].decode('hex') expected_plain_text = item['plaintext'].decode('hex') cipher_text = item['ciphertext'].decode('hex') # Decrypt assumes data contains IV and is padded, but test data is # not padded data = iv + cipher_text decrypted = aes.decrypt(key=key, data=data) self.assertEqual(decrypted, expected_plain_text)
def test_aes_encrypt(self, mock_func): aes = AESCrypto() for item in AES_CBC_TEST_VECTORS: key = item['key'].decode('hex') iv = item['iv'].decode('hex') plain_text = item['plaintext'].decode('hex') expected_cipher_text = item['ciphertext'].decode('hex') mock_func.return_value = iv encrypted_with_iv_and_pad = aes.encrypt(key=key, data=plain_text) # Remove IV encrypted = encrypted_with_iv_and_pad[len(iv):] # Unpad encrypted = encrypted[:-len(plain_text)] self.assertEqual(encrypted, expected_cipher_text) # Also test round-trip decrypted = aes.decrypt(key=key, data=encrypted_with_iv_and_pad) self.assertEqual(decrypted, plain_text)
class HybridCryptoEncrypter(HybridCryptoMixin): """ Class which provides functionality for encrypting data using hybrid cryptosystem. """ def __init__(self, keys_path): """ :param keys_path: Path to the directory containg public keys. :type keys_path: ``str`` """ if not os.path.exists(keys_path): raise ValueError('Directory with public keys doesnt exist') self._keys_path = keys_path self._keyczar = Encrypter.Read(self._keys_path) self._aes = AESCrypto() def encrypt(self, data): """ Encrypt provided data. """ if len(data) > self.long_message_threshold: cipher_text = self._encrypt_long_message(data=data) else: cipher_text = self._encrypt_short_message(data=data) return cipher_text def _encrypt_short_message(self, data): """ Encrypt a short message using public-key cryptography. :param data: Data to encrypt. :type data: ``str`` """ header = 'hpkc' data = str(data) if len(data) > self.long_message_threshold: raise ValueError('Data is too long and cant be encrypted using ' 'PKC') cipher_text = header + self._keyczar.Encrypt(data) return cipher_text def _encrypt_long_message(self, data): """ Encrypt a long message using hybrid cryptography. Data is encrypted using the following approach: 1. Generate fresh symmetric AES key for the data encapsulation scheme 2. Encrypt provided data using data encapsulation scheme 3. Encrypt generates AES key using key encapsulation scheme (PKC) 4. Assemble a final message - header + pkc_encrypted_aes_key + delimiter + aes_encrypted_data :param data: Data to encrypt. :type data: ``str`` """ header = 'haes' # Generate a fresh symmetric key for the data encapsulation scheme aes_key = RandBytes(n=self.aes_key_size) aes_key = str(aes_key) # Encrypt message using data encapsulation scheme (AES) aes_cipher_text = self._aes.encrypt(key=aes_key, data=data) aes_cipher_text = Base64WSEncode(aes_cipher_text) # Encrypt AES key using key encapsulation scheme key_cipher_text = self._keyczar.Encrypt(aes_key) # Assemble a final message parts = [header, key_cipher_text, self.delimiter, aes_cipher_text] msg_cipher_text = ''.join(parts) return msg_cipher_text def _aes_encrypt(self, key, data): header = 'paes' cipher_text = self._aes.encrypt(key=key, data=data) msg_cipher_text = header + cipher_text return msg_cipher_text
class HybridCryptoDecrypter(HybridCryptoMixin): def __init__(self, keys_path): """ :param keys_path: Path to the directory containg private keys. :type keys_path: ``str`` """ if not os.path.exists(keys_path): raise ValueError('Directory with private keys doesnt exist') self._keys_path = keys_path self._keyczar = Crypter.Read(self._keys_path) self._aes = AESCrypto() def decrypt(self, data): """ Decypt provided data. """ header = self._get_header(data=data) if header not in ['hpkc', 'haes']: msg = 'Invalid cipher text (missing or corrupted header)' raise ValueError(msg) if header == 'haes': plain_text = self._decrypt_long_message(data=data) elif header == 'hpkc': plain_text = self._decrypt_short_message(data=data) return plain_text def _decrypt_short_message(self, data): """ Decrypt a short message. """ data = self._remove_header(data=data) plain_text = self._keyczar.Decrypt(data) return plain_text def _decrypt_long_message(self, data): """ Decrypt a long message. """ data = self._remove_header(data=data) delimiter_index = data.rindex(self.delimiter) aes_key_cipher_text = data[:delimiter_index] aes_key = self._keyczar.Decrypt(aes_key_cipher_text) data_cipher_text = data[delimiter_index + 1:] data_cipher_text = Base64WSDecode(data_cipher_text) plain_text = self._aes.decrypt(key=aes_key, data=data_cipher_text) return plain_text def _aes_decrypt(self, key, data): header = self._get_header(data=data) if header != 'paes': raise ValueError('Invalid or missing header') data = self._remove_header(data=data) plain_text = self._aes.decrypt(key=key, data=data) return plain_text