I've put together a series of slides as well as a Python implementation of AES, the symmetric-key cryptosystem.
Source: pyAES.py
Sample Usage: (color added for clarity)[brandon@zodiac pyAES]$ cat > testfile.txt The sky was the color of television tuned to a dead channel. [brandon@zodiac pyAES]$ ./pyAES.py -e testfile.txt -o testfile_encrypted.txt Password: Encrypting file: testfile.txt Encryption complete. [brandon@zodiac pyAES]$ ./pyAES.py -d testfile_encrypted.txt -o testfile_decrypted.txt Password: Decrypting file: testfile_encrypted.txt Decryption complete. [brandon@zodiac pyAES]$ cat testfile_decrypted.txt The sky was the color of television tuned to a dead channel. [brandon@zodiac pyAES]$ md5sum * 19725cef7495fd55540728759a6262c8 pyAES.py 2fffc9072a7c09f4f97862c0bceb6021 testfile_decrypted.txt 3e57070eaf1b4adf7f43b38e1c5ee631 testfile_encrypted.txt 2fffc9072a7c09f4f97862c0bceb6021 testfile.txt
Symmetric Key Cryptography
AES - The Advanced Encryption Standard
AES Operations
AES Operation - AddRoundKey
AES Operation - SubBytes
# do sbox transform on each of the values in the state table def subBytes(state): for i in range(len(state)): state[i] = sbox[state[i]]
# sbox transformations are invertible >>> sbox[237] 85 >>> sboxInv[85] 237 >>> sbox[55] 154 >>> sbox[154] 184 >>> sboxInv[184] 154 >>> sboxInv[154] 55
AES Operation - ShiftRows
# returns a copy of the word shifted n bytes (chars) positive # values for n shift bytes left, negative values shift right def rotate(word, n): return word[n:]+word[0:n] # iterate over each "virtual" row in the state table # and shift the bytes to the LEFT by the appropriate # offset def shiftRows(state): for i in range(4): state[i*4:i*4+4] = rotate(state[i*4:i*4+4],i)
AES Operation - MixColumns
# Galois Multiplication def galoisMult(a, b): p = 0 hiBitSet = 0 for i in range(8): if b & 1 == 1: p ^= a hiBitSet = a & 0x80 a <<= 1 if hiBitSet == 0x80: a ^= 0x1b b >>= 1 return p % 256 # mixColumn does Galois multiplication on a state column def mixColumn(column): temp = copy(column) column[0] = galoisMult(temp[0],2) ^ galoisMult(temp[3],1) ^ \ galoisMult(temp[2],1) ^ galoisMult(temp[1],3) column[1] = galoisMult(temp[1],2) ^ galoisMult(temp[0],1) ^ \ galoisMult(temp[3],1) ^ galoisMult(temp[2],3) column[2] = galoisMult(temp[2],2) ^ galoisMult(temp[1],1) ^ \ galoisMult(temp[0],1) ^ galoisMult(temp[3],3) column[3] = galoisMult(temp[3],2) ^ galoisMult(temp[2],1) ^ \ galoisMult(temp[1],1) ^ galoisMult(temp[0],3)
AES - Pulling It All Together
The AES Cipher operates using a varying number of rounds, based on the size of the cipher key.AES - Key Expansion Operations
AES key expansion consists of several primitive operations:# takes 4-byte word and iteration number def keyScheduleCore(word, i): # rotate word 1 byte to the left word = rotate(word, 1) newWord = [] # apply sbox substitution on all bytes of word for byte in word: newWord.append(sbox[byte]) # XOR the output of the rcon[i] transformation with the first part of the word newWord[0] = newWord[0]^rcon[i] return newWord
AES - Key Expansion Algorithm (256-bit)
(Pythonic) Pseudo-code for AES Key Expansion:# returns a 16-byte round key based on an expanded key and round number def createRoundKey(expandedKey, n): return expandedKey[(n*16):(n*16+16)]
AES - Encrypting a Single Block
AES - Encrypting a Single Block (Demo)
>>> key = passwordToKey("s0m3_p@ssw0rD") >>> key [62, 142, 78, 2, 164, 231, 18, 196, 148, 177, 82, 186, 240, 44, 136, 242, 23, 13, 20, 169, 248, 69, 163, 79, 13, 155, 97, 200, 241, 15, 76, 15] >>> plaintext = textToBlock("Hiro Protagonist") >>> plaintext [72, 105, 114, 111, 32, 80, 114, 111, 116, 97, 103, 111, 110, 105, 115, 116] >>> blockToText(plaintext) 'Hiro Protagonist' >>> ciphertext = aesEncrypt(plaintext, key) *** aesMain *** initial state: [72, 105, 114, 111, 32, 80, 114, 111, 116, 97, 103, 111, 110, 105, 115, 116] state after adding roundKey0: [118, 231, 60, 109, 132, 183, 96, 171, 224, 208, 53, 213, 158, 69, 251, 134] *** AES Round1 *** state after subBytes: [56, 148, 235, 60, 95, 169, 208, 98, 225, 112, 150, 3, 11, 110, 15, 68] state after shiftRows: [56, 148, 235, 60, 169, 208, 98, 95, 150, 3, 225, 112, 68, 11, 110, 15] state after mixColumns: [66, 80, 228, 230, 148, 33, 121, 29, 106, 95, 226, 146, 255, 98, 121, 117] state after addRoundKey: [85, 93, 240, 79, 108, 100, 218, 82, 103, 196, 131, 90, 14, 109, 53, 122] <-- SNIP --> *** AES Round 14 (final) *** state after subBytes: [0, 229, 171, 70, 93, 137, 135, 251, 99, 182, 88, 166, 228, 229, 251, 97] state after shiftRows: [0, 229, 171, 70, 137, 135, 251, 93, 88, 166, 99, 182, 97, 228, 229, 251] state after addRoundKey: [195, 123, 205, 183, 213, 202, 50, 223, 223, 164, 99, 86, 126, 34, 107, 142] >>> ciphertext [195, 123, 205, 183, 213, 202, 50, 223, 223, 164, 99, 86, 126, 34, 107, 142] >>> blockToText(ciphertext) '\xc3{\xcd\xb7\xd5\xca2\xdf\xdf\xa4cV~"k\x8e' >>> cleartext = aesDecrypt(ciphertext, key) *** aesMainInv *** initial state: [195, 123, 205, 183, 213, 202, 50, 223, 223, 164, 99, 86, 126, 34, 107, 142] *** AES Round 14 *** state after addRoundKey: [0, 229, 171, 70, 137, 135, 251, 93, 88, 166, 99, 182, 97, 228, 229, 251] state after shiftRowsInv: [0, 229, 171, 70, 93, 137, 135, 251, 99, 182, 88, 166, 228, 229, 251, 97] state after subBytesInv: [82, 42, 14, 152, 141, 242, 234, 99, 0, 121, 94, 197, 174, 42, 99, 216] <-- SNIP --> *** AES Round 0 (final) *** state after adding roundKey0: [72, 105, 114, 111, 32, 80, 114, 111, 116, 97, 103, 111, 110, 105, 115, 116] >>> cleartext [72, 105, 114, 111, 32, 80, 114, 111, 116, 97, 103, 111, 110, 105, 115, 116] >>> blockToText(cleartext) 'Hiro Protagonist'