Files
adaptive-multi-protocol-gat…/ampg/bip340.py
T

93 lines
2.9 KiB
Python

from __future__ import annotations
import hashlib
SECP256K1_FIELD_SIZE = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
SECP256K1_GENERATOR = (
0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8,
)
Point = tuple[int, int] | None
def tagged_hash(tag: str, value: bytes) -> bytes:
tag_hash = hashlib.sha256(tag.encode("utf-8")).digest()
return hashlib.sha256(tag_hash + tag_hash + value).digest()
def bip340_verify(public_key: bytes, message: bytes, signature: bytes) -> bool:
"""Verify a BIP-340 signature over the 32-byte AMPG contract digest."""
if len(public_key) != 32 or len(message) != 32 or len(signature) != 64:
return False
point = _lift_x(int.from_bytes(public_key, "big"))
if point is None:
return False
r = int.from_bytes(signature[:32], "big")
s = int.from_bytes(signature[32:], "big")
if r >= SECP256K1_FIELD_SIZE or s >= SECP256K1_ORDER:
return False
challenge = int.from_bytes(
tagged_hash("BIP0340/challenge", signature[:32] + public_key + message), "big"
) % SECP256K1_ORDER
result = point_add(
point_mul(s, SECP256K1_GENERATOR),
point_mul(SECP256K1_ORDER - challenge, point),
)
return result is not None and result[1] % 2 == 0 and result[0] == r
def _lift_x(x: int) -> Point:
if x >= SECP256K1_FIELD_SIZE:
return None
y_sq = (pow(x, 3, SECP256K1_FIELD_SIZE) + 7) % SECP256K1_FIELD_SIZE
y = pow(y_sq, (SECP256K1_FIELD_SIZE + 1) // 4, SECP256K1_FIELD_SIZE)
if pow(y, 2, SECP256K1_FIELD_SIZE) != y_sq:
return None
return x, y if y % 2 == 0 else SECP256K1_FIELD_SIZE - y
def point_add(left: Point, right: Point) -> Point:
if left is None:
return right
if right is None:
return left
x1, y1 = left
x2, y2 = right
if x1 == x2 and y1 != y2:
return None
if left == right:
if y1 == 0:
return None
slope = (
(3 * x1 * x1)
* pow(2 * y1, SECP256K1_FIELD_SIZE - 2, SECP256K1_FIELD_SIZE)
% SECP256K1_FIELD_SIZE
)
else:
slope = (
(y2 - y1)
* pow(
(x2 - x1) % SECP256K1_FIELD_SIZE,
SECP256K1_FIELD_SIZE - 2,
SECP256K1_FIELD_SIZE,
)
% SECP256K1_FIELD_SIZE
)
x3 = (slope * slope - x1 - x2) % SECP256K1_FIELD_SIZE
y3 = (slope * (x1 - x3) - y1) % SECP256K1_FIELD_SIZE
return x3, y3
def point_mul(scalar: int, point: Point) -> Point:
result: Point = None
addend = point
while scalar:
if scalar & 1:
result = point_add(result, addend)
addend = point_add(addend, addend)
scalar >>= 1
return result