refactor: consolidate blockchain explorer into single app and update backup ignore patterns
- Remove standalone explorer-web app (README, HTML, package files) - Add /web endpoint to blockchain-explorer for web interface access - Update .gitignore to exclude application backup archives (*.tar.gz, *.zip) - Add backup documentation files to .gitignore (BACKUP_INDEX.md, README.md) - Consolidate explorer functionality into main blockchain-explorer application
This commit is contained in:
66
apps/wallet/src/app/crypto/encryption.py
Normal file
66
apps/wallet/src/app/crypto/encryption.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from argon2.low_level import Type as Argon2Type, hash_secret_raw
|
||||
from nacl.bindings import (
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt,
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt,
|
||||
)
|
||||
|
||||
from ..security import wipe_buffer
|
||||
|
||||
|
||||
class EncryptionError(Exception):
|
||||
"""Raised when encryption or decryption fails."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class EncryptionSuite:
|
||||
"""Argon2id + XChaCha20-Poly1305 helper functions."""
|
||||
|
||||
salt_bytes: int = 16
|
||||
nonce_bytes: int = 24
|
||||
key_bytes: int = 32
|
||||
argon_time_cost: int = 3
|
||||
argon_memory_cost: int = 64 * 1024 # kibibytes
|
||||
argon_parallelism: int = 2
|
||||
|
||||
def _derive_key(self, *, password: str, salt: bytes) -> bytes:
|
||||
password_bytes = password.encode("utf-8")
|
||||
return hash_secret_raw(
|
||||
secret=password_bytes,
|
||||
salt=salt,
|
||||
time_cost=self.argon_time_cost,
|
||||
memory_cost=self.argon_memory_cost,
|
||||
parallelism=self.argon_parallelism,
|
||||
hash_len=self.key_bytes,
|
||||
type=Argon2Type.ID,
|
||||
)
|
||||
|
||||
def encrypt(self, *, password: str, plaintext: bytes, salt: bytes, nonce: bytes) -> bytes:
|
||||
key = self._derive_key(password=password, salt=salt)
|
||||
try:
|
||||
return crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
message=plaintext,
|
||||
aad=b"",
|
||||
nonce=nonce,
|
||||
key=key,
|
||||
)
|
||||
except Exception as exc: # pragma: no cover
|
||||
raise EncryptionError("encryption failed") from exc
|
||||
|
||||
def decrypt(self, *, password: str, ciphertext: bytes, salt: bytes, nonce: bytes) -> bytes:
|
||||
key_bytes = bytearray(self._derive_key(password=password, salt=salt))
|
||||
try:
|
||||
return crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
ciphertext=ciphertext,
|
||||
aad=b"",
|
||||
nonce=nonce,
|
||||
key=bytes(key_bytes),
|
||||
)
|
||||
except Exception as exc:
|
||||
raise EncryptionError("decryption failed") from exc
|
||||
finally:
|
||||
wipe_buffer(key_bytes)
|
||||
Reference in New Issue
Block a user