SPB Git

spb/wp34_uqo

Public
3.0 KB · 99 lines python
1"""ECDSA over secp256k1 (SEC 1 v2.0 §4.1) with RFC 6979 deterministic nonces, pure Python, exact arithmetic."""2from __future__ import annotations3import hashlib, hmac4from . import secp256k1 as k15from .ec import Curve67E: Curve = k1.CURVE8n = k1.n91011def bits2int(b: bytes, qlen: int = 256) -> int:12    z = int.from_bytes(b, "big")13    blen = len(b) * 814    return z >> (blen - qlen) if blen > qlen else z151617def rfc6979_nonce(d: int, h1: bytes) -> int:18    """Deterministic k per RFC 6979 §3.2 with HMAC-SHA256 (qlen = 256)."""19    x = d.to_bytes(32, "big")20    hz = (bits2int(h1) % n).to_bytes(32, "big")21    V = b"\x01" * 3222    Kk = b"\x00" * 3223    Kk = hmac.new(Kk, V + b"\x00" + x + hz, hashlib.sha256).digest()24    V = hmac.new(Kk, V, hashlib.sha256).digest()25    Kk = hmac.new(Kk, V + b"\x01" + x + hz, hashlib.sha256).digest()26    V = hmac.new(Kk, V, hashlib.sha256).digest()27    while True:28        V = hmac.new(Kk, V, hashlib.sha256).digest()29        k = bits2int(V)30        if 1 <= k < n:31            return k32        Kk = hmac.new(Kk, V + b"\x00", hashlib.sha256).digest()33        V = hmac.new(Kk, V, hashlib.sha256).digest()343536def sign(d: int, msg_hash: bytes, k: int | None = None, low_s: bool = True) -> tuple[int, int]:37    z = bits2int(msg_hash) % n38    if k is None:39        k = rfc6979_nonce(d, msg_hash)40    R = E.mul(k, E.G)41    r = R[0] % n42    s = pow(k, -1, n) * (z + r * d) % n43    if r == 0 or s == 0:44        raise ValueError("degenerate signature; choose another nonce")45    if low_s and s > n // 2:46        s = n - s47    return r, s484950def verify(Q, msg_hash: bytes, sig: tuple[int, int]) -> bool:51    r, s = sig52    if not (1 <= r < n and 1 <= s < n) or Q is None or not E.is_on_curve(Q):53        return False54    z = bits2int(msg_hash) % n55    w = pow(s, -1, n)56    u1, u2 = z * w % n, r * w % n57    X = E.add(E.mul(u1, E.G), E.mul(u2, Q))58    return X is not None and X[0] % n == r596061def verify_shamir(Q, msg_hash: bytes, sig: tuple[int, int]) -> bool:62    """Same predicate using Shamir's trick (simultaneous double scalar multiplication)."""63    r, s = sig64    if not (1 <= r < n and 1 <= s < n) or Q is None:65        return False66    z = bits2int(msg_hash) % n67    w = pow(s, -1, n)68    u1, u2 = z * w % n, r * w % n69    X = shamir(u1, E.G, u2, Q)70    return X is not None and X[0] % n == r717273def shamir(u1: int, P, u2: int, Q):74    GQ = E.add(P, Q)75    R = None76    for i in range(max(u1.bit_length(), u2.bit_length()) - 1, -1, -1):77        R = E.double(R)78        b1, b2 = (u1 >> i) & 1, (u2 >> i) & 179        if b1 and b2:80            R = E.add(R, GQ)81        elif b1:82            R = E.add(R, P)83        elif b2:84            R = E.add(R, Q)85    return R868788def pubkey(d: int):89    return E.mul(d, E.G)909192def recover_private_key_from_nonce_reuse(sig1, sig2, z1: int, z2: int) -> int:93    """Textbook nonce-reuse recovery on synthetic signatures (r1 == r2): d = (s1 z2 − s2 z1)/(r (s2 − s1))."""94    r, s1 = sig195    r2, s2 = sig296    assert r == r297    k = (z1 - z2) * pow(s1 - s2, -1, n) % n98    return (s1 * k - z1) * pow(r, -1, n) % n99
Files · main
Menu

Theme