I'm making a password manager with python that stores and encrypts passwords (using the pycriptodome module) with a main password set by the user, but the decryption does not work Here's the function I'm using to decrypt the passwords
def decrypt_data(key, iv, ct): # Makes the credentials readable and usable
print(f'{bcolors.warning}\nDebug decrypt_data(1):\tiv:\t\t\t{iv}') # debug text
print(f'Debug decrypt_data(2):\tiv variable type:\t{type(iv)}') # debug text
print(f'Debug decrypt_data(3):\tiv_bytes:\t\t{bytes(iv, 'utf-8')}') # debug text
print(f'Debug decrypt_data(4):\tiv_bytes type:\t\t{type(bytes(iv, 'utf-8'))}\n{bcolors.endc}') # debug text
cipher = AES.new(key, AES.MODE_CBC)
pt = unpad(cipher.decrypt(bytes(ct, 'utf-8')), AES.block_size)
print(f'{bcolors.okcyan}Decripting done{bcolors.endc}')
return decrypted.decode("UTF-8")
Console output:
Debug decrypt_data(1): iv: +00X7cdUZm7L61ifRb9EDQ==
Debug decrypt_data(2): iv variable type: <class 'str'>
Debug decrypt_data(3): iv_bytes: b'+00X7cdUZm7L61ifRb9EDQ=='
Debug decrypt_data(4): iv_bytes type: <class 'bytes'>
Traceback (most recent call last):
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 151, in <module>
print(f'{bcolors.warning}Debug main(5):\t\tdecrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}') # debug text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 107, in decrypt_data
pt = unpad(cipher.decrypt(bytes(ct, 'utf-8')), AES.block_size)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Utente\AppData\Local\Programs\Python\Python312\Lib\site-packages\Crypto\Cipher\_mode_cbc.py", line 246, in decrypt
raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode
Just for reference here's the code for the encryption and part of the main function as well
def encrypt_data(key, pt): # protects the credentials in the file once saved
print(f'\nDebug encrypt_data(1):\tplaintext:\t\t{pt}') # debug text
print(f'Debug encrypt_data(2):\tbytetext:\t\t{bytes(pt, 'utf-8')}') # debug text
print(f'Debug encrypt_data(3):\tpadded plaintext:\t{pad(bytes(pt, 'utf-8'), AES.block_size)}') # debug text
cipher = AES.new(key, AES.MODE_CBC)
ct_bytes = cipher.encrypt(pad(bytes(pt, 'utf-8'), AES.block_size))
print(f'Debug encrypt_data(4):\tct_bytes:\t\t{ct_bytes}') # debug text
iv = b64encode(cipher.iv).decode('utf-8')
ct = b64encode(ct_bytes).decode('utf-8')
print(f'Debug encrypt_data(5):\tb64 decoded ct:\t\t{ct}') # debug text
print(f'Debug encrypt_data(6):\tiv:\t\t\t{iv}') # debug text
print(f'Debug encrypt_data(7):\tciphertext:\t\t{ct}\n') # debug text
print(f'Encripting done')
return ct, iv
if __name__ == '__main__':
# login screen
if not os.path.exists(credentials_file):
signin()
main_pw = login()
padded_key = pad(bytes(main_pw, 'utf-8'), AES.block_size) # Pad the key to the correct length
print(f'Debug main(1):\t\tmain_pw:\t\t{main_pw}') # debug text
print(f'Debug main(2):\t\tbyte_pw:\t\t{bytes(main_pw, 'utf-8')}') # debug text
print(f'Debug main(3):\t\tpadded_key:\t\t{padded_key}') # debug text
print(f'Debug main(4):\t\tencrypted main_pw:\t{encrypt_data(padded_key, main_pw)[0]}') # debug text
print(f'Debug main(5):\t\tdecrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}') # debug text
print(f'Debug main(6):\t\tunpadded decrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}\n') # debug text
this is the full output of the console:
Debug main(1): main_pw: test
Debug main(2): byte_pw: b'test'
Debug main(3): padded_key: b'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
Debug encrypt_data(1): plaintext: test
Debug encrypt_data(2): bytetext: b'test'
Debug encrypt_data(3): padded plaintext: b'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
Debug encrypt_data(4): ct_bytes: b'I\xdd\x95\x9fXl"\xa1\xd9Xfd~\xc5\xc2\xcd'
Debug encrypt_data(5): b64 decoded ct: Sd2Vn1hsIqHZWGZkfsXCzQ==
Debug encrypt_data(6): iv: lmuFGlygOos07SPYpBWNBw==
Debug encrypt_data(7): ciphertext: Sd2Vn1hsIqHZWGZkfsXCzQ==
Encripting done
Debug main(4): encrypted main_pw: Sd2Vn1hsIqHZWGZkfsXCzQ==
Debug encrypt_data(1): plaintext: test
Debug encrypt_data(2): bytetext: b'test'
Debug encrypt_data(3): padded plaintext: b'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
Debug encrypt_data(4): ct_bytes: b'\t\r\xfbd\x07\x1d\xcay>\xa49\xdeK\xd9W\xbb'
Debug encrypt_data(5): b64 decoded ct: CQ37ZAcdynk+pDneS9lXuw==
Debug encrypt_data(6): iv: +00X7cdUZm7L61ifRb9EDQ==
Debug encrypt_data(7): ciphertext: CQ37ZAcdynk+pDneS9lXuw==
Encripting done
Debug encrypt_data(1): plaintext: test
Debug encrypt_data(2): bytetext: b'test'
Debug encrypt_data(3): padded plaintext: b'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
Debug encrypt_data(4): ct_bytes: b'=\xf6\xa4\xcetfD\xe4\x12\xef\xff\xce\x0f(]t'
Debug encrypt_data(5): b64 decoded ct: PfakznRmROQS7//ODyhddA==
Debug encrypt_data(6): iv: w/FfW0RCcLqv8FjqRtCpxg==
Debug encrypt_data(7): ciphertext: PfakznRmROQS7//ODyhddA==
Encripting done
Debug decrypt_data(1): iv: +00X7cdUZm7L61ifRb9EDQ==
Debug decrypt_data(2): iv variable type: <class 'str'>
Debug decrypt_data(3): iv_bytes: b'+00X7cdUZm7L61ifRb9EDQ=='
Debug decrypt_data(4): iv_bytes type: <class 'bytes'>
Traceback (most recent call last):
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 151, in <module>
print(f'Debug main(5):\t\tdecrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}') # debug text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 107, in decrypt_data
pt = unpad(cipher.decrypt(bytes(ct, 'utf-8')), AES.block_size)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Utente\AppData\Local\Programs\Python\Python312\Lib\site-packages\Crypto\Cipher\_mode_cbc.py", line 246, in decrypt
raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode
[processo terminato con codice 1 (0x00000001)]
I tried padding before decrypting using pt = unpad(cipher.decrypt(pad(bytes(ct, 'utf-8'), AES.block_size)), AES.block_size)
instead of pt = unpad(cipher.decrypt(pad(bytes(ct, 'utf-8'), AES.block_size)), AES.block_size)
but it gave this error:
Traceback (most recent call last):
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 151, in <module>
print(f'Debug main(5):\t\tdecrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}') # debug text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 107, in decrypt_data
pt = unpad(cipher.decrypt(pad(bytes(ct, 'utf-8'), AES.block_size)), AES.block_size)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Utente\AppData\Local\Programs\Python\Python312\Lib\site-packages\Crypto\Util\Padding.py", line 92, in unpad
raise ValueError("Padding is incorrect.")
ValueError: Padding is incorrect.
[processo terminato con codice 1 (0x00000001)]
Here's a simplified and functioning version of your code that, for simplicity, accepts base64 encoded plain-text strings as key and IV material (these are easy to store in e.g. text files).
Do note that CBC mode is probably not what you want since it doesn't provide any authentication (i.e. that you're getting back the text that was originally encrypted).
import base64
import secrets
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
def encrypt_data(key_str: str, plaintext: str) -> tuple[str, str]:
key_bytes = base64.b64decode(key_str.encode("utf-8"))
cipher = AES.new(key_bytes, AES.MODE_CBC)
ct_bytes = cipher.encrypt(pad(plaintext.encode("utf-8"), AES.block_size))
iv = base64.b64encode(cipher.iv).decode("utf-8")
ciphertext = base64.b64encode(ct_bytes).decode("utf-8")
return ciphertext, iv
def decrypt_data(key_str: str, iv_str: str, ciphertext_str: str) -> str:
key_bytes = base64.b64decode(key_str.encode("utf-8"))
iv_bytes = base64.b64decode(iv_str.encode("utf-8"))
ciphertext_bytes = base64.b64decode(ciphertext_str.encode("utf-8"))
cipher = AES.new(key_bytes, AES.MODE_CBC, iv=iv_bytes)
plaintext_bytes = unpad(cipher.decrypt(ciphertext_bytes), AES.block_size)
return plaintext_bytes.decode("UTF-8")
key_bytes = secrets.token_bytes(32)
key_str = base64.b64encode(key_bytes).decode("utf-8")
plaintext = "Hello, World!"
print(f"{key_str=}, {plaintext=}")
ciphertext, iv = encrypt_data(key_str, plaintext)
print(f"{ciphertext=}, {iv=}")
decrypted = decrypt_data(key_str, iv, ciphertext)
print(f"{decrypted=}")
This prints out (e.g.)
key_str='Zdu1DUQC/dZhGC8Q1n0raD8BlW7pfzLZxwmPqn0o6s8=', plaintext='Hello, World!'
ciphertext='Es7w8eWcRHGhfpwBDnr6vQ==', iv='Grf/VFkH8Qp7VS6RVj2e2g=='
decrypted='Hello, World!'