1

I'm trying to make a password manager and I am using a KDF to make the key and then use AES GCM to encrypt each row in the database. Each row has a different salt used in the key. I have followed the documentation on pycryptodome to encrypt and decrypt data using the example code, and everything works fine, except for the MAC check.

I have checked multiple times and everything is exactly the same between encryption and decryption, the nonce, salt, tag, ciphertext etc.

How can I fix this? (the code is below)

class Crypto(PasswordDatabase):
    def __init__(self):
        PasswordDatabase.__init__(self)
        self.db = None

    def encrypt_db(self):
        self.db = self.get_database()
        master_password = b'password'

        with open("passwords.txt", "w") as file:
            for i in range(len(self.db)):

                current_tuple = list(self.db[i])
                del current_tuple[0]
                current_tuple = tuple(current_tuple)
                plaintext = ",".join(current_tuple)

                salt = get_random_bytes(16)
                key = PBKDF2(master_password, salt, 16, count=1000000, hmac_hash_module=SHA512)
                file.write(f"salt={salt},")

                header = b"header"
                cipher = AES.new(key, AES.MODE_GCM)
                cipher.update(header)
                ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode())

                json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
                json_v = [b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag)]
                result = json.dumps(dict(zip(json_k, json_v)))

                print(result, "\n")
                file.write(result + "\n")

    def decrypt_db(self):
        with open("passwords.txt", "r") as file:
            master_password = b"password"

            for line in file:
                stripped_line = line.strip()
                ssalt = re.findall("salt=(b'.*'),", str(stripped_line))
                salt = ssalt[0]

                key = PBKDF2(master_password, salt, 16, count=1000000, hmac_hash_module=SHA512)

                json_input = re.findall("salt=b'.*',({.*})", str(stripped_line))
                b64 = json.loads(json_input[0])
                json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
                jv = {k:b64decode(b64[k]) for k in json_k}

                cipher = AES.new(key, AES.MODE_GCM, nonce=jv['nonce'])
                cipher.update(jv['header'])
                plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag'])
                print(plaintext)


if __name__ == "__main__":
    crypto = Crypto()
    crypto.encrypt_db()
    crypto.decrypt_db()
  • 2
    Your key is the wrong size during encryption, it seems you are mixing AES-256 during encryption with AES-128 during decryption. – Maarten Bodewes Feb 26 '21 at 22:48
  • @MaartenBodewes I see that I have 1 key as 32 bytes and the other as 16 bytes. I looked back in the documentation and saw that they have to be 16 bytes so I changed them both to 16 bytes. However, I still get the `MAC check failed` error – Dejidestroyer666 Bob Feb 27 '21 at 00:15
  • In the end you sometimes have to print out all the components as hexadecimals right before you feed it to the cipher. If they don't match then something is amiss. – Maarten Bodewes Feb 27 '21 at 00:45
  • @MaartenBodewes I have printed out all components as hexadecimals and none of the values get changed. For some reason, whenever I read the salt back from the file, the file reader adds extra ```\```s. For example the salt in the file would be `'b'\xea\xee\xd8\x9aJ>\xbc\xa5$\xf8\x8c\x88R\xcf\x97N''` and then I'd read the file and then it would say the salt was `'b\'\\xea\\xee\\xd8\\x9aJ>\\xbc\\xa5$\\xf8\\x8c\\x88R\\xcf\\x97N\''`, it added extra ```\```s. – Dejidestroyer666 Bob Feb 27 '21 at 10:30
  • 1
    @MaartenBodewes I fixed the file reader randomly adding ```\```s by encoding it to base64 before its written to the file, and then decoding it after. The MAC check now doesn't fail. Thanks for your help :) – Dejidestroyer666 Bob Feb 27 '21 at 10:43

1 Answers1

1

The file reader was randomly adding extra \s to the salt, so I encoded the salt with base64 before it was written to the file, and then decoded it after it was read from the file to decrypt. The MAC check now doesn't fail.