"""EdDSA — Ed25519 signing and verification. Ported from net.i2p.crypto.eddsa.EdDSAEngine. Wraps Python's cryptography library for Ed25519 operations. """ from cryptography.hazmat.primitives.asymmetric.ed25519 import ( Ed25519PrivateKey, Ed25519PublicKey, ) from cryptography.exceptions import InvalidSignature class EdDSAEngine: """Ed25519 signature engine. Provides sign/verify operations using Ed25519-SHA-512 (RFC 8032). Uses one-shot mode internally (avoids the double-hashing issue of the Java standard Signature API). """ SIGNATURE_LENGTH = 64 PUBLIC_KEY_LENGTH = 32 PRIVATE_KEY_LENGTH = 32 @staticmethod def sign(data: bytes, private_key: bytes, offset: int = 0, length: int = -1) -> bytes: """Sign data with Ed25519 private key. Args: data: data to sign private_key: 32-byte Ed25519 private key (seed) offset: start offset in data length: number of bytes to sign (-1 for all from offset) Returns: 64-byte signature """ if length < 0: length = len(data) - offset key = Ed25519PrivateKey.from_private_bytes(private_key[:32]) return key.sign(data[offset : offset + length]) @staticmethod def verify(data: bytes, signature: bytes, public_key: bytes, offset: int = 0, length: int = -1) -> bool: """Verify an Ed25519 signature. Args: data: data that was signed signature: 64-byte signature public_key: 32-byte Ed25519 public key offset: start offset in data length: number of bytes that were signed Returns: True if signature is valid. """ if length < 0: length = len(data) - offset try: key = Ed25519PublicKey.from_public_bytes(public_key[:32]) key.verify(signature, data[offset : offset + length]) return True except (InvalidSignature, ValueError): return False # Aliases matching Java API sign_one_shot = sign verify_one_shot = verify class EdDSAKeyPair: """Ed25519 key pair generation and management.""" __slots__ = ("_private_key", "_public_key", "_private_bytes", "_public_bytes") def __init__(self, private_key: Ed25519PrivateKey) -> None: self._private_key = private_key self._public_key = private_key.public_key() self._private_bytes = private_key.private_bytes_raw() self._public_bytes = self._public_key.public_bytes_raw() @classmethod def generate(cls) -> "EdDSAKeyPair": """Generate a new Ed25519 key pair.""" return cls(Ed25519PrivateKey.generate()) @classmethod def from_private_bytes(cls, seed: bytes) -> "EdDSAKeyPair": """Create key pair from 32-byte private key seed.""" return cls(Ed25519PrivateKey.from_private_bytes(seed[:32])) @property def private_key(self) -> bytes: """32-byte private key seed.""" return self._private_bytes @property def public_key(self) -> bytes: """32-byte public key.""" return self._public_bytes def sign(self, data: bytes) -> bytes: """Sign data, returns 64-byte signature.""" return self._private_key.sign(data) def verify(self, data: bytes, signature: bytes) -> bool: """Verify signature against public key.""" try: self._public_key.verify(signature, data) return True except (InvalidSignature, ValueError): return False def public_key_from_private(private_key: bytes) -> bytes: """Derive 32-byte public key from 32-byte private key seed.""" key = Ed25519PrivateKey.from_private_bytes(private_key[:32]) return key.public_key().public_bytes_raw()