45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
|
|
"""
|
||
|
|
AES-256-GCM Cookie encryption / decryption utilities.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import base64
|
||
|
|
import hashlib
|
||
|
|
|
||
|
|
from Crypto.Cipher import AES
|
||
|
|
|
||
|
|
|
||
|
|
def derive_key(raw_key: str) -> bytes:
|
||
|
|
"""Derive a 32-byte key from an arbitrary string using SHA-256."""
|
||
|
|
return hashlib.sha256(raw_key.encode("utf-8")).digest()
|
||
|
|
|
||
|
|
|
||
|
|
def encrypt_cookie(plaintext: str, key: bytes) -> tuple[str, str]:
|
||
|
|
"""
|
||
|
|
Encrypt a cookie string with AES-256-GCM.
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
(ciphertext_b64, iv_b64) — both base64-encoded strings.
|
||
|
|
"""
|
||
|
|
cipher = AES.new(key, AES.MODE_GCM)
|
||
|
|
ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode("utf-8"))
|
||
|
|
# Append the 16-byte tag to the ciphertext so decryption can verify it
|
||
|
|
ciphertext_with_tag = ciphertext + tag
|
||
|
|
ciphertext_b64 = base64.b64encode(ciphertext_with_tag).decode("utf-8")
|
||
|
|
iv_b64 = base64.b64encode(cipher.nonce).decode("utf-8")
|
||
|
|
return ciphertext_b64, iv_b64
|
||
|
|
|
||
|
|
|
||
|
|
def decrypt_cookie(ciphertext_b64: str, iv_b64: str, key: bytes) -> str:
|
||
|
|
"""
|
||
|
|
Decrypt a cookie string previously encrypted with encrypt_cookie.
|
||
|
|
|
||
|
|
Raises ValueError on decryption failure (wrong key, corrupted data, etc.).
|
||
|
|
"""
|
||
|
|
raw = base64.b64decode(ciphertext_b64)
|
||
|
|
nonce = base64.b64decode(iv_b64)
|
||
|
|
# Last 16 bytes are the GCM tag
|
||
|
|
ciphertext, tag = raw[:-16], raw[-16:]
|
||
|
|
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
|
||
|
|
plaintext = cipher.decrypt_and_verify(ciphertext, tag)
|
||
|
|
return plaintext.decode("utf-8")
|