commit 240cbbf372da62257abbf65ddb115ef229e6695c
parent 8895490e763e50bb96b7b49a785f079a4ae727d6
Author: lash <dev@holbrook.no>
Date: Wed, 29 Mar 2023 10:29:41 +0100
Add typed data signing support (ERC191 v0x01 / ERC712)
Diffstat:
5 files changed, 26 insertions(+), 79 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
@@ -1,3 +1,5 @@
+* 0.6.6
+ - EIP712 Typed data signer support
* 0.6.5
- Add signer for ERC191 validator message (version 00)
- Expose encoded messages for signing
diff --git a/funga/eth/message.py b/funga/eth/message.py
@@ -1,9 +1,11 @@
# external imports
import sha3
+import logging
# local imports
from .encoding import to_checksum_address
+logg = logging.getLogger(__name__)
def to_digest(data):
h = sha3.keccak_256()
@@ -15,6 +17,18 @@ def to_validator_message(data, validator, digest=False):
a = to_checksum_address(validator)
v = bytes.fromhex(a)
r = b'\x19\x00' + v + data
+ logg.debug('raw message data: ' + r.hex())
+ if digest:
+ r = to_digest(r)
+ return r
+
+
+# ERC191/ERC712 - version 0x01
+def to_typed_message(data, domain, digest=False):
+ assert len(data) == 32
+ assert len(domain) == 32
+ r = b'\x19\x01' + domain + data
+ logg.debug('raw message data: ' + r.hex())
if digest:
r = to_digest(r)
return r
@@ -24,6 +38,7 @@ def to_validator_message(data, validator, digest=False):
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
+ logg.debug('raw message data: ' + r.hex())
if digest:
r = to_digest(r)
return r
diff --git a/funga/eth/signer/defaultsigner.py b/funga/eth/signer/defaultsigner.py
@@ -11,6 +11,7 @@ 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
+from funga.eth.message import to_typed_message
logg = logging.getLogger(__name__)
@@ -62,24 +63,17 @@ class EIP155Signer(Signer):
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
-
+
+ def sign_typed_message(self, address, domain, message, password=None):
+ message_to_sign = to_typed_message(message, domain, 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))
diff --git a/setup.py b/setup.py
@@ -33,7 +33,7 @@ f.close()
setup(
name="funga-eth",
- version="0.6.5",
+ version="0.6.6",
description="Ethereum implementation of the funga keystore and signer",
author="Louis Holbrook",
author_email="dev@holbrook.no",
diff --git a/tests/test_keystore_reference.py b/tests/test_keystore_reference.py
@@ -1,64 +0,0 @@
-#!/usr/bin/python
-
-# standard imports
-import unittest
-import logging
-import base64
-import os
-
-# external imports
-import psycopg2
-from psycopg2 import sql
-from cryptography.fernet import Fernet, InvalidToken
-
-# local imports
-from funga.eth.keystore.sql import SQLKeystore
-from funga.error import UnknownAccountError
-
-logging.basicConfig(level=logging.DEBUG)
-logg = logging.getLogger()
-
-
-class TestDatabase(unittest.TestCase):
-
- conn = None
- cur = None
- symkey = None
- address_hex = None
- db = None
-
- def setUp(self):
- logg.debug('setup')
- # arbitrary value
- symkey_hex = 'E92431CAEE69313A7BE9E443C4ABEED9BF8157E9A13553B4D5D6E7D51B5021D9'
- self.symkey = bytes.fromhex(symkey_hex)
- self.address_hex = '9FA61f0E52A5C51b43f0d32404625BC436bb7041'
-
- kw = {
- 'symmetric_key': self.symkey,
- }
- self.db = SQLKeystore('postgres+psycopg2://postgres@localhost:5432/signer_test', **kw)
- self.address_hex = self.db.new('foo')
- #self.address_hex = add_0x(address_hex)
-
-
- def tearDown(self):
- self.db.db_session.execute('DROP INDEX ethereum_address_idx;')
- self.db.db_session.execute('DROP TABLE ethereum;')
- self.db.db_session.commit()
-
-
-
- def test_get_key(self):
- logg.debug('getting {}'.format(self.address_hex))
- self.db.get(self.address_hex, 'foo')
- with self.assertRaises(InvalidToken):
- self.db.get(self.address_hex, 'bar')
-
- bogus_account = '0x' + os.urandom(20).hex()
- with self.assertRaises(UnknownAccountError):
- self.db.get(bogus_account, 'bar')
-
-
-if __name__ == '__main__':
- unittest.main()