Search code examples
pythonencryptioncryptographyaesaes-gcm

Decrypt an encrypted message with AES GCM in Python


I use the AES method to encrypt a sentance called from a txt file. I used the GCM Mode and created a specific key too. Everything is working (the code is below).

from Crypto.Cipher import AES
from Crypto.Protocol.KDF import scrypt
from Crypto.Util.number import long_to_bytes

number = 1
flag = open("sentance.txt", "rb").read()
key = scrypt(long_to_bytes(number), b"code", 32, N = 2 ** 10, r = 8, p = 1)
HexMyKey = key.hex()
cipher = AES.new(key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(flag)

enc = cipher.nonce + ciphertext + tag
HexEncryptedOriginalMessage = enc.hex()

I try to implement the decryption process, that is to say I only have the key (HexMyKeyvalue) and the encrypted message (HexEncryptedOriginalMessage value) and I want to decrypt it. But the thing is that I miss something ..
I wrote the code below but I have that error message.

TypeError: decrypt_and_verify() missing 1 required positional argument: 'received_mac_tag

from Crypto.Cipher import AES
from Crypto.Protocol.KDF import scrypt
from Crypto.Util.number import long_to_bytes

key = bytes.fromhex(HexMykey)
data = bytes.fromhex(HexEncryptedOriginalMessage)
cipher = AES.new(key, AES.MODE_GCM)
dec = cipher.decrypt_and_verify(data)

Do you know how I could decrypt that encrypted original message ?
Any help would be greatly appreciated !


Solution

  • PyCryptodome has a good documentation. The GCM example there uses JSON for concatenating/separating nonce, ciphertext, and tag, but the principle is the same and can easily be applied to your code.

    Since you are using the implicitly derived nonce, be aware that PyCryptodome applies a 16 bytes nonce. Note, however, that the recommendation for GCM is a 12 bytes nonce (s. here, Note section).

    The following decryption example uses a key and ciphertext created with the code you posted for encryption:

    from Crypto.Cipher import AES
    HexMyKey = '6f9b706748f616fb0cf39d274638ee29813dbad675dd3d976e80bde4ccd7546a'
    HexEncryptedOriginalMessage = '6b855acc799213c987a0e3fc4ddfb7719c9b87fcf0a0d35e2e781609143b6e2d8e743cf4aea728002a9fc77ef834'
    key = bytes.fromhex(HexMyKey)
    data = bytes.fromhex(HexEncryptedOriginalMessage)
    cipher = AES.new(key, AES.MODE_GCM, data[:16]) # nonce
    try:
        dec = cipher.decrypt_and_verify(data[16:-16], data[-16:]) # ciphertext, tag
        print(dec) # b'my secret data'
    except ValueError:
        print("Decryption failed")
    

    If authentication fails, decrypt_and_verify() generates a ValueError.

    PyCryptodome also allows for GCM decryption without prior authentication:

    cipher = AES.new(key, AES.MODE_GCM, data[:16]) # nonce
    dec = cipher.decrypt(data[16:-16]) # ciphertext
    print(dec) # b'my secret data'
    

    However, this should not be done for GCM for security reasons, since a ciphertext is only trustworthy after successful authentication.

    Furthermore, the encryption and decryption codes are somewhat inconsistent in that the encryption uses scrypt as the key derivation function and the decryption uses the derived key directly. Normally, one would expect that the key is also derived during decryption. Possibly you take this shortcut only for testing purposes.