¿La firma de ejemplo en BIP143 no se valida?

En BIP143, el primer ejemplo tiene una firma de testigo y no parece validar/ser correcto. Escribí un programa de python súper simple para demostrar

import ecdsa
import asn1

#Importing the 3 pieces of data from the example to byte arrays
pub = bytearray.fromhex("025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357")
sighash = bytearray.fromhex("c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670")
dersigscript = bytearray.fromhex("304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee")

#deocding the DEC encoding
decoder = asn1.Decoder()
decoder.start(bytes(dersigscript))
tag, sigscript = decoder.read()

#stripping off the script  potion so we just have a signature
sig = bytearray(sigscript)[2:66]

vk = ecdsa.VerifyingKey.from_string(pub,  curve=ecdsa.SECP256k1)
vk.verify(sig, sighash)

Si alguien puede arrojar algo de luz sobre lo estúpido que estoy haciendo, o una referencia de ejemplo que lo desglosa, para que funcione, sería realmente útil. También intenté invertir el orden de los bytes en las 8 combinaciones de las 3 entradas. Dicho esto, he usado la clave privada y pública publicada para hacer mis propias firmas/validación, por lo que estoy bastante seguro de que están en el orden correcto, pero menos seguro acerca de la firma.

¡¡Gracias!!

----EDIT-----
Solo quería enumerar otras cosas que se probaron para completar

  1. como señaló pieter, estaba decodificando incorrectamente la clave privada. S y R se dividen en 2 variables diferentes
304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8eebee0
se convierte
3044 02 20 3609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a 02 20 573a954c4518331561406f90300e8f3358f51928d43eedebeed21
entonces el sig sin formato de 64 bytes es 609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a573a954c4518331561406f90300e8f3358f51928d43c212a67eabeed02
desafortunadamente eso todavía no funcionó

Además, intenté usar pythons native der style ecdsa, que aún no tuvo éxito
desde ecdsa.util importar sigencode_der, sigdecode_der
vk.verify(bytes(sig), bytes(sighash), sigdecode=sigdecode_der)
y
vk.verify(bytes(sig), bytes(sighash), hashlib.sha256, sigdecode=sigdecode_der)
donde sig comienza con 0x30:
3044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad401072656fe94dc6

lo que es interesante es que la biblioteca de python no me dio ningún error, excepto una mala firma. Así que parece que todavía no puedo encontrar ninguna pista sobre cómo hacer esto con una biblioteca estándar. Si lo averiguo, publicaré una respuesta.

Respuestas (2)

Usando esta biblioteca:

from cryptotools import Signature, PublicKey, hex_to_bytes

pub = PublicKey.from_hex('025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357')
sig = Signature.from_hex('304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee')
sighash = hex_to_bytes('c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670')

