Implement signed ElGamal encryption
This commit is contained in:
parent
93f5dcfe42
commit
2ec44db65d
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
@ -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))
|
Loading…
Reference in New Issue
Block a user