Beispiel #1
0
def encode_defunct(
        primitive: bytes = None,
        *,
        hexstr: str = None,
        text: str = None) -> SignableMessage:
    r"""
    Encode a message for signing, using an old, unrecommended approach.

    Only use this method if you must have compatibility with
    :meth:`w3.eth.sign() <web3.eth.Eth.sign>`.

    EIP-191 defines this as "version ``E``".

    .. NOTE: This standard includes the number of bytes in the message as a part of the header.
        Awkwardly, the number of bytes in the message is encoded in decimal ascii.
        So if the message is 'abcde', then the length is encoded as the ascii
        character '5'. This is one of the reasons that this message format is not preferred.
        There is ambiguity when the message '00' is encoded, for example.

    Supply exactly one of the three arguments: bytes, a hex string, or a unicode string.

    :param primitive: the binary message to be signed
    :type primitive: bytes or int
    :param str hexstr: the message encoded as hex
    :param str text: the message as a series of unicode characters (a normal Py3 str)
    :returns: The EIP-191 encoded message, ready for signing

    .. doctest:: python

        >>> from wan_account.messages import encode_defunct
        >>> from eth_utils.curried import to_hex, to_bytes

        >>> message_text = "I♥SF"
        >>> encode_defunct(text=message_text)
        SignableMessage(version=b'E', header=b'thereum Signed Message:\n6', body=b'I\xe2\x99\xa5SF')

        These four also produce the same hash:
        >>> encode_defunct(to_bytes(text=message_text))
        SignableMessage(version=b'E', header=b'thereum Signed Message:\n6', body=b'I\xe2\x99\xa5SF')

        >>> encode_defunct(bytes(message_text, encoding='utf-8'))
        SignableMessage(version=b'E', header=b'thereum Signed Message:\n6', body=b'I\xe2\x99\xa5SF')

        >>> to_hex(text=message_text)
        '0x49e299a55346'
        >>> encode_defunct(hexstr='0x49e299a55346')
        SignableMessage(version=b'E', header=b'thereum Signed Message:\n6', body=b'I\xe2\x99\xa5SF')

        >>> encode_defunct(0x49e299a55346)
        SignableMessage(version=b'E', header=b'thereum Signed Message:\n6', body=b'I\xe2\x99\xa5SF')
    """
    message_bytes = to_bytes(primitive, hexstr=hexstr, text=text)
    msg_length = str(len(message_bytes)).encode('utf-8')

    # Encoding version E defined by EIP-191
    return SignableMessage(
        b'E',
        b'thereum Signed Message:\n' + msg_length,
        message_bytes,
    )
Beispiel #2
0
def encode_intended_validator(validator_address: Union[Address, str],
                              primitive: bytes = None,
                              *,
                              hexstr: str = None,
                              text: str = None) -> SignableMessage:
    """
    Encode a message using the "intended validator" approach (ie~ version 0)
    defined in EIP-191_.

    Supply the message as exactly one of these three arguments:
    bytes as a primitive, a hex string, or a unicode string.

    .. WARNING:: Note that this code has not gone through an external audit.
        Also, watch for updates to the format, as the EIP is still in DRAFT.

    :param validator_address: which on-chain contract is capable of validating this message,
        provided as a checksummed address or in native bytes.
    :param primitive: the binary message to be signed
    :type primitive: bytes or int
    :param str hexstr: the message encoded as hex
    :param str text: the message as a series of unicode characters (a normal Py3 str)
    :returns: The EIP-191 encoded message, ready for signing

    .. _EIP-191: https://eips.ethereum.org/EIPS/eip-191
    """
    if not is_valid_address(validator_address):
        raise ValidationError(
            f"Cannot encode message with 'Validator Address': {validator_address}. "
            "It must be a checksum address, or an address converted to bytes.")
    message_bytes = to_bytes(primitive, hexstr=hexstr, text=text)
    return SignableMessage(
        HexBytes(b'\x00'),  # version 0, as defined in EIP-191
        to_canonical_address(validator_address),
        message_bytes,
    )
