andelf / tronpy

TRON Python Client Library.
MIT License
204 stars 96 forks source link

tronpy.exceptions.BadSignature for signature from TronWeb #101

Open keshe4ka opened 1 year ago

keshe4ka commented 1 year ago

I get a signature line from the frontend that looks like this: 0x1d1b0779da653630d29fc4f1ea1e5a109a30d52e21e7657fa896d2fccc3b430b14089377e13b6ed35ef371a1c91873773d568219d1100fa8595e5f2eec39e3e41c (TronWeb signMessageV2 documentation example). I'm trying to create a Signature object
signature = Signature.fromhex(decoded_token['signature'][2:])
but I'm getting a
tronpy.exceptions.BadSignature error due to assert signature_bytes[-1] in [0, 1]
What can I do?

keshe4ka commented 1 year ago

I was able to solve this problem for myself by writing my own implementation:

import sha3
from tronpy.keys import Signature

TRON_MESSAGE_PREFIX = "\x19TRON Signed Message:\n"

def hash_message(message):
    if isinstance(message, str):
        message = message.encode()

    if isinstance(message, list):
        message = bytearray(message)

    message_length = str(len(message)).encode()

    h = sha3.keccak_256()
    h.update(TRON_MESSAGE_PREFIX.encode())
    h.update(message_length)
    h.update(message)

    return h.digest()

def recover_address_from_msg_hash(signature_hex_str: str, message_hash: bytes):
    """Recover public key(address) from message hash and signature."""
    signature_bytes = bytes.fromhex(signature_hex_str[2:])
    r = signature_bytes[:32]
    s = signature_bytes[32:64]
    v = signature_bytes[64] - 27  # Subtract 27 to make it 0 or 1
    signature_bytes = r + s + v.to_bytes(1, 'big')
    signature_obj = Signature(signature_bytes)

    public_key = signature_obj.recover_public_key_from_msg_hash(message_hash)
    return public_key.to_base58check_address()

I wrote this code after looking at the implementation of verifyMessageV2 in TronWeb, I hope it will be useful for those who face the same problem.