Skip to content

Instantly share code, notes, and snippets.

@SafeEval
Last active November 7, 2022 02:46
Show Gist options
  • Save SafeEval/e4ae7ffe8ca7137d69bf7124cea91017 to your computer and use it in GitHub Desktop.
Save SafeEval/e4ae7ffe8ca7137d69bf7124cea91017 to your computer and use it in GitHub Desktop.
Example of PyCA's high level AESGCM interface.
#!/usr/bin/env python3
"""
pyca-aesgcm-example.py
Author: Jack Sullivan
Example of using PyCA cryptography's high level AESGCM interface
to encrypt and decrypt data. Also demonstrates how modifying ciphertext
data or the auth tag (MAC) invalidates decryption.
The high level AESGCM interface doesn't seem to separate out the ciphertext
data from the auth tag, or bundle it with the IV.
https://cryptography.io/en/latest/hazmat/primitives/aead/#cryptography.hazmat.primitives.ciphers.aead.AESGCM
"""
import binascii
import os
from cryptography.exceptions import InvalidTag
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
TRIGGER_INVALID_TAG = False
def main():
# Show the plaintext.
plaintext_string = "a"
plaintext = plaintext_string.encode()
print("\nPlain: ", plaintext)
display_data("Plain", plaintext)
# Existing static key.
static_key_string = "d1884987a6d4dd93da9c5e24f8cccb5935a65f8e4f078cf02df9cbc4d628369e"
static_key = hex_string_to_bytes(static_key_string)
display_data("S Key", static_key)
# Generate key at runtime.
keylen = 256
generated_key = AESGCM.generate_key(bit_length=keylen)
display_data("G Key", generated_key)
# High level AES GCM instance.
aesgcm = AESGCM(static_key)
# Additional data to authenticate. Can be None if none was passed during encryption.
# associated_data = b"authenticated but unencrypted data"
associated_data = None
# Generate IV. Never reuse an IV for multiple messages.
iv_nonce = os.urandom(12)
display_data("IV", iv_nonce)
# Encrypt the plaintext.
# 1 byte PT --> 29 byte CT = 12byte IV + 16byte data + 1byte tag
ciphertext = aesgcm.encrypt(iv_nonce, plaintext, associated_data)
display_data("Cipher", ciphertext)
# Decompose ciphertext.
ct_data = [x for x in ciphertext[0:16]]
ct_tag = ciphertext[16:]
display_data("CT data", bytes(ct_data))
display_data("CT tag", bytes(ct_tag))
# Decrypt the ciphertext.
plaintext = aesgcm.decrypt(iv_nonce, ciphertext, associated_data)
display_data("Plain", plaintext)
print("Plain: ", plaintext)
# Fail at decrypting data with modified auth tag.
if TRIGGER_INVALID_TAG:
print("\n")
bad_ct_tag = [x+1 for x in ct_tag]
display_data("Bad tag", bytes(bad_ct_tag))
bad_ciphertext = bytes(ct_data + bad_ct_tag)
try:
plaintext = aesgcm.decrypt(iv_nonce, bad_ciphertext, associated_data)
except InvalidTag as e:
print("Error: Invalid AES-GCM auth tag")
def bytes_to_hex_string(bytes, spaced=True):
"""Get a pretty string of hex bytes, separated by spaces."""
sep = " " if spaced else None
pretty_hex_string = binascii.hexlify(bytes, sep=sep)
return pretty_hex_string.decode("utf-8")
def hex_string_to_bytes(hex_string):
"""Get bytes from a hex string."""
bytes = binascii.unhexlify(hex_string.replace(' ', ''))
return bytes
def display_data(name, data):
"""Display data value and size is standard format"""
bytelen = len(data)
bitlen = bytelen * 8
hex_string = bytes_to_hex_string(data)
print(f"HEX for {name}\t({bytelen} bytes, {bitlen} bits):\t{hex_string}")
if __name__ == "__main__":
main()
Plain: b'a'
HEX for Plain (1 bytes, 8 bits): 61
HEX for S Key (32 bytes, 256 bits): d1 88 49 87 a6 d4 dd 93 da 9c 5e 24 f8 cc cb 59 35 a6 5f 8e 4f 07 8c f0 2d f9 cb c4 d6 28 36 9e
HEX for G Key (32 bytes, 256 bits): 97 74 eb 46 67 20 b3 9c 4d b9 63 fc f8 92 96 d0 bd fc 3b c0 ff a5 c5 5c 57 bd 62 2f a7 a0 8b c5
HEX for IV (12 bytes, 96 bits): e6 f5 d3 46 88 55 c9 56 b0 ee 43 33
HEX for Cipher (17 bytes, 136 bits): ce d5 aa 0e 7a f6 3f 32 7c 41 36 c6 f1 e0 0d 8c b9
HEX for CT data (16 bytes, 128 bits): ce d5 aa 0e 7a f6 3f 32 7c 41 36 c6 f1 e0 0d 8c
HEX for CT tag (1 bytes, 8 bits): b9
HEX for Plain (1 bytes, 8 bits): 61
Plain: b'a'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment