28 lines
952 B
Python
28 lines
952 B
Python
|
|
"""AES-256-GCM helpers matching static/js/app.js's encryptBytes/decryptBytes
|
||
|
|
(Web Crypto AES-GCM, 12-byte IV hex-encoded, ciphertext base64, key handled
|
||
|
|
as raw bytes) — diora's books are end-to-end encrypted client-side, so the
|
||
|
|
server (and this module) only ever sees ciphertext plus the key the user
|
||
|
|
supplies out-of-band.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import base64
|
||
|
|
|
||
|
|
from cryptography.exceptions import InvalidTag
|
||
|
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||
|
|
|
||
|
|
|
||
|
|
class DecryptError(Exception):
|
||
|
|
"""Ciphertext could not be decrypted with the given key (wrong key, or corrupt data)."""
|
||
|
|
|
||
|
|
|
||
|
|
def decrypt(key_b64: str, iv_hex: str, ciphertext_b64: str) -> bytes:
|
||
|
|
try:
|
||
|
|
key = base64.b64decode(key_b64)
|
||
|
|
iv = bytes.fromhex(iv_hex)
|
||
|
|
ct = base64.b64decode(ciphertext_b64)
|
||
|
|
return AESGCM(key).decrypt(iv, ct, None)
|
||
|
|
except (InvalidTag, ValueError) as e:
|
||
|
|
raise DecryptError(str(e)) from e
|