What am I doing – 7/7/2019

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.

In the Wikipedia article for AES, it mentions a S-box, or substitution box, which mathematicians call a finite field. The Rijndael S-box article (AES is based on Rijndael) describes the S-box used in AES and it includes C code. I used the C code to get intermediate calculations in the S-box generation procedure. I then compared the results with a Python version to accurately generate the AES S-box. The following Python code generates the S-box as defined in FIPS 197.

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)

Leave a Reply

Your email address will not be published. Required fields are marked *