Implement string/object encryption

This commit is contained in:
Yingtong Li 2017-09-26 23:09:33 +10:00
parent 00ac2f96aa
commit 93f5dcfe42
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
5 changed files with 99 additions and 16 deletions

View File

@ -91,6 +91,10 @@ class BigInt(EosObject):
def __str__(self): def __str__(self):
return str(self.impl) return str(self.impl)
def __int__(self):
# WARNING: This will yield unexpected results for large numbers
return int(str(self.impl))
def __pow__(self, other, modulo=None): def __pow__(self, other, modulo=None):
if not isinstance(other, BigInt): if not isinstance(other, BigInt):
other = BigInt(other) other = BigInt(other)

View File

@ -37,7 +37,7 @@ class BigInt(EosObject):
return BigInt(self.impl.__pow__(other.impl, modulo.impl)) return BigInt(self.impl.__pow__(other.impl, modulo.impl))
def nbits(self): def nbits(self):
return math.ceil(math.log2(self.impl)) return math.ceil(math.log2(self.impl)) if self.impl > 0 else 0
def serialise(self): def serialise(self):
return str(self) return str(self)

View File

@ -38,7 +38,7 @@ class BitStream(EosObject):
if nbits is None: if nbits is None:
nbits = self.remaining nbits = self.remaining
if nbits > self.remaining: if nbits > self.remaining:
nbits = self.remaining raise Exception('Not enough bits to read from BitString')
val = (self.impl >> (self.remaining - nbits)) & ((ONE << nbits) - ONE) val = (self.impl >> (self.remaining - nbits)) & ((ONE << nbits) - ONE)
self.ptr += nbits self.ptr += nbits
@ -51,15 +51,53 @@ class BitStream(EosObject):
# ^---- # ^----
if nbits is None: if nbits is None:
nbits = bits.nbits() nbits = bits.nbits()
if nbits < bits.nbits():
raise Exception('Too many bits to write to BitString')
self.impl = ((self.impl >> self.remaining) << (self.remaining + nbits)) | (bits << self.remaining) | (self.impl & ((ONE << self.remaining) - 1)) self.impl = ((self.impl >> self.remaining) << (self.remaining + nbits)) | (bits << self.remaining) | (self.impl & ((ONE << self.remaining) - 1))
self.ptr += nbits self.ptr += nbits
self.nbits += nbits self.nbits += nbits
def read_string(self):
length = self.read(32)
length = length.__int__() # JS attempts to call this twice if we do it in one line
if is_python:
ba = bytearray()
for i in range(length):
ba.append(int(self.read(7)))
return ba.decode('ascii')
else:
ba = []
for i in range(length):
val = self.read(7)
val = val.__int__()
ba.append(val)
return String.fromCharCode(*ba)
def write_string(self, strg):
self.write(BigInt(len(strg)), 32) # TODO: Arbitrary lengths
# TODO: Support non-ASCII encodings
if is_python:
ba = strg.encode('ascii')
for i in range(len(strg)):
self.write(BigInt(ba[i]), 7)
else:
for i in range(len(strg)):
self.write(BigInt(strg.charCodeAt(i)), 7)
# Make the size of this BitStream a multiple of the block_size # Make the size of this BitStream a multiple of the block_size
def multiple_of(self, block_size): def multiple_of(self, block_size, pad_at_end=False):
if self.nbits % block_size != 0: if self.nbits % block_size != 0:
self.nbits += (block_size - (self.nbits % block_size)) diff = block_size - (self.nbits % block_size)
if pad_at_end:
# Suitable for structured data
self.seek(self.nbits)
self.write(ZERO, diff)
else:
# Suitable for raw numbers
self.nbits += diff
return self # For convenient chaining return self # For convenient chaining
def map(self, func, block_size): def map(self, func, block_size):

View File

