#!/usr/bin/python3 # Author: SI from eth_account.account import to_standard_signature_bytes from eth_keys import keys from eth_utils import (big_endian_to_int, to_bytes) from hexbytes import HexBytes from eth_keys.backends.native.jacobian import (inv, fast_multiply, fast_add) from eth_keys.constants import (SECPK1_G as G, SECPK1_N as N) def recover_public_key(message_hash, signature): message_hash_bytes = HexBytes(message_hash) if len(message_hash_bytes) != 32: raise ValueError("The message hash must be exactly 32-bytes") signature_bytes = HexBytes(signature) signature_obj = keys.Signature(signature_bytes = to_standard_signature_bytes(signature_bytes)) return signature_obj.recover_public_key_from_msg_hash(message_hash_bytes) def forge(public_key, a = 0, b = 1): t = public_key.to_bytes() Y = big_endian_to_int(t[:32]), big_endian_to_int(t[32:]) r, y = fast_add(fast_multiply(G, a), fast_multiply(Y, b)) s_raw = r * inv(b, N) % N v_raw = (y % 2) ^ (0 if s_raw * 2 < N else 1) s = s_raw if s_raw * 2 < N else N - s_raw v = v_raw + 27 z = a * s_raw % N eth_signature_bytes = to_bytes(r).rjust(32, b'\0') + to_bytes(s).rjust(32, b'\0') + to_bytes(v) return '0x' + to_bytes(z).rjust(32, b'\0').hex(), '0x' + eth_signature_bytes.hex() hsh = '0xe50051a0af89748fe098cef3b163b6dc586a664e726791bb2a582ad364f42683' sig = '0x2bdbc1826efc039719a28a9f4dbab9f4a2692d83de478300261a0e49019b63ee67c202ecc4ebdf82693da47824ac4fcf21f793400d85696034c4de9537c6ce491b' pub = recover_public_key(hsh, sig) addr = pub.to_checksum_address() print('recovered (checksum) address:', addr) a, b = 0, 1 fhsh, fsig = forge(pub, a, b) print('forged message hash:', fhsh) print('forged signature:', fsig) fpub = recover_public_key(fhsh, fsig) faddr = fpub.to_checksum_address() print('recovered address check:', 'correct' if faddr == addr else 'wrong!')