aboutsummaryrefslogtreecommitdiffhomepage
path: root/visualizer.py
blob: 0f46687901f618406be895513c1621b66d9baf06 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#!/usr/bin/python
import copy
import sys

from constants import round_constants
from constants import sbox

matrix_size = 4


def hex_to_matrix(hex_array):
    """
    Converts hex arrays to 4x4 matrices
    :param hex_array: Array of 16 hex chars
    :return: 4x4 matrix representation
    """
    hex_matrix = []
    for i in range(16):
        if i % matrix_size == 0:
            hex_matrix.append([hex_array[i]])
        else:
            hex_matrix[int(i / matrix_size)].append(hex_array[i])
    return hex_matrix


def int_to_hex_string(number):
    """
    Converts an integer to it's regarding hex representation
    :param number: Number to convert as base-10 integer
    :return: string of hex representation
    """
    if type(number) == int:
        return "%0.2X" % number
    else:
        return str(number)


def text_to_hex(text):
    """
    Converts text to a array-hex representation
    :param text: String/ASCII/UNICODE text
    :return: Array of hex chars
    """
    hex_array = []
    for char in text:
        hex_array.append(ord(char))
    hex_array = hex_array + [0x01] * (16 - len(hex_array))
    return hex_array


def key_expansion(key_matrix):
    """
    Expands a single key matrix to 11 distinct ones
    :param key_matrix: Matrix of master passphrase
    :return: Array of 11 round keys
    """
    round_keys = [key_matrix]
    round_key = key_matrix[:]
    for r in range(0, 10):
        new_key = copy.deepcopy(round_key)
        last = round_key[3]
        new_key[3] = round_last(new_key[3], r)
        new_key[0] = xor_matrix(new_key[0], new_key[3])
        new_key[1] = xor_matrix(new_key[1], new_key[0])
        new_key[2] = xor_matrix(new_key[2], new_key[1])
        new_key[3] = xor_matrix(last, new_key[2])
        round_key = new_key
        round_keys += [new_key]
    return round_keys


def round_last(round_key, r):
    """
    Applies special actions for bottom row
    :param round_key: Complete matrix round key
    :param r: Round index
    :return: Modified bottom row (last column)
    """
    # Shift bottom row
    last_column = round_key[1:] + round_key[:1]

    # Byte substitution (sbox uses hex representation as index)
    for column in range(matrix_size):
        last_column[column] = sbox[last_column[column]]

    # Adding round constant
    last_column[0] = last_column[0] ^ round_constants[r]
    return last_column


def encrypt(text, passphrase):
    """
    Encrypts a text using a 128-Bit passphrase
    :param text: Plain text
    :param passphrase: 128-Bit passphrase (16 chars)
    :return: Encrypted text as hex
    """
    key_matrix = hex_to_matrix(text_to_hex(passphrase))
    text_matrix = hex_to_matrix(text_to_hex(text))
    round_keys = key_expansion(key_matrix)
    merged_matrix = xor_matrices(key_matrix, text_matrix)

    print("XORing\n{0}\nwith {1}".format(matrix(text_matrix, text), matrix(key_matrix, passphrase)))
    print("Result: ↘\n{0}".format(matrix(merged_matrix)))
    print("---\nExpanded key {0}\nto".format(matrix(key_matrix, passphrase)))
    print("{0}\n[Keys 2..9]\n{1}".format(
        matrix(round_keys[1], "Key 1"),
        matrix(round_keys[10], "Key 10")
    ))

    # 9 intermediate rounds (+1 round without mixing) for 128 Bit key size
    for r in range(10):
        confused_matrix = confusion(copy.deepcopy(merged_matrix))
        diffused_matrix = diffusion(copy.deepcopy(confused_matrix))
        print("---\nConfused (sbox, SubBytes)\n{0}\nto\n{1}".format(
            matrix(merged_matrix, "Result matrix of previous round " + str(r)),
            matrix(confused_matrix, "Confused matrix of round " + str(r + 1))
        ))
        print("---\nDiffused (ShiftRows)\n'Confused matrix of round {0}'\nto\n{1}".format(
            r + 1,
            matrix(diffused_matrix, "Diffused matrix of round " + str(r + 1))
        ))

        if r == 9:
            merged_matrix = xor_matrices(diffused_matrix, round_keys[r + 1])
            print("---\nMerged (XOR)\n'Diffused matrix of round {0}' with\n{1}\nto {2}".format(
                r + 1,
                matrix(round_keys[r + 1], "Round key"),
                matrix(merged_matrix, "Final matrix")
            ))
        else:
            mixed_matrix = mix_columns(diffused_matrix)
            merged_matrix = xor_matrices(mixed_matrix, round_keys[r + 1])
            print("---\nMixed columns (MixColumns) of\n'Diffused matrix of round {0}' to\n{1}".format(
                r + 1,
                matrix(merged_matrix, "Mixed matrix of round " + str(r + 1))
            ))
            print("---\nMerged (XOR)\n'Mixed matrix of round {0}' with\n{1}\nto {2}".format(
                r + 1,
                matrix(round_keys[r + 1], "Round key"),
                matrix(merged_matrix, "Final matrix of round " + str(r + 1))
            ))
    flat_matrix = [int_to_hex_string(item) for sublist in merged_matrix for item in sublist]
    print("=> Result:\n{0}\n'Flattened' → {1}\n'Joined' → {2}".format(
        matrix([list(reversed(element)) for element in list(zip(*reversed(copy.deepcopy(merged_matrix))))], "Rotated"),
        flat_matrix,
        " ".join(flat_matrix)
    ))


