Estoy cambiando de la biblioteca ecdsa de Python puro a la biblioteca coincurve mucho más rápida para firmar datos. También me gustaría cambiar a coincurve para verificar las firmas (incluidas las firmas antiguas creadas por la biblioteca ecdsa ).
Parece que las firmas creadas con ecdsa no son (¿siempre?) válidas en coincurve . ¿Podría alguien explicar por qué esto no funciona? Además, parece que la biblioteca de cryptography puede validar tanto las firmas ecdsa como las firmas coincurve sin problemas, de manera consistente.
Lo que es aún más confuso, si ejecuta el siguiente script varias veces, es que a veces imprime el punto 3 y otras veces no. ¿Por qué coincurve solo ocasionalmente encontraría válida la firma?
pip install ecdsa cryptography coincurve import ecdsa import hashlib import coincurve from coincurve.ecdsa import deserialize_compact, cdata_to_der from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import Prehashed ecdsa_private_key = ecdsa.SigningKey.generate(ecdsa.SECP256k1, None, hashlib.sha256) ecdsa_pub = ecdsa_private_key.get_verifying_key() message = b"Hello world!" digest = hashlib.sha256(message).digest() serialized_signature = ecdsa_private_key.sign_digest_deterministic(digest, hashfunc=hashlib.sha256) signature = cdata_to_der(deserialize_compact(serialized_signature)) cc_private_key = coincurve.PrivateKey(ecdsa_private_key.to_string()) cc_pub = cc_private_key.public_key crypto_pub = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), cc_pub.format(True)) if ecdsa_pub.verify_digest(serialized_signature, digest) is True: print("1. ecdsa can validate its own signature") crypto_pub.verify(signature, digest, ec.ECDSA(Prehashed(hashes.SHA256()))) print("2. cryptography can validate ecdsa signature (raises exception if not valid)") if cc_pub.verify(signature, digest, None) is False: print("3. coincurve will not validate ecdsa signature") signature = cc_private_key.sign(digest, None) crypto_pub.verify(signature, digest, ec.ECDSA(Prehashed(hashes.SHA256()))) print("4. cryptography can validate coincurve signature (raises exception if not valid)") if cc_pub.verify(signature, digest, None) is True: print("5. coincurve will validate its own signature")Bitcoin y la biblioteca coincurve usan firmas canónicas, mientras que esto no es cierto para la biblioteca ecdsa .
¿Qué significa firma canónica ?
En general, si (r,s) es una firma válida, entonces (r,s') := (r,-s mod n) también es una firma válida ( n es el orden del punto base).
Una firma canónica usa el valor s' = -s mod n = n - s en lugar de s , es decir, la firma (r, ns) , si s > n/2 , seg aquí .
Todas las firmas de la biblioteca ecdsa que no fueron validadas con éxito por la biblioteca coincurve en su programa de prueba tienen un s > n/2 y, por lo tanto, no son canónicas, mientras que aquellas que fueron validadas con éxito son canónicas.
Entonces, la solución es simplemente canonizar la firma de la biblioteca ecdsa , por ejemplo:
def canonize(s_bytes): n = 115792089237316195423570985008687907852837564279074904382605163141518161494337 s = int.from_bytes(s_bytes, byteorder='big') if s > n//2: s = n - s return s.to_bytes(32, byteorder='big') ... serialized_signature = serialized_signature[:32] + canonize(serialized_signature[32:]) # Fix: canonize signature = cdata_to_der(deserialize_compact(serialized_signature)) ...Con esta solución, la biblioteca de coincurve valida con éxito todas las firmas de la biblioteca ecdsa en su programa de prueba.