work towards fixing

This commit is contained in:
Thomas Farstrike
2025-05-19 11:21:54 +02:00
parent c7181b4900
commit e1c9c51955
3 changed files with 38 additions and 28 deletions
+1 -3
View File
@@ -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)
+35 -14
View File
@@ -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
+2 -11
View File
@@ -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