¿Cómo puedo verificar una firma criptográfica que fue producida por un par de claves de direcciones de Ethereum?

Si tengo una firma criptográfica de un par de claves de direcciones de Ethereum, ¿cómo puedo verificar esa firma?

Respuestas (1)

Solidez y Serpiente tienen ecrecoverpara este fin.

ecrecover(bytes32 datos, uint8 v, bytes32 r, bytes32 s) devuelve (dirección)

Los argumentos de la función son:

dataes lo que se firmó . Dado que son 32 bytes, eso generalmente significa que los datos iniciales se codifican primero a 32 bytes, antes de que se firmen.

v, r, ses la firma. ( ves la identificación de recuperación: un valor de 1 byte que especifica el signo y la finitud del punto de la curva; este valor está en el rango de [27, 30], sin embargo, el protocolo Ethereum declara inválidas las dos posibilidades superiores, que representan valores infinitos)

Nota importante con los ejemplos a continuación, sha3 es Keccak-256 .

Aquí hay un fragmento en Solidity :

contract Auth {      
    function verify(address p, bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(bool) {
        // Note: this only verifies that signer is correct.
        // You'll also need to verify that the hash of the data
        // is also correct.
        return ecrecover(hash, v, r, s) == p;
    }
}

Aquí hay un ejemplo en Serpent :

def test_ecrecover(h, v, r, s):
    return(ecrecover(h, v, r, s))

El código de prueba correspondiente en Python (requisitos bitcoiny ethereumpaquetes):

import bitcoin as b
from ethereum import tester, utils


class TestECRecover(object):

    CONTRACT = """
def test_ecrecover(h, v, r, s):
    return(ecrecover(h, v, r, s))
"""

    def setup_class(cls):
        cls.s = tester.state()
        cls.c = cls.s.abi_contract(cls.CONTRACT)
        cls.snapshot = cls.s.snapshot()

    def setup_method(self, method):
        self.s.revert(self.snapshot)

    def test_ecrecover(self):
        priv = b.sha256('some big long brainwallet password')
        pub = b.privtopub(priv)

        msghash = b.sha256('the quick brown fox jumps over the lazy dog')
        V, R, S = b.ecdsa_raw_sign(msghash, priv)
        assert b.ecdsa_raw_verify(msghash, (V, R, S), pub)

        addr = utils.sha3(b.encode_pubkey(pub, 'bin')[1:])[12:]
        assert utils.privtoaddr(priv) == addr

        result = self.c.test_ecrecover(utils.big_endian_to_int(msghash.decode('hex')), V, R, S)
        assert result == utils.big_endian_to_int(addr)

Bajo el capó, ecrecoverutiliza el ECDSARECOVER contrato precompilado ubicado en la dirección 1.


Nota: Geth y web3.eth.sign agregarán un prefijo al datamensaje antes de firmar.

El método sign calcula una firma específica de Ethereum con: sign(keccak256("\x19Ethereum Signed Message:\n" + len(mensaje) + mensaje))).

Al agregar un prefijo al mensaje, la firma calculada se puede reconocer como una firma específica de Ethereum. Esto evita el uso indebido en el que una DApp maliciosa puede firmar datos arbitrarios (por ejemplo, transacciones) y usar la firma para hacerse pasar por la víctima.

Para este caso, el segundo argumento verify()debe ser keccak256("\x19Ethereum Signed Message:\n", len(message), message)en lugar de keccak256(message).

Relacionado: ecrecover de Geth y web3.eth.sign

Si bien ya es una respuesta muy completa, ¿puede agregar qué devuelve ecrecover en caso de una firma buena o no válida?
Según tengo entendido, una buena firma devolverá la dirección que hizo la firma. Una mala firma devolverá una dirección diferente. No hay forma de saber si una firma es válida o no sin conocer también la dirección en la que se suponía que debía firmarse.
De los documentos de solidez en ecrecover: "recupere la dirección asociada con la clave pública de la firma de curva elíptica o devuelva cero en caso de error"
Gracias al editor anónimo que agregó el importante comentario de código: "También deberá verificar que el hash de los datos también sea correcto".
Si la firma es incorrecta, aún obtendrá una dirección válida de esto. Entonces, ¿solo tiene que hacer que el firmante incluya la dirección en algún lugar del mensaje original para verificar que sea correcto?
O, por el contrario, @sudo si el hash es incorrecto, aún obtiene una dirección válida pero incorrecta.