¿Cómo convierto el valor x de la clave pública en y en Python y lo verifico?

He estado usando la utilidad de dirección BTC que me permite escribir algo como:

020F031CA83F3FB372BD6C2430119E0B947CF059D19CDEA98F4CEFFEF620C584F9

Luego, al hacer clic en un botón, el programa puede escupir el valor Y o decirme si ese valor X no es válido (IE: no tiene un valor Y válido).

Como ejemplo, el resultado de este sería Válido y se expandiría para producir lo siguiente:

040F031CA83F3FB372BD6C2430119E0B947CF059D19CDEA98F4CEFFEF620C584F9F064F1FDE4BC07D4F48C5114680AD1ADAF5F6EAA2166F7E4B4887703A681B548

¿Alguien sabe el código para hacer una o ambas funciones en Python? (si el código es para hexadecimal o decimal, no importa)

Vale la pena señalar que el primer número hexadecimal es una "clave pública comprimida" y el segundo es la "clave pública sin comprimir" asociada. Dada la única clave privada común, es posible sintetizar cualquier clave pública que esté estrechamente relacionada entre sí.

Respuestas (1)

Primero, debe comprender cuáles son realmente los dos formatos. El primero es el formato SEC comprimido y el segundo es el formato SEC sin comprimir . La diferencia entre los dos es que el formato comprimido solo incluye el valor X y la paridad del valor Y, mientras que el formato sin comprimir incluye los valores X e Y.

El 02al principio del valor comprimido indica que el valor Y debe ser par. 03indicaría el valor impar. El 04al principio del valor sin comprimir indica que siguen los valores X e Y. Por eso 0F031CA83F3FB372BD6C2430119E0B947CF059D19CDEA98F4CEFFEF620C584F9es igual para ambos valores.

Para obtener el valor de Y, en realidad no es tan difícil. Necesita saber un poco sobre la criptografía de curva elíptica. Específicamente, bitcoin usa SECP256K1 cuya curva está representada por y^2 = x^3 + 7. Esto se hace módulo P, que en nuestro caso es FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F. Entonces, reemplazando X = 0F031CA83F3FB372BD6C2430119E0B947CF059D19CDEA98F4CEFFEF620C584F9al lado izquierdo de la fórmula, obtienes:

(x^3 + 7) mod p = EBD56984BA6A88F5D40BB496D9A7C70AC3D8DDF5F7C287E8AABEC904E3D41DB5

El código Python para esto es bastante simple:

$ python
>>> p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
>>> x = 0x0F031CA83F3FB372BD6C2430119E0B947CF059D19CDEA98F4CEFFEF620C584F9
>>> "%X" % ((x**3 + 7) % p)
'EBD56984BA6A88F5D40BB496D9A7C70AC3D8DDF5F7C287E8AABEC904E3D41DB5'

Ahora tienes que sacar la raíz cuadrada para obtener Y, lo que debería dar dos valores, uno par y otro impar (debido a que p es impar). Este es un cálculo mucho más complicado, sobre el que puede leer aquí: http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python

Esto se implementa en una biblioteca llamada pycoin: https://github.com/richardkiss/pycoin

Puedes calcularlo así:

>>> import pycoin
>>> from pycoin.ecdsa.numbertheory import modular_sqrt
$ python
>>> p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
>>> x = 0x0F031CA83F3FB372BD6C2430119E0B947CF059D19CDEA98F4CEFFEF620C584F9
>>> y_squared = (x**3 + 7) % p
>>> modular_sqrt(y_squared, p)
7058650705029786666096901756991264635816989543710944821299269679858359092967
>>> y = modular_sqrt(y_squared, p)
>>> "%X" % y
'F9B0E021B43F82B0B73AEEB97F52E5250A09155E99081B4B7788FB597E46E7'

Este es el valor impar (termina en 7), por lo que debe calcular el otro valor posible de y, que es simplemente p - y

>>> "%X" % (p-y)
`F064F1FDE4BC07D4F48C5114680AD1ADAF5F6EAA2166F7E4B4887703A681B548`

Este es el número par que son los últimos 64 caracteres del hexadecimal que comienza con 04.

Tenga en cuenta que la dirección se calcula a partir de los formatos SEC comprimidos o sin comprimir. Este cálculo es un hash SHA256 seguido de un hash RIPEMD160. Esto no es reversible, por lo que no puede obtener el formato SEC de la dirección, aunque puede hacerlo al revés.