Generar una dirección de Ethereum a partir de una clave pública
Una dirección de Ethereum corresponde a los últimos 20 bytes del keccak256 de la clave pública. El algoritmo de la clave pública es secp256k1, el mismo que se utiliza en Bitcoin.
Dado que es un algoritmo de curva elíptica, la clave pública es un par (x, y) que corresponde a un punto en la curva elíptica.
Generar la clave pública de curva elíptica
La clave pública es la concatenación de x e y, y a eso es a lo que le aplicamos el hash.
A continuación se proporciona el código.
from ecpy.curves import Curve
from ecpy.keys import ECPublicKey, ECPrivateKey
from sha3 import keccak_256
private_key = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
cv = Curve.get_curve('secp256k1')
pv_key = ECPrivateKey(private_key, cv)
pu_key = pv_key.get_public_key()
# equivalent alternative for illustration:
# concat_x_y = bytes.fromhex(hex(pu_key.W.x)[2:] + hex(pu_key.W.y)[2:])
concat_x_y = pu_key.W.x.to_bytes(32, byteorder='big') + pu_key.W.y.to_bytes(32, byteorder='big')
eth_addr = '0x' + keccak_256(concat_x_y).digest()[-20:].hex()
print('private key: ', hex(private_key))
print('eth_address: ', eth_addr)
La biblioteca ecpy se encuentra aquí https://github.com/cslashm/ECPy. Esta biblioteca implementa las matemáticas de la curva elíptica en Python, por lo que no será tan rápida como un wrapper de la implementación en C de Bitcoin, la cual es utilizada por la biblioteca coincurve.
Sin embargo, la implementación en Python te permite ver paso a paso las matemáticas de la curva elíptica utilizadas para derivar la clave pública.
Puedes usar este código para generar una vanity address de Ethereum por fuerza bruta, pero ten en cuenta que si tu fuente de aleatoriedad no es segura o tiene muy pocos bits de entropía, podrías ser víctima de un hackeo similar a este.
Generar una clave privada con Python lanzando monedas o dados
Puedes generar una dirección de Ethereum a partir de una clave privada tú mismo lanzando una moneda 256 veces y escribiendo en una cadena de texto un 1 si es cara y un 0 si es cruz.
Supongamos que obtienes el siguiente resultado:
result = b'1100001011001101010001001100101000001111101101111011001000110001101100011101101011010001011000101111100110010101001001101110111011001000100001010101111100001100100110010010111110110100000010011111100000110101001110000101100101011111001101010001100001000'
Puedes convertir esto en una clave privada utilizando el siguiente código en Python:
# 2 means base 2 for binary
private_key = hex(int(result, 2))
# private key is 0x1859a89941f6f646363b5a2c5f32a4ddd910abe19325f6813f06a70b2be6a308
Luego, introduce esa clave privada en el código de la sección anterior y habrás generado tu dirección con tu propia aleatoriedad.
Esto mismo se puede lograr más rápido lanzando un dado de 16 caras 64 veces y escribiendo la cadena hexadecimal que se produce carácter por carácter. Ten en cuenta que la mayoría de los dados no tienen una representación para el número cero, por lo que tendrás que restar 1 a cada resultado.
Si solo tienes dados tradicionales de seis caras, puedes escribir una cadena en base 6 (no olvides restar 1 a cada lanzamiento) y hacer una conversión de base a binario. Tendrás que seguir lanzando hasta que tengas al menos 256 bits para tu clave privada. Si eres particularmente paranoico con la aleatoriedad, puedes usar dados de grado de casino.
Ten cuidado al usar la biblioteca de números aleatorios incorporada en Python. No está diseñada para ser criptográficamente segura. Te recomendamos familiarizarte con la aleatoriedad criptográficamente segura si eres nuevo en el tema.
Por lo general, no puedes inicializar una hardware wallet utilizando este método porque la frase de recuperación de 24 palabras que utilizan no es lo mismo que una clave privada utilizada para firmar transacciones. La frase de recuperación de 24 palabras se usa para derivar múltiples claves privadas para los diferentes tipos de criptomonedas que contiene la wallet.
Matemáticas: Acerca de secp256k1: Clave pública a partir de la clave privada
La forma de la curva
secp256k1 define la forma de la curva elíptica (mod p) donde p es el número primo 115792089237316195423570985008687907853269984665640564039457584007908834671663
O
El primo p no debe confundirse con el orden de la curva. No todos los valores satisfacen la ecuación anterior. Sin embargo, se garantiza que las operaciones en la curva sean cerradas. Es decir, si se suman o multiplican dos puntos válidos, el resultado será un número válido dentro de la curva.
El punto de partida
El otro parámetro importante en secp256k1 es el punto de partida G. Dado que G es un punto en la curva elíptica, es bidimensional y tiene los siguientes parámetros:
x = 55066263022277343669578718895168534326250603453777594175500187360389116729240
y = 32670510020758816978083085130507043184471273380659243275938904335757337482424
Para comprobar que G es un punto válido, podemos introducir los números en Python:
x = 55066263022277343669578718895168534326250603453777594175500187360389116729240
y = 32670510020758816978083085130507043184471273380659243275938904335757337482424
p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
assert pow(y, 2, p) == (pow(x, 3) + 7) % p
Para crear un par de claves pública/privada, se genera un número aleatorio s (esta es la clave secreta). El punto G se suma a sí mismo s veces y el nuevo punto (x, y) es la clave pública. No es factible derivar s a partir de G y (x, y) si s es lo suficientemente grande.
Firmar mensajes en este esquema se reduce a demostrar que conoces s sin revelarlo.
Sumar G a sí mismo s veces es lo mismo que multiplicar s * G. De hecho, podemos ver esta operación a un nivel más bajo eliminando algunas de las abstracciones que proporciona la biblioteca.
from ecpy.curves import Curve
from sha3 import keccak_256
private_key = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
cv = Curve.get_curve('secp256k1')
pu_key = private_key * cv.generator # just multiplying the private key by generator point (EC multiplication)
concat_x_y = pu_key.x.to_bytes(32, byteorder='big') + pu_key.y.to_bytes(32, byteorder='big')
eth_addr = '0x' + keccak_256(concat_x_y).digest()[-20:].hex()
print('private key: ', hex(private_key))
print('eth_address: ', eth_addr)
La clave pública es simplemente la clave privada multiplicada por el punto G en la curva elíptica secp256k1. Eso es todo.
Aprende más
Echa un vistazo a nuestro avanzado bootcamp de blockchain hoy y conviértete en un desarrollador de blockchain que domina lo difícil que otros programadores desconocen.
Publicado originalmente el 30 de enero de 2023