diff --git a/internal_filesystem/lib/secp256k1.py b/internal_filesystem/lib/secp256k1.py index faea1730..d9233fa2 100644 --- a/internal_filesystem/lib/secp256k1.py +++ b/internal_filesystem/lib/secp256k1.py @@ -2,10 +2,8 @@ import os import hashlib import binascii -#from ._libsecp256k1 import ffi, lib from secp256k1_compat import ffi, lib # Use compatibility layer - EC_COMPRESSED = lib.SECP256K1_EC_COMPRESSED EC_UNCOMPRESSED = lib.SECP256K1_EC_UNCOMPRESSED @@ -273,7 +271,7 @@ class PrivateKey(ECDSA): else: if raw: if not isinstance(privkey, bytes) or len(privkey) != 32: - raise TypeError('privkey must be composed of 32 bytes') + raise TypeError(f'privkey must be composed of 32 bytes: {privkey} and {self.private_key} and {len(privkey)}') self.set_raw_privkey(privkey) else: self.deserialize(privkey) diff --git a/internal_filesystem/lib/secp256k1_compat.py b/internal_filesystem/lib/secp256k1_compat.py index e861816c..ac598c9c 100644 --- a/internal_filesystem/lib/secp256k1_compat.py +++ b/internal_filesystem/lib/secp256k1_compat.py @@ -1,7 +1,6 @@ # secp256k1_compat.py: Compatibility layer for secp256k1.py to use MicroPython's usecp256k1 module -# Mimics cffi's ffi and lib objects -import usecp256k1 # Your MicroPython C module +import usecp256k1 # Constants (from libsecp256k1) SECP256K1_CONTEXT_SIGN = 1 << 8 # 256 @@ -11,52 +10,74 @@ SECP256K1_EC_UNCOMPRESSED = 0 # Dummy ffi class to mimic cffi class FFI: + NULL = None # Mimic cffi's NULL pointer + def new(self, type_str): - # For 'unsigned char[74]' or 'size_t *' - if 'unsigned char' in type_str: + if 'char' in type_str: size = int(type_str.split('[')[1].rstrip(']')) return bytearray(size) elif 'size_t *' in type_str: - return [0] # Simulate pointer to size_t + return [0] raise ValueError(f"Unsupported ffi type: {type_str}") def buffer(self, obj, size=None): - # Return bytes from bytearray or slice if isinstance(obj, list): return bytes(obj) return bytes(obj[:size] if size is not None else obj) + def memmove(self, dst, src, n): + if isinstance(src, bytes): + src = bytearray(src) + dst[:n] = src[:n] + + def callback(self, signature): + # Simplified decorator to mark functions without setting attributes + def decorator(func): + return func # Return func as-is + return decorator + # Dummy lib class to map to usecp256k1 functions class Lib: - # Constants SECP256K1_EC_COMPRESSED = SECP256K1_EC_COMPRESSED SECP256K1_EC_UNCOMPRESSED = SECP256K1_EC_UNCOMPRESSED SECP256K1_CONTEXT_SIGN = SECP256K1_CONTEXT_SIGN SECP256K1_CONTEXT_VERIFY = SECP256K1_CONTEXT_VERIFY def secp256k1_context_create(self, flags): - # Context is managed by usecp256k1, return dummy object return object() def secp256k1_ecdsa_signature_serialize_der(self, ctx, output, outputlen, raw_sig): - # Call usecp256k1_ecdsa_signature_serialize_der try: result = usecp256k1.usecp256k1_ecdsa_signature_serialize_der(raw_sig) if result is None: - return 0 # Mimic libsecp256k1 failure - # Copy result to output buffer + return 0 output[:len(result)] = result outputlen[0] = len(result) - return 1 # Mimic libsecp256k1 success + return 1 except ValueError: - return 0 # Handle errors like invalid signature length + return 0 + + def secp256k1_ecdh(self, ctx, output, pubkey, seckey, hashfn=FFI.NULL, hasharg=FFI.NULL): + try: + result = usecp256k1.usecp256k1_ecdh(pubkey, seckey) + if result is None: + return 0 + output[:32] = result + return 1 + except ValueError: + return 0 # Instantiate ffi and lib ffi = FFI() lib = Lib() -# Feature flags (set based on your usecp256k1 module's capabilities) +# Feature flags HAS_RECOVERABLE = hasattr(usecp256k1, 'usecp256k1_ecdsa_sign_recoverable') HAS_SCHNORR = hasattr(usecp256k1, 'usecp256k1_schnorrsig_sign') HAS_ECDH = hasattr(usecp256k1, 'usecp256k1_ecdh') HAS_EXTRAKEYS = hasattr(usecp256k1, 'usecp256k1_keypair_create') + +# Define copy_x for ECDH +def copy_x(output, x32, y32, data): + ffi.memmove(output, x32, 32) + return 1 diff --git a/internal_filesystem/lib/secrets.py b/internal_filesystem/lib/secrets.py index 29cf7e11..24bc87bf 100644 --- a/internal_filesystem/lib/secrets.py +++ b/internal_filesystem/lib/secrets.py @@ -1,5 +1,3 @@ -# By PiggyOS - # secrets.py: Compatibility layer for CPython's secrets module in MicroPython # Uses urandom for cryptographically secure randomness # Implements SystemRandom, choice, randbelow, randbits, token_bytes, token_hex, @@ -38,7 +36,8 @@ class SystemRandom: def _getrandbytes(self, n): """Return n random bytes.""" - return bytearray(urandom.getrandbits(8) for _ in range(n)) + # Use bytes directly for compatibility with CPython secrets + return bytes(urandom.getrandbits(8) for _ in range(n)) def choice(self, seq): """Return a randomly chosen element from a non-empty sequence.""" @@ -86,17 +85,12 @@ def token_urlsafe(nbytes=None): nbytes = 32 if nbytes < 0: raise ValueError("number of bytes must be non-negative") - # Base64 encoding: 4 chars per 3 bytes, so we need ceil(nbytes * 4/3) chars - # Generate enough bytes to ensure we have at least nbytes after encoding raw_bytes = token_bytes(nbytes) - # Use URL-safe base64 encoding (replaces '+' with '-', '/' with '_') encoded = ubinascii.b2a_base64(raw_bytes).decode().rstrip('\n=') - # Ensure length corresponds to nbytes (truncate if needed) return encoded[:int(nbytes * 4 / 3)] def compare_digest(a, b): """Return True if a and b are equal in constant time, else False.""" - # Convert to bytes if strings if isinstance(a, str): a = a.encode() if isinstance(b, str): @@ -105,10 +99,7 @@ def compare_digest(a, b): raise TypeError("both inputs must be bytes-like or strings") if len(a) != len(b): return False - # Constant-time comparison to prevent timing attacks result = 0 for x, y in zip(a, b): result |= x ^ y return result == 0 - -