def confusion(merged_matrix):
    """
    Applies confusion by running bytes through sbox (SubBytes)
    :param merged_matrix: Merged matrix of key and text
    :return: New "confused" matrix
    """
    merged_matrix = copy.deepcopy(merged_matrix)
    for i in range(matrix_size):
        for j in range(matrix_size):
            merged_matrix[i][j] = sbox[merged_matrix[i][j]]
    return merged_matrix


def diffusion(merged_matrix):
    """
    Shifts the merged matrix to the left (ShiftRows)
    :param merged_matrix: Merged matrix of key and text
    :return: New "diffused" matrix
    """
    merged_matrix = copy.deepcopy(merged_matrix)
    # Rotate matrix CCW
    merged_matrix = [list(element) for element in list(zip(*reversed(merged_matrix)))]
    # Shift matrix
    for i in range(matrix_size):
        merged_matrix[i] = merged_matrix[i][-i:] + merged_matrix[i][:-i]
    # Rotate matrix back (CW)
    merged_matrix = [list(element)[::-1] for element in list(zip(*reversed(merged_matrix)))][::-1]
    return merged_matrix


def mix_columns(merged_matrix):
    """
    Mixes columns with AES MixColumns algorithm (MixColumns)
    :param merged_matrix: Merged matrix of key and text
    :return: New "mixed" matrix
    """
    merged_matrix = copy.deepcopy(merged_matrix)
    magic = lambda x: (((x << 1) ^ 0x1B) & 0xFF) if (x & 0x80) else (x << 1)
    for i in range(4):
        a = merged_matrix[i]
        t = a[0] ^ a[1] ^ a[2] ^ a[3]
        u = a[0]
        a[0] ^= t ^ magic(a[0] ^ a[1])
        a[1] ^= t ^ magic(a[1] ^ a[2])
        a[2] ^= t ^ magic(a[2] ^ a[3])
        a[3] ^= t ^ magic(a[3] ^ u)
    return merged_matrix


def xor_matrix(first, second):
    """
    XORs 1-dimensional matrices
    :param first: First matrix
    :param second: Second matrix
    :return: XORed (first) matrix
    """
    first = copy.deepcopy(first)
    for i in range(4):
        first[i] = first[i] ^ second[i]
    return first


def xor_matrices(first, second):
    """
    XORs 2-dimensional matrices
    :param first: First matrix
    :param second: Second matrix
    :return: XORed (first) matrix
    """
    first = copy.deepcopy(first)
    for i in range(len(first)):
        first[i] = xor_matrix(first[i], second[i])
    return first


def matrix(matrix_array, string=""):
    """
    Visualizes matrix arrays
    :param matrix_array: 2x2 matrix array
    :param string: Note of the array
    :return: Printable string representation
    """
    new_array = [list(reversed(element)) for element in list(zip(*reversed(copy.deepcopy(matrix_array))))]
    for i in range(len(new_array)):
        new_array[i].insert(0, "\t|")
    output = ""
    if string != "":
        output += "'{0}' ↘\n".format(string)
    output += " |\n".join([" ".join([int_to_hex_string(cell) for cell in row]) for row in new_array]) + " |"
    return output


encrypt(sys.argv[1], sys.argv[2])