def _and(self, ciphertext1, ciphertext2, modulus=DEFAULT_MODULUS): # sets bit1 to (bit1 & bit2) temp = Bit.copy(ciphertext2, self.public_key) self.adjust_level(ciphertext1, temp) addition_subroutine(ciphertext1.value, temp.value, modulus) ciphertext1.depth += 1
def test_encode_decode(): data1 = bytearray(1) data2 = bytearray(1) data1[0] = 1 data2[0] = 2 encoded_data1 = encode(data1) encoded_data2 = encode(data2) addition_subroutine(encoded_data1, encoded_data2, DEFAULT_MODULUS) assert decode(encoded_data1)[0] == 3, (decode(encoded_data1)[0], 3)
def cipher(message, key, iv=None, mode=None): data = bytearray(message) key = bytearray(key) if iv is not None: addition_subroutine(data, bytearray(iv), 256) output = bytearray() for block in slide(data, 16): addition_subroutine(block, output[-16:], 256) output.extend(encrypt(block, key, rounds=4)) return output
def verify(data, signature, public_key, modulus=256): """ Verify a 256 bit signature using the public key. """ validator = bytearray(32) for count, byte in enumerate(signature): for bit in range(8): if byte & 1: addition_subroutine(validator, public_key[(count * 8) + bit], modulus) byte >>= 1 if validator == data: return True else: return False
def distribute(self, ciphertext1, ciphertext2, ciphertext3, modulus=DEFAULT_MODULUS): """ Computes a & (b ^ c) """ if ciphertext2.depth or ciphertext3.depth: raise ValueError("Cannot distribute unless term depth == 0;\nciphertext2.depth: {}; ciphertext3.depth: {};".format(ciphertext1.depth, ciphertext2.depth)) temp = Bit.copy(ciphertext2, self.public_key) temp2 = Bit.copy(ciphertext3, self.public_key) temp.increase_depth(1) addition_subroutine(temp.value, temp2.value, modulus) print publickey.decrypt(temp.value, _UNIT_TEST_PRIVATE_KEY) multiplication_subroutine(temp.value, [3 for count in range(len(ciphertext1.value))], modulus) print publickey.decrypt(temp.value, _UNIT_TEST_PRIVATE_KEY) self._and(ciphertext1, temp, modulus)
def test_encrypt_decrypt(): message = bytearray("Testing!" * 256) key = generate_key() ciphertext = encrypt(message, key) plaintext = decrypt(ciphertext, key) assert plaintext == message, (plaintext, message) message2 = bytearray(range(16)) message3 = bytearray(range(1, 17)) answer = bytearray((message2[index] + message3[index]) % 256 for index in range(len(message2))) ciphertext2 = encrypt(message2, key) ciphertext3 = encrypt(message3, key) addition_subroutine(ciphertext2, ciphertext3, 256) _answer = decrypt(ciphertext2, key) assert _answer == answer, (_answer, answer)
def test_encrypt_decrypt(): public_key, private_key = generate_keypair() message = "Testing!" ciphertext = encrypt(message, public_key) plaintext = decrypt(ciphertext, private_key) assert plaintext == message, (plaintext, message) ciphertext2 = encrypt(message, public_key) assert ciphertext2 != ciphertext, "Ciphertexts are not randomized" plaintext2 = decrypt(ciphertext2, private_key) assert plaintext2 == message addition_subroutine(ciphertext, ciphertext2, DEFAULT_MODULUS) addition_subroutine(plaintext, plaintext2, DEFAULT_MODULUS) plaintext3 = decrypt(ciphertext, private_key) assert plaintext3 == plaintext print "Passed public key encryption and private key decryption unit test"
def test_encode_decode(): data1 = list(bytearray(8)) data2 = list(bytearray(8)) data1[0] = 1 data2[0] = 2 _data1 = encode(data1) _data1 = decode(_data1) assert _data1 == data1, (_data1, data1) correct_answer = data1[:] addition_subroutine(correct_answer, data2, DEFAULT_MODULUS) data1 = encode(data1) data2 = encode(data2) addition_subroutine(data1, data2, DEFAULT_MODULUS) answer = decode(data1) assert answer == correct_answer
def encrypt(message, public_key, ciphertext_count=16, prng=lambda amount: bytearray(urandom(amount)), modulus=DEFAULT_MODULUS, blocksize=BLOCKSIZE): """ usage: encrypt(message : bytes, public_key : bytearray) => ciphertext : bytearray Public key encryption method, based on homomorphic encryption. A public key consists of encryptions of the numbers 0-255, in order. All math is done on ciphertexts: - To encrypt one byte, add together a random subset of integers such that the sum equals the message byte. - c = r1 + r2 + r3 ... + rN + (m - (r1 + r2 + r3 ... + rN)) Encryption can send one 8-bit value per 256-bit ciphertext. This results in a 32x increase in data size. Ciphertexts are partially homormophic. Works on arbitrarily long messages, albeit one byte at a time. Output is the concatenation of the ciphertexts. This method only provides confidentiality of the message, it does not provide message integrity. """ output = [] size = len(message) key_bytes = iter(prng(size * ciphertext_count)) assert not isinstance(message, int) null_block = [0 for count in range(blocksize)] for symbol in (bytearray(message) if isinstance(message, str) else message): ciphertext_byte = null_block[:] _key_byte = 0 for count in range(ciphertext_count): key_byte = next(key_bytes) _key_byte += key_byte ciphertext_key_byte = public_key[key_byte * blocksize:(key_byte + 1) * blocksize] #print len(ciphertext_key_byte), key_byte, blocksize, len(public_key), key_byte * blocksize, (key_byte + 1) * blocksize addition_subroutine(ciphertext_byte, ciphertext_key_byte, modulus) final_key_byte = modular_subtraction(symbol, _key_byte, modulus) final_ciphertext = public_key[final_key_byte * blocksize:(final_key_byte + 1) * blocksize] addition_subroutine(ciphertext_byte, final_ciphertext, modulus) output.extend(ciphertext_byte) return output
def test_encrypt_decrypt(): data_size = 8 message = list(bytearray(data_size)) _message = message[:] key = generate_key(data_size) encrypt(message, key) decrypt(message, key) assert message == _message, (message, _message) message2 = list(bytearray(range(8))) message3 = list(bytearray(reversed(range(8)))) answer = list( bytearray((message2[index] + message3[index]) % DEFAULT_MODULUS for index in range(8))) answer2 = list( bytearray((message2[index] * message3[index]) % DEFAULT_MODULUS for index in range(8))) encrypt(message2, key) encrypt(message3, key) _m2, _m3 = message2[:], message3[:] addition_subroutine(message2, message3, DEFAULT_MODULUS) multiplication_subroutine(_m2, _m3, DEFAULT_MODULUS) _message2 = decrypt(message2, key, multiplier=1) __m2 = decrypt(_m2, key, multiplier=2) # print [byte for byte in _message2], [byte for byte in answer] # print [byte for byte in __m2], [byte for byte in answer2], __m2 == answer2, len(__m2), len(answer2), type(__m2), type(answer2) if _message2 == answer: print("Ciphertexts support addition") else: print("Ciphertexts do not support addition") if __m2 == answer2: print("Ciphertexts support multiplication") else: print("Ciphertexts do not support multiplication") def test_function(data, key, iv, mode=None): keysize, data_size = len(key), len(data) / 2 key = bytearray(key) if keysize < data_size * 3: key.extend(urandom((data_size * 3) - keysize)) data = bytearray(data[:data_size]) encrypt(data, bytearray(key)) return bytes(data)
def test_homomorphic_permutation(_encrypt, _decrypt): ROUNDS = 10 data = bytearray(8) data[0] = 1 key = generate_key(len(data)) _data = data[:] ciphertext1 = _encrypt(data, key, ROUNDS) print("Ciphertext1: {}".format(list(ciphertext1))) data = _decrypt(ciphertext1, key, ROUNDS) assert data == _data, (data, _data) data2 = bytearray(8) data2[0] = 2 _data2 = data2[:] ciphertext2 = _encrypt(data2, key, ROUNDS) print("Ciphertext2: {}".format(list(ciphertext2))) data2 = _decrypt(ciphertext2, key, ROUNDS) assert data2 == _data2, (data2, _data2) addition_subroutine(ciphertext1, ciphertext2, 256) print("Ciphertext1 + Ciphertext2: {}".format(list(ciphertext1))) addition_subroutine(data, data2, 256) ciphertext1 = _decrypt(ciphertext1, key, ROUNDS, multiplier=2) assert ciphertext1 == data, (ciphertext1, data) message0 = bytearray(urandom(8)) message1 = bytearray(urandom(8)) message2 = bytearray(urandom(8)) message0[0] = 1 message1[0] = 2 message2[0] = 3 _encrypt(message0, key, ROUNDS) _encrypt(message1, key, ROUNDS) _encrypt(message2, key, ROUNDS) addition_subroutine(message0, message1, 256) addition_subroutine(message0, message2, 256) _decrypt(message0, key, ROUNDS, multiplier=2) assert message0[0] == 1 + 2 + 3, message1 data = bytearray(urandom(8)) data2 = bytearray(urandom(8)) data[0] = 7 data2[0] = 131 print("Data: {}".format(list(data))) print("Data2: {}".format(list(data2))) _encrypt(data, key, ROUNDS) _encrypt(data2, key, ROUNDS) print("Ciphertext: {}".format(list(data))) print("Ciphertext2: {}".format(list(data2)))
def distribute(self, ciphertext1, ciphertext2, ciphertext3, modulus=DEFAULT_MODULUS): """ Computes a & (b ^ c) """ if ciphertext2.depth or ciphertext3.depth: raise ValueError( "Cannot distribute unless term depth == 0;\nciphertext2.depth: {}; ciphertext3.depth: {};" .format(ciphertext1.depth, ciphertext2.depth)) temp = Bit.copy(ciphertext2, self.public_key) temp2 = Bit.copy(ciphertext3, self.public_key) temp.increase_depth(1) addition_subroutine(temp.value, temp2.value, modulus) print publickey.decrypt(temp.value, _UNIT_TEST_PRIVATE_KEY) multiplication_subroutine( temp.value, [3 for count in range(len(ciphertext1.value))], modulus) print publickey.decrypt(temp.value, _UNIT_TEST_PRIVATE_KEY) self._and(ciphertext1, temp, modulus)
def test_encrypt_decrypt(): data_size = 8 message = list(bytearray(data_size)) _message = message[:] key = generate_key(data_size) encrypt(message, key) decrypt(message, key) assert message == _message, (message, _message) message2 = list(bytearray(range(8))) message3 = list(bytearray(reversed(range(8)))) answer = list(bytearray((message2[index] + message3[index]) % DEFAULT_MODULUS for index in range(8))) answer2 = list(bytearray((message2[index] * message3[index]) % DEFAULT_MODULUS for index in range(8))) encrypt(message2, key) encrypt(message3, key) _m2, _m3 = message2[:], message3[:] addition_subroutine(message2, message3, DEFAULT_MODULUS) multiplication_subroutine(_m2, _m3, DEFAULT_MODULUS) _message2 = decrypt(message2, key, multiplier=1) __m2 = decrypt(_m2, key, multiplier=2) # print [byte for byte in _message2], [byte for byte in answer] # print [byte for byte in __m2], [byte for byte in answer2], __m2 == answer2, len(__m2), len(answer2), type(__m2), type(answer2) if _message2 == answer: print("Ciphertexts support addition") else: print("Ciphertexts do not support addition") if __m2 == answer2: print("Ciphertexts support multiplication") else: print("Ciphertexts do not support multiplication") def test_function(data, key, iv, mode=None): keysize, data_size = len(key), len(data) / 2 key = bytearray(key) if keysize < data_size * 3: key.extend(urandom((data_size * 3) - keysize)) data = bytearray(data[:data_size]) encrypt(data, bytearray(key)) return bytes(data)
def test_encrypt_decrypt(): from utilities import addition_subroutine key = generate_key() ciphertext1 = encrypt([1], key) ciphertext2 = encrypt([2], key) addition_subroutine(ciphertext1, ciphertext2, DEFAULT_MODULUS) addition_subroutine(ciphertext1, ciphertext2, DEFAULT_MODULUS) answer = decrypt(ciphertext1, key) assert answer == [5], answer data = [1 << count for count in range(8)] data2 = bytearray(urandom(8)) correct_answer = [(data[index] + data2[index]) % DEFAULT_MODULUS for index in range(8)] ciphertext1 = encrypt(data, key) ciphertext2 = encrypt(data2, key) addition_subroutine(ciphertext1, ciphertext2, DEFAULT_MODULUS) answer = decrypt(ciphertext1, key) assert answer == correct_answer, (answer, correct_answer)
def homomorphic_xor(ciphertext1, ciphertext2, modulus=DEFAULT_MODULUS): addition_subroutine(ciphertext1, ciphertext2, modulus)
def homomorphic_and(ciphertext1, ciphertext2, operation_count, modulus=DEFAULT_MODULUS): addition_subroutine(ciphertext1, ciphertext2, modulus) return operation_count + 1
def xor(self, ciphertext1, ciphertext2, modulus=DEFAULT_MODULUS): temp = Bit.copy(ciphertext2, self.public_key) self.adjust_level(ciphertext1, temp) addition_subroutine(ciphertext1.value, temp.value, modulus)