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()