jeff tran
jeff tran

Reputation: 51

pycryptodome : MAC Check Failed (using decrypt_and_verify)

I am working on an encryption program with Pycryptodome in Python 3.6 I am trying to encrypt a file and then decrypt it and verify the MAC tag. When I get to verify it, an error is thrown

import os
from Crypto.Cipher import AES

bib Cryptodome imported

aad = b'any thing'
nonce = b'\xde\xe2G\xca\xe8Lq9\xeb\x8b\x84\xe7'
key = b'\xde\xe9\xad\xe9\x14_\x07\x1aq7C\\\xd7\x9c\xae\xfcJ\x1c%\xec\xe6C\xbe\xf0eO\xfaJ1\x08\x0c\xae'

I set the nonce and key as constant just to start things first. then i will use nonce = get_random_bytes(12) unique for every file.

def encrypt(filename):
    chunksize = 64 * 1024
    outputFile = "(encrypted)" + filename
    filesize = str(os.path.getsize(filename))
    cipher = AES.new(key, AES.MODE_GCM, nonce)
    cipher.update(aad)
    with open(filename, 'rb') as infile:
        with open(outputFile, 'wb') as outfile:
            outfile.write(filesize.encode('utf-8'))

            while True:
                chunk = infile.read(chunksize)

                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += ' '.encode('utf-8') * (16 - (len(chunk) % 16))
                ciphertext,sender_tag = cipher.encrypt_and_digest(chunk)

                print (sender_tag)
                outfile.write(ciphertext)

the decryption part using decrypt_and_verify so no need to worry about which come first the decryption or verification

def decrypt(filename,received_tag):
    chunksize = 64 * 1024

    outputFile = "(clear)"+ filename
    cipher = AES.new(key, AES.MODE_GCM, nonce)
    cipher.update(aad)
    with open(filename, 'rb') as infile: 
        with open(outputFile, 'wb') as outfile:
            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                clearmessage = cipher.decrypt_and_verify(chunk, b'received_tag')
                outfile.write(clearmessage)
            outfile.truncate()

def Main():
    choice = input("Would you like to (E)ncrypt or (D)ecrypt?: ")
    if choice == 'E':
        filename = input("File to encrypt: ")
        #password = input("Password: ")
        encrypt(filename)
        print ("Done.")
    elif choice == 'D':
        filename = input("File to decrypt: ")
    #password = input("Password: ")
        received_tag = input("enter received tag:")
        decrypt(filename,received_tag)
        print ("Done.")
    else:
        print ("No Option selected, closing...")

if __name__ == '__main__':
    Main()

And this is the error:

raise ValueError("MAC check failed")
ValueError: MAC check failed

I don't know where i messed up . by the way i get a tag similar to b'\x1c\xd1\xd8\x1a6\x07\xf3G\x8c_s\x94"*(b'

Update : i did correct a mistake in the code sender_tag,ciphertext = cipher.encrypt_and_digest(chunk) insted of ciphertext,sender_tag = cipher.encrypt_and_digest(chunk) but still the problem persists

Upvotes: 5

Views: 3164

Answers (1)

SquareRootOfTwentyThree
SquareRootOfTwentyThree

Reputation: 7766

Quite a few of things look dodgy:

  • You write the file size, but then you don't read it back. Actually, I don't think you should write it at all.
  • You pad the chunk to the 16 bytes boundary before encrypting it, but GCM does not require that.
  • None should not be fixed, but random per encryption.
  • Nonce and MAC tag are typically stored along with the ciphertext (which therefore becomes longer than the plaintext).
  • By writing chunks independently one from the other you are allowing an attacker to remove, duplicate and reorder chunks in transit.

Upvotes: 3

Related Questions