def keyed_homomorphic_permutation(state, key, rounds=4): """ Ultimately, xors together random groups of state bits, such that each bit is made up of bit_i XOR bit_j XOR bit_k ... for a large enough number of random terms. """ a, b, c, d = bytes_to_words(state, 4) key = key[:] for i in range(rounds): key_schedule(key) # randomize the position of bits a, b, c, d = bit_permutation((a, b, c, d), bytes_to_words(key, 4)) # stack lots of bits on top of each other a, b, c, d = mix_columns(a, b, c, d) b = rotate_left(b, 1, 32) c = rotate_left(c, 2, 32) d = rotate_left(d, 3, 32) a, b, c, d = mix_columns(a, b, c, d) b = rotate_left(b, 4, 32) c = rotate_left(c, 8, 32) d = rotate_left(d, 12, 32) a, b, c, d = mix_columns(a, b, c, d) b = rotate_left(b, 8, 32) c = rotate_left(c, 12, 32) d = rotate_left(d, 16, 32) a, b, c, d = mix_columns(a, b, c, d) key_schedule(key) a, b, c, d = bit_permutation((a, b, c, d), bytes_to_words(key, 4)) state[:] = words_to_bytes((a, b, c, d), 4)
def invert_keyed_homomorphic_permutation(state, key, rounds=4): assert isinstance(state, bytearray), type(state) a, b, c, d = bytes_to_words(state, 4) key = key[:] for round in range(rounds + 1): key_schedule(key) a, b, c, d = invert_bit_permutation((a, b, c, d), bytes_to_words(key, 4)) invert_key_schedule(key) for round in reversed(range(rounds)): a, b, c, d = invert_mix_columns(a, b, c, d) b = rotate_right(b, 8, 32) c = rotate_right(c, 12, 32) d = rotate_right(d, 16, 32) a, b, c, d = invert_mix_columns(a, b, c, d) b = rotate_right(b, 4, 32) c = rotate_right(c, 8, 32) d = rotate_right(d, 12, 32) a, b, c, d = invert_mix_columns(a, b, c, d) b = rotate_right(b, 1, 32) c = rotate_right(c, 2, 32) d = rotate_right(d, 3, 32) a, b, c, d = invert_mix_columns(a, b, c, d) a, b, c, d = invert_bit_permutation((a, b, c, d), bytes_to_words(key, 4)) invert_key_schedule(key) state[:] = words_to_bytes((a, b, c, d), 4)
def generate_public_key(private_key): """ Generate a public key, given the secret key of a symmetric homomorphic cryptosystem. A public key consists of encryptions of the range of numbers 0-255, in order. Encryption consists of (randomly) selecting numbers that sum to the value of the message (represented as an 8-bit byte) First, the integers 0-255 are encrypted using a linear secret key cipher to produce the pre-public key. Then, the pre-public key is used to generate a second set of encryptions of the integers 0-255, using public key encryption. This second set is released as the public key. The pre-public key functions as a public key, but the ciphertexts provide a known plaintext attack on a linear cipher. The public key encryption technique is more secure then the linear cipher, and the resulting ciphertexts also carry the homomorphic property. So they are suitable for use as a public key. - You can generate a new, random, valid public key from any other public key. """ pre_public_key = [] for byte in range(256): ciphertext = homomorphic_encrypt(byte, private_key) pre_public_key.append(bytes_to_words(ciphertext, 4)) public_key = [] for byte in range(256): message = bytearray() message.append(byte) ciphertext = encrypt(message, pre_public_key)[0] public_key.append(bytes_to_words(ciphertext, 4)) return public_key
def derive_public_key(private_key): pre_public_key = [] for byte in range(256): message = bytearray(8) message[0] = byte ciphertext = homomorphic_encrypt(message, private_key, bytearray(sha256(private_key + message + "pre_public").digest())) pre_public_key.append(bytes_to_words(ciphertext, 4)) public_key = [] for byte in range(256): message = bytearray() message.append(byte) ciphertext = encrypt(message, pre_public_key, prng=lambda amount: random(amount, private_key, chr(byte)))[0] # encrypt returns a list with 1 ciphertext in it public_key.append(bytes_to_words(ciphertext, 4)) return public_key
def test_encrypt_decrypt(): byte_size = 8 data = bytearray(16 * byte_size) key = bytearray(16 * byte_size) data[-1] = 1 rounds = 16 bit_size = byte_size * 8 size = (bit_size, ((2 ** bit_size) - 1), bit_size - (3 * byte_size)) data = bytes_to_words(data, byte_size) key = bytes_to_words(key, byte_size) plaintext = data[:] encrypt(data, key, rounds, size) print ''.join(bytes(integer_to_bytes(block, byte_size)) for block in data) print [byte for byte in data]
def encrypt(self, data, key, iv, size=(64, (2 ** 64) - 1, 40)): word64 = lambda _data: bytes_to_words(bytearray(_data), 8) output = word64(data) key += "\x00" * (128 - len(key)) key = word64(key) assert len(key) == 16, len(key) iv += "\x00" * (128 - len(iv)) assert len(iv) == 128, len(iv) iv = word64(iv) assert len(iv) == 16, len(iv) encrypt(output, key, iv, size) return bytes(words_to_bytes(output, 8))
def derive_public_key(private_key): pre_public_key = [] for byte in range(256): message = bytearray(8) message[0] = byte ciphertext = homomorphic_encrypt( message, private_key, bytearray(sha256(private_key + message + "pre_public").digest())) pre_public_key.append(bytes_to_words(ciphertext, 4)) public_key = [] for byte in range(256): message = bytearray() message.append(byte) ciphertext = encrypt( message, pre_public_key, prng=lambda amount: random(amount, private_key, chr(byte)))[ 0] # encrypt returns a list with 1 ciphertext in it public_key.append(bytes_to_words(ciphertext, 4)) return public_key
def generate_public_key(encryption_function, key): """ Generate a public key, given an encryption function and key that outputs ciphertexts that are partially homomorphic. A public key consists of encryptions of the range of numbers 0-255, in order. Encryption consists of (randomly) selecting numbers that sum to the value of the message (represented as an 8-bit byte) """ public_key = [] for byte in range(256): message = bytearray( 8) # we only need to encrypt 8 bits, but the data block is 64 bits message[0] = byte ciphertext = encryption_function(message, key) public_key.append(bytes_to_words(ciphertext, 4)) return public_key
def test_stream_cipher_diffusion(): wordsize = 1 size = generate_params_for_wordsize(wordsize) seed = bytes_to_words(bytearray(16 * wordsize), wordsize) key = seed[:] seed2 = key[:] seed3 = key[:] seed2[-2] = 1 seed3[-2] = 2 data = seed[:] data2 = data[:] data3 = data[:] stream_cipher(data, seed, key, size) stream_cipher(data2, seed2, key[:15] + [1], size) stream_cipher(data3, seed3, key[:14] + [1, 0], size) _bytes = lambda _data: words_to_bytes(_data, wordsize) bits = lambda _data: ''.join(format(byte, 'b').zfill(8) for byte in _bytes(_data)) print _bytes(data)
def load_ciphertext(saved_ciphertext): output = [] for entry in slide(bytearray(saved_ciphertext), BLOCKSIZE * 2): output.extend(bytes_to_words(entry, 2)) return output
def load_private_key(saved_private_key): return bytearray(bytes_to_words(bytearray(saved_private_key), 2))
def load_public_key(saved_key): output = [] for entry in slide(bytearray(saved_key), BLOCKSIZE * 2): output.extend(bytes_to_words(entry, 2)) return output
def load_ciphertext(saved_ciphertext): output = [] for entry in slide(bytearray(saved_ciphertext), BLOCKSIZE * 2): output.extend(bytes_to_words(entry, 2)) return output
def load_private_key(saved_private_key): return bytearray(bytes_to_words(bytearray(saved_private_key), 2))
def load_public_key(saved_key): public_key = [] for line in slide(saved_key, 16): public_key.append(bytes_to_words(line, 4)) return public_key
def blockcipher32(key, m, rounds=1): key = bytes_to_words(bytearray(key), 4) m = bytes_to_words(bytearray(m), 4)[0] return words_to_bytes([_cic_blockcipher32_encrypt(key, m, rounds)], 4)
def load_public_key(saved_key): output = [] for entry in slide(bytearray(saved_key), BLOCKSIZE * 2): output.extend(bytes_to_words(entry, 2)) return output
def iv_generator(key, seed, wordsize=8, mask=(2 ** 64) - 1): state = bytes_to_words(seed, wordsize) bit_width = wordsize * 8 while True: permutation(state, key, mask, bit_width) yield words_to_bytes(state, wordsize)