You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

63 lines
1.9 KiB
Python

#!/usr/bin/env python
import json
import unpaddedbase64
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto.Util import Counter
class EncryptionError(Exception):
pass
def decrypt(ciphertext: bytes, key: str, hash: str, iv: str):
"""Decrypt an encrypted attachment.
Args:
ciphertext (bytes): The data to decrypt.
key (str): AES_CTR JWK key object.
hash (str): Base64 encoded SHA-256 hash of the ciphertext.
iv (str): Base64 encoded 16 byte AES-CTR IV.
Returns:
The plaintext bytes.
Raises:
EncryptionError if the integrity check fails.
"""
expected_hash = unpaddedbase64.decode_base64(hash)
h = SHA256.new()
h.update(ciphertext)
if h.digest() != expected_hash:
raise EncryptionError("Mismatched SHA-256 digest.")
try:
byte_key: bytes = unpaddedbase64.decode_base64(key)
except (BinAsciiError, TypeError):
raise EncryptionError("Error decoding key.")
try:
# Drop last 8 bytes, which are 0
byte_iv: bytes = unpaddedbase64.decode_base64(iv)[:8]
except (BinAsciiError, TypeError):
raise EncryptionError("Error decoding initial values.")
ctr = Counter.new(64, prefix=byte_iv, initial_value=0)
try:
cipher = AES.new(byte_key, AES.MODE_CTR, counter=ctr)
except ValueError as e:
raise EncryptionError(e)
return cipher.decrypt(ciphertext)
# if __name__ == "__main__":
# with open('images/output', 'wb') as output:
# with open('images/LUJAssHxtTWsnYPbSlTcMdvl.octet-stream', 'rb') as cipher:
# with open('images/LUJAssHxtTWsnYPbSlTcMdvl.metadata', 'r') as rawmeta:
# meta = json.load(rawmeta)
# key = meta['file']['key']
# decrypted = decrypt_attachment(cipher.read(), key['k'], meta['file']['hashes']['sha256'], meta['file']['iv'])
# output.write(decrypted)