# Simplified AES implementation in Python

Simplified AES, created by Edward Schaefer and two of his students at Santa Clara University in 2003, is described in the paper “A Simplified AES Algorithm and Its Linear and Differential Cryptoanalyses”, Cryptologia, Vol. XXVII (2), pages 148-177. Like Simplified DES, its purpose is educational, since its key and block size are very small (16-bit and 8-bit, respectively). Nonetheless, due to its simplicity, it is possible for students to encrypt or decrypt a block doing all operations by hand, making it easier for them to understand the structure and operation of its elder brother, AES.

```#!/usr/bin/python3
#
# Author: Joao H de A Franco (jhafranco@acm.org)
#
# Description: Simplified AES implementation in Python 3
#
# Date: 2012-02-11
#
#          (CC BY-NC-SA 3.0)
#===========================================================
import sys

# S-Box
sBox  = [0x9, 0x4, 0xa, 0xb, 0xd, 0x1, 0x8, 0x5,
0x6, 0x2, 0x0, 0x3, 0xc, 0xe, 0xf, 0x7]

# Inverse S-Box
sBoxI = [0xa, 0x5, 0x9, 0xb, 0x1, 0x7, 0x8, 0xf,
0x6, 0x0, 0x2, 0x3, 0xc, 0x4, 0xd, 0xe]

# Round keys: K0 = w0 + w1; K1 = w2 + w3; K2 = w4 + w5
w = [None] * 6

def mult(p1, p2):
"""Multiply two polynomials in GF(2^4)/x^4 + x + 1"""
p = 0
while p2:
if p2 & 0b1:
p ^= p1
p1 <<= 1
if p1 & 0b10000:
p1 ^= 0b11
p2 >>= 1
return p & 0b1111

def intToVec(n):
"""Convert a 2-byte integer into a 4-element vector"""
return [n >> 12, (n >> 4) & 0xf, (n >> 8) & 0xf,  n & 0xf]

def vecToInt(m):
"""Convert a 4-element vector into 2-byte integer"""
return (m << 12) + (m << 8) + (m << 4) + m

return [i ^ j for i, j in zip(s1, s2)]

def sub4NibList(sbox, s):
"""Nibble substitution function"""
return [sbox[e] for e in s]

def shiftRow(s):
"""ShiftRow function"""
return [s, s, s, s]

def keyExp(key):
"""Generate the three round keys"""
def sub2Nib(b):
"""Swap each nibble and substitute it using sBox"""
return sBox[b >> 4] + (sBox[b & 0x0f] << 4)

Rcon1, Rcon2 = 0b10000000, 0b00110000
w = (key & 0xff00) >> 8
w = key & 0x00ff
w = w ^ Rcon1 ^ sub2Nib(w)
w = w ^ w
w = w ^ Rcon2 ^ sub2Nib(w)
w = w ^ w

def encrypt(ptext):
"""Encrypt plaintext block"""
def mixCol(s):
return [s ^ mult(4, s), s ^ mult(4, s),
s ^ mult(4, s), s ^ mult(4, s)]

state = intToVec(((w << 8) + w) ^ ptext)
state = mixCol(shiftRow(sub4NibList(sBox, state)))
state = addKey(intToVec((w << 8) + w), state)
state = shiftRow(sub4NibList(sBox, state))
return vecToInt(addKey(intToVec((w << 8) + w), state))

def decrypt(ctext):
"""Decrypt ciphertext block"""
def iMixCol(s):
return [mult(9, s) ^ mult(2, s), mult(9, s) ^ mult(2, s),
mult(9, s) ^ mult(2, s), mult(9, s) ^ mult(2, s)]

state = intToVec(((w << 8) + w) ^ ctext)
state = sub4NibList(sBoxI, shiftRow(state))
state = iMixCol(addKey(intToVec((w << 8) + w), state))
state = sub4NibList(sBoxI, shiftRow(state))
return vecToInt(addKey(intToVec((w << 8) + w), state))

if __name__ == '__main__':
# Test vectors from "Simplified AES" (Steven Gordon)
# (http://hw.siit.net/files/001283.pdf)

plaintext = 0b1101011100101000
key = 0b0100101011110101
ciphertext = 0b0010010011101100
keyExp(key)
try:
assert encrypt(plaintext) == ciphertext
except AssertionError:
print("Encryption error")
print(encrypt(plaintext), ciphertext)
sys.exit(1)
try:
assert decrypt(ciphertext) == plaintext
except AssertionError:
print("Decryption error")
print(decrypt(ciphertext), plaintext)
sys.exit(1)
print("Test ok!")
sys.exit()
```