Implement string/object encryption
This commit is contained in:
parent
00ac2f96aa
commit
93f5dcfe42
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user