SPB Git

spb/wp34_uqo

Public
3.2 KB · 96 lines python
1"""BIP340 Schnorr signatures over secp256k1 — reference implementation following the BIP text, exact arithmetic."""2from __future__ import annotations3import hashlib, random4from . import secp256k1 as k15from .ec import Curve, sqrt_mod67E: Curve = k1.CURVE8n, p = k1.n, k1.p91011def tagged_hash(tag: str, msg: bytes) -> bytes:12    th = hashlib.sha256(tag.encode()).digest()13    return hashlib.sha256(th + th + msg).digest()141516def bytes_from_int(x: int) -> bytes:17    return x.to_bytes(32, "big")181920def int_from_bytes(b: bytes) -> int:21    return int.from_bytes(b, "big")222324def has_even_y(P) -> bool:25    return P is not None and P[1] % 2 == 0262728def lift_x(x: int):29    if x >= p:30        return None31    c = (pow(x, 3, p) + 7) % p32    y = pow(c, (p + 1) // 4, p)33    if y * y % p != c:34        return None35    return (x, y if y % 2 == 0 else p - y)363738def pubkey_gen(seckey: bytes) -> bytes:39    d0 = int_from_bytes(seckey)40    if not (1 <= d0 <= n - 1):41        raise ValueError("secret key out of range")42    P = E.mul(d0, E.G)43    return bytes_from_int(P[0])444546def sign(msg: bytes, seckey: bytes, aux_rand: bytes) -> bytes:47    d0 = int_from_bytes(seckey)48    if not (1 <= d0 <= n - 1):49        raise ValueError("secret key out of range")50    P = E.mul(d0, E.G)51    d = d0 if has_even_y(P) else n - d052    t = bytes_from_int(d ^ int_from_bytes(tagged_hash("BIP0340/aux", aux_rand)))53    k0 = int_from_bytes(tagged_hash("BIP0340/nonce", t + bytes_from_int(P[0]) + msg)) % n54    if k0 == 0:55        raise RuntimeError("nonce is zero")56    R = E.mul(k0, E.G)57    k = n - k0 if not has_even_y(R) else k058    e = int_from_bytes(tagged_hash("BIP0340/challenge", bytes_from_int(R[0]) + bytes_from_int(P[0]) + msg)) % n59    sig = bytes_from_int(R[0]) + bytes_from_int((k + e * d) % n)60    if not verify(msg, bytes_from_int(P[0]), sig):61        raise RuntimeError("signature failed self-check")62    return sig636465def verify(msg: bytes, pubkey: bytes, sig: bytes) -> bool:66    if len(pubkey) != 32 or len(sig) != 64:67        return False68    P = lift_x(int_from_bytes(pubkey))69    r = int_from_bytes(sig[0:32])70    s = int_from_bytes(sig[32:64])71    if P is None or r >= p or s >= n:72        return False73    e = int_from_bytes(tagged_hash("BIP0340/challenge", sig[0:32] + pubkey + msg)) % n74    R = E.add(E.mul(s, E.G), E.mul(n - e, P))75    return R is not None and has_even_y(R) and R[0] == r767778def batch_verify(items: list[tuple[bytes, bytes, bytes]], rng: random.Random | None = None) -> bool:79    """Batch verification: Σ a_i s_i G = Σ a_i R_i + Σ a_i e_i P_i with random 128-bit weights (a_1 = 1)."""80    rng = rng or random.SystemRandom()81    S = 082    rhs = None83    for i, (msg, pubkey, sig) in enumerate(items):84        P = lift_x(int_from_bytes(pubkey))85        r, s = int_from_bytes(sig[:32]), int_from_bytes(sig[32:])86        if P is None or r >= p or s >= n:87            return False88        R = lift_x(r)89        if R is None:90            return False91        e = int_from_bytes(tagged_hash("BIP0340/challenge", sig[:32] + pubkey + msg)) % n92        ai = 1 if i == 0 else rng.getrandbits(128)93        S = (S + ai * s) % n94        rhs = E.add(rhs, E.add(E.mul(ai, R), E.mul(ai * e % n, P)))95    return E.mul(S, E.G) == rhs96
Files · main
Menu

Theme