def DecryptBlock(k_obj: KalynaObject, c_text: Union[str, int], roundkeys: dict, inv_tables: list, inv_mix_col_t: list, inv_c_mat: np.array): l = k_obj.BlockSize t = k_obj.NumRounds k_t = roundkeys[t] k_obj.state = to_bytes( hex(c_text)[2:], k_obj.state.shape[1], k_obj.NumStateMatrixRows, k_obj.HexKeyLength, True) sub_method(k_obj, k_t) invMixColumns(k_obj, inv_mix_col_t, inv_c_mat) inv_shift = [-((i * l) // 512) for i in range(len(k_obj.state[0]))] inv_rot_word(k_obj, inv_shift) inv_sub_bytes(k_obj, inv_tables) for i in range(t - 1, 0, -1): rk = roundkeys[i] xor_round_key(k_obj, rk) invMixColumns(k_obj, inv_mix_col_t, inv_c_mat) inv_rot_word(k_obj, inv_shift) inv_sub_bytes(k_obj, inv_tables) rk = roundkeys[0] sub_method(k_obj, rk) return k_obj.state
def sub_method(k_obj: KalynaObject, byte2: np.array, modulus: int = 2**64): """ Returns the difference of the internal state matrix and byte2 modulo some input. Modulus = 2**64 (default). :param k_obj: Kalyna object. :param byte2: 2nd byte to be subtracted. :param modulus: Default value according to the paper. :return: (np array) Difference of both bytes modulo "modulus". """ byte1 = k_obj.state assert byte1.shape == byte2.shape, "Bytes should be of the same shape." length, width = byte1.shape slc = 2 * width f_block = list() b1_w = to_words(np.fliplr(byte1)) b2_w = to_words(np.fliplr(byte2)) for i in range(length): b1 = int(b1_w[slc * i:slc * (i + 1)], 16) b2 = int(b2_w[slc * i:slc * (i + 1)], 16) d = (b1 - b2) % modulus v = hex(d)[2:] # for hex whose starting 0 is stripped off by python while len(v) < slc: v = '0' + v f_block.append(v) f_block = ''.join(f_block) o_block = np.flipud( to_bytes(f_block, width, k_obj.NumStateMatrixRows, k_obj.HexKeyLength, True)) k_obj.state = o_block
def EncryptBlock(k_obj: KalynaObject, p_text: Union[str, int], roundkeys: dict, tables: list, mix_col_t: list, c_mat: np.array): k_0 = roundkeys[0] k_obj.state = to_bytes( hex(p_text)[2:], k_obj.state.shape[1], k_obj.NumStateMatrixRows, k_obj.HexKeyLength, True) add_method(k_obj, k_0) for j in range(1, k_obj.NumRounds): sub_bytes(k_obj, tables) shift = [ math.floor((i * k_obj.BlockSize) / 512) for i in range(len(k_obj.state[0])) ] rot_word(k_obj, shift) mixColumns(k_obj, mix_col_t, c_mat) k_v = roundkeys[j] xor_round_key(k_obj, k_v) sub_bytes(k_obj, tables) shift = [(i * k_obj.BlockSize) // 512 for i in range(len(k_obj.state[0]))] rot_word(k_obj, shift) mixColumns(k_obj, mix_col_t, c_mat) k_t = roundkeys[k_obj.NumRounds] add_method(k_obj, k_t) return k_obj.state
def KeyExpansionKT(k_obj: KalynaObject, main_key: int, k_alpha: np.array, k_omega: np.array, tables: list, mix_col_t: list, c_mat: np.array, state_0: int): l = k_obj.BlockSize # Pad state_0 word to appropriate length as keyLengthSize init_state = '0' * (k_obj.HexKeyLength - len(hex(state_0)[2:])) + hex(state_0)[2:] k_alpha_byte = k_alpha k_omega_byte = k_omega init_state_byte = to_bytes(init_state, k_obj.state.shape[1], k_obj.NumStateMatrixRows, k_obj.HexKeyLength, True) k_obj.state = init_state_byte add_method(k_obj, k_alpha_byte) shift = [(i * l) // 512 for i in range(len(k_obj.state[0]))] EncryptRound(k_obj, tables, mix_col_t, c_mat, shift) xor_round_key(k_obj, k_omega_byte) EncryptRound(k_obj, tables, mix_col_t, c_mat, shift) add_method(k_obj, k_alpha_byte) EncryptRound(k_obj, tables, mix_col_t, c_mat, shift) K_sigma = k_obj.state return K_sigma
def KalynaDecrypt(parameters: tuple, c_text: Union[str, int], main_key: int, state_0: int): l, k, t, c = parameters rks = KalynaKeyExpansion(parameters, main_key, state_0) k_d = KalynaObject(l, k, t, c) return DecryptBlock(k_d, c_text, rks, all_inv_tables, all_inv_mix_cols, inverse_mds_matrix)
def KalynaEncrypt(parameters: tuple, p_text: Union[str, int], main_key: int, state_0: int): l, k, t, c = parameters rks = KalynaKeyExpansion(parameters, main_key, state_0) k_e = KalynaObject(l, k, t, c) return EncryptBlock(k_e, p_text, rks, all_sub_tables, all_mix_cols, mds_matrix)
def inv_rot_word(k_obj: KalynaObject, inv_shift: list): """ Implements the inverse permutation step. :param k_obj: Kalyna object. :param inv_shift: Permutation values. -ve for left shift and +ve for right shift. :return: Shifted state matrix. """ vals = k_obj.state assert len(inv_shift) == vals.shape[ 1], "Length of inv_shift list and state-matrix dim[1] must be same." items = vals.transpose() for i in range(len(inv_shift)): items[i] = np.roll(items[i], inv_shift[i], axis=0) k_obj.state = items.transpose()
def inv_sub_bytes(k_obj: KalynaObject, inv_tables: list): """ Implements the inverse mapping step of the state matrix. :param k_obj: Kalyna object. :param inv_tables: Forward substitution tables. :return: Mapped state matrix. """ num_col, c, h = k_obj.state.shape[ 1], k_obj.NumStateMatrixRows, k_obj.HexKeyLength inv_sub_block = to_bytes([ inv_tables[j % 4][byte[j]] for byte in k_obj.state for j in range(len(byte)) ], num_col, c, h) k_obj.state = inv_sub_block
def KalynaKeyExpansion(parameters: tuple, main_key: int, state_0: int): l, k, t, c = parameters # Declare KalynaObject for round key generation k_c = KalynaObject(l, k, t, c) KT = 0 if k == l: k0_b = key_to_bytes( hex(main_key)[2:], k_c.state.shape[1], c, k_c.PadKeyLength) # k_alpha k1_b = key_to_bytes( hex(main_key)[2:], k_c.state.shape[1], c, k_c.PadKeyLength) # k_omega KT = KeyExpansionKT(k_c, main_key, k0_b, k1_b, all_sub_tables, all_mix_cols, mds_matrix, state_0) k_c.state = KT elif k == 2 * l: # Number of State Matrix Rows i.e. k_obj.NumStateMatrixRows for key handling is different for k=2*l cases # k_alpha k0 = LsbReturn(main_key, l, k_c.PadKeyLength) k0_b = key_to_bytes( hex(k0)[2:], k_c.state.shape[1], c, k_c.HexKeyLength) # k_omega k1 = MsbReturn(main_key, l, k_c.PadKeyLength) k1_b = key_to_bytes( hex(k1)[2:], k_c.state.shape[1], c, k_c.HexKeyLength) KT = KeyExpansionKT(k_c, main_key, k0_b, k1_b, all_sub_tables, all_mix_cols, mds_matrix, state_0) k_c.state = KT roundkeys = KeyExpansionEven(k_c, KT, main_key, all_sub_tables, all_mix_cols, mds_matrix) roundkeys = KeyExpansionOdd(k_c, roundkeys) return roundkeys
def xor_round_key(k_obj: KalynaObject, byte2: np.array): """ Returns the XOR result of the internal state matrix of Kalyna object with byte2. :param k_obj: Kalyna object. :param byte2: 2nd byte. :return app_block: (array) XOR result. """ byte_block = k_obj.state assert byte_block.shape == byte2.shape, "byte1 and byte2 are not of same shape." length, width = byte2.shape[0], byte2.shape[1] xor_block = [ list(map(xor, byte_block[i], byte2[i])) for i in range(length) ] k_obj.state = to_bytes(xor_block, width, k_obj.NumStateMatrixRows, k_obj.HexKeyLength)
def mixColumns(k_obj: KalynaObject, tables: list, v: np.array): """ Implements the Galois Field GF(2^8) transformation step. :param k_obj: Kalyna object. :param tables: Forward transformation tables. :param v: MDS vector matrix implemented as 8x8 numpy array :return: State matrix. """ r_array = k_obj.state g = list() for byte in r_array: # Looping on mds_matrix for row_v in range(len(byte)): pr = 0 for col_v in range(len(byte)): y = v[row_v][col_v] pr ^= tables[y][byte[col_v]] g.append(pr) k_obj.state = to_bytes(g, k_obj.state.shape[1], k_obj.NumStateMatrixRows, k_obj.HexKeyLength)
def invMixColumns(k_obj: KalynaObject, inv_tables: list, c_mat_inv: np.array): """ Implements the inverse Galois Field GF(2^8) transformation step. :param k_obj: Kalyna object. :param inv_tables: Forward transformation tables. :param c_mat_inv: MDS inverse vector matrix implemented as 8x8 numpy array :return: State matrix. """ trans = {173: 0, 149: 1, 118: 2, 168: 3, 47: 4, 73: 5, 215: 6, 202: 7} r_array = k_obj.state g = list() for byte in r_array: # Looping on mds_matrix for row_v in range(len(byte)): pr = 0 for col_v in range(len(byte)): y = c_mat_inv[row_v][col_v] ind = trans[y] pr ^= inv_tables[ind][byte[col_v]] g.append(pr) k_obj.state = to_bytes(g, k_obj.state.shape[1], k_obj.NumStateMatrixRows, k_obj.HexKeyLength)
def KeyExpansionEven(k_obj: KalynaObject, k_sigma: np.array, main_key: int, tables: list, mix_col_t: list, c_mat: np.array): # Design tmv firstly tmv_0 = int("0001" * (k_obj.HexKeyLength // 4), 16) roundkeys = dict() l = k_obj.BlockSize k = k_obj.KeyLength for i in range(0, k_obj.NumRounds + 1, 2): # print("Round number: {}".format(i)) # tmv left-shift and length check in case if MSB is added for shift >=8 tmv = tmv_0 << (i // 2) tmv_byte = key_to_bytes( hex(tmv)[2:], k_obj.state.shape[1], k_obj.NumStateMatrixRows, k_obj.HexKeyLength) # right shift the main_key acc to following conditions if k == l: cipher_key = circular_right_shift(main_key, 32 * i, k) ckb = key_to_bytes( hex(cipher_key)[2:], k_obj.state.shape[1], k_obj.NumStateMatrixRows, k_obj.PadKeyLength) cipher_key_byte = np.array(ckb, int) elif k == 2 * l: # Number of State Matrix Rows i.e. k_obj.NumStateMatrixRows for key handling is different for k=2*l cases if i % 4 == 0: cipher_key = circular_right_shift(main_key, 16 * i, k) ckb = key_to_bytes( hex(cipher_key)[2:], k_obj.state.shape[1], 2 * k_obj.NumStateMatrixRows, k_obj.PadKeyLength) cipher_key_byte = np.array(ckb[:k_obj.NumStateMatrixRows], int) else: cipher_key = circular_right_shift(main_key, 64 * (i // 4), k) ckb = key_to_bytes( hex(cipher_key)[2:], k_obj.state.shape[1], 2 * k_obj.NumStateMatrixRows, k_obj.PadKeyLength) cipher_key_byte = np.array(ckb[k_obj.NumStateMatrixRows:], int) else: cipher_key = main_key raise ValueError( "Incorrect values of k:KeyLength = {} and l:BlockSize = {} !". format(k, l)) # Key expansion process kt_round = add_method_simple(k_sigma, tmv_byte, k_obj.NumStateMatrixRows, k_obj.HexKeyLength) k_obj.state = kt_round add_method(k_obj, cipher_key_byte) shift = [(i * l) // 512 for i in range(len(k_obj.state[0]))] EncryptRound(k_obj, tables, mix_col_t, c_mat, shift) xor_round_key(k_obj, kt_round) EncryptRound(k_obj, tables, mix_col_t, c_mat, shift) add_method(k_obj, kt_round) roundkeys[i] = k_obj.state return roundkeys