Implement signed ElGamal encryption

This commit is contained in:
Yingtong Li 2017-09-27 18:09:15 +10:00
parent 93f5dcfe42
commit 2ec44db65d
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
7 changed files with 83 additions and 15 deletions

View File

@ -150,7 +150,7 @@ class EosObject(metaclass=EosObjectType):
@property @property
def hash(self): def hash(self):
return EosObject.to_sha256(EosObject.serialise_and_wrap(self)) return EosObject.to_sha256(EosObject.to_json(EosObject.serialise_and_wrap(self)))[0]
@staticmethod @staticmethod
def serialise_and_wrap(value, object_type=None): def serialise_and_wrap(value, object_type=None):
@ -179,16 +179,20 @@ class EosObject(metaclass=EosObjectType):
return JSON.parse(value) return JSON.parse(value)
@staticmethod @staticmethod
def to_sha256(value): def to_sha256(*values):
from eos.core.bigint import BigInt
if is_python: if is_python:
sha_obj = hashlib.sha256() sha_obj = hashlib.sha256()
for value in values:
sha_obj.update(value.encode('utf-8')) sha_obj.update(value.encode('utf-8'))
return base64.b64encode(sha_obj.digest()).decode('utf-8') return base64.b64encode(sha_obj.digest()).decode('utf-8'), BigInt(sha_obj.hexdigest(), 16)
else: else:
# TNYI: This is completely borked # TNYI: This is completely borked
sha_obj = __pragma__('js', '{}', 'new lib.jsSHA("SHA-256", "TEXT")') sha_obj = __pragma__('js', '{}', 'new lib.jsSHA("SHA-256", "TEXT")')
for value in values:
sha_obj.js_update(value) sha_obj.js_update(value)
return sha_obj.getHash('B64') return sha_obj.getHash('B64'), BigInt(sha_obj.getHash('HEX'), 16)
class EosList(EosObject): class EosList(EosObject):
def __init__(self, *args): def __init__(self, *args):

View File

@ -95,7 +95,7 @@ class ObjectTestCase(EosTestCase):
class HashTestCase(EosTestCase): class HashTestCase(EosTestCase):
def test_hash(self): def test_hash(self):
self.assertEqual(EosObject.to_sha256('Hello World!'), 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=') self.assertEqual(EosObject.to_sha256('Hello World!')[0], 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=')
class BigIntTestCase(EosTestCase): class BigIntTestCase(EosTestCase):
def test_basic(self): def test_basic(self):

View File

@ -16,4 +16,9 @@
import eos.core.objects import eos.core.objects
import eos.core.bigint import eos.core.bigint
import eos.core.bitstring
import eos.base.election
import eos.base.workflow
import eos.psr.bitstream
import eos.psr.crypto

View File

@ -18,4 +18,4 @@ import eos.js
import eos.core.tests import eos.core.tests
import eos.base.tests import eos.base.tests
import eos.psgjjr.tests import eos.psr.tests

View File