Beispiel #3
0
    def sign(self, message=None, private_key=None, message_hexstr=None, message_text=None):
        '''
        Sign the message provided. This is equivalent to
        :meth:`w3.eth.sign() <web3.eth.Eth.sign>`
        but with a local private key instead of an account in a connected client.

        Caller must supply exactly one of the message types:
        in bytes, a hex string, or a unicode string. The message will automatically
        be prepended with the text indicating that it is a message (preventing it
        from being used to sign a transaction). The prefix is:
        ``b'\\x19Ethereum Signed Message:\\n'``
        concatenated with the number of bytes in the message.

        :param message: the message message to be signed
        :type message: bytes or int
        :param private_key: the key to sign the message with
        :type private_key: hex str, bytes or int
        :param str message_hexstr: the message encoded as hex
        :param str message_text: the message as a series of unicode characters (a normal Py3 str)
        :returns: Various details about the signature - most
          importantly the fields: v, r, and s
        :rtype: ~eth_account.datastructures.AttributeDict

        .. code-block:: python

            >>> msg = "I♥SF"
            >>> key = "0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364"
            >>> Account.sign(message_text=msg, private_key=key)
            {'message': b'I\\xe2\\x99\\xa5SF',
             'messageHash': HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'),  # noqa: E501
             'r': 104389933075820307925104709181714897380569894203213074526835978196648170704563,
             's': 28205917190874851400050446352651915501321657673772411533993420917949420456142,
             'signature': HexBytes('0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c'),  # noqa: E501
             'v': 28}

            # these are all equivalent:
            >>> Account.sign(w3.toBytes(text=msg), key)
            >>> Account.sign(bytes(msg, encoding='utf-8'), key)

            >>> Web3.toHex(text=msg)
            '0x49e299a55346'
            >>> Account.sign(message_hexstr='0x49e299a55346', private_key=key)
            >>> Account.sign(0x49e299a55346, key)
        '''
        msg_bytes = to_bytes(message, hexstr=message_hexstr, text=message_text)
        msg_hash = self.hashMessage(msg_bytes)
        key_bytes = HexBytes(private_key)
        key = self._keys.PrivateKey(key_bytes)
        (v, r, s, eth_signature_bytes) = sign_message_hash(key, msg_hash)
        return AttributeDict({
            'message': HexBytes(msg_bytes),
            'messageHash': msg_hash,
            'r': r,
            's': s,
            'v': v,
            'signature': HexBytes(eth_signature_bytes),
        })
Beispiel #4
0
def verify(public_key: str,
           signature: str,
           message: Optional[str] = None,
           message_hash: Optional[str] = None) -> bool:
    """
    Verify XinFin signature by public key.

    :param public_key: XinFin public key.
    :type public_key: str.
    :param signature: Signed message data.
    :type signature: str.
    :param message: Message data, default to ``None``.
    :type message: str.
    :param message_hash: Message data hash, default to ``None``.
    :type message_hash: str.

    :return: bool -- Verified signature.

    >>> from pyxdc.signature import verify
    >>> verify(public_key="03d8799336beacc6b2e7f86f46bce4ad5cabf1ec7a0d6241416985e3b29fe1cc85", message="meherett", signature="74ad07a84b87fa3efa2f0e825506fb8bbee41021ca77a30e8ffa2bd66d47d99917d4a0587185e78a051a9cb80ebf65c7d62dbeedb7f9a029f961d70b52a10dc001")
    True
    >>> verify(public_key="03d8799336beacc6b2e7f86f46bce4ad5cabf1ec7a0d6241416985e3b29fe1cc85", message_hash="4bbbfd0c33fea618f4a9aa75c02fe76e50fa59798af021bc34f7856f3259c685", signature="74ad07a84b87fa3efa2f0e825506fb8bbee41021ca77a30e8ffa2bd66d47d99917d4a0587185e78a051a9cb80ebf65c7d62dbeedb7f9a029f961d70b52a10dc001")
    True
    """

    if message:
        message_bytes = curried.to_bytes(primitive=None,
                                         hexstr=None,
                                         text=message)
        msg_length = str(len(message_bytes)).encode('utf-8')
        joined = b'\x19' + b'E' + b'thereum Signed Message:\n' + msg_length + message_bytes
        keccak_256_message = keccak_256()
        keccak_256_message.update(joined)
        message_hash = keccak_256_message.digest()
    elif message_hash:
        message_hash = unhexlify(message_hash)
    else:
        raise ValueError("Message data or hash is required to sign.")

    signature = unhexlify(signature)
    validate_recoverable_signature_bytes(signature)
    r = big_endian_to_int(signature[0:32])
    s = big_endian_to_int(signature[32:64])
    v = ord(signature[64:65])
    validate_compressed_public_key_bytes(unhexlify(public_key))
    uncompressed_public_key = decompress_public_key(unhexlify(public_key))

    return ecdsa_raw_verify(msg_hash=message_hash,
                            rs=(r, s),
                            public_key_bytes=uncompressed_public_key)
