Created
May 23, 2025 17:29
-
-
Save jaonoctus/baa5640ec3e6994de6fe65c7b7984493 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# === 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