Skip to content

Instantly share code, notes, and snippets.

Last active April 7, 2023 09:50
What would you like to do?
A simple/simplistic implementation of AES in pure Python.
#My AES implementation
# By Daniel Miller
def xor(s1, s2):
return tuple(a^b for a,b in zip(s1, s2))
class AES(object):
class __metaclass__(type):
def __init__(cls, name, bases, classdict):
cls.Gmul = {}
for f in (0x02, 0x03, 0x0e, 0x0b, 0x0d, 0x09):
cls.Gmul[f] = tuple(cls.gmul(f, x) for x in range(0,0x100))
Rcon = ( 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a )
Sbox = (
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
Sbox_inv = (
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
def rot_word(word):
return word[1:] + word[:1]
def sub_word(word):
return (AES.Sbox[b] for b in word)
def key_schedule(self):
expanded = []
expanded.extend(map(ord, self.key))
for i in range(self.nk, self.nb * ( + 1)):
t = expanded[(i-1)*4:i*4]
if i % self.nk == 0:
t = xor( AES.sub_word( AES.rot_word(t) ), (AES.Rcon[i // self.nk],0,0,0) )
elif self.nk > 6 and i % self.nk == 4:
t = AES.sub_word(t)
expanded.extend( xor(t, expanded[(i-self.nk)*4:(i-self.nk+1)*4]))
return expanded
def add_round_key(self, rkey):
for i, b in enumerate(rkey):
self.state[i] ^= b
def sub_bytes(self):
for i, b in enumerate(self.state):
self.state[i] = AES.Sbox[b]
def inv_sub_bytes(self):
for i, b in enumerate(self.state):
self.state[i] = AES.Sbox_inv[b]
def shift_rows(self):
rows = []
for r in range(4):
rows.append( self.state[r::4] )
rows[r] = rows[r][r:] + rows[r][:r]
self.state = [ r[c] for c in range(4) for r in rows ]
def inv_shift_rows(self):
rows = []
for r in range(4):
rows.append( self.state[r::4] )
rows[r] = rows[r][4-r:] + rows[r][:4-r]
self.state = [ r[c] for c in range(4) for r in rows ]
def gmul(a, b):
p = 0
for c in range(8):
if b & 1:
p ^= a
a <<= 1
if a & 0x100:
a ^= 0x11b
b >>= 1
return p
def mix_columns(self):
ss = []
for c in range(4):
col = self.state[c*4:(c+1)*4]
AES.Gmul[0x02][col[0]] ^ AES.Gmul[0x03][col[1]] ^ col[2] ^ col[3] ,
col[0] ^ AES.Gmul[0x02][col[1]] ^ AES.Gmul[0x03][col[2]] ^ col[3] ,
col[0] ^ col[1] ^ AES.Gmul[0x02][col[2]] ^ AES.Gmul[0x03][col[3]],
AES.Gmul[0x03][col[0]] ^ col[1] ^ col[2] ^ AES.Gmul[0x02][col[3]],
self.state = ss
def inv_mix_columns(self):
ss = []
for c in range(4):
col = self.state[c*4:(c+1)*4]
AES.Gmul[0x0e][col[0]] ^ AES.Gmul[0x0b][col[1]] ^ AES.Gmul[0x0d][col[2]] ^ AES.Gmul[0x09][col[3]],
AES.Gmul[0x09][col[0]] ^ AES.Gmul[0x0e][col[1]] ^ AES.Gmul[0x0b][col[2]] ^ AES.Gmul[0x0d][col[3]],
AES.Gmul[0x0d][col[0]] ^ AES.Gmul[0x09][col[1]] ^ AES.Gmul[0x0e][col[2]] ^ AES.Gmul[0x0b][col[3]],
AES.Gmul[0x0b][col[0]] ^ AES.Gmul[0x0d][col[1]] ^ AES.Gmul[0x09][col[2]] ^ AES.Gmul[0x0e][col[3]],
self.state = ss
def cipher(self, block):
#print "round[ 0].input: {0}".format(block.encode('hex'))
n = self.nb * 4
self.state = map(ord, block)
keys = self.key_schedule()
#print "round[ 0].k_sch: {0}".format(keys[0:n].encode('hex'))
for r in range(1,
#print "round[{0}].start: {1}".format(r,self.state.encode('hex'))
#print "round[{0}].s_box: {1}".format(r,self.state.encode('hex'))
#print "round[{0}].s_row: {1}".format(r,self.state.encode('hex'))
#print "round[{0}].m_col: {1}".format(r,self.state.encode('hex'))
k = keys[r*n:(r+1)*n]
#print "round[{0}].k_sch: {1}".format(r,k.encode('hex'))
#print "output: {0}".format(self.state.encode('hex'))
return "".join(map(chr, self.state))
def inv_cipher(self, block):
#print "round[ 0].iinput: {0}".format(block.encode('hex'))
n = self.nb * 4
self.state = map(ord, block)
keys = self.key_schedule()
k = keys[*n:(*n]
#print "round[ 0].ik_sch: {0}".format(k.encode('hex'))
for r in range(, 0, -1):
#print "round[{0}].istart: {1}".format(r,self.state.encode('hex'))
#print "round[{0}].is_row: {1}".format(r,self.state.encode('hex'))
#print "round[{0}].is_box: {1}".format(r,self.state.encode('hex'))
k = keys[r*n:(r+1)*n]
#print "round[{0}].ik_sch: {1}".format(r,k.encode('hex'))
#print "round[{0}].ik_add: {1}".format(r,self.state.encode('hex'))
#print "round[{0}].im_col: {1}".format(r,self.state.encode('hex'))
#print "output: {0}".format(self.state.encode('hex'))
return "".join(map(chr, self.state))
class AES_128(AES):
def __init__(self):
self.nb = 4 = 10
self.nk = 4
if __name__=="__main__":
key = "2b7e151628aed2a6abf7158809cf4f3c".decode('hex')
#key = "000102030405060708090a0b0c0d0e0f".decode('hex')
check = (
#("00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a"),
("6bc1bee22e409f96e93d7e117393172a", "3ad77bb40d7a3660a89ecaf32466ef97"),
("ae2d8a571e03ac9c9eb76fac45af8e51", "f5d3d58503b9699de785895a96fdbaaf"),
("30c81c46a35ce411e5fbc1191a0a52ef", "43b1cd7f598ece23881b00e3ed030688"),
("f69f2445df4f9b17ad2b417be66c3710", "7b0c785e27e8ad3f8223207104725dd4"),
crypt = AES_128()
crypt.key = key
for c in check:
p = c[0].decode('hex')
v = c[1].decode('hex')
t = crypt.cipher(p)
if t == v:
print "yay!"
print "{0} != {1}".format(t.encode('hex'), c[1])
t = crypt.inv_cipher(v)
if t == p:
print "yay!"
print "{0} != {1}".format(t.encode('hex'), c[1])
Copy link

DarthKakkadus commented Feb 11, 2018

is this it ? does this code end with the else statement in lines 217-218 or is there something that I am overlooking?

Copy link

xacrimon commented Mar 6, 2018

This is not meant for executing, it is meant for importing and using the AES class

Copy link

by any chance, do you have ported version of python3?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment