|
@@ -0,0 +1,320 @@
|
|
|
+from __future__ import print_function
|
|
|
+
|
|
|
+
|
|
|
+class SpeckCipher(object):
|
|
|
+ """Speck Block Cipher Object"""
|
|
|
+
|
|
|
+
|
|
|
+ __valid_setups = {32: {64: 22},
|
|
|
+ 48: {72: 22, 96: 23},
|
|
|
+ 64: {96: 26, 128: 27},
|
|
|
+ 96: {96: 28, 144: 29},
|
|
|
+ 128: {128: 32, 192: 33, 256: 34}}
|
|
|
+
|
|
|
+ __valid_modes = ['ECB', 'CTR', 'CBC', 'PCBC', 'CFB', 'OFB']
|
|
|
+
|
|
|
+ def encrypt_round(self, x, y, k):
|
|
|
+ """Complete One Round of Feistel Operation"""
|
|
|
+ rs_x = ((x << (self.word_size - self.alpha_shift)) + (x >> self.alpha_shift)) & self.mod_mask
|
|
|
+
|
|
|
+ add_sxy = (rs_x + y) & self.mod_mask
|
|
|
+
|
|
|
+ new_x = k ^ add_sxy
|
|
|
+
|
|
|
+ ls_y = ((y >> (self.word_size - self.beta_shift)) + (y << self.beta_shift)) & self.mod_mask
|
|
|
+
|
|
|
+ new_y = new_x ^ ls_y
|
|
|
+
|
|
|
+ return new_x, new_y
|
|
|
+
|
|
|
+ def decrypt_round(self, x, y, k):
|
|
|
+ """Complete One Round of Inverse Feistel Operation"""
|
|
|
+
|
|
|
+ xor_xy = x ^ y
|
|
|
+
|
|
|
+ new_y = ((xor_xy << (self.word_size - self.beta_shift)) + (xor_xy >> self.beta_shift)) & self.mod_mask
|
|
|
+
|
|
|
+ xor_xk = x ^ k
|
|
|
+
|
|
|
+ msub = ((xor_xk - new_y) + self.mod_mask_sub) % self.mod_mask_sub
|
|
|
+
|
|
|
+ new_x = ((msub >> (self.word_size - self.alpha_shift)) + (msub << self.alpha_shift)) & self.mod_mask
|
|
|
+
|
|
|
+ return new_x, new_y
|
|
|
+
|
|
|
+ def __init__(self, key, key_size=128, block_size=128, mode='ECB', init=0, counter=0):
|
|
|
+
|
|
|
+
|
|
|
+ try:
|
|
|
+ self.possible_setups = self.__valid_setups[block_size]
|
|
|
+ self.block_size = block_size
|
|
|
+ self.word_size = self.block_size >> 1
|
|
|
+ except KeyError:
|
|
|
+ print('Invalid block size!')
|
|
|
+ print('Please use one of the following block sizes:', [x for x in self.__valid_setups.keys()])
|
|
|
+ raise
|
|
|
+
|
|
|
+
|
|
|
+ try:
|
|
|
+ self.rounds = self.possible_setups[key_size]
|
|
|
+ self.key_size = key_size
|
|
|
+ except KeyError:
|
|
|
+ print('Invalid key size for selected block size!!')
|
|
|
+ print('Please use one of the following key sizes:', [x for x in self.possible_setups.keys()])
|
|
|
+ raise
|
|
|
+
|
|
|
+
|
|
|
+ self.mod_mask = (2 ** self.word_size) - 1
|
|
|
+
|
|
|
+
|
|
|
+ self.mod_mask_sub = (2 ** self.word_size)
|
|
|
+
|
|
|
+
|
|
|
+ if self.block_size == 32:
|
|
|
+ self.beta_shift = 2
|
|
|
+ self.alpha_shift = 7
|
|
|
+ else:
|
|
|
+ self.beta_shift = 3
|
|
|
+ self.alpha_shift = 8
|
|
|
+
|
|
|
+
|
|
|
+ try:
|
|
|
+ self.iv = init & ((2 ** self.block_size) - 1)
|
|
|
+ self.iv_upper = self.iv >> self.word_size
|
|
|
+ self.iv_lower = self.iv & self.mod_mask
|
|
|
+ except (ValueError, TypeError):
|
|
|
+ print('Invalid IV Value!')
|
|
|
+ print('Please Provide IV as int')
|
|
|
+ raise
|
|
|
+
|
|
|
+
|
|
|
+ try:
|
|
|
+ self.counter = counter & ((2 ** self.block_size) - 1)
|
|
|
+ except (ValueError, TypeError):
|
|
|
+ print('Invalid Counter Value!')
|
|
|
+ print('Please Provide Counter as int')
|
|
|
+ raise
|
|
|
+
|
|
|
+
|
|
|
+ try:
|
|
|
+ position = self.__valid_modes.index(mode)
|
|
|
+ self.mode = self.__valid_modes[position]
|
|
|
+ except ValueError:
|
|
|
+ print('Invalid cipher mode!')
|
|
|
+ print('Please use one of the following block cipher modes:', self.__valid_modes)
|
|
|
+ raise
|
|
|
+
|
|
|
+
|
|
|
+ try:
|
|
|
+ self.key = key & ((2 ** self.key_size) - 1)
|
|
|
+ except (ValueError, TypeError):
|
|
|
+ print('Invalid Key Value!')
|
|
|
+ print('Please Provide Key as int')
|
|
|
+ raise
|
|
|
+
|
|
|
+
|
|
|
+ self.key_schedule = [self.key & self.mod_mask]
|
|
|
+ l_schedule = [(self.key >> (x * self.word_size)) & self.mod_mask for x in
|
|
|
+ range(1, self.key_size // self.word_size)]
|
|
|
+
|
|
|
+ for x in range(self.rounds - 1):
|
|
|
+ new_l_k = self.encrypt_round(l_schedule[x], self.key_schedule[x], x)
|
|
|
+ l_schedule.append(new_l_k[0])
|
|
|
+ self.key_schedule.append(new_l_k[1])
|
|
|
+
|
|
|
+ print(self.key_schedule)
|
|
|
+
|
|
|
+ def encrypt(self, plaintext):
|
|
|
+ try:
|
|
|
+ b = (plaintext >> self.word_size) & self.mod_mask
|
|
|
+ a = plaintext & self.mod_mask
|
|
|
+ except TypeError:
|
|
|
+ print('Invalid plaintext!')
|
|
|
+ print('Please provide plaintext as int')
|
|
|
+ raise
|
|
|
+
|
|
|
+ if self.mode == 'ECB':
|
|
|
+ b, a = self.encrypt_function(b, a)
|
|
|
+
|
|
|
+ elif self.mode == 'CTR':
|
|
|
+ true_counter = self.iv + self.counter
|
|
|
+ d = (true_counter >> self.word_size) & self.mod_mask
|
|
|
+ c = true_counter & self.mod_mask
|
|
|
+ d, c = self.encrypt_function(d, c)
|
|
|
+ b ^= d
|
|
|
+ a ^= c
|
|
|
+ self.counter += 1
|
|
|
+
|
|
|
+ elif self.mode == 'CBC':
|
|
|
+ b ^= self.iv_upper
|
|
|
+ a ^= self.iv_lower
|
|
|
+ b, a = self.encrypt_function(b, a)
|
|
|
+
|
|
|
+ self.iv_upper = b
|
|
|
+ self.iv_lower = a
|
|
|
+ self.iv = (b << self.word_size) + a
|
|
|
+
|
|
|
+ elif self.mode == 'PCBC':
|
|
|
+ f, e = b, a
|
|
|
+ b ^= self.iv_upper
|
|
|
+ a ^= self.iv_lower
|
|
|
+ b, a = self.encrypt_function(b, a)
|
|
|
+ self.iv_upper = (b ^ f)
|
|
|
+ self.iv_lower = (a ^ e)
|
|
|
+ self.iv = (self.iv_upper << self.word_size) + self.iv_lower
|
|
|
+
|
|
|
+ elif self.mode == 'CFB':
|
|
|
+ d = self.iv_upper
|
|
|
+ c = self.iv_lower
|
|
|
+ d, c = self.encrypt_function(d, c)
|
|
|
+ b ^= d
|
|
|
+ a ^= c
|
|
|
+ self.iv_upper = b
|
|
|
+ self.iv_lower = a
|
|
|
+ self.iv = (b << self.word_size) + a
|
|
|
+
|
|
|
+ elif self.mode == 'OFB':
|
|
|
+ d = self.iv_upper
|
|
|
+ c = self.iv_lower
|
|
|
+ d, c = self.encrypt_function(d, c)
|
|
|
+ self.iv_upper = d
|
|
|
+ self.iv_lower = c
|
|
|
+ self.iv = (d << self.word_size) + c
|
|
|
+
|
|
|
+ b ^= d
|
|
|
+ a ^= c
|
|
|
+
|
|
|
+ ciphertext = (b << self.word_size) + a
|
|
|
+
|
|
|
+ return ciphertext
|
|
|
+
|
|
|
+ def decrypt(self, ciphertext):
|
|
|
+ try:
|
|
|
+ b = (ciphertext >> self.word_size) & self.mod_mask
|
|
|
+ a = ciphertext & self.mod_mask
|
|
|
+ except TypeError:
|
|
|
+ print('Invalid ciphertext!')
|
|
|
+ print('Please provide plaintext as int')
|
|
|
+ raise
|
|
|
+
|
|
|
+ if self.mode == 'ECB':
|
|
|
+ b, a = self.decrypt_function(b, a)
|
|
|
+
|
|
|
+ elif self.mode == 'CTR':
|
|
|
+ true_counter = self.iv + self.counter
|
|
|
+ d = (true_counter >> self.word_size) & self.mod_mask
|
|
|
+ c = true_counter & self.mod_mask
|
|
|
+ d, c = self.encrypt_function(d, c)
|
|
|
+ b ^= d
|
|
|
+ a ^= c
|
|
|
+ self.counter += 1
|
|
|
+
|
|
|
+ elif self.mode == 'CBC':
|
|
|
+ f, e = b, a
|
|
|
+ b, a = self.decrypt_function(b, a)
|
|
|
+ b ^= self.iv_upper
|
|
|
+ a ^= self.iv_lower
|
|
|
+
|
|
|
+ self.iv_upper = f
|
|
|
+ self.iv_lower = e
|
|
|
+ self.iv = (f << self.word_size) + e
|
|
|
+
|
|
|
+ elif self.mode == 'PCBC':
|
|
|
+ f, e = b, a
|
|
|
+
|
|
|
+ b, a = self.decrypt_function(b, a)
|
|
|
+
|
|
|
+ b ^= self.iv_upper
|
|
|
+ a ^= self.iv_lower
|
|
|
+ self.iv_upper = (b ^ f)
|
|
|
+ self.iv_lower = (a ^ e)
|
|
|
+ self.iv = (self.iv_upper << self.word_size) + self.iv_lower
|
|
|
+
|
|
|
+ elif self.mode == 'CFB':
|
|
|
+ d = self.iv_upper
|
|
|
+ c = self.iv_lower
|
|
|
+ self.iv_upper = b
|
|
|
+ self.iv_lower = a
|
|
|
+ self.iv = (b << self.word_size) + a
|
|
|
+ d, c = self.encrypt_function(d, c)
|
|
|
+
|
|
|
+ b ^= d
|
|
|
+ a ^= c
|
|
|
+
|
|
|
+ elif self.mode == 'OFB':
|
|
|
+ d = self.iv_upper
|
|
|
+ c = self.iv_lower
|
|
|
+ d, c = self.encrypt_function(d, c)
|
|
|
+
|
|
|
+ self.iv_upper = d
|
|
|
+ self.iv_lower = c
|
|
|
+ self.iv = (d << self.word_size) + c
|
|
|
+
|
|
|
+ b ^= d
|
|
|
+ a ^= c
|
|
|
+
|
|
|
+ plaintext = (b << self.word_size) + a
|
|
|
+
|
|
|
+ return plaintext
|
|
|
+
|
|
|
+ def encrypt_function(self, upper_word, lower_word):
|
|
|
+
|
|
|
+ x = upper_word
|
|
|
+ y = lower_word
|
|
|
+
|
|
|
+
|
|
|
+ for k in self.key_schedule:
|
|
|
+ rs_x = ((x << (self.word_size - self.alpha_shift)) + (x >> self.alpha_shift)) & self.mod_mask
|
|
|
+
|
|
|
+ add_sxy = (rs_x + y) & self.mod_mask
|
|
|
+
|
|
|
+ x = k ^ add_sxy
|
|
|
+
|
|
|
+ ls_y = ((y >> (self.word_size - self.beta_shift)) + (y << self.beta_shift)) & self.mod_mask
|
|
|
+
|
|
|
+ y = x ^ ls_y
|
|
|
+
|
|
|
+ return x,y
|
|
|
+
|
|
|
+
|
|
|
+ def decrypt_function(self, upper_word, lower_word):
|
|
|
+
|
|
|
+ x = upper_word
|
|
|
+ y = lower_word
|
|
|
+
|
|
|
+
|
|
|
+ for k in reversed(self.key_schedule):
|
|
|
+ xor_xy = x ^ y
|
|
|
+
|
|
|
+ y = ((xor_xy << (self.word_size - self.beta_shift)) + (xor_xy >> self.beta_shift)) & self.mod_mask
|
|
|
+
|
|
|
+ xor_xk = x ^ k
|
|
|
+
|
|
|
+ msub = ((xor_xk - y) + self.mod_mask_sub) % self.mod_mask_sub
|
|
|
+
|
|
|
+ x = ((msub >> (self.word_size - self.alpha_shift)) + (msub << self.alpha_shift)) & self.mod_mask
|
|
|
+
|
|
|
+
|
|
|
+ return x,y
|
|
|
+
|
|
|
+
|
|
|
+ def update_iv(self, new_iv=None):
|
|
|
+ if new_iv:
|
|
|
+ try:
|
|
|
+ self.iv = new_iv & ((2 ** self.block_size) - 1)
|
|
|
+ self.iv_upper = self.iv >> self.word_size
|
|
|
+ self.iv_lower = self.iv & self.mod_mask
|
|
|
+ except TypeError:
|
|
|
+ print('Invalid Initialization Vector!')
|
|
|
+ print('Please provide IV as int')
|
|
|
+ raise
|
|
|
+ return self.iv
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+
|
|
|
+ cipher = SpeckCipher(0x0102030405060708, 64, 32, 'ECB')
|
|
|
+
|
|
|
+
|
|
|
+ g = cipher.encrypt(0xdeadbeefdeadbeef)
|
|
|
+ print(hex(g))
|