Skip to content

Instantly share code, notes, and snippets.

@secdev02
Created June 12, 2025 04:26
Show Gist options
  • Save secdev02/3a1b6b57760b75f2c89dd6bf6b27a6b9 to your computer and use it in GitHub Desktop.
Save secdev02/3a1b6b57760b75f2c89dd6bf6b27a6b9 to your computer and use it in GitHub Desktop.
SHALL WE PLAY A GAME?
#!/usr/bin/env python3
"""
Script to compute a public key on secp256k1 using a custom generator point.
"""
import hashlib
import base58
# secp256k1 curve parameters
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F # Field prime
A = 0 # Curve parameter a
B = 7 # Curve parameter b
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 # Order of the curve
# Standard secp256k1 generator point G
STANDARD_GX = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
STANDARD_GY = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
# Custom generator point (instead of standard G)
CUSTOM_GX = 0x3b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63
CUSTOM_GY = 0x3f3979bf72ae8202983dc989aec7f2ff2ed91bdd69ce02fc0700ca100e59ddf3
# Private key
PRIVATE_KEY = 0x00000000000000000000000000000000000000000000ea1a5c66dcc11b5ad180
def mod_inverse(a, m):
"""Compute modular inverse using extended Euclidean algorithm."""
if a < 0:
a = (a % m + m) % m
g, x, _ = extended_gcd(a, m)
if g != 1:
raise Exception('Modular inverse does not exist')
return x % m
def extended_gcd(a, b):
"""Extended Euclidean Algorithm."""
if a == 0:
return b, 0, 1
gcd, x1, y1 = extended_gcd(b % a, a)
x = y1 - (b // a) * x1
y = x1
return gcd, x, y
def point_add(x1, y1, x2, y2):
"""Add two points on the elliptic curve."""
if x1 is None: # Point at infinity
return x2, y2
if x2 is None: # Point at infinity
return x1, y1
if x1 == x2:
if y1 == y2:
# Point doubling
s = (3 * x1 * x1 + A) * mod_inverse(2 * y1, P) % P
else:
# Points are inverses, result is point at infinity
return None, None
else:
# Point addition
s = (y2 - y1) * mod_inverse(x2 - x1, P) % P
x3 = (s * s - x1 - x2) % P
y3 = (s * (x1 - x3) - y1) % P
return x3, y3
def point_multiply(k, x, y):
"""Multiply a point by a scalar using double-and-add method."""
if k == 0:
return None, None # Point at infinity
if k == 1:
return x, y
result_x, result_y = None, None # Point at infinity
addend_x, addend_y = x, y
while k:
if k & 1:
result_x, result_y = point_add(result_x, result_y, addend_x, addend_y)
addend_x, addend_y = point_add(addend_x, addend_y, addend_x, addend_y)
k >>= 1
return result_x, result_y
def sha256(data):
"""Compute SHA-256 hash."""
return hashlib.sha256(data).digest()
def ripemd160(data):
"""Compute RIPEMD-160 hash."""
return hashlib.new('ripemd160', data).digest()
def hash160(data):
"""Compute HASH160 (SHA-256 followed by RIPEMD-160)."""
return ripemd160(sha256(data))
def encode_base58_check(payload, version=0x00):
"""Encode data with Base58Check encoding."""
# Add version byte
versioned_payload = bytes([version]) + payload
# Calculate checksum (first 4 bytes of double SHA-256)
checksum = sha256(sha256(versioned_payload))[:4]
# Concatenate and encode
full_payload = versioned_payload + checksum
return base58.b58encode(full_payload).decode('ascii')
def compute_and_display_pubkey(gx, gy, generator_name):
"""Compute and display public key for a given generator point."""
print(f"\n{generator_name} GENERATOR:")
print("=" * (len(generator_name) + 11))
print(f"Generator Point:")
print(f" X: 0x{gx:x}")
print(f" Y: 0x{gy:x}")
# Verify the generator point is on the curve
if (gy * gy - gx * gx * gx - B) % P != 0:
print(f"ERROR: {generator_name} generator point is not on the secp256k1 curve!")
return
print("Generator point verified to be on the curve.")
# Compute public key: Q = private_key * G
pub_x, pub_y = point_multiply(PRIVATE_KEY, gx, gy)
if pub_x is None or pub_y is None:
print("ERROR: Public key computation resulted in point at infinity!")
return
print(f"\nPUBLIC KEY RESULTS ({generator_name}):")
print("=" * (len(generator_name) + 22))
# 1. Hexadecimal format (uncompressed)
uncompressed_hex = f"04{pub_x:064x}{pub_y:064x}"
print(f"Hex (Uncompressed): {uncompressed_hex}")
# 2. Hexadecimal format (compressed)
prefix = "02" if pub_y % 2 == 0 else "03"
compressed_hex = f"{prefix}{pub_x:064x}"
print(f"Hex (Compressed): {compressed_hex}")
# 3. Decimal format
print(f"Decimal X: {pub_x}")
print(f"Decimal Y: {pub_y}")
# 4. Bitcoin address format (compressed public key)
compressed_pubkey_bytes = bytes.fromhex(compressed_hex)
pubkey_hash = hash160(compressed_pubkey_bytes)
bitcoin_address = encode_base58_check(pubkey_hash, version=0x00)
print(f"Bitcoin Address (P2PKH): {bitcoin_address}")
print(f"\nVERIFICATION ({generator_name}):")
print("=" * (len(generator_name) + 16))
print(f"Public key point is on curve: {(pub_y * pub_y - pub_x * pub_x * pub_x - B) % P == 0}")
def main():
print("secp256k1 Public Key Computation")
print("=" * 40)
print(f"Private Key: 0x{PRIVATE_KEY:x}")
# Compute public key using standard secp256k1 generator G
compute_and_display_pubkey(STANDARD_GX, STANDARD_GY, "STANDARD")
# Compute public key using custom generator point
compute_and_display_pubkey(CUSTOM_GX, CUSTOM_GY, "CUSTOM")
if __name__ == "__main__":
main()
Create public keys on curve with different Generator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment