def test_streamcipher2(): data = list(bytearray(16)) key = bytearray(16) seed = bytearray(16) size = (8, 255, 5) #(64, 0xFFFFFFFFFFFFFFFF, 40) data[1] = 1 stream_cipher(data, key, seed, size) print words_to_bytes(data, size[0] / 8) # print [byte for byte in data] data2 = list(bytearray(16)) data2[0] = 1 stream_cipher(data2, key, seed, size, mode="encrypt") print words_to_bytes(data2, size[0] / 8)
def test_streamcipher2(): data = list(bytearray(16)) key = bytearray(16) seed = bytearray(16) size = (8, 255, 5)#(64, 0xFFFFFFFFFFFFFFFF, 40) data[1] = 1 stream_cipher(data, key, seed, size) print words_to_bytes(data, size[0] / 8) # print [byte for byte in data] data2 = list(bytearray(16)) data2[0] = 1 stream_cipher(data2, key, seed, size, mode="encrypt") print words_to_bytes(data2, size[0] / 8)
def stream_cipher(data, key, seed, modulus=2 ** 32, wordsize=32): #assert isinstance(data, bytearray) state = bytes_to_words(bytearray(data), 4) prng_key, seed_key = key xor_subroutine(seed, seed_key) assert len(seed) == 4, len(seed) a, b, c, d = seed k0, k1, k2, k3 = prng_key a, b, c, d = add_key(a, b, c, d, k0, k1, k2, k3) a, b, c, d = permutation(a, b, c, d, modulus) assert len(state) for index in range(len(state) - 1): a, b, c, d = add_key(a, b, c, d, k0, k1, k2, k3) a, b, c, d = add_key(a, b, c, d, index, index + 1, index + 2, index + 3) b, c, d, a = permutation(a, b, c, d) state[index] ^= (a + b) % modulus state[(index + 1)] ^= (c + d) % modulus _data = words_to_bytes(state, 4) try: data[:] = _data except (ValueError, TypeError): pass return _data
def encrypt(data, key): random_mask = bytearray(32) xor_subroutine(data, random_mask) xor_subroutine(random_mask, key[:32]) state = bytes_to_words(data, 4) keyed_bit_transposition(state, bytes_to_words(key[32:64], 4)) data[:] = words_to_bytes(state, 4) + random_mask
def decrypt8(ciphertext, key): output = words_to_bytes(invert_bit_permutation128(bytes_to_words(ciphertext, 4), key), 4) padding = output[1:] permutation(padding) for padding_byte in padding: output[0] ^= padding_byte return output[0]
def encrypt64(data, key, output_type="bytes"): """ Encrypt 64 bits of data using key. Encryption is: - Randomized - The randomizing value is kept secret/not sent in the clear - Homomorphic - Supports unlimited fully homomorphic operations - D(E(x) ^ E(y)) == x ^ y - D(E(x) & E(y)) == x & y Encryption is performed by generating a 64-bit random padding value and then concatenating the padding to the 64-bit message, and finally applying a keyed bit permutation on the result. To compute the XOR/AND of 2 ciphertexts, simply XOR/AND the ciphertexts together. - An encryption of 0 should be added to the target of any AND afterwards - This is due to the tendency of AND to set bits to 0. - The noise from the ciphertext will ensure hamming weight on the target ciphertext stays balanced""" padding = new_key(2, 4) # generate 2 32-bit words inputs = tuple(bytes_to_words(bytearray(data), 4)) + padding if output_type == "bytes": return words_to_bytes(bit_permutation128(inputs, key), 4) else: if output_type != "words": raise ValueError("Invalid output_type '{}'".format(output_type)) return bit_permutation128(inputs, key)
def encrypt(data, key, rounds=2): state = bytes_to_words(data, WORDSIZE) key = bytes_to_words(key, WORDSIZE) for round in range(rounds): permutation512(state, key) data[:] = words_to_bytes(state, WORDSIZE)
def memory_hard_hash(data, rounds, hash_key=HASH_KEY, modulus=WORD_COUNT): output = [0 for count in range(STATE_SIZE)] hash_input = bytes_to_words(bytearray(pad_input(data, STATE_SIZE * WORD_BYTES)), WORD_BYTES) for round in range(rounds): for index, word in enumerate(hash_input): value = hash_key[(word + round + output[index % STATE_SIZE]) % modulus] addition_subroutine(output, value, modulus) return bytes(words_to_bytes(output, WORD_BYTES))
def invert_little_swap(self): word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [ in_bytes[index] for index in (11, 5, 4, 15, 12, 6, 9, 0, 13, 3, 14, 8, 1, 10, 2, 7) ] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def little_swap(self): word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [ in_bytes[index] for index in (7, 12, 14, 9, 2, 1, 5, 15, 11, 6, 13, 0, 4, 8, 10, 3) ] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def memory_hard_hash(data, rounds, hash_key=HASH_KEY, modulus=WORD_COUNT): output = [0 for count in range(STATE_SIZE)] hash_input = bytes_to_words( bytearray(pad_input(data, STATE_SIZE * WORD_BYTES)), WORD_BYTES) for round in range(rounds): for index, word in enumerate(hash_input): value = hash_key[(word + round + output[index % STATE_SIZE]) % modulus] addition_subroutine(output, value, modulus) return bytes(words_to_bytes(output, WORD_BYTES))
def compression_function(data, rounds=ROUNDS): output = [0, 0, 0, 0] for a, b, c, d in slide(bytes_to_words(data, 8), 4): a ^= output[0]; b ^= output[1]; c ^= output[2]; d ^= output[3]; for round in range(rounds): a, b, c, d = sbox(a, b, c, d) a, b, c, d = linear_layer(a, b, c, d) output[0] ^= a; output[1] ^= b; output[2] ^= c; output[3] ^= d; return bytes(words_to_bytes(output, 8))
def decrypt(data, key, parity=1): state = bytes_to_words(data[:32], 4) invert_keyed_bit_transposition(state, bytes_to_words(key[32:64], 4)) data[:32] = words_to_bytes(state, 4) random_mask = data[32:64] if parity: xor_subroutine(random_mask, key[:32]) xor_subroutine(data, random_mask) del data[32:64] return data
def compression_function(data): o0, o1, o2, o3 = 0, 1, 8 , 64 for a, b, c, d in slide(bytes_to_words(data, 4), 4): a, b, c, d = mix_words(a, b, c, d) s0, s1, s2, s3 = mix_words(a, 1, 8, 64) s0, s1, s2, s3 = mix_words(s0, b, s1, s3) s0, s1, s2, s3 = mix_words(s0, s1, c, s3) s0, s1, s2, s3 = mix_words(s0, s1, s2, d) o0 ^= s0; o1 ^= s1; o2 ^= s2; o3 ^= s3; return words_to_bytes((o0, o1, o2, o3), 4)
def encrypt8(byte, key): padding = bytearray(urandom(15)) permutation(padding) for padding_byte in padding: byte ^= padding_byte invert_permutation(padding) inputs = bytearray() inputs.append(byte) inputs.extend(padding) return words_to_bytes(bit_permutation128(bytes_to_words(inputs, 4), key), 4)
def generate_key_stream(key, nonce, block_count): for index, byte in enumerate(key): nonce[index] ^= byte state = [block128 for block128 in slide(bytes_to_words(nonce, 4), 4)] output = [] while len(output) < block_count: for round in range(2): state = mix_blocks(*state) output.extend(state[0] + state[1] + state[2] + state[3]) return words_to_bytes(output, 4)
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 decrypt(data, key, rounds=2): state = bytes_to_words(data, WORDSIZE) _key = bytes_to_words(key, WORDSIZE) keys = key_schedule(_key, rounds) xor_subroutine(state, keys[0]) for round in reversed(range(rounds)): invert_permutation512(state, keys[round + 1]) xor_subroutine(state, keys[0]) data[:] = words_to_bytes(state, WORDSIZE)
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 decrypt64v2(data, key, output_type="bytes"): """ Decrypts data encrypted by encrypt64v2. Returns resulting plaintext message. Inverts the bit transposition, unmasks the data, and discards the padding. """ output = list(invert_bit_permutation128(bytes_to_words(data, 4), key)) output[0] ^= output[2] output[1] ^= output[3] if output_type == "bytes": return words_to_bytes(output, 4)[:8] else: if output_type != "words": raise ValueError("Invalid output_type '{}'".format(output_type)) return output
def decrypt64(data, key, output_type="bytes"): """ Decrypts ciphertext produced by encrypt64. First, the bit permutation is reversed. Then the padding is xor'd with the message and discarded. It is crucial for the long term security of the secret key that the random padding is not leaked.""" output = invert_bit_permutation128(bytes_to_words(data, 4), key) if output_type == "bytes": return words_to_bytes(output, 4)[:8] else: if output_type != "words": raise ValueError("Invalid output_type '{}'".format(output_type)) return invert_bit_permutation128(bytes_to_words(data, 4), key)
def decrypt(data, key, rounds=2): state = bytes_to_words(data, 4) _key = bytes_to_words(key, 4) keys = key_schedule(_key, rounds) xor_subroutine(state, keys[0]) for round in reversed(range(rounds)): invert_permutation256(state) invert_keyed_bit_transposition(state, keys[round + 1]) xor_subroutine(state, keys[0]) data[:] = words_to_bytes(state, 4)
def compression_function(data): o0, o1, o2, o3 = 0, 1, 8, 64 for a, b, c, d in slide(bytes_to_words(data, 4), 4): a, b, c, d = mix_words(a, b, c, d) s0, s1, s2, s3 = mix_words(a, 1, 8, 64) s0, s1, s2, s3 = mix_words(s0, b, s1, s3) s0, s1, s2, s3 = mix_words(s0, s1, c, s3) s0, s1, s2, s3 = mix_words(s0, s1, s2, d) o0 ^= s0 o1 ^= s1 o2 ^= s2 o3 ^= s3 return words_to_bytes((o0, o1, o2, o3), 4)
def compression_function(data, rounds=ROUNDS): data = bytes_to_words(data, 8) # convert 8-bit words to 64-bit words a, b, c, d = (0, 0, 0, 0) counter = 1 for in0, in1, in2, in3 in slide(data, 4): # work on 4 64-bit words at a time (256 bit state) # print "Digesting: ", in0, in1, in2, in3 a, b, c, d = add_block(a, b, c, d, in0, in1, in2, in3, counter) assert counter <= INTEGER64_OVERFLOW for round in range(rounds): a, b, c, d = round_function(a, b, c, d) a, b, c, d = add_block(a, b, c, d, in0, in1, in2, in3, counter) counter += 1 a, b, c, d = round_function(a, b, c, d) return bytes(words_to_bytes((a, b, c, d), 8))
def stream_cipher(plaintext, key, seed, modulus=2**32, wordsize=32): # make state 2x key. 256 bit key, 512 bit state. 8 64-bit words, or 16-32 bit words # 4 instances of 4-32 bit words via SIMD would keep the latency low # 4 instances of 4-8 bit words via SIMD would be scalable? prng_key, seed_key = key seed = seed[:] xor_subroutine(seed, seed_key) key = itertools.cycle(prng_key) size = len(seed) state = bytes_to_words(bytearray(plaintext), 4) plaintext_size = len(state) plaintext_index = 0 break_flag = False # requires one round warmup for full diffusion # a + b, a + c, a + d # b + c, b + d # c + d assert size - 1 for index in range(size - 1): for index2 in range(index + 1, size): permutation(seed, index, index2, key, modulus, wordsize) while True: for index in range(size - 1): for index2 in range(index + 1, size): permutation(seed, index, index2, key, modulus, wordsize) state[plaintext_index] ^= (seed[index] + seed[index2]) % modulus plaintext_index += 1 if plaintext_index == plaintext_size: break_flag = True break if break_flag: break if break_flag: break _plaintext = words_to_bytes(state, 4) try: plaintext[:] = _plaintext except TypeError: pass return _plaintext
def stream_cipher(plaintext, key, seed, modulus=2 ** 32, wordsize=32): # make state 2x key. 256 bit key, 512 bit state. 8 64-bit words, or 16-32 bit words # 4 instances of 4-32 bit words via SIMD would keep the latency low # 4 instances of 4-8 bit words via SIMD would be scalable? prng_key, seed_key = key seed = seed[:] xor_subroutine(seed, seed_key) key = itertools.cycle(prng_key) size = len(seed) state = bytes_to_words(bytearray(plaintext), 4) plaintext_size = len(state) plaintext_index = 0 break_flag = False # requires one round warmup for full diffusion # a + b, a + c, a + d # b + c, b + d # c + d assert size - 1 for index in range(size - 1): for index2 in range(index + 1, size): permutation(seed, index, index2, key, modulus, wordsize) while True: for index in range(size - 1): for index2 in range(index + 1, size): permutation(seed, index, index2, key, modulus, wordsize) state[plaintext_index] ^= (seed[index] + seed[index2]) % modulus plaintext_index += 1 if plaintext_index == plaintext_size: break_flag = True break if break_flag: break if break_flag: break _plaintext = words_to_bytes(state, 4) try: plaintext[:] = _plaintext except TypeError: pass return _plaintext
def rotate_left1(self): # 0 1 2 3 # 4 5 6 7 # 8 9 10 11 #12 13 14 15 # 1 2 3 0 # 5 6 7 4 # 9 10 11 8 #13 14 15 12 word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [in_bytes[index] for index in (1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12)] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def rotate_left1(self): # 0 1 2 3 # 4 5 6 7 # 8 9 10 11 #12 13 14 15 # 1 2 3 0 # 5 6 7 4 # 9 10 11 8 #13 14 15 12 word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [ in_bytes[index] for index in (1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12) ] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def stream_cipher(data, key, nonce): state = bytes_to_words(bytearray(data), 4) nonce = bytes_to_words(bytearray(nonce), 4) key = bytes_to_words(bytearray(key), 4) blocks = len(state) - 4 assert blocks > 0 try: for index in range(blocks): nonce = list(keyed_permutation(*(nonce + key + [index + 1]))) state[(index * 4) + 0] ^= nonce[0] state[(index * 4) + 1] ^= nonce[1] state[(index * 4) + 2] ^= nonce[2] state[(index * 4) + 3] ^= nonce[3] except IndexError: pass _data = words_to_bytes(state, 4) try: data[:] = _data except (TypeError, ValueError): pass return _data
def invert_permutation2(state): a, b, c, d = bytes_to_words(state, 4) #key = 0 #for byte in state: # key ^= byte a, b, c, d = invert_mix_columns(a, b, c, d)#, key) b = rotate_right(b, 8) c = rotate_right(c, 12) d = rotate_right(d, 16) a, b, c, d = invert_mix_columns(a, b, c, d)#, key) b = rotate_right(b, 4) c = rotate_right(c, 8) d = rotate_right(d, 12) a, b, c, d = invert_mix_columns(a, b, c, d)#, key) b = rotate_right(b, 1) c = rotate_right(c, 2) d = rotate_right(d, 3) a, b, c, d = invert_mix_columns(a, b, c, d)#, key) state[:16] = words_to_bytes((a, b, c, d), 4)
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 rotate_left3(self): # 3 0 1 2 # 7 4 5 6 #11 8 9 10 #15 12 13 14 # 3 4 9 14 # 7 8 13 2 #11 12 1 6 #15 0 5 10 # 3 8 13 10 # 7 12 1 14 #11 0 5 2 #15 4 9 6 word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [ in_bytes[index] for index in (3, 0, 1, 2, 7, 4, 5, 6, 11, 8, 9, 10, 15, 12, 13, 14) ] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def rotate_down(self): # 0 1 2 3 # 4 5 6 7 # 8 9 10 11 #12 13 14 15 # 0 5 10 15 # 4 9 14 3 # 8 13 2 7 #12 1 6 11 # 0 13 10 7 # inverse # 4 1 14 11 # 8 5 2 15 #12 9 6 3 word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [ in_bytes[index] for index in (0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11) ] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def rotate_left3(self): # 3 0 1 2 # 7 4 5 6 #11 8 9 10 #15 12 13 14 # 3 4 9 14 # 7 8 13 2 #11 12 1 6 #15 0 5 10 # 3 8 13 10 # 7 12 1 14 #11 0 5 2 #15 4 9 6 word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [in_bytes[index] for index in (3, 0, 1, 2, 7, 4, 5, 6, 11, 8, 9, 10, 15, 12, 13, 14)] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def rotate_down(self): # 0 1 2 3 # 4 5 6 7 # 8 9 10 11 #12 13 14 15 # 0 5 10 15 # 4 9 14 3 # 8 13 2 7 #12 1 6 11 # 0 13 10 7 # inverse # 4 1 14 11 # 8 5 2 15 #12 9 6 3 word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [in_bytes[index] for index in (0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11)] self.words[:] = bytes_to_words(shuffled_bytes, 4)
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 rotate_left2(self): # 2 3 0 1 # 6 7 4 5 #10 11 8 9 #14 15 12 13 # 2 7 8 13 # 6 11 12 1 #10 15 0 5 #14 3 4 9 # 2 11 12 1 # 6 15 0 5 #10 3 4 9 #14 7 8 13 word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [in_bytes[index] for index in (2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13)] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def rotate_left2(self): # 2 3 0 1 # 6 7 4 5 #10 11 8 9 #14 15 12 13 # 2 7 8 13 # 6 11 12 1 #10 15 0 5 #14 3 4 9 # 2 11 12 1 # 6 15 0 5 #10 3 4 9 #14 7 8 13 word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [ in_bytes[index] for index in (2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13) ] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def encrypt64v2(data, key, output_type="bytes"): """ Encrypt 64 bits of data using key. Returns the resulting ciphertext Encryption is: - Randomized - The randomizing value is kept secret/not sent in the clear - Homomorphic - Supports unlimited partially homomorphic operationss - D(E(x) ^ E(y)) == x ^ y Encryption is performed by generating a 64-bit random padding value and masking the data with it, then concatenating the padding to the masked 64-bit message, and finally applying a keyed bit permutation on the result. To compute the XOR of 2 ciphertexts, simply XOR the ciphertexts together.""" padding = new_key(2, 4) inputs = bytes_to_words(bytearray(data), 4) inputs[0] ^= padding[0] inputs[1] ^= padding[1] inputs = tuple(inputs) + padding if output_type == "bytes": return words_to_bytes(bit_permutation128(inputs, key), 4) else: if output_type != "words": raise ValueError("Invalid output_type '{}'".format(output_type)) return bit_permutation128(inputs, key)
def permutation2(state): key = 0 for byte in state: key ^= byte a, b, c, d = bytes_to_words(state, 4) a, b, c, d = mix_columns(a, b, c, d)#, key) b = rotate_left(b, 1) c = rotate_left(c, 2) d = rotate_left(d, 3) a, b, c, d = mix_columns(a, b, c, d)#, key) b = rotate_left(b, 4) c = rotate_left(c, 8) d = rotate_left(d, 12) a, b, c, d = mix_columns(a, b, c, d)#, key) b = rotate_left(b, 8) c = rotate_left(c, 12) d = rotate_left(d, 16) a, b, c, d = mix_columns(a, b, c, d)#, key) state[:16] = words_to_bytes((a, b, c, d), 4)
def little_swap(self): word_size = self.word_size words = self.words in_bytes = words_to_bytes(words, 4) shuffled_bytes = [in_bytes[index] for index in (7, 12, 14, 9, 2, 1, 5, 15, 11, 6, 13, 0, 4, 8, 10, 3)] self.words[:] = bytes_to_words(shuffled_bytes, 4)
def hash_function(data): data = bytes_to_words(pad_input(bytearray(data), 64), 4) return bytes(words_to_bytes(compression_function(data), 4))
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 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)