Avoid using PyCryptodome for AES-GCM decryption of file contents

This removes all PyCryptodome dependencies
This commit is contained in:
RunasSudo 2024-05-27 22:23:29 +10:00
parent 2af1d5385b
commit 8aa4c8dc48
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A

View File

@ -17,7 +17,10 @@
from .aes import aes_siv_decrypt, aes_siv_encrypt
from .b64url import b64url_decode, b64url_encode
from Crypto.Cipher import AES
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import GCM
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
from cryptography.hazmat.primitives.keywrap import aes_key_unwrap, InvalidUnwrap
@ -228,8 +231,7 @@ def decrypt_file(vault_path, primary_master_key, hashed_directory_id, filename):
header_tag = ciphertext_header[-16:]
# Decrypt header to obtain the content key
cipher = AES.new(primary_master_key, AES.MODE_GCM, nonce=header_nonce)
plaintext_header = cipher.decrypt_and_verify(header_payload, header_tag)
plaintext_header = AESGCM(primary_master_key).decrypt(header_nonce, header_payload + header_tag, None)
content_key = plaintext_header[8:]
# Decrypt file in chunks corresponding to 32 KiB plaintext
@ -241,15 +243,13 @@ def decrypt_file(vault_path, primary_master_key, hashed_directory_id, filename):
chunk_payload = ciphertext_chunk[12:-16]
chunk_tag = ciphertext_chunk[-16:]
# Chunk is encrypted with AES-GCM
cipher = AES.new(content_key, AES.MODE_GCM, nonce=chunk_nonce)
# Chunk is encrypted with AES-GCM - chunk number and header nonce are passed as AAD
# cryptography.hazmat.primitives.ciphers.aead.AESGCM does not support multiple AAD so must do this manually
cipher = Cipher(AES(content_key), GCM(chunk_nonce, chunk_tag)).decryptor()
cipher.authenticate_additional_data(struct.pack('>Q', chunk_num))
cipher.authenticate_additional_data(header_nonce)
plaintext_chunk = cipher.update(chunk_payload) + cipher.finalize()
# Chunk number and header nonce are passed as AAD
# Contrary to the Cryptomator documentation, chunk number is a 64-bit not 32-bit integer - https://github.com/cryptomator/docs/pull/54
cipher.update(struct.pack('>Q', chunk_num))
cipher.update(header_nonce)
plaintext_chunk = cipher.decrypt_and_verify(chunk_payload, chunk_tag)
plaintext.extend(plaintext_chunk)
return bytes(plaintext)