Recuperación de clave pública Python ECDSA

¿Cómo recupera la clave pública EC de una firma de estilo VRS en python?

Estoy tratando de configurar un cifrado entre dos partes a través de ECIES, pero primero quería obtener la clave pública a través de la recuperación de una transacción. El siguiente es un ejemplo de transacción de mi consola geth con la que he estado trabajando.

eth.getTransaction("0x912b33e09311a72b988af66918abcd53f43831688c7acb6b44870c491f073dab")
{
  blockHash: "0xfdc5a1c9e1a0dcdfb2b9884919b79157a04eb15b0b58c91131e957f2803f8195",
  blockNumber: 100449,
  from: "0xc6f4f527587ea4a03aa85e0322783592367c1b9a",
  gas: 100000,
  gasPrice: 18000000000,
  hash: "0x912b33e09311a72b988af66918abcd53f43831688c7acb6b44870c491f073dab",
  input: "0x",
  nonce: 21,
  r: "0xab90122dc4e4bbdbb14ef22ad3ae21aecc19a1c90a9c8989c68b26cc782ff303",
  s: "0x36e5f275147049d3afd5d33b735cc9313d2c1aad3ab401aefdce678128e2f1d0",
  to: "0xa593094cebb06bf34df7311845c2a34996b52324",
  transactionIndex: 0,
  v: "0x1c",
  value: 1000000000000
}
web3.sha3(eth.getRawTransaction("0x912b33e09311a72b988af66918abcd53f43831688c7acb6b44870c491f073dab"),{encoding:'hex'})
"0x912b33e09311a72b988af66918abcd53f43831688c7acb6b44870c491f073dab"

De la transacción anterior, tengo la materia prima para la firma (p. ej., V, R, S) y el remitente (es decir, el campo "de") que puedo usar para verificar la dirección.

Estoy tratando de hacer esto en python.

sender = "0xc6f4f527587ea4a03aa85e0322783592367c1b9a"
hash = "0x912b33e09311a72b988af66918abcd53f43831688c7acb6b44870c491f073dab"
r = "0xab90122dc4e4bbdbb14ef22ad3ae21aecc19a1c90a9c8989c68b26cc782ff303"
s ="0x36e5f275147049d3afd5d33b735cc9313d2c1aad3ab401aefdce678128e2f1d0"
v = "0x1c"
import bitcoin
import ethereum.keys #for a good sha3 implementation
from rlp.utils import decode_hex, encode_hex
rawhash = decode_hex(hash[2:]) # omit the 0x
vrs = (int(v[2:],16),int(r[2:],16),int(s[2:],16)) # omit the 0x and convert to integers based on base16 (hex)
rec_pub = bitcoin.ecdsa_raw_recover(rawhash,vrs) ## Trouble line!
enc_rec_pub = bitcoin.encode_pubkey(rec_pub,"bin")
rec_address = encode_hex(ethereum.keys.sha3(enc_rec_pub[1:]))[24:]
if rec_address == sender[2:]:
    print "[+++] The address was recovered from the public key! :-)"
else:
    print "[-] This address is not the same :-("

El ecdsa_raw_recover nunca da como resultado el valor correcto. Abrí el archivo de claves para verificar la clave pública/privada y la siguiente línea nunca da como resultado el valor correcto.

rec_pub = bitcoin.ecdsa_raw_recover(rawhash,vrs)

¿Hay algún aspecto de esto que me falta o una mejor recomendación sobre cómo lograrlo? ¡Gracias de antemano!

Respuestas (1)

¡ El rawhashque usaste no está bien!

Lea esto: Cómo generar el rawhash en python .

Como tiene el paquete ethereumen su código, la forma más sencilla de obtener la clave pública es:

#!/usr/bin/env python

import rlp
import ethereum
import ethereum.transactions


class MyTransaction(ethereum.transactions.Transaction):

    _publickey = None

    @property
    def publickey(self):
        secpk1n = ethereum.transactions.secpk1n
        if not self._publickey:
            if self.v in (27, 28):
                vee = self.v
                rlpdata = rlp.encode(
                    self, ethereum.transactions.UnsignedTransaction)
            elif self.v >= 37:
                vee = self.v - self.network_id * 2 - 8
                assert vee in (27, 28)
                rlpdata = rlp.encode(
                    rlp.infer_sedes(self).serialize(self)[:-3] \
                    + [self.network_id, '', ''])
            else:
                return None  # Invalid V value
            if self.r >= secpk1n or self.s >= secpk1n or self.r == 0 \
                    or self.s == 0:
                return None  # Invalid signature values!
            sighash = ethereum.utils.sha3(rlpdata)
            pub = ethereum.utils.ecrecover_to_pub(sighash, vee, self.r, self.s)
            if pub == b'\x00' * 64:
                return None  # Invalid signature (zero privkey cannot sign)
            self._publickey = pub
        return self._publickey

    @publickey.setter
    def publickey(self, value):
        self._publickey = value


def main():
    nonce = 21
    gasprice = 18000000000
    startgas = 100000
    to = '0xa593094cebb06bf34df7311845c2a34996b52324'
    value = 1000000000000
    data = ''
    sender = "0xc6f4f527587ea4a03aa85e0322783592367c1b9a"
    r = "0xab90122dc4e4bbdbb14ef22ad3ae21aecc19a1c90a9c8989c68b26cc782ff303"
    s ="0x36e5f275147049d3afd5d33b735cc9313d2c1aad3ab401aefdce678128e2f1d0"
    v = "0x1c"
    r = int(r, 16)
    s = int(s, 16)
    v = int(v, 16)
    tx = MyTransaction(nonce, gasprice, startgas, to, value, data, v, r, s)
    assert sender[2:] == ethereum.utils.sha3(tx.publickey)[-20:].hex()

if __name__ == '__main__':
    main()
¿Qué sucede si solo tiene la firma y los valores v, r, s?