def key_expansion(key): expanded_key = [GF28.GF28(0)] * ((WORDS_PER_STATE * (ROUNDS + 1)) * BYTES_PER_WORD) # fill out the first four words with (4 * 8 = 32 bytes) the initial key for i in range(len(key)): expanded_key[i] = key[i] # fill out now according to the rule: # if it's a multiple of four, take the previous word, circular shift back once, # and then take each byte through the subst. table before XOR'ing with the # round constant # otherwise, leave it the same # then XOR the previous byte with the one NK back iterator = 4 * BYTES_PER_WORD # this takes place PER WORD (so operating on 4 bytes at a time) while (iterator < len(expanded_key)): prev_word = expanded_key[iterator - BYTES_PER_WORD:iterator] if iterator % (4 * BYTES_PER_WORD) == 0: rotate_key_word(prev_word) sub_bytes(prev_word) some_const = get_round_constant( math.floor(iterator / (BYTES_PER_WORD * WORDS_PER_STATE))) for idx, byte in enumerate(prev_word): prev_word[idx] = prev_word[idx] + some_const[idx] word_four_back = expanded_key[iterator - (4 * BYTES_PER_WORD):iterator - (3 * BYTES_PER_WORD)] new_word = [GF28.GF28(0)] * 4 for idx in range(iterator, iterator + BYTES_PER_WORD): it = idx % 4 expanded_key[idx] = prev_word[it] + word_four_back[it] iterator += BYTES_PER_WORD return expanded_key
def modify_list_into_GF28(original_list): modified_list = [] if isinstance(original_list, bytes) or isinstance(original_list, bytearray): for i in range(len(original_list)): modified_list.append(GF28.GF28(original_list[i])) elif isinstance(original_list, str): for i in range(len(original_list)): modified_list.append(GF28.GF28(ord(original_list[i]))) else: raise TypeError("input is of an unsupported type") return modified_list
def modify_IV_into_GF28(text_IV): # we only accept a string for this that is an integer (between 0 and 255) # each integer should be delimited by dashes (i.e. -) and there should be 16 # integers result_in_GF28 = [] if type(text_IV) is bytearray: for i in text_IV: result_in_GF28.append(GF28.GF28(i)) return result_in_GF28 values = text_IV.split('-') if (len(values)) != 16: raise ValueError("IV is not correct length for AES") for let in values: result_in_GF28.append(GF28.GF28(int(let))) return result_in_GF28
def create_table(): with open(filename, 'w') as holder: subst = {} inverse = {} for num in range(256): num = GF28.GF28(num, bypass_modcheck=True) # works with 0 b/c of hack in GF28.py byte_stuff = num.inverse() res = GF28.GF28() for i in range(5): res = res + byte_stuff byte_stuff.rotate_bit() res = res + GF28.GF28(0x63) if num.number > 256 or res.number > 256: raise ValueError("Num or res out of range") subst[num.number] = res.number inverse[res.number] = num.number holder.write(json.dumps([subst, inverse]))
def inv_mix_cols(state): new_state = [GF28.GF28(0)] * 16 for row in range(0, 4): for col in range(0, 4): index = 4 * row + col # row from mix mtrx, # col from state mtrx new_state[index] = GF28.dot_prod_in_GF28( INV_MIX_MTX[4 * row:4 * (row + 1)], state[col:16:4]) return new_state
def sub_bytes(state): for idx, byte in enumerate(state): state[idx] = GF28.GF28(SUBS_TABLE[str(byte.number)], True) return
def PKCS_padd(array_GF28): padding = 16 - (len(array_GF28) % 16) array_GF28.extend([GF28.GF28(padding)] * padding) return array_GF28
def get_round_constant(round_idx): if (round_idx < 1): raise ValueError("Index should be higher for calling round constant") round_res = [GF28.GF28(0)] * 4 round_res[0] = GF28.GF28(1 << (round_idx - 1)) return round_res
def unscramble_state(state_arr): state_output = [GF28.GF28(0)] * 16 for idx, GF28_elt in enumerate(state_arr): quot_rem = divmod(idx, 4) state_output[quot_rem[1] * 4 + quot_rem[0]] = state_arr[idx] return state_output
def fill_state_array(text): state_arr = [GF28.GF28(0)] * 16 for idx, elt_GF28 in enumerate(state_arr): quot_rem = divmod(idx, 4) state_arr[idx] = text[quot_rem[1] * 4 + quot_rem[0]] return state_arr
# takes a round index and returns a round constant for key expansion # that represents a poly of degree # less than 4 with elements from GF28 def get_round_constant(round_idx): if (round_idx < 1): raise ValueError("Index should be higher for calling round constant") round_res = [GF28.GF28(0)] * 4 round_res[0] = GF28.GF28(1 << (round_idx - 1)) return round_res # fixing this to let the offset determine which row somehthing actually is MIX_MTX = [2, 3, 1, 1, 1, 2, 3, 1, 1, 1, 2, 3, 3, 1, 1, 2] for idx, val in enumerate(MIX_MTX): MIX_MTX[idx] = GF28.GF28(val) # TODO write a matrix inverse function INV_MIX_MTX = [14, 11, 13, 9, 9, 14, 11, 13, 13, 9, 14, 11, 11, 13, 9, 14] for idx, val in enumerate(INV_MIX_MTX): INV_MIX_MTX[idx] = GF28.GF28(val) SUBS_TABLE = {} INV_SUBS_TABLE = {} if not (os.path.exists(filename)): create_table() with open(filename, 'r') as sub_r: both_tables = json.loads(sub_r.read()) SUBS_TABLE = both_tables[0] INV_SUBS_TABLE = both_tables[1] def print_GF28_list(list_t):