I learn many things from this smaller secp256k1.
Uncompressed public key: 042f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4d8ac222636e5e 3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6
Three x values:
x1 = 21505829891763648114329055987619236494102133314575206970830385799158076338148
x2 = 23285849548026170226712523888619559634478006467037872208296602441247713932904
x3 = 71000409797526377082529405132449111724689844884027484860330595767503044400611
Two y values:
y1 = 98003708678762621233683240503080860129026887322874138805529884920309963580118
y2 = 17788380558553574189887744505607047724243097342766425233927699087598871091545
Enter private key for (x1, y1) = (21505829891763648114329055987619236494102133314575206970830385799158076338148, 98003708678762621233683240503080860129026887322874138805529884920309963580118): 5
Six public keys with private keys:
Public key 1: 042f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4d8ac222636e5e 3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6 [Private key: 5]
Validated: k1 matches
Public key 2: 04337b52e3acda49dff79f54fbccb94671a045693ee0d097cc138c694695a83668d8ac222636e5e 3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6 [Private key: 72798312578463789091060122408687194401800723498338030560477939572921828405753]
Validated: k2 matches
Public key 3: 049cf8cecf391e958cb2ac03df28ea6865772f120342cdcd7c20cac14eb816d5e3d8ac222636e5e 3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6 [Private key: 42993776658852406332510862600000713451036840780736873822127223568596333088579]
Validated: k3 matches
Public key 4: 042f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe42753ddd9c91a1 c292b24562259363bd90877d8e454f297bf235782c459539959 [Private key: 115792089237316195423570985008687907852837564279074904382605163141518161494332]
Validated: k4 matches
Public key 5: 04337b52e3acda49dff79f54fbccb94671a045693ee0d097cc138c694695a836682753ddd9c91a1 c292b24562259363bd90877d8e454f297bf235782c459539959 [Private key: 42993776658852406332510862600000713451036840780736873822127223568596333088584]
Validated: k5 matches
Public key 6: 049cf8cecf391e958cb2ac03df28ea6865772f120342cdcd7c20cac14eb816d5e32753ddd9c91a1 c292b24562259363bd90877d8e454f297bf235782c459539959 [Private key: 72798312578463789091060122408687194401800723498338030560477939572921828405758]
Validated: k6 matches
Sum of private keys for y1 = 115792089237316195423570985008687907852837564279074904382605163141518161494337 (should be n or 2n)
Sum of private keys for y2 = 231584178474632390847141970017375815705675128558149808765210326283036322988674 (should be n or 2n)
Endomorphs!
public key 2 = beta * P.x || lambda * P (mod p)
public key 3 = beta2 * P.x
public key 4 = p - P.y() == inverse of p * G (mod p)
public key 5 = x*beta%p, p-y == N-pvk*lmda%N
public key 6 = x*beta2%p, p-y == N-pvk*lmda2%N
python script that compute all of them in one shot (if helpful to anyone):
from ecdsa.ellipticcurve import Point
from ecdsa.curves import SECP256k1
# secp256k1 parameters
curve = SECP256k1.curve
p = curve.p()
G = SECP256k1.generator
# Compute β = 2^((p-1)/3) mod p
beta = pow(2, (p - 1) // 3, p)
beta2 = 60197513588986302554485582024885075108884032450952339817679072026166228089408
lmbda = 37718080363155996902926221483475020450927657555482586988616620542887997980018
lmbda2 = 78074008874160198520644763525212887401909906723592317393988542598630163514319
# print(p)
def parse_pubkey(pubkey: str):
"""Extracts x, y coordinates from an uncompressed '04' format public key."""
if not pubkey.startswith('04') or len(pubkey) != 130:
raise ValueError("Invalid uncompressed public key format")
x = int(pubkey[2:66], 16)
y = int(pubkey[66:], 16)
return Point(SECP256k1.curve, x, y, SECP256k1.order)
# beta * P.x == lambda * P (mod p)
def endomorphism(P: Point) -> Point:
x_new = (beta * P.x()) % p
return Point(curve, x_new, P.y())
# p - P.y() == inverse of p * G (mod p) (negation of y)
def endomorphism2(P: Point) -> Point:
# x_new = ( P.x() * beta2) % p
y_new = ( p - P.y() ) % p
return Point(curve, P.x(), y_new)
def endomorphism3(P: Point) -> Point:
x_new = (beta2 * P.x()) % p
return Point(curve, x_new, P.y())
def endomorphism4(P: Point) -> Point:
x_new = (beta * P.x()) % p
y_new = (p - P.y()) % p
return Point(curve, x_new, y_new)
def endomorphism5(P: Point) -> Point:
x_new = (beta2 * P.x()) % p
y_new = (p - P.y()) % p
return Point(curve, x_new, y_new)
pubkey = "042f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4d8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6"
# compute given public key or P integer, decomment one or another.
P = 6 * G
# P = parse_pubkey(pubkey)
P_endomorph = endomorphism(P)
P_endomorph2 = endomorphism2(P)
P_endomorph3 = endomorphism3(P)
P_endomorph4 = endomorphism4(P)
P_endomorph5 = endomorphism5(P)
print("Original P :", (P.x(), P.y()))
print(f"Originak P key 04{P.x():x}{P.y():x}")
print('---------------------------------')
print("Endomorph P [beta * P.x == lambda * P (mod p)]:", (P_endomorph.x(), P_endomorph.y()))
print(f"Endomorph φ(P) key 04{P_endomorph.x():064x}{P_endomorph.y():064x}")
print('---------------------------------')
print("Endomorph2 P [p - P.y() == inverse of p * G (mod p) (negation of y)]:", (P_endomorph2.x(), P_endomorph2.y()))
print(f"Endomorph2 φ(P) key 04{P_endomorph2.x():x}{P_endomorph2.y():x}")
print('---------------------------------')
print("Endomorph3 P [beta2 * P.x == 2nd beta iteration]:", (P_endomorph3.x(), P_endomorph3.y()))
print(f"Endomorph3 φ(P) key 04{P_endomorph3.x():x}{P_endomorph3.y():x}")
print('---------------------------------')
print("Endomorph4 P [x*beta%p, p-y == N-pvk*lmda%N]:", (P_endomorph4.x(), P_endomorph4.y()))
print(f"Endomorph4 φ(P) key 04{P_endomorph4.x():x}{P_endomorph4.y():x}")
print('---------------------------------')
print("Endomorph5 P [x*beta2%p, p-y == N-pvk*lmda2%N]:", (P_endomorph5.x(), P_endomorph5.y()))
print(f"Endomorph5 φ(P) key 04{P_endomorph5.x():x}{P_endomorph5.y():x}")