Implement basic crypto
This commit is contained in:
parent
a2f0859ac3
commit
b2a568ddd5
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@
|
|||||||
/venv
|
/venv
|
||||||
__javascript__
|
__javascript__
|
||||||
__pycache__
|
__pycache__
|
||||||
|
refs
|
||||||
|
@ -28,11 +28,11 @@ for f in eos.js_tests; do
|
|||||||
perl -0777 -pi -e "s/__pragma__ \('.*?'\)//gs" eos/__javascript__/$f.js
|
perl -0777 -pi -e "s/__pragma__ \('.*?'\)//gs" eos/__javascript__/$f.js
|
||||||
|
|
||||||
# Transcrypt by default suppresses stack traces for some reason??
|
# Transcrypt by default suppresses stack traces for some reason??
|
||||||
perl -0777 -pi -e "s/__except0__.__cause__ = null;//g" eos/__javascript__/$f.js
|
perl -0777 -pi -e 's/__except0__.__cause__ = null;//g' eos/__javascript__/$f.js
|
||||||
|
|
||||||
# Disable handling of special attributes
|
# Disable handling of special attributes
|
||||||
perl -0777 -pi -e "s/var __specialattrib__ = function \(attrib\) \{/var __specialattrib__ = function (attrib) { return false;/g" eos/__javascript__/$f.js
|
perl -0777 -pi -e 's/var __specialattrib__ = function \(attrib\) \{/var __specialattrib__ = function (attrib) { return false;/g' eos/__javascript__/$f.js
|
||||||
|
|
||||||
# Transcrypt bug
|
# Transcrypt bug
|
||||||
perl -0777 -pi -e "s/EosObject.EosObject/EosObject/g" eos/__javascript__/$f.js
|
perl -0777 -pi -e 's/property.call \((.*?), \g1.\g1.__impl__(.*?)\)/property.call ($1, $1.__impl__$2)/g' eos/__javascript__/$f.js
|
||||||
done
|
done
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
from eos.core.objects import EosObject
|
from eos.core.objects import EosObject
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
# Load jsbn{,2}.js
|
# Load jsbn{,2}.js
|
||||||
lib = __pragma__('js', '''
|
lib = __pragma__('js', '''
|
||||||
(function() {{
|
(function() {{
|
||||||
@ -34,6 +36,8 @@ lib = __pragma__('js', '''
|
|||||||
|
|
||||||
class BigInt(EosObject):
|
class BigInt(EosObject):
|
||||||
def __init__(self, a, b=10):
|
def __init__(self, a, b=10):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
if isinstance(a, str):
|
if isinstance(a, str):
|
||||||
self.impl = lib.nbi()
|
self.impl = lib.nbi()
|
||||||
self.impl.fromString(a, b)
|
self.impl.fromString(a, b)
|
||||||
@ -94,6 +98,41 @@ class BigInt(EosObject):
|
|||||||
modulo = BigInt(modulo)
|
modulo = BigInt(modulo)
|
||||||
return BigInt(self.impl.modPow(other.impl, modulo.impl))
|
return BigInt(self.impl.modPow(other.impl, modulo.impl))
|
||||||
|
|
||||||
|
def serialise(self):
|
||||||
|
return str(self)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def deserialise(cls, value):
|
||||||
|
return cls(value)
|
||||||
|
|
||||||
|
# Returns a random BigInt from lower_bound to upper_bound, both inclusive
|
||||||
|
@classmethod
|
||||||
|
def noncrypto_random(cls, lower_bound, upper_bound):
|
||||||
|
if not isinstance(lower_bound, cls):
|
||||||
|
lower_bound = cls(lower_bound)
|
||||||
|
if not isinstance(upper_bound, cls):
|
||||||
|
upper_bound = cls(upper_bound)
|
||||||
|
|
||||||
|
bound_range = upper_bound - lower_bound + 1
|
||||||
|
bound_range_bits = bound_range.impl.bitLength()
|
||||||
|
|
||||||
|
# Generate a sufficiently large number; work 32 bits at a time
|
||||||
|
current_range = 0 # bits
|
||||||
|
max_int = 2 ** 32 - 1
|
||||||
|
big_number = cls(0)
|
||||||
|
while current_range < bound_range_bits:
|
||||||
|
random_number = cls(random.randint(0, max_int))
|
||||||
|
big_number = (big_number << 32) | random_number
|
||||||
|
current_range = current_range + 32
|
||||||
|
|
||||||
|
# Since this is the non-crypto version, just do it modulo
|
||||||
|
return lower_bound + (big_number % bound_range)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def crypto_random(cls, lower_bound, upper_bound):
|
||||||
|
# TODO
|
||||||
|
return cls.noncrypto_random(lower_bound, upper_bound)
|
||||||
|
|
||||||
# TNYI: No native pow() support
|
# TNYI: No native pow() support
|
||||||
def pow(a, b, c=None):
|
def pow(a, b, c=None):
|
||||||
if not isinstance(a, BigInt):
|
if not isinstance(a, BigInt):
|
||||||
|
@ -16,8 +16,13 @@
|
|||||||
|
|
||||||
from eos.core.objects import EosObject
|
from eos.core.objects import EosObject
|
||||||
|
|
||||||
|
import random
|
||||||
|
system_random = random.SystemRandom()
|
||||||
|
|
||||||
class BigInt(EosObject):
|
class BigInt(EosObject):
|
||||||
def __init__(self, a, b=10):
|
def __init__(self, a, b=10):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
self.impl = int(a, b) if isinstance(a, str) else int(a)
|
self.impl = int(a, b) if isinstance(a, str) else int(a)
|
||||||
|
|
||||||
def __pow__(self, other, modulo=None):
|
def __pow__(self, other, modulo=None):
|
||||||
@ -29,11 +34,23 @@ class BigInt(EosObject):
|
|||||||
modulo = BigInt(modulo)
|
modulo = BigInt(modulo)
|
||||||
return BigInt(self.impl.__pow__(other.impl, modulo.impl))
|
return BigInt(self.impl.__pow__(other.impl, modulo.impl))
|
||||||
|
|
||||||
for func in [
|
def serialise(self):
|
||||||
'__add__', '__sub__', '__mul__', '__mod__', '__or__', '__lshift__', '__xor__',
|
return str(self)
|
||||||
'__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__',
|
|
||||||
'__str__'
|
@classmethod
|
||||||
]:
|
def deserialise(cls, value):
|
||||||
|
return cls(value)
|
||||||
|
|
||||||
|
# Returns a random BigInt from lower_bound to upper_bound, both inclusive
|
||||||
|
@classmethod
|
||||||
|
def noncrypto_random(cls, lower_bound, upper_bound):
|
||||||
|
return cls(random.randint(int(lower_bound), int(upper_bound)))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def crypto_random(cls, lower_bound, upper_bound):
|
||||||
|
return cls(system_random.randint(int(lower_bound), int(upper_bound)))
|
||||||
|
|
||||||
|
for func in ['__add__', '__sub__', '__mul__', '__mod__', '__or__', '__lshift__', '__xor__']:
|
||||||
def make_operator_func(func_):
|
def make_operator_func(func_):
|
||||||
# Create a closure
|
# Create a closure
|
||||||
def operator_func(self, other):
|
def operator_func(self, other):
|
||||||
@ -42,3 +59,21 @@ for func in [
|
|||||||
return BigInt(getattr(self.impl, func_)(other.impl))
|
return BigInt(getattr(self.impl, func_)(other.impl))
|
||||||
return operator_func
|
return operator_func
|
||||||
setattr(BigInt, func, make_operator_func(func))
|
setattr(BigInt, func, make_operator_func(func))
|
||||||
|
|
||||||
|
for func in ['__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__']:
|
||||||
|
def make_operator_func(func_):
|
||||||
|
# Create a closure
|
||||||
|
def operator_func(self, other):
|
||||||
|
if not isinstance(other, BigInt):
|
||||||
|
other = BigInt(other)
|
||||||
|
return getattr(self.impl, func_)(other.impl)
|
||||||
|
return operator_func
|
||||||
|
setattr(BigInt, func, make_operator_func(func))
|
||||||
|
|
||||||
|
for func in ['__str__', '__int__']:
|
||||||
|
def make_operator_func(func_):
|
||||||
|
# Create a closure
|
||||||
|
def operator_func(self):
|
||||||
|
return getattr(self.impl, func_)()
|
||||||
|
return operator_func
|
||||||
|
setattr(BigInt, func, make_operator_func(func))
|
||||||
|
@ -38,6 +38,10 @@ class EosTestCase:
|
|||||||
raise Error('Assertion failed: ' + str(a) + ' != ' + str(b))
|
raise Error('Assertion failed: ' + str(a) + ' != ' + str(b))
|
||||||
|
|
||||||
def assertEqualJSON(self, a, b):
|
def assertEqualJSON(self, a, b):
|
||||||
|
if isinstance(a, EosObject):
|
||||||
|
a = EosObject.serialise_and_wrap(a)
|
||||||
|
if isinstance(b, EosObject):
|
||||||
|
b = EosObject.serialise_and_wrap(b)
|
||||||
self.assertEqual(EosObject.to_json(a), EosObject.to_json(b))
|
self.assertEqual(EosObject.to_json(a), EosObject.to_json(b))
|
||||||
|
|
||||||
def py_only(func):
|
def py_only(func):
|
||||||
|
@ -18,3 +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
|
||||||
|
84
eos/psgjjr/crypto.py
Normal file
84
eos/psgjjr/crypto.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from eos.core.bigint import *
|
||||||
|
from eos.core.objects import *
|
||||||
|
|
||||||
|
class CyclicGroup(EmbeddedObject):
|
||||||
|
p = EmbeddedObjectField(BigInt)
|
||||||
|
g = EmbeddedObjectField(BigInt)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def q(self):
|
||||||
|
# p = 2q + 1
|
||||||
|
return (self.p - ONE) // TWO
|
||||||
|
|
||||||
|
def random_element(self, crypto_random=True):
|
||||||
|
crypto_method = BigInt.crypto_random if crypto_random else BigInt.noncrypto_random
|
||||||
|
return crypto_method(ONE, self.p - ONE)
|
||||||
|
|
||||||
|
# RFC 3526
|
||||||
|
DEFAULT_GROUP = CyclicGroup(
|
||||||
|
p=BigInt('FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF', 16),
|
||||||
|
g=TWO
|
||||||
|
)
|
||||||
|
|
||||||
|
class EGPublicKey(EmbeddedObject):
|
||||||
|
group = EmbeddedObjectField(CyclicGroup)
|
||||||
|
X = EmbeddedObjectField(BigInt)
|
||||||
|
|
||||||
|
# HAC 8.18
|
||||||
|
def encrypt(self, message):
|
||||||
|
# Choose an element 1 <= k <= p - 2
|
||||||
|
k = BigInt.crypto_random(ONE, self.group.p - TWO)
|
||||||
|
|
||||||
|
gamma = pow(self.group.g, k, self.group.p)
|
||||||
|
delta = (message * pow(self.X, k, self.group.p)) % self.group.p
|
||||||
|
|
||||||
|
return EGCiphertext(public_key=self, gamma=gamma, delta=delta)
|
||||||
|
|
||||||
|
class EGPrivateKey(EmbeddedObject):
|
||||||
|
public_key = EmbeddedObjectField(EGPublicKey)
|
||||||
|
x = EmbeddedObjectField(BigInt)
|
||||||
|
|
||||||
|
# HAC 8.17
|
||||||
|
@staticmethod
|
||||||
|
def generate():
|
||||||
|
# Choose an element 1 <= x <= p - 2
|
||||||
|
x = BigInt.crypto_random(ONE, DEFAULT_GROUP.p - TWO)
|
||||||
|
# Calculate the public key as G^x
|
||||||
|
X = pow(DEFAULT_GROUP.g, x, DEFAULT_GROUP.p)
|
||||||
|
|
||||||
|
pk = EGPublicKey(group=DEFAULT_GROUP, X=X)
|
||||||
|
sk = EGPrivateKey(public_key=pk, x=x)
|
||||||
|
return sk
|
||||||
|
|
||||||
|
# HAC 8.18
|
||||||
|
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')
|
||||||
|
|
||||||
|
gamma_inv = pow(ciphertext.gamma, self.public_key.group.p - ONE - self.x, self.public_key.group.p)
|
||||||
|
|
||||||
|
return (gamma_inv * ciphertext.delta) % self.public_key.group.p
|
||||||
|
|
||||||
|
class EGCiphertext(EmbeddedObject):
|
||||||
|
public_key = EmbeddedObjectField(EGPublicKey)
|
||||||
|
gamma = EmbeddedObjectField(BigInt) # G^k
|
||||||
|
delta = EmbeddedObjectField(BigInt) # M X^k
|
28
eos/psgjjr/tests.py
Normal file
28
eos/psgjjr/tests.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from eos.core.tests import *
|
||||||
|
|
||||||
|
from eos.core.bigint import *
|
||||||
|
from eos.psgjjr.crypto import *
|
||||||
|
|
||||||
|
class EGTestCase(EosTestCase):
|
||||||
|
def test_eg(self):
|
||||||
|
pt = BigInt.noncrypto_random(ONE, DEFAULT_GROUP.p - ONE)
|
||||||
|
sk = EGPrivateKey.generate()
|
||||||
|
ct = sk.public_key.encrypt(pt)
|
||||||
|
m = sk.decrypt(ct)
|
||||||
|
self.assertEqualJSON(pt, m)
|
Loading…
Reference in New Issue
Block a user