Implement signed ElGamal encryption
This commit is contained in:
parent
93f5dcfe42
commit
2ec44db65d
@ -150,7 +150,7 @@ class EosObject(metaclass=EosObjectType):
|
||||
|
||||
@property
|
||||
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
|
||||
def serialise_and_wrap(value, object_type=None):
|
||||
@ -179,16 +179,20 @@ class EosObject(metaclass=EosObjectType):
|
||||
return JSON.parse(value)
|
||||
|
||||
@staticmethod
|
||||
def to_sha256(value):
|
||||
def to_sha256(*values):
|
||||
from eos.core.bigint import BigInt
|
||||
|
||||
if is_python:
|
||||
sha_obj = hashlib.sha256()
|
||||
sha_obj.update(value.encode('utf-8'))
|
||||
return base64.b64encode(sha_obj.digest()).decode('utf-8')
|
||||
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")')
|
||||
sha_obj.js_update(value)
|
||||
return sha_obj.getHash('B64')
|
||||
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):
|
||||
|
@ -95,7 +95,7 @@ class ObjectTestCase(EosTestCase):
|
||||
|
||||
class HashTestCase(EosTestCase):
|
||||
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):
|
||||
def test_basic(self):
|
||||
|
@ -16,4 +16,9 @@
|
||||
|
||||
import eos.core.objects
|
||||
import eos.core.bigint
|
||||
import eos.core.bitstring
|
||||
|
||||
import eos.base.election
|
||||
import eos.base.workflow
|
||||
|
||||
import eos.psr.bitstream
|
||||
import eos.psr.crypto
|
||||
|
@ -18,4 +18,4 @@ import eos.js
|
||||
|
||||
import eos.core.tests
|
||||
import eos.base.tests
|
||||
import eos.psgjjr.tests
|
||||
import eos.psr.tests
|
||||
|
@ -58,19 +58,21 @@ class EGPublicKey(EmbeddedObject):
|
||||
return EGCiphertext(public_key=self, gamma=gamma, delta=delta)
|
||||
|
||||
class EGPrivateKey(EmbeddedObject):
|
||||
pk_class = EGPublicKey
|
||||
|
||||
public_key = EmbeddedObjectField(EGPublicKey)
|
||||
x = EmbeddedObjectField(BigInt)
|
||||
|
||||
# HAC 8.17
|
||||
@staticmethod
|
||||
def generate(group=DEFAULT_GROUP):
|
||||
@classmethod
|
||||
def generate(cls, group=DEFAULT_GROUP):
|
||||
# Choose an element 1 <= x <= p - 2
|
||||
x = BigInt.crypto_random(ONE, group.p - TWO)
|
||||
# Calculate the public key as G^x
|
||||
X = pow(group.g, x, group.p)
|
||||
|
||||
pk = EGPublicKey(group=group, X=X)
|
||||
sk = EGPrivateKey(public_key=pk, x=x)
|
||||
pk = cls.pk_class(group=group, X=X)
|
||||
sk = cls(public_key=pk, x=x)
|
||||
return sk
|
||||
|
||||
# HAC 8.18
|
||||
@ -90,3 +92,52 @@ class EGCiphertext(EmbeddedObject):
|
||||
public_key = EmbeddedObjectField(EGPublicKey)
|
||||
gamma = EmbeddedObjectField(BigInt) # G^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)
|
@ -17,8 +17,8 @@
|
||||
from eos.core.tests import *
|
||||
|
||||
from eos.core.bigint import *
|
||||
from eos.psgjjr.bitstream import *
|
||||
from eos.psgjjr.crypto import *
|
||||
from eos.psr.bitstream import *
|
||||
from eos.psr.crypto import *
|
||||
|
||||
class EGTestCase(EosTestCase):
|
||||
def test_eg(self):
|
||||
@ -28,6 +28,14 @@ class EGTestCase(EosTestCase):
|
||||
m = sk.decrypt(ct)
|
||||
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):
|
||||
def test_bitstream(self):
|
||||
bs = BitStream(BigInt('100101011011', 2))
|
Loading…
Reference in New Issue
Block a user