calculating the modular cubic root, determine all the values of X for a given Y coordinate. Usually, you can find 0, 1, 2, up to 3 values of X
that share the same Y.
update:import ecdsa
from ecdsa.ellipticcurve import Point
from sympy import symbols, Eq, solve
target = "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
if target.startswith("04"):
Upub = target[2:]
y_value = int(Upub[64:], 16)
elif target.startswith("02") or target.startswith("03"):
x = int(target[2:], 16)
curve = ecdsa.SECP256k1.curve
y_square = (x**3 + curve.a() * x + curve.b()) % curve.p()
y_value = pow(y_square, (curve.p() + 1) // 4, curve.p())
if (target.startswith("03") and y_value % 2 == 0) or (target.startswith("02") and y_value % 2 != 0):
y_value = curve.p() - y_value
Upub = '04' + hex(x)[2:].zfill(64) + hex(y_value)[2:].zfill(64)
else:
raise ValueError("null")
p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
beta = 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee
xcubed = (y_value * y_value - 7) % p
print("xcubed = 0x%x" % xcubed)
x1 = pow(xcubed, (p + 2) // 9, p)
x2 = (x1 * beta) % p
x3 = (x2 * beta) % p
UncompressedPublicKey1 = '04' + hex(x1)[2:].zfill(64) + hex(y_value)[2:].zfill(64)
compressedPublicKey1 = ('02' if y_value % 2 == 0 else '03') + hex(x1)[2:].zfill(64)
UncompressedPublicKey2 = '04' + hex(x2)[2:].zfill(64) + hex(y_value)[2:].zfill(64)
compressedPublicKey2 = ('02' if y_value % 2 == 0 else '03') + hex(x2)[2:].zfill(64)
UncompressedPublicKey3 = '04' + hex(x3)[2:].zfill(64) + hex(y_value)[2:].zfill(64)
compressedPublicKey3 = ('02' if y_value % 2 == 0 else '03') + hex(x3)[2:].zfill(64)
print("point 1:")
print(f"Uncompressed: {UncompressedPublicKey1}")
print(f"Compressed: {compressedPublicKey1}\n")
print("point 2:")
print(f"Uncompressed: {UncompressedPublicKey2}")
print(f"Compressed: {compressedPublicKey2}\n")
print("point 3:")
print(f"Uncompressed: {UncompressedPublicKey3}")
print(f"Compressed: {compressedPublicKey3}")
Example of 3 points that share Y:
cPub: 0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
X= 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
Y= b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777
cPub: 0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
H160: adde4c73c7b9cee17da6c7b3e2b2eea1a0dcbe67
Addr: 1GrLCmVQXoyJXaPJQdqssNqwxvha1eUo2E
cPub: 03bcace2e99da01887ab0102b696902325872844067f15e98da7bba04400b88fcb
X= bcace2e99da01887ab0102b696902325872844067f15e98da7bba04400b88fcb
Y= b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777
cPub: 03bcace2e99da01887ab0102b696902325872844067f15e98da7bba04400b88fcb
H160: 516becf48ea6f02f20591504925d87bc62e9014f
Addr: 18RX2Lmi8cm2Bab8eABwyPgr9h8GSDjond
cPub: 03c994b69768832bcbff5e9ab39ae8d1d3763bbf1e531bed98fe51de5ee84f50fb
X= c994b69768832bcbff5e9ab39ae8d1d3763bbf1e531bed98fe51de5ee84f50fb
Y= b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777
cPub: 03c994b69768832bcbff5e9ab39ae8d1d3763bbf1e531bed98fe51de5ee84f50fb
H160: 9226f4f38f59b60ad6d41fd78c7dcadb40cf1515
Addr: 1EKnKn1b4vQF81CxpBb2v2QiUhrjJsA4RK
calculate their respective pk, when one of them is known.private_key = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140
N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
lambda_value = 0x5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72
private_key_x2 = (private_key * pow(lambda_value , 1, N)) % N
private_key_x3 = (private_key * pow(lambda_value , 2, N)) % N
print("Private key x1: 0x%x" % private_key)
print("Private key x2: 0x%x" % private_key_x2)
print("Private key x3: 0x%x" % private_key_x3)