diff --git a/h/security/encryption.py b/h/security/encryption.py index 034a743d436..9989600d587 100644 --- a/h/security/encryption.py +++ b/h/security/encryption.py @@ -1,8 +1,10 @@ import base64 +import json import os from Cryptodome.Hash import SHA512 from Cryptodome.Protocol.KDF import HKDF +from jose import jwe from passlib.context import CryptContext DEFAULT_ENTROPY = 32 @@ -69,3 +71,8 @@ def token_urlsafe(nbytes=None): nbytes = DEFAULT_ENTROPY tok = os.urandom(nbytes) return base64.urlsafe_b64encode(tok).rstrip(b"=").decode("ascii") + + +def decrypt_jwe_dict(secret: bytes, payload: str) -> dict: + """Decrypts the JWE payloads into a dictionary.""" + return json.loads(jwe.decrypt(payload, secret.ljust(32)[:32])) diff --git a/tests/h/security/encryption_test.py b/tests/h/security/encryption_test.py index cb9393f638b..add593d2c6a 100644 --- a/tests/h/security/encryption_test.py +++ b/tests/h/security/encryption_test.py @@ -6,7 +6,12 @@ from hypothesis import strategies as st from passlib.context import CryptContext -from h.security.encryption import derive_key, password_context, token_urlsafe +from h.security.encryption import ( + decrypt_jwe_dict, + derive_key, + password_context, + token_urlsafe, +) REASONABLE_INFO = st.text(alphabet=string.printable) REASONABLE_KEY_MATERIAL = st.binary(min_size=8, max_size=128) @@ -152,3 +157,24 @@ def test_token_urlsafe_no_args(): assert isinstance(tok, str) assert len(tok) > 32 + + +class TestDecryptDict: + def test_decrypt_dict(self, secret, jwe, json): + plain_text_dict = decrypt_jwe_dict(secret, "payload") + + jwe.decrypt.assert_called_once_with("payload", secret.ljust(32)) + json.loads.assert_called_once_with(jwe.decrypt.return_value) + assert plain_text_dict == json.loads.return_value + + @pytest.fixture + def secret(self): + return b"VERY SECRET" + + @pytest.fixture + def json(self, patch): + return patch("h.security.encryption.json") + + @pytest.fixture + def jwe(self, patch): + return patch("h.security.encryption.jwe")