Simon Cipher¶
Implementation of Simon cipher¶
# Reference: https://github.com/inmcm/Simon_Speck_Ciphers/blob/master/Python/simonspeckciphers/simon/simon.py
# For the sake of simplicity, we modified some functions of the original implementation
from collections import deque
class SimonCipher(object):
"""Simon Block Cipher Object"""
# Z Arrays (stored bit reversed for easier usage)
z0 = 0b01100111000011010100100010111110110011100001101010010001011111
z1 = 0b01011010000110010011111011100010101101000011001001111101110001
z2 = 0b11001101101001111110001000010100011001001011000000111011110101
z3 = 0b11110000101100111001010001001000000111101001100011010111011011
z4 = 0b11110111001001010011000011101000000100011011010110011110001011
# valid cipher configurations stored:
# block_size:{key_size:(number_rounds,z sequence)}
__valid_setups = {32: {64: (32, z0)},
48: {72: (36, z0), 96: (36, z1)},
64: {96: (42, z2), 128: (44, z3)},
96: {96: (52, z2), 144: (54, z3)},
128: {128: (68, z2), 192: (69, z3), 256: (72, z4)}}
def __init__(self, key, key_size=128, block_size=128):
"""
Initialize an instance of the Simon block cipher.
:param key: Int representation of the encryption key
:param key_size: Int representing the encryption key in bits
:param block_size: Int representing the block size in bits
:return: None
"""
# Setup block/word size
self.possible_setups = self.__valid_setups[block_size]
self.block_size = block_size
self.word_size = self.block_size >> 1
self.rounds, self.zseq = self.possible_setups[key_size]
self.key_size = key_size
# Create Properly Sized bit mask for truncating addition and left shift outputs
self.mod_mask = (2 ** self.word_size) - 1
# Check key length
assert len(key) == (self.key_size // self.word_size)
self.key = key
# Pre-compute the key schedule
self.key_schedule()
def key_schedule(self):
m = self.key_size // self.word_size
m1 = m - 1
self.round_keys = []
# Create list of subwords from encryption key
k_init = self.key
k_reg = deque(k_init) # Use queue to manage key subwords
round_constant = self.mod_mask ^ 3 # Round Constant is 0xFFFF..FC
# Generate all round keys
for x in range(self.rounds):
s3 = self.word_size - 3
rs_3 = ((k_reg[0] << s3) + (k_reg[0] >> 3)) & self.mod_mask
if m == 4:
rs_3 = rs_3 ^ k_reg[2]
s1 = self.word_size - 1
rs_1 = ((rs_3 << s1) + (rs_3 >> 1)) & self.mod_mask
c_z = ((self.zseq >> (x % 62)) & 1) ^ round_constant
new_k = c_z ^ rs_1 ^ rs_3 ^ k_reg[m1]
self.round_keys.append(k_reg.pop())
k_reg.appendleft(new_k)
return self.round_keys
def encrypt(self, l, r):
x = l
y = r
# Run Encryption Steps For Appropriate Number of Rounds
for k in self.round_keys:
# Generate all circular shifts
s1 = self.word_size - 1
s8 = self.word_size - 8
s2 = self.word_size - 2
ls_1_x = ((x >> s1) + (x << 1)) & self.mod_mask
ls_8_x = ((x >> s8) + (x << 8)) & self.mod_mask
ls_2_x = ((x >> s2) + (x << 2)) & self.mod_mask
# XOR Chain
xor_1 = (ls_1_x & ls_8_x) ^ y
xor_2 = xor_1 ^ ls_2_x
y = x
x = k ^ xor_2
return x, y
def decrypt(self, l, r):
x = l
y = r
# Run Encryption Steps For Appropriate Number of Rounds
for k in reversed(self.round_keys):
# Generate all circular shifts
ls_1_x = ((x >> (self.word_size - 1)) + (x << 1)) & self.mod_mask
ls_8_x = ((x >> (self.word_size - 8)) + (x << 8)) & self.mod_mask
ls_2_x = ((x >> (self.word_size - 2)) + (x << 2)) & self.mod_mask
# XOR Chain
xor_1 = (ls_1_x & ls_8_x) ^ y
xor_2 = xor_1 ^ ls_2_x
y = x
x = k ^ xor_2
return x, y
Then, we can test the above implementation with some test vectors given by the authors of Simon
k3264 = [0x1918, 0x1110, 0x0908, 0x0100]
l3264, r3264 = 0x6565, 0x6877
c3264 = SimonCipher(k3264, key_size=64, block_size=32)
t3264 = c3264.encrypt(l3264, r3264)
assert t3264 == (0xc69b, 0xe9bb)
k128256 = [0x1f1e1d1c1b1a1918, 0x1716151413121110, 0x0f0e0d0c0b0a0908, 0x0706050403020100]
l128256, r128256 = 0x74206e69206d6f6f, 0x6d69732061207369
c128256 = SimonCipher(k128256, key_size=256, block_size=128)
t128256 = c128256.encrypt(l128256, r128256)
assert t128256 == (0x8d2b5579afc8a3a0, 0x3bf72a87efe7b868)
Build a circuit for Simon cipher¶
We use a bitwise circuit to construct the Simon cipher.
from circkit.bitwise import BitwiseCircuit
C = BitwiseCircuit()
key = C.add_inputs(n=4, format="k%d")
msg = C.add_inputs(n=2, format="m%d")
simon = SimonCipher(key, key_size=64, block_size=32)
cpt = simon.encrypt(msg[0], msg[1])
C.add_output(cpt)
Then, we can evaluate the circuit to test its correctness.
k3264 = [0x1918, 0x1110, 0x0908, 0x0100]
m3264 = [0x6565, 0x6877]
inp = k3264 + m3264
out = C.evaluate(inp)
print(out)
[50843, 59835]
Note that we are using bitwise circuit and the truncation
x & self.mod_mask is done automatically in the circuit. Therefore it
is not necessary to have this truncation in the circuit. In the above
Simon cipher, we still put it there because it is needed for the
verification of the Simon implementation before building its circuit.