diff --git a/eth_account/account.py b/eth_account/account.py index 93fbc44e..247af6dc 100644 --- a/eth_account/account.py +++ b/eth_account/account.py @@ -592,8 +592,9 @@ def signHash(self, message_hash, private_key): Instead, prefer :meth:`~eth_account.account.Account.sign_message`, which cannot accidentally sign a transaction. - .. CAUTION:: Deprecated for :meth:`~eth_account.account.Account.sign_message`. - This method will be removed in v0.6 + .. CAUTION:: Deprecated for + :meth:`~eth_account.account.Account.unsafe_sign_hash`. + This method will be removed in v0.13 :param message_hash: the 32-byte message hash to be signed :type message_hash: hex str, bytes or int @@ -604,12 +605,34 @@ def signHash(self, message_hash, private_key): :rtype: ~eth_account.datastructures.SignedMessage """ warnings.warn( - "signHash is deprecated in favor of sign_message", + "signHash is deprecated in favor of sign_message or unsafe_sign_hash" + " depending on your use case", category=DeprecationWarning, stacklevel=2, ) return self._sign_hash(message_hash, private_key) + @combomethod + def unsafe_sign_hash(self, message_hash, private_key): + """ + Sign the provided hash. + + .. WARNING:: *Never* sign a hash that you didn't generate, + it can be an arbitrary transaction. For example, it might + send all of your account's ether to an attacker. + Instead, prefer :meth:`~eth_account.account.Account.sign_message`, + which cannot accidentally sign a transaction. + + :param message_hash: the 32-byte message hash to be signed + :type message_hash: hex str, bytes or int + :param private_key: the key to sign the message with + :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey` + :returns: Various details about the signature - most + importantly the fields: v, r, and s + :rtype: ~eth_account.datastructures.SignedMessage + """ + return self._sign_hash(message_hash, private_key) + @combomethod def _sign_hash( self, diff --git a/eth_account/messages.py b/eth_account/messages.py index 721de314..121a1971 100644 --- a/eth_account/messages.py +++ b/eth_account/messages.py @@ -331,8 +331,9 @@ def defunct_hash_message( """ Convert the provided message into a message hash, to be signed. - .. CAUTION:: Intended for use with the deprecated - :meth:`eth_account.account.Account.signHash`. + .. CAUTION:: Intended for use with + :meth:`eth_account.account.Account.unsafe_sign_hash`. + or the deprecated :meth:`eth_account.account.Account.signHash`. This is for backwards compatibility only. All new implementations should use :meth:`encode_defunct` instead. diff --git a/eth_account/signers/base.py b/eth_account/signers/base.py index 508cdca6..5e93eb86 100644 --- a/eth_account/signers/base.py +++ b/eth_account/signers/base.py @@ -53,8 +53,26 @@ def signHash(self, message_hash): but without specifying the private key. .. CAUTION:: Deprecated for - :meth:`~eth_account.signers.base.BaseAccount.sign_message`. - To be removed in v0.6 + :meth:`~eth_account.signers.base.BaseAccount.unsafe_sign_hash`. + To be removed in v0.13 + + :param bytes message_hash: 32 byte hash of the message to sign + """ + + @abstractmethod + def unsafe_sign_hash(self, message_hash): + """ + Sign the hash of a message. + + .. WARNING:: *Never* sign a hash that you didn't generate, + it can be an arbitrary transaction. For example, it might + send all of your account's ether to an attacker. + Instead, prefer :meth:`~eth_account.account.Account.sign_message`, + which cannot accidentally sign a transaction. + + This uses the same structure + as in :meth:`~eth_account.account.Account.unsafe_sign_hash` + but without specifying the private key. :param bytes message_hash: 32 byte hash of the message to sign """ diff --git a/eth_account/signers/local.py b/eth_account/signers/local.py index 77524262..3745be1e 100644 --- a/eth_account/signers/local.py +++ b/eth_account/signers/local.py @@ -70,6 +70,12 @@ def signHash(self, message_hash): private_key=self.key, ) + def unsafe_sign_hash(self, message_hash): + return self._publicapi.unsafe_sign_hash( + message_hash, + private_key=self.key, + ) + def sign_message(self, signable_message): """ Generate a string with the encrypted key. diff --git a/newsfragments/263.deprecation.rst b/newsfragments/263.deprecation.rst new file mode 100644 index 00000000..35c1bcff --- /dev/null +++ b/newsfragments/263.deprecation.rst @@ -0,0 +1 @@ +Provide an upgrade path to remove ``signHash`` diff --git a/tests/core/test_accounts.py b/tests/core/test_accounts.py index 5a7b0239..e94214e9 100644 --- a/tests/core/test_accounts.py +++ b/tests/core/test_accounts.py @@ -441,6 +441,46 @@ def test_sign_message_against_sign_hash_as_hex(keyed_acct, message_bytes): assert signed_via_hash_hex == signed_via_message_hex +@given(st.binary()) +def test_sign_message_against_unsafe_sign_hash_as_bytes(keyed_acct, message_bytes): + # sign via hash + msg_hash = defunct_hash_message(message_bytes) + signed_via_hash = keyed_acct.unsafe_sign_hash(msg_hash) + + # sign via message + signable_message = encode_defunct(message_bytes) + signed_via_message = keyed_acct.sign_message(signable_message) + + assert signed_via_hash == signed_via_message + + +@given(st.binary()) +def test_sign_message_against_unsafe_sign_hash_as_hex(keyed_acct, message_bytes): + message_hex = to_hex(message_bytes) + + # sign via hash + msg_hash_hex = defunct_hash_message(hexstr=message_hex) + signed_via_hash_hex = keyed_acct.unsafe_sign_hash(msg_hash_hex) + + # sign via message + signable_message_hex = encode_defunct(hexstr=message_hex) + signed_via_message_hex = keyed_acct.sign_message(signable_message_hex) + + assert signed_via_hash_hex == signed_via_message_hex + + +@given(st.text()) +def test_sign_message_against_unsafe_sign_hash_as_text(keyed_acct, message_text): + # sign via hash + msg_hash = defunct_hash_message(text=message_text) + signed_via_hash = keyed_acct.unsafe_sign_hash(msg_hash) + + # sign via message + signable_message = encode_defunct(text=message_text) + signed_via_message = keyed_acct.sign_message(signable_message) + assert signed_via_hash == signed_via_message + + @pytest.mark.parametrize( "message, key, expected_bytes, expected_hash, v, r, s, signature", (