from app.core._utils.b58 import b58encode, b58decode try: import nacl.encoding import nacl.signing import nacl.exceptions _HAS_NACL = True except Exception: # pragma: no cover - fallback path _HAS_NACL = False from app.core._utils.hash import blake3_digest if _HAS_NACL: class Signer: def __init__(self, seed: bytes): if len(seed) != 32: raise ValueError("Seed must be 32 bytes") self.signing_key = nacl.signing.SigningKey(seed) self.verify_key = self.signing_key.verify_key def sign(self, data_bytes: bytes) -> str: signed_message = self.signing_key.sign(data_bytes) signature = signed_message.signature return b58encode(signature).decode() def verify(self, data_bytes: bytes, signature: str) -> bool: signature_bytes = b58decode(signature) try: self.verify_key.verify(data_bytes, signature_bytes) return True except nacl.exceptions.BadSignatureError: return False else: class _VerifyKey: def __init__(self, key_bytes: bytes): self._key_bytes = key_bytes def encode(self) -> bytes: return self._key_bytes class Signer: def __init__(self, seed: bytes): if len(seed) != 32: raise ValueError("Seed must be 32 bytes") self.seed = seed self.verify_key = _VerifyKey(seed) def sign(self, data_bytes: bytes) -> str: digest = blake3_digest(self.seed + data_bytes) return b58encode(digest).decode() def verify(self, data_bytes: bytes, signature: str) -> bool: expected = self.sign(data_bytes) return expected == signature