Skip to content

Instantly share code, notes, and snippets.

@jaonoctus
Created May 23, 2025 17:29
Show Gist options
  • Save jaonoctus/baa5640ec3e6994de6fe65c7b7984493 to your computer and use it in GitHub Desktop.
Save jaonoctus/baa5640ec3e6994de6fe65c7b7984493 to your computer and use it in GitHub Desktop.
# === Curve setup: secp256k1 ===
F = FiniteField(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
E = EllipticCurve([F(0), F(7)])
G = E.lift_x(F(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798))
# --- Hash function H: SHA256 → integer mod n (BIP340 tagged hash) ---
def H_bip340_challenge(data):
import hashlib
tag = b"BIP0340/challenge"
tag_hash = hashlib.sha256(tag).digest()
h = hashlib.sha256(tag_hash + tag_hash + data).digest()
return Integer(int.from_bytes(h, 'big')) % n
# --- Helper: always lift x to point with even y ---
def lift_x_even_y(x):
P = E.lift_x(F(x))
return P if int(P.xy()[1]) % 2 == 0 else -P
# --- Keypair setup ---
k_a = randint(1, n - 1)
P_a = k_a * G
k_b = randint(1, n - 1)
P_b = k_b * G
nostr_message = b"Hello world - Nostr event content"
print("=== Adaptor Signature with Alice/Bob Setup ===")
print("Alice's private key (k_a): ", hex(k_a))
print("Alice's public key (P_a): ", f"({hex(int(P_a.xy()[0]))}, {hex(int(P_a.xy()[1]))})")
print("Bob's private key (k_b): ", hex(k_b))
print("Bob's public key (P_b): ", f"({hex(int(P_b.xy()[0]))}, {hex(int(P_b.xy()[1]))})")
print("Nostr message: ", nostr_message)
print()
# === Step 1: Alice creates signature ===
r_a = randint(1, n - 1)
R_a = r_a * G
if int(R_a.xy()[1]) % 2 == 1:
r_a = n - r_a
R_a = -R_a # Enforce even y
R_a_x_bytes = int(R_a.xy()[0]).to_bytes(32, 'big')
P_a_x_bytes = int(P_a.xy()[0]).to_bytes(32, 'big')
e_a = H_bip340_challenge(R_a_x_bytes + P_a_x_bytes + nostr_message)
t = (r_a + e_a * k_a) % n
print("=== Step 1: Alice creates signature ===")
print("Alice's nonce (r_a): ", hex(r_a))
print("Alice's nonce point (R_a): ", f"({hex(int(R_a.xy()[0]))}, {hex(int(R_a.xy()[1]))})")
print("Alice's challenge (e_a): ", hex(e_a))
print("Secret signature scalar (t): ", hex(t))
print("Public nonce shared (R_a_x): ", hex(int(R_a.xy()[0])))
print()
# === Step 2: Bob recomputes T ===
R_a_x_received = int(R_a.xy()[0])
R_a_reconstructed = lift_x_even_y(R_a_x_received)
e_a_bob = H_bip340_challenge(R_a_x_bytes + P_a_x_bytes + nostr_message)
T = R_a_reconstructed + e_a_bob * P_a
print("=== Step 2: Bob recomputes T ===")
print("Received R_a_x: ", hex(R_a_x_received))
print("Reconstructed R_a: ", f"({hex(int(R_a_reconstructed.xy()[0]))}, {hex(int(R_a_reconstructed.xy()[1]))})")
print("Bob's challenge (e_a): ", hex(e_a_bob))
print("Computed adaptor point (T): ", f"({hex(int(T.xy()[0]))}, {hex(int(T.xy()[1]))})")
print("T matches expected? ", R_a == R_a_reconstructed and e_a == e_a_bob)
print()
# === Step 3: Bob creates adaptor signature ===
cashu_message = b"Cashu proof secret for payment"
r_b = randint(1, n - 1)
R_b = r_b * G
if int(R_b.xy()[1]) % 2 == 1:
r_b = n - r_b
R_b = -R_b # Enforce even y
R_adaptor = R_b + T
while int(R_adaptor.xy()[1]) % 2 == 1:
r_b = randint(1, n - 1)
R_b = r_b * G
if int(R_b.xy()[1]) % 2 == 1:
r_b = n - r_b
R_b = -R_b
R_adaptor = R_b + T
R_adaptor_x_bytes = int(R_adaptor.xy()[0]).to_bytes(32, 'big')
P_b_x_bytes = int(P_b.xy()[0]).to_bytes(32, 'big')
e_b = H_bip340_challenge(R_adaptor_x_bytes + P_b_x_bytes + cashu_message)
s_adaptor = (r_b + e_b * k_b) % n
print("=== Step 3: Bob creates adaptor signature ===")
print("Bob's nonce (r_b): ", hex(r_b))
print("Bob's nonce point (R_b): ", f"({hex(int(R_b.xy()[0]))}, {hex(int(R_b.xy()[1]))})")
print("Adaptor nonce (R_adaptor = R_b + T):", f"({hex(int(R_adaptor.xy()[0]))}, {hex(int(R_adaptor.xy()[1]))})")
print("Bob's challenge (e_b): ", hex(e_b))
print("Adaptor signature scalar (s_adaptor):", hex(s_adaptor))
print()
# === Step 4: Alice completes signature ===
s_complete = (s_adaptor + t) % n
print("=== Step 4: Alice completes signature ===")
print("Complete signature scalar (s_complete):", hex(s_complete))
print()
# === Step 5: Bob extracts secret ===
t_extracted = (s_complete - s_adaptor) % n
print("=== Step 5: Bob extracts secret ===")
print("Extracted secret (t): ", hex(t_extracted))
print("Original secret (t): ", hex(t))
print("Extraction successful? ", t == t_extracted)
print()
# === Verification ===
nostr_signature_s = t_extracted
verification_left = nostr_signature_s * G
verification_right = R_a + e_a * P_a
print("=== Verification ===")
print("Nostr signature verification: ", verification_left == verification_right)
print("Cashu signature verification: ", s_complete * G == R_adaptor + e_b * P_b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment