I am doing a super small and simple program, where it seeks to understand this module a little more, but something very strange happens.
The code below returns:
Traceback (most recent call last):
File ".\crypto.py", line 100, in <module> init = Emisor()
File ".\crypto.py", line 15, in __init__ self.encrypt()
File ".\crypto.py", line 71, in encrypt Receptor(cipher_text,tag_mac,self.key)
File ".\crypto.py", line 84, in __init__ self.decrypt(self.cipher_text,self.tag_mac,self.key)
File ".\crypto.py", line 93, in decrypt plain_text = cipher.decrypt_and_verify(cipher_text,tag_mac)
File "C:\Users\EQUIPO\AppData\Local\Programs\Python\Python37-32\lib\site-packages\Crypto\Cipher\_mode_gcm.py", line 569, in decrypt_and_verify self.verify(received_mac_tag)
File "C:\Users\EQUIPO\AppData\Local\Programs\Python\Python37-32\lib\site-packages\Crypto\Cipher\_mode_gcm.py", line 510, in verify raise ValueError("MAC check failed")
ValueError: MAC check failed
I have already read many pdfs, documentation, videos, blogs, etc. But I can't find the solution.
PDTA: that is "nonce" and that is that of the "header", there is a method called update() and that is where I should put the header, I am a bit lost with that and the nonce.
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
class Transmitter():
def __init__(self):
self.random_password = None
self.message_plain = True
self.key = None
self.password_option()
self.text_option()
self.encrypt()
def password_option(self):
while ( self.random_password == None ):
random = input("\nDo you want to generate a random symmetric key? (y/n)\n\n>>> ").strip().lower()
if random == "y":
self.random_password = True
self.random()
elif random == "n":
self.random_password = False
self.random()
else:
pass
def text_option(self):
if self.message_plain:
question = input("\nHow will you enter your message?\n\n[1] file\n\n[2] directly in the program\n\n>>> ").strip()
if question == "1":
path = input(r"\nEnter the file path\n\n>>> ").strip()
name = path.split("\\")[-1]
self.message_plain = open(name,mode = "rb")
elif question == "2":
self.message_plain = input("\nEnter your message\n\n>>> ").strip()
self.message_plain = self.message_plain.encode("utf-8")
def random(self):
if self.random_password:
self.key = get_random_bytes(16)
else:
self.key = input("\nEnter your password\n\n>>> ").strip()
def encrypt(self):
cipher = AES.new(self.key,AES.MODE_GCM)
cipher.update(b"header")
cipher_text,tag_mac = cipher.encrypt_and_digest(self.message_plain)
Receiver(cipher_text,tag_mac,self.key)
class Receiver():
def __init__(self,cipher_text,tag_mac,key):
self.cipher_text = cipher_text
self.tag_mac = tag_mac
self.key = key
self.decrypt(self.cipher_text,self.tag_mac,self.key)
def decrypt(self,cipher_text,tag_mac,key):
#try:
# nonce = aes_cipher.nonce
cipher = AES.new(key,AES.MODE_GCM)
cipher.update(b"header")
plain_text = cipher.decrypt_and_verify(cipher_text,tag_mac)
#except ValueError:
# print("\nAn error has occurred.")
if __name__ == '__main__':
init = Transmitter()
You have forgotten to transfer the nonce. The nonce can be transferred in plain and doesn't need to be in the AAD either, as the nonce is automatically verified for AEAD ciphers such as GCM.
From the documentation:
nonce(bytes) – the value of the fixed nonce. It must be unique for the combination message/key. If not present, the library creates a random nonce (16 bytes long for AES).
So you need to retrieve and transmit the nonce if you want to use the default random generated 16 byte one.
However, as GCM is more secure and more performant for a 12 byte nonce, I'd rather generate a (secure random) 12 byte nonce yourself, and use / transmit that. The library seems to default on a random nonce of 16 bytes as that is the block size of AES. That's usually a good idea, but not for GCM.
Of course, you should not simply send the key, the secret key should be established beforehand.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With