@ -58,19 +58,21 @@ class EGPublicKey(EmbeddedObject):
return EGCiphertext(public_key=self, gamma=gamma, delta=delta) return EGCiphertext(public_key=self, gamma=gamma, delta=delta)
class EGPrivateKey(EmbeddedObject): class EGPrivateKey(EmbeddedObject):
pk_class = EGPublicKey
public_key = EmbeddedObjectField(EGPublicKey) public_key = EmbeddedObjectField(EGPublicKey)
x = EmbeddedObjectField(BigInt) x = EmbeddedObjectField(BigInt)
# HAC 8.17 # HAC 8.17
@staticmethod @classmethod
def generate(group=DEFAULT_GROUP): def generate(cls, group=DEFAULT_GROUP):
# Choose an element 1 <= x <= p - 2 # Choose an element 1 <= x <= p - 2
x = BigInt.crypto_random(ONE, group.p - TWO) x = BigInt.crypto_random(ONE, group.p - TWO)
# Calculate the public key as G^x # Calculate the public key as G^x
X = pow(group.g, x, group.p) X = pow(group.g, x, group.p)
pk = EGPublicKey(group=group, X=X) pk = cls.pk_class(group=group, X=X)
sk = EGPrivateKey(public_key=pk, x=x) sk = cls(public_key=pk, x=x)
return sk return sk
# HAC 8.18 # HAC 8.18
@ -90,3 +92,52 @@ class EGCiphertext(EmbeddedObject):
public_key = EmbeddedObjectField(EGPublicKey) public_key = EmbeddedObjectField(EGPublicKey)
gamma = EmbeddedObjectField(BigInt) # G^k gamma = EmbeddedObjectField(BigInt) # G^k
delta = EmbeddedObjectField(BigInt) # M X^k delta = EmbeddedObjectField(BigInt) # M X^k
# Signed ElGamal per Schnorr & Jakobssen
class SEGPublicKey(EGPublicKey):
def encrypt(self, message):
message += ONE # Dodgy hack to allow zeroes
if message <= ZERO:
raise Exception('Invalid message')
if message >= self.group.p:
raise Exception('Invalid message')
# Choose an element 1 <= k <= p - 2
r = BigInt.crypto_random(ONE, self.group.p - TWO)
s = BigInt.crypto_random(ONE, self.group.p - TWO)
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))
z = s + c*r
return SEGCiphertext(public_key=self, gamma=gamma, delta=delta, c=c, z=z)
class SEGPrivateKey(EGPrivateKey):
pk_class = SEGPublicKey
def decrypt(self, ciphertext):
if (
ciphertext.gamma <= ZERO or ciphertext.gamma >= self.public_key.group.p or
ciphertext.delta <= ZERO or ciphertext.delta >= self.public_key.group.p
):
raise Exception('Ciphertext is malformed')
gs = (pow(self.public_key.group.g, ciphertext.z, self.public_key.group.p) * pow(ciphertext.gamma, self.public_key.group.p - ONE - ciphertext.c, self.public_key.group.p)) % self.public_key.group.p
_, c = EosObject.to_sha256(str(gs), str(ciphertext.gamma), str(ciphertext.delta))
if ciphertext.c != c:
raise Exception('Signature is invalid')
gamma_inv = pow(ciphertext.gamma, self.public_key.group.p - ONE - self.x, self.public_key.group.p)
pt = (gamma_inv * ciphertext.delta) % self.public_key.group.p
return pt - ONE
class SEGCiphertext(EGCiphertext):
public_key = EmbeddedObjectField(SEGPublicKey)
c = EmbeddedObjectField(BigInt)
z = EmbeddedObjectField(BigInt)

View File

@ -17,8 +17,8 @@
from eos.core.tests import * from eos.core.tests import *
from eos.core.bigint import * from eos.core.bigint import *
from eos.psgjjr.bitstream import * from eos.psr.bitstream import *
from eos.psgjjr.crypto import * from eos.psr.crypto import *
class EGTestCase(EosTestCase): class EGTestCase(EosTestCase):
def test_eg(self): def test_eg(self):
@ -28,6 +28,14 @@ class EGTestCase(EosTestCase):
m = sk.decrypt(ct) m = sk.decrypt(ct)
self.assertEqualJSON(pt, m) self.assertEqualJSON(pt, m)
class SEGTestCase(EosTestCase):
def test_eg(self):
pt = DEFAULT_GROUP.random_element()
sk = SEGPrivateKey.generate()
ct = sk.public_key.encrypt(pt)
m = sk.decrypt(ct)
self.assertEqualJSON(pt, m)
class BitStreamTestCase(EosTestCase): class BitStreamTestCase(EosTestCase):
def test_bitstream(self): def test_bitstream(self):
bs = BitStream(BigInt('100101011011', 2)) bs = BitStream(BigInt('100101011011', 2))