@ -42,6 +42,13 @@ class EGPublicKey(EmbeddedObject):
# HAC 8.18 # HAC 8.18
def encrypt(self, message): 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 # Choose an element 1 <= k <= p - 2
k = BigInt.crypto_random(ONE, self.group.p - TWO) k = BigInt.crypto_random(ONE, self.group.p - TWO)
@ -76,7 +83,8 @@ class EGPrivateKey(EmbeddedObject):
gamma_inv = pow(ciphertext.gamma, self.public_key.group.p - ONE - self.x, self.public_key.group.p) 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 pt = (gamma_inv * ciphertext.delta) % self.public_key.group.p
return pt - ONE
class EGCiphertext(EmbeddedObject): class EGCiphertext(EmbeddedObject):
public_key = EmbeddedObjectField(EGPublicKey) public_key = EmbeddedObjectField(EGPublicKey)

View File

@ -28,17 +28,6 @@ class EGTestCase(EosTestCase):
m = sk.decrypt(ct) m = sk.decrypt(ct)
self.assertEqualJSON(pt, m) self.assertEqualJSON(pt, m)
def test_eg_block(self):
test_group = CyclicGroup(p=BigInt('11'), g=BigInt('2'))
pt = BigInt('11010010011111010100101', 2)
sk = EGPrivateKey.generate(test_group)
ct = BitStream(pt).multiple_of(test_group.p.nbits() - 1).map(sk.public_key.encrypt, test_group.p.nbits() - 1)
for i in range(len(ct)):
self.assertTrue(ct[i].gamma < test_group.p)
self.assertTrue(ct[i].delta < test_group.p)
m = BitStream.unmap(ct, sk.decrypt, test_group.p.nbits() - 1).read()
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))
@ -65,3 +54,47 @@ class BitStreamTestCase(EosTestCase):
expect = [0b1001, 0b0101, 0b1011] expect = [0b1001, 0b0101, 0b1011]
for i in range(len(expect)): for i in range(len(expect)):
self.assertEqual(result[i], expect[i]) self.assertEqual(result[i], expect[i])
def test_strings(self):
bs = BitStream()
bs.write_string('Hello World!')
bs.seek(0)
self.assertEqual(bs.read(32), len('Hello World!'))
bs.seek(0)
self.assertEqual(bs.read_string(), 'Hello World!')
class BlockEGTestCase(EosTestCase):
@classmethod
def setUpClass(cls):
class Person(TopLevelObject):
name = StringField()
address = StringField(default=None)
def say_hi(self):
return 'Hello! My name is ' + self.name
cls.Person = Person
#cls.test_group = CyclicGroup(p=BigInt('11'), g=BigInt('2'))
cls.test_group = CyclicGroup(p=BigInt('283'), g=BigInt('60'))
cls.sk = EGPrivateKey.generate(cls.test_group)
def test_basic(self):
pt = BigInt('11010010011111010100101', 2)
ct = BitStream(pt).multiple_of(self.test_group.p.nbits() - 1).map(self.sk.public_key.encrypt, self.test_group.p.nbits() - 1)
for i in range(len(ct)):
self.assertTrue(ct[i].gamma < self.test_group.p)
self.assertTrue(ct[i].delta < self.test_group.p)
m = BitStream.unmap(ct, self.sk.decrypt, self.test_group.p.nbits() - 1).read()
self.assertEqualJSON(pt, m)
def test_object(self):
obj = self.Person(name='John Smith')
pt = EosObject.to_json(EosObject.serialise_and_wrap(obj))
bs = BitStream()
bs.write_string(pt)
bs.multiple_of(self.test_group.p.nbits() - 1, True)
ct = bs.map(self.sk.public_key.encrypt, self.test_group.p.nbits() - 1)
bs2 = BitStream.unmap(ct, self.sk.decrypt, self.test_group.p.nbits() - 1)
m = bs2.read_string()
obj2 = EosObject.deserialise_and_unwrap(EosObject.from_json(m))
self.assertEqualJSON(obj, obj2)