From a242f1f666aec5eb1787e3e11d7ac02f474e7302 Mon Sep 17 00:00:00 2001 From: Yingtong Li Date: Thu, 28 Sep 2017 20:02:20 +1000 Subject: [PATCH] Refactor hashing out into separate library --- eos/core/hashing/__init__.py | 82 +++++++++++++++++++ eos/core/{objects => hashing}/jssha-sha256.js | 0 eos/core/objects/__init__.py | 24 +----- eos/core/tests.py | 3 +- eos/js.py | 4 + eos/psr/crypto.py | 5 +- eos/psr/mixnet.py | 5 +- eos/psr/tests.py | 3 +- 8 files changed, 99 insertions(+), 27 deletions(-) create mode 100644 eos/core/hashing/__init__.py rename eos/core/{objects => hashing}/jssha-sha256.js (100%) diff --git a/eos/core/hashing/__init__.py b/eos/core/hashing/__init__.py new file mode 100644 index 0000000..ef40bb7 --- /dev/null +++ b/eos/core/hashing/__init__.py @@ -0,0 +1,82 @@ +# Eos - Verifiable elections +# Copyright © 2017 RunasSudo (Yingtong Li) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +try: + __pragma__ = __pragma__ + is_python = False +except: + is_python = True + def __pragma__(*args): + pass + +from eos.core.bigint import * + +# Libraries +# ========= + +if is_python: + __pragma__('skip') + import base64 + import hashlib + __pragma__('noskip') +else: + # Load jssha-sha256.js + lib = __pragma__('js', ''' + (function() {{ + {} + var exports = {{}}; + exports.jsSHA = window.jsSHA; + return exports; + }})()''', __include__('eos/core/hashing/jssha-sha256.js')) + +# Implementation +# ============== + +class SHA256: + def __init__(self): + if is_python: + self.impl = hashlib.sha256() + else: + # TNYI: This is completely borked + self.impl = __pragma__('js', '{}', 'new lib.jsSHA("SHA-256", "TEXT")') + + def update_text(self, *values): + for value in values: + if is_python: + self.impl.update(value.encode('utf-8')) + else: + self.impl.js_update(value) + return self + + def update_bigint(self, *values): + for value in values: + self.update_text(str(value)) + return self + + def hash_as_b64(self): + if is_python: + return base64.b64encode(self.impl.digest()).decode('utf-8') + else: + return self.impl.getHash('B64') + + def hash_as_hex(self): + if is_python: + return self.impl.hexdigest() + else: + return self.impl.getHash('HEX') + + def hash_as_bigint(self): + return BigInt(self.hash_as_hex(), 16) diff --git a/eos/core/objects/jssha-sha256.js b/eos/core/hashing/jssha-sha256.js similarity index 100% rename from eos/core/objects/jssha-sha256.js rename to eos/core/hashing/jssha-sha256.js diff --git a/eos/core/objects/__init__.py b/eos/core/objects/__init__.py index 63b82c6..bf0511d 100644 --- a/eos/core/objects/__init__.py +++ b/eos/core/objects/__init__.py @@ -36,16 +36,14 @@ if is_python: import uuid __pragma__('noskip') else: - # Load json.js, jssha-sha256.js + # Load json.js lib = __pragma__('js', ''' (function() {{ - {} {} var exports = {{}}; exports.stringify = stringify_main; - exports.jsSHA = window.jsSHA; return exports; - }})()''', __include__('eos/core/objects/json.js'), __include__('eos/core/objects/jssha-sha256.js')) + }})()''', __include__('eos/core/objects/json.js')) # Database # ======== @@ -155,7 +153,7 @@ class EosObject(metaclass=EosObjectType): @property def hash(self): - return EosObject.to_sha256(EosObject.to_json(EosObject.serialise_and_wrap(self)))[0] + return SHA256().update_text(EosObject.to_json(EosObject.serialise_and_wrap(self))).hash_as_b64() @staticmethod def serialise_and_wrap(value, object_type=None): @@ -184,22 +182,6 @@ class EosObject(metaclass=EosObjectType): return json.loads(value) else: return JSON.parse(value) - - @staticmethod - def to_sha256(*values): - from eos.core.bigint import BigInt - - if is_python: - sha_obj = hashlib.sha256() - for value in values: - sha_obj.update(value.encode('utf-8')) - return base64.b64encode(sha_obj.digest()).decode('utf-8'), BigInt(sha_obj.hexdigest(), 16) - else: - # TNYI: This is completely borked - sha_obj = __pragma__('js', '{}', 'new lib.jsSHA("SHA-256", "TEXT")') - for value in values: - sha_obj.js_update(value) - return sha_obj.getHash('B64'), BigInt(sha_obj.getHash('HEX'), 16) class EosList(EosObject): def __init__(self, *args): diff --git a/eos/core/tests.py b/eos/core/tests.py index 1658cfe..5d8490f 100644 --- a/eos/core/tests.py +++ b/eos/core/tests.py @@ -16,6 +16,7 @@ from eos.core.bigint import * from eos.core.objects import * +from eos.core.hashing import * # Common library things # =================== @@ -95,7 +96,7 @@ class ObjectTestCase(EosTestCase): class HashTestCase(EosTestCase): def test_hash(self): - self.assertEqual(EosObject.to_sha256('Hello World!')[0], 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=') + self.assertEqual(SHA256().update_text('Hello World!').hash_as_b64(), 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=') class BigIntTestCase(EosTestCase): def test_basic(self): diff --git a/eos/js.py b/eos/js.py index bfe188c..17eaf9f 100644 --- a/eos/js.py +++ b/eos/js.py @@ -16,9 +16,13 @@ import eos.core.objects import eos.core.bigint +import eos.core.hashing import eos.base.election import eos.base.workflow import eos.psr.bitstream import eos.psr.crypto +import eos.psr.election +import eos.psr.mixnet +import eos.psr.workflow diff --git a/eos/psr/crypto.py b/eos/psr/crypto.py index e401d84..fe16cec 100644 --- a/eos/psr/crypto.py +++ b/eos/psr/crypto.py @@ -16,6 +16,7 @@ from eos.core.bigint import * from eos.core.objects import * +from eos.core.hashing import * from eos.base.election import * class CyclicGroup(EmbeddedObject): @@ -120,7 +121,7 @@ class SEGPublicKey(EGPublicKey): gamma = pow(self.group.g, r, self.group.p) # h delta = (message * pow(self.X, r, self.group.p)) % self.group.p # f - _, c = EosObject.to_sha256(str(pow(self.group.g, s, self.group.p)), str(gamma), str(delta)) + c = SHA256().update_bigint(pow(self.group.g, s, self.group.p), gamma, delta).hash_as_bigint() z = s + c*r @@ -136,6 +137,6 @@ class SEGCiphertext(EGCiphertext): def is_signature_valid(self): gs = (pow(self.public_key.group.g, self.z, self.public_key.group.p) * pow(self.gamma, self.public_key.group.p - ONE - self.c, self.public_key.group.p)) % self.public_key.group.p - _, c = EosObject.to_sha256(str(gs), str(self.gamma), str(self.delta)) + c = SHA256().update_bigint(gs, self.gamma, self.delta).hash_as_bigint() return self.c == c diff --git a/eos/psr/mixnet.py b/eos/psr/mixnet.py index 564280c..1d3c0a9 100644 --- a/eos/psr/mixnet.py +++ b/eos/psr/mixnet.py @@ -16,6 +16,7 @@ from eos.core.bigint import * from eos.core.objects import * +from eos.core.hashing import * from eos.psr.election import * class RPCMixnet: @@ -63,7 +64,7 @@ class RPCMixnet: for i in range(len(permutations_and_reenc)): val = permutations_and_reenc[i] val_obj = MixChallengeResponse(index=val[0], reenc=val[1], rand=val[2]) - commitments.append(EosObject.to_sha256(EosObject.to_json(val_obj.serialise()))[1]) + commitments.append(SHA256().update_text(EosObject.to_json(val_obj.serialise())).hash_as_bigint()) else: for i in range(len(permutations_and_reenc)): # Find the answer that went to 'i' @@ -71,7 +72,7 @@ class RPCMixnet: val = permutations_and_reenc[idx] val_obj = MixChallengeResponse(index=idx, reenc=val[1], rand=val[3]) - commitments.append(EosObject.to_sha256(EosObject.to_json(val_obj.serialise()))[1]) + commitments.append(SHA256().update_text(EosObject.to_json(val_obj.serialise())).hash_as_bigint()) self.params = permutations_and_reenc return shuffled_answers, commitments diff --git a/eos/psr/tests.py b/eos/psr/tests.py index 2d9ce0e..8b14122 100644 --- a/eos/psr/tests.py +++ b/eos/psr/tests.py @@ -17,6 +17,7 @@ from eos.core.tests import * from eos.core.bigint import * +from eos.core.hashing import * from eos.psr.bitstream import * from eos.psr.crypto import * from eos.psr.election import * @@ -158,7 +159,7 @@ class MixnetTestCase(EosTestCase): for i in range(len(pts)): perm, reencs, rand = mixnet.challenge(i) val_obj = MixChallengeResponse(index=perm, reenc=reencs, rand=rand) - self.assertEqual(commitments[i], EosObject.to_sha256(EosObject.to_json(val_obj.serialise()))[1]) + self.assertEqual(commitments[i], SHA256().update_text(EosObject.to_json(val_obj.serialise())).hash_as_bigint()) if mixnet.is_left: verify_shuffle(i, perm, reencs)