>>> sig.verify_hash(sighash, pub)
True
Espera espera ingenio. ¿Es la razón por la que nunca veo que esto se haga con una biblioteca estándar porque el algoritmo de firma de bitcoin no cumple con RFC?!?! Desde aquí en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm dice que la firma es de 71-73 bytes para la firma que no es correcta. esto es alucinante
@ noone392 ¿a qué RFC te refieres? Las firmas de Afaik bitcoin siguen la especificación de codificación DER github.com/bitcoin/bips/blob/master/…
es compatible con la codificación DER, me refiero a la especificación ECDSA real como RFC 6979. la firma debe tener exactamente 64 bytes para ser compatible con SECP256k1. Y la firma proporcionada, aunque coincide con la wiki, no tiene la longitud correcta para cumplir. Básicamente, no creo que puedas llamar a esto ECDSA, sino que es una firma de bitcoin que usa matemática de curva elíptica.
Bitcoin usa ECDSA, con firmas codificadas por DER. Sigue exactamente esas especificaciones. ECDSA es un algoritmo de firma, no la codificación.
@PieterWuille, ¿a qué especificación se refiere exactamente? porque en mi ejemplo, DER lo decodifico y uso lo que creo que es la parte de clave sin procesar de 64 bytes.
Ok, su conversión de DER a formato de firma de 64 bytes es incorrecta. Una firma DER consta de un "marcador de estructura" de 2 bytes (que dice que sigue una tupla), luego un "marcador de entero" de 2 bytes, seguido del entero R (número variable de bytes), luego otro "marcador de entero" de 2 bytes. marcador", seguido del entero S (número variable de bytes). Debe extraer los elementos R y S, rellenarlos a 32 bytes cada uno (anteponiendo/eliminando ceros al principio) y luego concatenarlos.
Probablemente pueda hacer eso con el módulo asn1 al decodificar la variable "sigscript" que obtiene (en realidad no tiene un nombre apropiado, es solo una tupla de elementos de firma, no hay script involucrado) nuevamente con asn1.
@PieterWuille Gracias por encontrar ese problema. tu ayuda es muy apreciada. Por alguna razón, recuerdo que ecdsa sig es una sola variable... de todos modos, tengo las 2 piezas de 32 bytes conectadas ahora que las estoy analizando. Desafortunadamente todavía no se verifica. También intenté usar sigdecode=sigdecode_der de python para proporcionar el formato codificado der pero tampoco tuve suerte...
Es probable que el problema sea que necesite usar VerifyingKey.verify_digest en lugar de .verify. Este último vuelve a codificar los mensajes, mientras que ya está proporcionando un mensaje cifrado.
@PieterWuille Eso lo hizo: -DI publicará la respuesta. gracias de nuevo
El estándar para ECDSA es X9.62, como se establece claramente en RFC6979, y no especifica ninguna codificación para la firma. RFC6979 en 2.4 paso 4 notas, correctamente, es "común" usar ASN.1 codificado en DER, pero eso no es un requisito, y absolutamente no es de longitud fija. El código de muestra en A.3 hace DER, pero eso es solo una muestra. Bitcoin usa DER para firmas de transacciones , pero el formato 'simple' de longitud fija para firmas de mensajes ; consulte bitcoin.stackexchange.com/questions/12554 y bitcoin.stackexchange.com/questions/90799
@dave_thompson_085 Gracias por la aclaración

ok Entonces, gracias a PieterWuille, pude (nosotros) descubrir por qué mi código no funcionaba y cómo usar una biblioteca estándar de python o openssl.
Estos fueron los siguientes problemas

  1. la decodificación DER para la biblioteca de python se usó incorrectamente. Necesitábamos iterar a través de todas las variables y extraer las dos piezas de la firma (r y s) por separado y recombinarlas.
  2. Debido a que las bibliotecas de firma estándar tienen el hash integrado en la firma/verificación, y bitcoin usa un hash doble, para que funcione con una biblioteca estándar, debe realizar el hash 1 vez manualmente y luego proporcionarlo a la biblioteca para realizar el segundo hash. Esto significa que no podemos usar sigHash como entrada, sino que tenemos que usar el hash preImage. Publicado a continuación se trata de la secuencia de comandos de python más corta posible que muestra cómo verificar las firmas con los ejemplos proporcionados tanto en formato sin formato como en formato codificado DER:
importar ecdsa
importar asn1
importar hashlib
#utilizado para la versión
desde ecdsa.util importar sigencode_der, sigdecode_der

#Importación de las 3 piezas de datos del ejemplo a matrices de bytes
pub = bytearray.fromhex("025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357")
DerEncodedSig = bytearray.fromhex("304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee")
prehashimage = bytearray.fromhex("0100000096b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd3752b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3bef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a010000001976a9141d0f172a0ecb48aee1be1f2687d2963ae33f71a188ac0046c32300000000ffffffff863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e51100000001000000")

#deocding la codificación DEC
decodificador = asn1.Decodificador()
decodificador.start(bytes(DerEncodedSig))
etiqueta, sigder = decodificador.leer()

sig1 = sigder[2:34]
sig2 = sigder[36:68]
sig = sig1+sig2

#Ahora necesitamos obtener el único hash sha256 de la preimagen
m = hashlib.sha256()
m.update(prehashimage)
singlesighash = m.digest()

#no usaremos el hash doble para nuestra verificación, pero esto muestra que es lo mismo que sigHash en bip143
n = hashlib.sha256()
n.update(singlesighash)
sigHash = n.resumen

vk = ecdsa.VerifyingKey.from_string(bytes(pub), curve=ecdsa.SECP256k1)
#tenemos que proporcionar el hash lib porque el valor predeterminado no es sha256
resultado = vk.verify(bytes(sig), bytes(singlesighash), hashlib.sha256)
imprimir (resultado)
#esta es la forma nativa y no requiere que decodifiquemos la firma pero es compatible con openssl
resultado = vk.verify(bytes(DerEncodedSig), bytes(singlesighash), hashlib.sha256, sigdecode=sigdecode_der)
imprimir (resultado)