Created
February 11, 2018 09:57
-
-
Save i5on9i/fcfe85328b5e83faf08e871e7b82eea1 to your computer and use it in GitHub Desktop.
ported aes.py to use python3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf8 -*- | |
""" | |
This is a port of Chris Veness' AES implementation: http://www.movable-type.co.uk/scripts/aes.html | |
Copyright ⓒ2005-2008 Chris Veness. Right of free use is granted for all | |
commercial or non-commercial use. No warranty of any form is offered. | |
Ported in 2009 by Markus Birth <markus@birth-online.de> | |
2018-02 by namh | |
ported to use in python 3 | |
""" | |
from __future__ import division | |
from __future__ import unicode_literals | |
import base64 | |
import datetime | |
import math | |
import time | |
import sys | |
python3 = True if sys.version_info >= (3,0) else False | |
"""Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1]""" | |
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 | |
] | |
"""Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]""" | |
Rcon = [ | |
[0x00, 0x00, 0x00, 0x00], | |
[0x01, 0x00, 0x00, 0x00], | |
[0x02, 0x00, 0x00, 0x00], | |
[0x04, 0x00, 0x00, 0x00], | |
[0x08, 0x00, 0x00, 0x00], | |
[0x10, 0x00, 0x00, 0x00], | |
[0x20, 0x00, 0x00, 0x00], | |
[0x40, 0x00, 0x00, 0x00], | |
[0x80, 0x00, 0x00, 0x00], | |
[0x1b, 0x00, 0x00, 0x00], | |
[0x36, 0x00, 0x00, 0x00] | |
] | |
def Cipher(input, w): | |
Nb = 4 | |
Nr = int(len(w)/Nb) - 1 | |
state = [ [0] * Nb, [0] * Nb, [0] * Nb, [0] * Nb ] | |
for i in range(0, 4*Nb): state[i%4][i//4] = input[i] | |
state = AddRoundKey(state, w, 0, Nb) | |
for round in range(1, Nr): | |
state = SubBytes(state, Nb) | |
state = ShiftRows(state, Nb) | |
state = MixColumns(state, Nb) | |
state = AddRoundKey(state, w, round, Nb) | |
state = SubBytes(state, Nb) | |
state = ShiftRows(state, Nb) | |
state = AddRoundKey(state, w, Nr, Nb) | |
output = [0] * 4*Nb | |
for i in range(4*Nb): output[i] = state[i%4][i//4] | |
return output | |
def SubBytes(s, Nb): | |
for r in range(4): | |
for c in range(Nb): | |
s[r][c] = Sbox[s[r][c]] | |
return s | |
def ShiftRows(s, Nb): | |
t = [0] * 4 | |
for r in range (1,4): | |
for c in range(4): t[c] = s[r][(c+r)%Nb] | |
for c in range(4): s[r][c] = t[c] | |
return s | |
def MixColumns(s, Nb): | |
for c in range(4): | |
a = [0] * 4 | |
b = [0] * 4 | |
for i in range(4): | |
a[i] = s[i][c] | |
b[i] = s[i][c]<<1 ^ 0x011b if s[i][c]&0x80 else s[i][c]<<1 | |
s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3] | |
s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3] | |
s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3] | |
s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3] | |
return s | |
def AddRoundKey(state, w, rnd, Nb): | |
for r in range(4): | |
for c in range(Nb): | |
state[r][c] ^= w[rnd*4+c][r] | |
return state | |
def KeyExpansion(key): | |
Nb = 4 | |
Nk = int(len(key)/4) | |
Nr = Nk + 6 | |
w = [0] * Nb*(Nr+1) | |
temp = [0] * 4 | |
for i in range(Nk): | |
r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]] | |
w[i] = r | |
for i in range(Nk, Nb*(Nr+1)): | |
w[i] = [0] * 4 | |
for t in range(4): temp[t] = w[i-1][t] | |
if i%Nk == 0: | |
temp = SubWord(RotWord(temp)) | |
for t in range(4): temp[t] ^= Rcon[int(i/Nk)][t] | |
elif Nk>6 and i%Nk == 4: | |
temp = SubWord(temp) | |
for t in range(4): w[i][t] = w[i-Nk][t] ^ temp[t] | |
return w | |
def SubWord(w): | |
for i in range(4): w[i] = Sbox[w[i]] | |
return w | |
def RotWord(w): | |
tmp = w[0] | |
for i in range(3): w[i] = w[i+1] | |
w[3] = tmp | |
return w | |
def encrypt(plaintext, password, nBits): | |
blockSize = 16 | |
if not nBits in (128, 192, 256): return "" | |
# plaintext = plaintext.encode("utf-8") | |
# password = password.encode("utf-8") | |
nBytes = nBits//8 | |
pwBytes = [0] * nBytes | |
for i in range(nBytes): pwBytes[i] = 0 if i>=len(password) else ord(password[i]) | |
key = Cipher(pwBytes, KeyExpansion(pwBytes)) | |
key += key[:nBytes-16] | |
counterBlock = [0] * blockSize | |
now = datetime.datetime.now() | |
nonce = time.mktime( now.timetuple() )*1000 + now.microsecond//1000 | |
nonceSec = int(nonce // 1000) | |
nonceMs = int(nonce % 1000) | |
for i in range(4): counterBlock[i] = urs(nonceSec, i*8) & 0xff | |
for i in range(4): counterBlock[i+4] = nonceMs & 0xff | |
# ctrTxt = b"" | |
# for i in range(8): ctrTxt += counterBlock[i] | |
ctrTxt = bytearray([]) | |
for i in range(8): ctrTxt.append(counterBlock[i]) | |
keySchedule = KeyExpansion(key) | |
blockCount = int(math.ceil(float(len(plaintext))/float(blockSize))) | |
ciphertxt = [0] * blockCount | |
for b in range(blockCount): | |
for c in range(4): counterBlock[15-c] = urs(b, c*8) & 0xff | |
for c in range(4): counterBlock[15-c-4] = urs(int(b/0x100000000), c*8) | |
cipherCntr = Cipher(counterBlock, keySchedule) | |
blockLength = blockSize if b<blockCount-1 else (len(plaintext)-1)%blockSize+1 | |
cipherChar = [0] * blockLength | |
for i in range(blockLength): | |
cipherChar[i] = cipherCntr[i] ^ ord(plaintext[b*blockSize+i]) | |
# cipherChar[i] = chr( cipherChar[i] ) | |
cipherChar[i] = cipherChar[i] | |
if python3: | |
ciphertxt[b] = bytes(cipherChar) | |
else: | |
ciphertxt[b] = b''.join([chr(c) for c in cipherChar ]) | |
ciphertext = ctrTxt + b''.join(ciphertxt) | |
ciphertext = base64.b64encode(ciphertext) | |
return ciphertext | |
def decrypt(ciphertext, password, nBits): | |
blockSize = 16 | |
if not nBits in (128, 192, 256): return "" | |
ciphertext = base64.b64decode(ciphertext) | |
# password = password.encode("utf-8") | |
nBytes = nBits//8 | |
pwBytes = [0] * nBytes | |
for i in range(nBytes): pwBytes[i] = 0 if i>=len(password) else ord(password[i]) | |
key = Cipher(pwBytes, KeyExpansion(pwBytes)) | |
key += key[:nBytes-16] | |
counterBlock = [0] * blockSize | |
ctrTxt = ciphertext[:8] | |
if python3: | |
for i in range(8): counterBlock[i] = ctrTxt[i] | |
else: | |
for i in range(8): counterBlock[i] = ord(ctrTxt[i]) | |
keySchedule = KeyExpansion(key) | |
nBlocks = int( math.ceil( float(len(ciphertext)-8) / float(blockSize) ) ) | |
ct = [0] * nBlocks | |
for b in range(nBlocks): | |
ct[b] = ciphertext[8+b*blockSize : 8+b*blockSize+blockSize] | |
ciphertext = ct | |
plaintxt = [0] * len(ciphertext) | |
for b in range(nBlocks): | |
for c in range(4): counterBlock[15-c] = urs(b, c*8) & 0xff | |
for c in range(4): counterBlock[15-c-4] = urs( int( float(b+1)/0x100000000-1 ), c*8) & 0xff | |
cipherCntr = Cipher(counterBlock, keySchedule) | |
plaintxtByte = [0] * len(ciphertext[b]) | |
for i in range(len(ciphertext[b])): | |
if python3: | |
plaintxtByte[i] = cipherCntr[i] ^ ciphertext[b][i] | |
else: | |
plaintxtByte[i] = cipherCntr[i] ^ ord(ciphertext[b][i]) | |
plaintxtByte[i] = chr(plaintxtByte[i]) | |
plaintxt[b] = "".join(plaintxtByte) | |
plaintext = "".join(plaintxt) | |
# plaintext = plaintext.decode("utf-8") | |
return plaintext | |
def urs(a, b): | |
a &= 0xffffffff | |
b &= 0x1f | |
if a&0x80000000 and b>0: | |
a = (a>>1) & 0x7fffffff | |
a = a >> (b-1) | |
else: | |
a = (a >> b) | |
return a | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment