Encrypt the provided plaintext using AES encryption. NOTE 1: This function return a string which is fully compatible with Keyczar.Encrypt() method. NOTE 2: This function is loosely based on keyczar AESKey.Encrypt() (Apache 2.0 license). The final encrypted string value consists o
(encrypt_key, plaintext)
| 220 | |
| 221 | |
| 222 | def cryptography_symmetric_encrypt(encrypt_key, plaintext): |
| 223 | """ |
| 224 | Encrypt the provided plaintext using AES encryption. |
| 225 | |
| 226 | NOTE 1: This function return a string which is fully compatible with Keyczar.Encrypt() method. |
| 227 | |
| 228 | NOTE 2: This function is loosely based on keyczar AESKey.Encrypt() (Apache 2.0 license). |
| 229 | |
| 230 | The final encrypted string value consists of: |
| 231 | |
| 232 | [message bytes][HMAC signature bytes for the message] where message consists of |
| 233 | [keyczar header plaintext][IV bytes][ciphertext bytes] |
| 234 | |
| 235 | NOTE: Header itself is unused, but it's added so the format is compatible with keyczar format. |
| 236 | |
| 237 | """ |
| 238 | if not isinstance(encrypt_key, AESKey): |
| 239 | raise TypeError( |
| 240 | "Encrypted key needs to be an AESkey class instance" |
| 241 | f" (was {type(encrypt_key)})." |
| 242 | ) |
| 243 | if not isinstance(plaintext, (six.text_type, six.string_types, six.binary_type)): |
| 244 | raise TypeError( |
| 245 | "Plaintext needs to either be a string/unicode or bytes" |
| 246 | f" (was {type(plaintext)})." |
| 247 | ) |
| 248 | |
| 249 | aes_key_bytes = encrypt_key.aes_key_bytes |
| 250 | hmac_key_bytes = encrypt_key.hmac_key_bytes |
| 251 | |
| 252 | if not isinstance(aes_key_bytes, six.binary_type): |
| 253 | raise TypeError(f"AESKey is not bytes (it is {type(aes_key_bytes)}).") |
| 254 | if not isinstance(hmac_key_bytes, six.binary_type): |
| 255 | raise TypeError(f"HMACKey is not bytes (it is {type(hmac_key_bytes)}).") |
| 256 | |
| 257 | if isinstance(plaintext, (six.text_type, six.string_types)): |
| 258 | # Convert data to bytes |
| 259 | data = plaintext.encode("utf-8") |
| 260 | else: |
| 261 | data = plaintext |
| 262 | |
| 263 | # Pad data |
| 264 | data = pkcs5_pad(data) |
| 265 | |
| 266 | # Generate IV |
| 267 | iv_bytes = os.urandom(KEYCZAR_AES_BLOCK_SIZE) |
| 268 | |
| 269 | backend = default_backend() |
| 270 | cipher = Cipher(algorithms.AES(aes_key_bytes), modes.CBC(iv_bytes), backend=backend) |
| 271 | encryptor = cipher.encryptor() |
| 272 | |
| 273 | # NOTE: We don't care about actual Keyczar header value, we only care about the length (5 |
| 274 | # bytes) so we simply add 5 0's |
| 275 | header_bytes = b"00000" |
| 276 | |
| 277 | ciphertext_bytes = encryptor.update(data) + encryptor.finalize() |
| 278 | msg_bytes = header_bytes + iv_bytes + ciphertext_bytes |
| 279 |