4

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 !

Julien
  • 45
  • 1
  • 3
  • 15
  • 1
    looks like you are missing the second argument to the `decrypt_and_verify` function which is `mac_tag`. [Docs](https://pycryptodome.readthedocs.io/en/latest/src/cipher/modern.html?highlight=decrypt_and_verify#decrypt_and_verify) – Brad Day Apr 28 '21 at 21:00
  • 1
    On encryption side you concatenated the enc value with "enc = cipher.nonce + ciphertext + tag" so for decryption you need to do reverse = split the data (= complete ciphertext) into nonce, ciphertext and gcmTag and feed them to cipher.decrypt – Michael Fehr Apr 28 '21 at 21:03
  • 1
    https://github.com/wolf43/AES-GCM-example/blob/master/aes_gcm.py – kelalaka Apr 28 '21 at 23:19
  • Thank you both of you for your answers. @Michael Ok I understand I have to split the enc part but I don't know how to do. I tried **ciphertext, tag, nonce = data** but I got the following error message **ValueError: too many values to unpack (expected 3)**. Have you got some ideas ? – Julien Apr 29 '21 at 05:45
  • @kelalaka I checked your link too and there is the split part too. But I didn't manage to implement it in my code.. – Julien Apr 29 '21 at 05:46

1 Answers1

5

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.

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Thank you very very much for your exhaustive answer, that's perfectly clear for me. Actually I did not take care to the 16 bytes nonce, didn't take care to it, to be honest..I used it in my script and it works like a charm, now I can decrypt the original encrypted message ! Thanks !! – Julien Apr 29 '21 at 07:48
  • 1
    I think this code must include the tag verification, too. – kelalaka Apr 29 '21 at 16:10
  • @kelalaka: `decrypt_and_verify()` throws an exception, I added the exception handling – Topaco Apr 29 '21 at 17:01
  • Way better, thanks. And be aware that GCM is very fragile, now [partition oracles](https://crypto.stackexchange.com/q/88716/18298) – kelalaka Apr 29 '21 at 19:08
  • 2
    Maybe [this is a better explanation of 12-byte nonce](https://stackoverflow.com/a/64507126/1820553) – kelalaka Apr 30 '21 at 09:59
  • @kelalaka - Yes your link/post explains the 12 bytes nonce for GCM very well. My link is not to explain the length, just to show that the PyCryptodome developers are aware of the discrepancy with the NIST recommendation and its consequences. – Topaco May 01 '21 at 07:55