Beispiel #5
0
def encode_defunct(primitive: bytes = None,
                   *,
                   hexstr: str = None,
                   text: str = None) -> SignableMessage:
    """
    Encoded as defined here: https://github.com/ethereum/eth-account/blob/master/eth_account/messages.py
    Except with Helios in the message instead of Ethereum
    """
    message_bytes = to_bytes(primitive, hexstr=hexstr, text=text)
    msg_length = str(len(message_bytes)).encode('utf-8')

    # Encoding version E defined by EIP-191
    return SignableMessage(
        b'H',
        b'elios Signed Message:\n' + msg_length,
        message_bytes,
    )
Beispiel #6
0
    def hashMessage(data=None, hexstr=None, text=None):
        '''
        Generate the message hash, including the prefix. See :meth:`~Account.sign`
        for more about the prefix. Supply exactly one of the three arguments.

        :param data: the message to sign, in primitive form
        :type data: bytes or int
        :param str hexstr: the message to sign, as a hex-encoded string
        :param str text: the message to sign, as a series of unicode points
        :returns: the hash of the message
        :rtype: ~hexbytes.main.HexBytes

        .. code-block:: python

            >>> msg = "I♥SF"
            >>> Account.hashMessage(text=msg)
            HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750')
        '''
        message_bytes = to_bytes(data, hexstr=hexstr, text=text)
        recovery_hasher = compose(HexBytes, keccak, signature_wrapper)
        return recovery_hasher(message_bytes)
Beispiel #7
0
def sign(private_key: str,
         message: Optional[str] = None,
         message_hash: Optional[str] = None) -> str:
    """
    Sign XinFin message data by private key.

    :param private_key: XinFin private key.
    :type private_key: str.
    :param message: Message data, default to ``None``.
    :type message: str.
    :param message_hash: Message data hash, default to ``None``.
    :type message_hash: str.

    :return: str -- XinFin signed message or signature.

    >>> from pyxdc.signature import sign
    >>> sign(private_key="4235d9ffc246d488d527177b654e7dd5c02f5c5abc2e2054038d6825224a24de", message="meherett")
    "74ad07a84b87fa3efa2f0e825506fb8bbee41021ca77a30e8ffa2bd66d47d99917d4a0587185e78a051a9cb80ebf65c7d62dbeedb7f9a029f961d70b52a10dc001"
    >>> sign(private_key="4235d9ffc246d488d527177b654e7dd5c02f5c5abc2e2054038d6825224a24de", message_hash="4bbbfd0c33fea618f4a9aa75c02fe76e50fa59798af021bc34f7856f3259c685")
    "74ad07a84b87fa3efa2f0e825506fb8bbee41021ca77a30e8ffa2bd66d47d99917d4a0587185e78a051a9cb80ebf65c7d62dbeedb7f9a029f961d70b52a10dc001"
    """

    if message:
        message_bytes = curried.to_bytes(primitive=None,
                                         hexstr=None,
                                         text=message)
        msg_length = str(len(message_bytes)).encode('utf-8')
        joined = b'\x19' + b'E' + b'thereum Signed Message:\n' + msg_length + message_bytes
        keccak_256_message = keccak_256()
        keccak_256_message.update(joined)
        message_hash = keccak_256_message.digest()
    elif message_hash:
        message_hash = unhexlify(message_hash)
    else:
        raise ValueError("Message data or hash is required to sign.")

    return ecdsa_raw_sign(msg_hash=message_hash,
                          private_key_bytes=unhexlify(private_key)).hex()