I am revisiting the cryptopals crypto challenges code I started last year. I didn’t creat a function to “score” English text, but I have that working now. A scoring function calculates a cumulative total of how far each character in decrypted text is from the beginning of a string of characters. The frequency in which a character occurs in a language determines where it’s placed in the reference string. I also have been testing AES128 encryption. For a reference encryption method, I used Pycryptodome. I used Pycryptodome to confirm functionality of the procedures I wrote with the output from the library.
import collections def rotl8(x, shift): return (x << shift | x >> (8 - shift)) % 256 def make_aes_sbox(): sbox = [None] * 256 p = 1 q = 1 initialized = 1 while p != 1 or initialized == 1: initialized = 0 p = (p ^ (p << 1) ^ (0x1B if p & 0x80 else 0)) % 256 q ^= (q << 1) % 256 q ^= (q << 2) % 256 q ^= (q << 4) % 256 q ^= (0x09 if q & 0x80 else 0) % 256 xformed = (q ^ rotl8(q, 1) ^ rotl8(q, 2) ^ rotl8(q, 3) ^ rotl8(q, 4)) % 256 sbox[p] = xformed ^ 0x63 sbox[0] = 0x63 return sbox
print(make_aes_sbox())
[99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22]
Additional code I worked on implements the FIPS 197 key expansion and cipher functions. This exercise reinvents the wheel, since a simple method call in a standard crypto package implements AES functionality in a few minutes. AES is a de facto standard widely implemented in most modern encryption systems, I thought it finally time to go more in-depth into such an important information security standard.
def rot_word(string, steps): dequeObject = collections.deque() for a in range(0, int(len(string)), 2): dequeObject.append(string[a:a + 2]) dequeObject.rotate(-steps) rot_string = "" for a in dequeObject: rot_string += a return rot_string def sub_word(sbox, string): sub_word_string = "" for a in range(0, int(len(string)), 2): sub_word_string += sbox[string[a:a + 2]] return sub_word_string def shift_rows(state): shifted_rows = "" for a in range(0, 8, 2): row = state[a: a + 2] + state[a + 8: a + 10] + state[a + 16: a + 18] + state[a + 24: a + 26] shifted_rows += rot_word(row, int(a / 2)) return_data = "" for a in range(0, 8, 2): return_data += shifted_rows[a: a + 2] + shifted_rows[a + 8: a + 10] + shifted_rows[a + 16: a + 18] + shifted_rows[a + 24: a + 26] return return_data def mix_column(r): a = [None] * 4 b = [None] * 4 for c in range(0, 4): a[c] = r[c] h = r[c] >> 7 % 256 if h == 1: h = 0xff b[c] = r[c] << 1 % 256 b[c] ^= 0x1B & h % 256 r[0] = (b[0] ^ a[3] ^ a[2] ^ b[1] ^ a[1]) % 256 r[1] = (b[1] ^ a[0] ^ a[3] ^ b[2] ^ a[2]) % 256 r[2] = (b[2] ^ a[1] ^ a[0] ^ b[3] ^ a[3]) % 256 r[3] = (b[3] ^ a[2] ^ a[1] ^ b[0] ^ a[0]) % 256 def mix_column_state(state): after_mix_column = "" for c in range(0, 4): start = 8 * c r0 = int("0x" + state[start: start + 2], 0) r1 = int("0x" + state[start + 2: start + 4], 0) r2 = int("0x" + state[start + 4: start + 6], 0) r3 = int("0x" + state[start + 6: start + 8], 0) test = [r0, r1, r2, r3] mix_column(test) column = "" for j in range(0, 4): column += hex(test[j])[2:].zfill(2) after_mix_column += column return after_mix_column CipherKey = "2b7e151628aed2a6abf7158809cf4f3c" # CipherKey = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b" # CipherKey = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" Input = "3243f6a8885a308d313198a2e0370734" CipherKey = "2b7e151628aed2a6abf7158809cf4f3c" RC = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36] block_length = 128 key_length = len(CipherKey) * 4 Nb = int(block_length / 32) Nk = int(key_length / 32) Nr = 10 w = [None] * (Nk * Nr + Nk) for i in range(0, Nk): w[i] = CipherKey[i * 8:i * 8 + 8] sbox = make_aes_sbox() for i in range(Nk, Nk * Nr + Nk): if not i % Nk: temp = w[(i - 1)] rot_word_string = rot_word(temp, 1) sub_word_string = sub_word(sbox, rot_word_string) RC_constant = RC[int((i - 4) / Nk)] << 24 sub_word_int = int("0x" + sub_word_string, 0) after_xor_with_Rcon = sub_word_int ^ RC_constant w[i] = hex(after_xor_with_Rcon ^ int("0x" + w[i - Nk], 0))[2:] print(i - Nk, w[i], temp, rot_word_string, sub_word_string, hex(RC_constant), hex(sub_word_int)) else: temp = w[(i - 1)] w[i] = hex(int("0x" + temp, 0) ^ int("0x" + w[i - Nk], 0))[2:] print(i - Nk, w[i], temp) round_key = [None] * (Nr + 1) for i in range(Nk, Nk * Nr + Nk, 4): round = int(i / 4 - 1) print(round) end_of_round = "" round_key[round] = w[4 * round] + w[4 * round + 1] + w[4 * round + 2] + w[4 * round + 3] if round == 0: start_of_round = Input for x in range(0, 32, 2): val = hex(int("0x" + start_of_round[x: x + 2], 0) ^ int("0x" + round_key[round][x: x + 2], 0))[2:] end_of_round += val.zfill(2) if round > 0: sub_word_state = sub_word(sbox, start_of_round) shifted_rows = shift_rows(sub_word_state) after_mix_column = mix_column_state(shifted_rows) print(start_of_round) print(sub_word_state) print(shifted_rows) print(after_mix_column) print(round_key[round]) for x in range(0, 32, 2): val = hex(int("0x" + after_mix_column[x: x + 2], 0) ^ int("0x" + round_key[round][x: x + 2], 0))[2:] end_of_round += val.zfill(2) start_of_round = end_of_round print(start_of_round) if round == 9: end_of_round = "" round += 1 print(round) round_key[round] = w[4 * round] + w[4 * round + 1] + w[4 * round + 2] + w[4 * round + 3] sub_word_state = sub_word(sbox, start_of_round) shifted_rows = shift_rows(sub_word_state) print(start_of_round) print(sub_word_state) print(shifted_rows) print(round_key[round]) for x in range(0, 32, 2): val = hex(int("0x" + shifted_rows[x: x + 2], 0) ^ int("0x" + round_key[round][x: x + 2], 0))[2:] end_of_round += val.zfill(2) print(end_of_round)