commit 8895490e763e50bb96b7b49a785f079a4ae727d6
parent 176cf237ff58d39bddc6817ba0b2174218252ecf
Author: lash <dev@holbrook.no>
Date: Mon, 27 Mar 2023 14:56:10 +0100
Add validator message handling (ERC191 v0x00)
Diffstat:
6 files changed, 71 insertions(+), 10 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
@@ -1,3 +1,6 @@
+* 0.6.5
+ - Add signer for ERC191 validator message (version 00)
+ - Expose encoded messages for signing
* 0.6.4
- Add passphrase file option to keyfile cli command
* 0.6.3
diff --git a/funga/eth/message.py b/funga/eth/message.py
@@ -0,0 +1,32 @@
+# external imports
+import sha3
+
+# local imports
+from .encoding import to_checksum_address
+
+
+def to_digest(data):
+ h = sha3.keccak_256()
+ h.update(data)
+ return h.digest()
+
+# ERC191 - version 0x00
+def to_validator_message(data, validator, digest=False):
+ a = to_checksum_address(validator)
+ v = bytes.fromhex(a)
+ r = b'\x19\x00' + v + data
+ if digest:
+ r = to_digest(r)
+ return r
+
+
+# ERC191 - version 0x45
+def to_personal_message(data, digest=False):
+ ethereumed_message_header = b'\x19\x45' + 'thereum Signed Message:\n{}'.format(len(data)).encode('utf-8')
+ r = ethereumed_message_header + data
+ if digest:
+ r = to_digest(r)
+ return r
+
+
+
diff --git a/funga/eth/runnable/msg.py b/funga/eth/runnable/msg.py
@@ -25,6 +25,7 @@ argparser.add_argument('-z', action='store_true', help='zero-length password')
argparser.add_argument('-v', action='store_true', help='be verbose')
argparser.add_argument('-0', dest='nonl', action='store_true', help='no newline at end of output')
argparser.add_argument('-b', '--binary', dest='binary', action='store_true', help='parse input as binary hex')
+argparser.add_argument('--validator', type=str, help='if set, will sign an ERC191 version 0 message')
argparser.add_argument('msg', type=str, help='Message to sign')
args = argparser.parse_args()
@@ -50,7 +51,14 @@ def main():
msg = bytes.fromhex(hx)
else:
msg = args.msg.encode('utf-8').hex()
- sig = signer.sign_ethereum_message(address, msg, password=passphrase)
+
+ sig = None
+ if args.validator:
+ logg.info('signing validator message (ERC191 version 0)')
+ sig = signer.sign_validator_message(address, args.validator, msg, password=passphrase)
+ else:
+ logg.info('signing personal message (ERC191 version 0x45)')
+ sig = signer.sign_ethereum_message(address, msg, password=passphrase)
r = sig.hex()
if not args.nonl:
diff --git a/funga/eth/signer/defaultsigner.py b/funga/eth/signer/defaultsigner.py
@@ -9,6 +9,8 @@ from hexathon import int_to_minbytes
# local imports
from funga.signer import Signer
from funga.eth.encoding import chain_id_to_v
+from funga.eth.message import to_personal_message
+from funga.eth.message import to_validator_message
logg = logging.getLogger(__name__)
@@ -41,7 +43,6 @@ class EIP155Signer(Signer):
def sign_ethereum_message(self, address, message, password=None):
-
#k = keys.PrivateKey(self.keyGetter.get(address, password))
#z = keys.ecdsa_sign(message_hash=g, private_key=k)
if type(message).__name__ == 'str':
@@ -55,15 +56,30 @@ class EIP155Signer(Signer):
logg.debug('unhandled format {}'.format(type(message).__name__))
raise ValueError('message must be type str or bytes, received {}'.format(type(message).__name__))
- ethereumed_message_header = b'\x19' + 'Ethereum Signed Message:\n{}'.format(len(message)).encode('utf-8')
- h = sha3.keccak_256()
- h.update(ethereumed_message_header + message)
- message_to_sign = h.digest()
-
+ message_to_sign = to_personal_message(message, digest=True)
z = self.sign_pure(address, message_to_sign, password)
return z
+ def sign_validator_message(self, address, validator, message, password=None):
+ #k = keys.PrivateKey(self.keyGetter.get(address, password))
+ #z = keys.ecdsa_sign(message_hash=g, private_key=k)
+ if type(message).__name__ == 'str':
+ logg.debug('signing message in "str" format: {}'.format(message))
+ #z = k.sign_msg(bytes.fromhex(message))
+ message = bytes.fromhex(message)
+ elif type(message).__name__ == 'bytes':
+ logg.debug('signing message in "bytes" format: {}'.format(message.hex()))
+ #z = k.sign_msg(message)
+ else:
+ logg.debug('unhandled format {}'.format(type(message).__name__))
+ raise ValueError('message must be type str or bytes, received {}'.format(type(message).__name__))
+
+ message_to_sign = to_validator_message(message, validator, digest=True)
+ z = self.sign_pure(address, message_to_sign, password)
+ return z
+
+
# TODO: generic sign should be moved to non-eth context
def sign_pure(self, address, message, password=None):
pk = coincurve.PrivateKey(secret=self.keyGetter.get(address, password))
@@ -77,3 +93,5 @@ class EIP155Signer(Signer):
elif dialect == 'eth':
return self.sign_ethereum_message(address, message, password=password)
raise ValueError('Unknown message sign dialect "{}"'.format(dialect))
+
+
diff --git a/requirements.txt b/requirements.txt
@@ -7,4 +7,4 @@ confini~=0.6.0
coincurve==15.0.0
hexathon~=0.1.6
pycryptodome==3.10.1
-funga==0.5.2
+funga~=0.5.2
diff --git a/setup.py b/setup.py
@@ -33,7 +33,7 @@ f.close()
setup(
name="funga-eth",
- version="0.6.4",
+ version="0.6.5",
description="Ethereum implementation of the funga keystore and signer",
author="Louis Holbrook",
author_email="dev@holbrook.no",
@@ -58,6 +58,6 @@ setup(
'eth-sign-msg=funga.eth.runnable.msg:main',
],
},
- url='https://git.defalsify.org/funga-eth.git',
+ url='https://git.defalsify.org/funga-eth',
include_package_data=True,
)