import six from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes, CipherContext, ) DEFAULT_IV = '\x00' * 16 def create_encryptor(key, iv=None): """ Create and return a new object which can do AES encryptions with the given key and initialization vector (IV). The default IV is 16 zero-bytes. The returned object is suitable for use with `encrypt_data` """ key = _validate_key(key) iv = _validate_iv(iv) cipher = Cipher( algorithms.AES(key), modes.CTR(iv), backend=default_backend() ) return cipher.encryptor() def encrypt_data(encryptor, plaintext): """ AES-encrypt `plaintext` with the given `encryptor`. :param encryptor: an instance previously returned from `create_encryptor` :param bytes plaintext: the data to encrypt :returns: ciphertext """ _validate_encryptor(encryptor) if not isinstance(plaintext, six.binary_type): raise ValueError('Plaintext was not bytes') return encryptor.update(plaintext) create_decryptor = create_encryptor decrypt_data = encrypt_data def _validate_encryptor(encryptor): """ raise ValueError if `encryptor` is not a valid object """ if not isinstance(encryptor, CipherContext): raise ValueError( "'encryptor' must be a CipherContext" ) def _validate_key(key): """ confirm `key` is suitable for AES encryption, or raise ValueError """ if not isinstance(key, six.binary_type): raise TypeError('Key was not bytes') if len(key) not in (16, 32): raise ValueError('Key was not 16 or 32 bytes long') return key def _validate_iv(iv): """ Returns a suitable initialiation vector. If `iv` is `None`, a default is returned. If `iv` is not a suitable initialization vector an error is raised. `iv` is returned if it valid. """ if iv is None: return DEFAULT_IV if not isinstance(iv, six.binary_type): raise TypeError('IV was not bytes') if len(iv) != 16: raise ValueError('IV was not 16 bytes long') return iv