From 6bc8fe70f6f584aa36b53262ab037d95cfb585c1 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 13 Jul 2025 10:47:45 -0400 Subject: [PATCH] Add optional orjson support --- requirements.lock | 1 + src/password_manager/encryption.py | 29 ++++++++++++++++++++---- src/password_manager/entry_management.py | 16 ++++++++++--- src/requirements.txt | 1 + 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/requirements.lock b/requirements.lock index 0318410..f762a5c 100644 --- a/requirements.lock +++ b/requirements.lock @@ -32,6 +32,7 @@ monero==1.1.1 multidict==6.6.3 mutmut==2.4.4 nostr-sdk==0.42.1 +orjson==3.10.18 packaging==25.0 parso==0.8.4 pgpy==0.6.0 diff --git a/src/password_manager/encryption.py b/src/password_manager/encryption.py index 6bcdc07..8c1f8de 100644 --- a/src/password_manager/encryption.py +++ b/src/password_manager/encryption.py @@ -2,7 +2,17 @@ import logging import traceback -import json + +try: + import orjson as json_lib # type: ignore + + JSONDecodeError = orjson.JSONDecodeError + USE_ORJSON = True +except Exception: # pragma: no cover - fallback for environments without orjson + import json as json_lib + from json import JSONDecodeError + + USE_ORJSON = False import hashlib import os import base64 @@ -145,7 +155,10 @@ class EncryptionManager: def save_json_data(self, data: dict, relative_path: Optional[Path] = None) -> None: if relative_path is None: relative_path = Path("seedpass_entries_db.json.enc") - json_data = json.dumps(data, indent=4).encode("utf-8") + if USE_ORJSON: + json_data = json_lib.dumps(data) + else: + json_data = json_lib.dumps(data, separators=(",", ":")).encode("utf-8") self.encrypt_and_save_file(json_data, relative_path) logger.debug(f"JSON data encrypted and saved to '{relative_path}'.") @@ -169,7 +182,10 @@ class EncryptionManager: try: decrypted_data = self.decrypt_data(encrypted_data) - data = json.loads(decrypted_data.decode("utf-8")) + if USE_ORJSON: + data = json_lib.loads(decrypted_data) + else: + data = json_lib.loads(decrypted_data.decode("utf-8")) # If it was a legacy file, re-save it in the new format now if is_legacy: @@ -178,7 +194,7 @@ class EncryptionManager: self.update_checksum(relative_path) return data - except (InvalidToken, InvalidTag, json.JSONDecodeError) as e: + except (InvalidToken, InvalidTag, JSONDecodeError) as e: logger.error( f"FATAL: Could not decrypt or parse data from {file_path}: {e}", exc_info=True, @@ -204,7 +220,10 @@ class EncryptionManager: decrypted_data = self.decrypt_data( encrypted_data ) # This now handles both formats - data = json.loads(decrypted_data.decode("utf-8")) + if USE_ORJSON: + data = json_lib.loads(decrypted_data) + else: + data = json_lib.loads(decrypted_data.decode("utf-8")) self.save_json_data(data, relative_path) # This always saves in V2 format self.update_checksum(relative_path) logger.info("Index file from Nostr was processed and saved successfully.") diff --git a/src/password_manager/entry_management.py b/src/password_manager/entry_management.py index aad4722..a9b90ae 100644 --- a/src/password_manager/entry_management.py +++ b/src/password_manager/entry_management.py @@ -15,7 +15,14 @@ completely deterministic passwords from a BIP-85 seed, ensuring that passwords a the same way every time. Salts would break this functionality and are not suitable for this software. """ -import json +try: + import orjson as json_lib # type: ignore + + USE_ORJSON = True +except Exception: # pragma: no cover - fallback when orjson is missing + import json as json_lib + + USE_ORJSON = False import logging import hashlib import sys @@ -1155,8 +1162,11 @@ class EntryManager: """ try: data = self._load_index() - json_content = json.dumps(data, indent=4) - checksum = hashlib.sha256(json_content.encode("utf-8")).hexdigest() + if USE_ORJSON: + json_bytes = json_lib.dumps(data) + else: + json_bytes = json_lib.dumps(data, separators=(",", ":")).encode("utf-8") + checksum = hashlib.sha256(json_bytes).hexdigest() # The checksum file path already includes the fingerprint directory checksum_path = self.checksum_file diff --git a/src/requirements.txt b/src/requirements.txt index 1bfbef1..64539a2 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -30,3 +30,4 @@ uvicorn>=0.35.0 httpx>=0.28.1 requests>=2.32 python-multipart +orjson