Python Equivalent Of Php's Mcrypt_rijndael_256 Cbc
I need a Python implementation of this function - I want to use it on appengine. I am not so good in Python so please help. function encrypt($data) { return base64_encode(mcrypt_
Solution 1:
Have you tried this one (also included below)? It implements the Rijndael block cipher for 16, 24 or 32 bytes. You are using the 256 bit (32 byte) version of the block cipher.
"""
A pure python (slow) implementation of rijndael with a decent interface
To include -
from rijndael import rijndael
To do a key setup -
r = rijndael(key, block_size = 16)
key must be a string of length 16, 24, or 32
blocksize must be 16, 24, or 32. Default is 16
To use -
ciphertext = r.encrypt(plaintext)
plaintext = r.decrypt(ciphertext)
If any strings are of the wrong length a ValueError is thrown
"""# ported from the Java reference code by Bram Cohen, April 2001# this code is public domain, unless someone makes # an intellectual property claim against the reference # code, in which case it can be made public domain by # deleting all the comments and renaming all the variablesimport copy
import string
shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]],
[[0, 0], [1, 5], [2, 4], [3, 3]],
[[0, 0], [1, 7], [3, 5], [4, 4]]]
# [keysize][block_size]
num_rounds = {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}}
A = [[1, 1, 1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 1, 1, 1, 1],
[1, 1, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 1, 1],
[1, 1, 1, 1, 0, 0, 0, 1]]
# produce log and alog tables, needed for multiplying in the# field GF(2^m) (generator = 3)
alog = [1]
for i inrange(255):
j = (alog[-1] << 1) ^ alog[-1]
if j & 0x100 != 0:
j ^= 0x11B
alog.append(j)
log = [0] * 256for i inrange(1, 255):
log[alog[i]] = i
# multiply two elements of GF(2^m)defmul(a, b):
if a == 0or b == 0:
return0return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255]
# substitution box based on F^{-1}(x)
box = [[0] * 8for i inrange(256)]
box[1][7] = 1for i inrange(2, 256):
j = alog[255 - log[i]]
for t inrange(8):
box[i][t] = (j >> (7 - t)) & 0x01
B = [0, 1, 1, 0, 0, 0, 1, 1]
# affine transform: box[i] <- B + A*box[i]
cox = [[0] * 8for i inrange(256)]
for i inrange(256):
for t inrange(8):
cox[i][t] = B[t]
for j inrange(8):
cox[i][t] ^= A[t][j] * box[i][j]
# S-boxes and inverse S-boxes
S = [0] * 256
Si = [0] * 256for i inrange(256):
S[i] = cox[i][0] << 7for t inrange(1, 8):
S[i] ^= cox[i][t] << (7-t)
Si[S[i] & 0xFF] = i
# T-boxes
G = [[2, 1, 1, 3],
[3, 2, 1, 1],
[1, 3, 2, 1],
[1, 1, 3, 2]]
AA = [[0] * 8for i inrange(4)]
for i inrange(4):
for j inrange(4):
AA[i][j] = G[i][j]
AA[i][i+4] = 1for i inrange(4):
pivot = AA[i][i]
if pivot == 0:
t = i + 1while AA[t][i] == 0and t < 4:
t += 1assert t != 4, 'G matrix must be invertible'for j inrange(8):
AA[i][j], AA[t][j] = AA[t][j], AA[i][j]
pivot = AA[i][i]
for j inrange(8):
if AA[i][j] != 0:
AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255]
for t inrange(4):
if i != t:
for j inrange(i+1, 8):
AA[t][j] ^= mul(AA[i][j], AA[t][i])
AA[t][i] = 0
iG = [[0] * 4for i inrange(4)]
for i inrange(4):
for j inrange(4):
iG[i][j] = AA[i][j + 4]
defmul4(a, bs):
if a == 0:
return0
r = 0for b in bs:
r <<= 8if b != 0:
r = r | mul(a, b)
return r
T1 = []
T2 = []
T3 = []
T4 = []
T5 = []
T6 = []
T7 = []
T8 = []
U1 = []
U2 = []
U3 = []
U4 = []
for t inrange(256):
s = S[t]
T1.append(mul4(s, G[0]))
T2.append(mul4(s, G[1]))
T3.append(mul4(s, G[2]))
T4.append(mul4(s, G[3]))
s = Si[t]
T5.append(mul4(s, iG[0]))
T6.append(mul4(s, iG[1]))
T7.append(mul4(s, iG[2]))
T8.append(mul4(s, iG[3]))
U1.append(mul4(t, iG[0]))
U2.append(mul4(t, iG[1]))
U3.append(mul4(t, iG[2]))
U4.append(mul4(t, iG[3]))
# round constants
rcon = [1]
r = 1for t inrange(1, 30):
r = mul(2, r)
rcon.append(r)
del A
del AA
del pivot
del B
del G
del box
del log
del alog
del i
del j
del r
del s
del t
del mul
del mul4
del cox
del iG
classrijndael:
def__init__(self, key, block_size = 16):
if block_size != 16and block_size != 24and block_size != 32:
raise ValueError('Invalid block size: ' + str(block_size))
iflen(key) != 16andlen(key) != 24andlen(key) != 32:
raise ValueError('Invalid key size: ' + str(len(key)))
self.block_size = block_size
ROUNDS = num_rounds[len(key)][block_size]
BC = block_size // 4# encryption round keys
Ke = [[0] * BC for i inrange(ROUNDS + 1)]
# decryption round keys
Kd = [[0] * BC for i inrange(ROUNDS + 1)]
ROUND_KEY_COUNT = (ROUNDS + 1) * BC
KC = len(key) // 4# copy user material bytes into temporary ints
tk = []
for i inrange(0, KC):
tk.append((ord(key[i * 4]) << 24) | (ord(key[i * 4 + 1]) << 16) |
(ord(key[i * 4 + 2]) << 8) | ord(key[i * 4 + 3]))
# copy values into round key arrays
t = 0
j = 0while j < KC and t < ROUND_KEY_COUNT:
Ke[t // BC][t % BC] = tk[j]
Kd[ROUNDS - (t // BC)][t % BC] = tk[j]
j += 1
t += 1
tt = 0
rconpointer = 0while t < ROUND_KEY_COUNT:
# extrapolate using phi (the round key evolution function)
tt = tk[KC - 1]
tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \
(S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \
(S[ tt & 0xFF] & 0xFF) << 8 ^ \
(S[(tt >> 24) & 0xFF] & 0xFF) ^ \
(rcon[rconpointer] & 0xFF) << 24
rconpointer += 1if KC != 8:
for i inrange(1, KC):
tk[i] ^= tk[i-1]
else:
for i inrange(1, KC // 2):
tk[i] ^= tk[i-1]
tt = tk[KC // 2 - 1]
tk[KC // 2] ^= (S[ tt & 0xFF] & 0xFF) ^ \
(S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \
(S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \
(S[(tt >> 24) & 0xFF] & 0xFF) << 24for i inrange(KC // 2 + 1, KC):
tk[i] ^= tk[i-1]
# copy values into round key arrays
j = 0while j < KC and t < ROUND_KEY_COUNT:
Ke[t // BC][t % BC] = tk[j]
Kd[ROUNDS - (t // BC)][t % BC] = tk[j]
j += 1
t += 1# inverse MixColumn where neededfor r inrange(1, ROUNDS):
for j inrange(BC):
tt = Kd[r][j]
Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \
U2[(tt >> 16) & 0xFF] ^ \
U3[(tt >> 8) & 0xFF] ^ \
U4[ tt & 0xFF]
self.Ke = Ke
self.Kd = Kd
defencrypt(self, plaintext):
iflen(plaintext) != self.block_size:
raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext)))
Ke = self.Ke
BC = self.block_size // 4
ROUNDS = len(Ke) - 1if BC == 4:
SC = 0elif BC == 6:
SC = 1else:
SC = 2
s1 = shifts[SC][1][0]
s2 = shifts[SC][2][0]
s3 = shifts[SC][3][0]
a = [0] * BC
# temporary work array
t = []
# plaintext to ints + keyfor i inrange(BC):
t.append((ord(plaintext[i * 4 ]) << 24 |
ord(plaintext[i * 4 + 1]) << 16 |
ord(plaintext[i * 4 + 2]) << 8 |
ord(plaintext[i * 4 + 3]) ) ^ Ke[0][i])
# apply round transformsfor r inrange(1, ROUNDS):
for i inrange(BC):
a[i] = (T1[(t[ i ] >> 24) & 0xFF] ^
T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^
T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^
T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i]
t = copy.copy(a)
# last round is special
result = []
for i inrange(BC):
tt = Ke[ROUNDS][i]
result.append((S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
result.append((S[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
return''.join(map(chr, result))
defdecrypt(self, ciphertext):
iflen(ciphertext) != self.block_size:
raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(ciphertext)))
Kd = self.Kd
BC = self.block_size // 4
ROUNDS = len(Kd) - 1if BC == 4:
SC = 0elif BC == 6:
SC = 1else:
SC = 2
s1 = shifts[SC][1][1]
s2 = shifts[SC][2][1]
s3 = shifts[SC][3][1]
a = [0] * BC
# temporary work array
t = [0] * BC
# ciphertext to ints + keyfor i inrange(BC):
t[i] = (ord(ciphertext[i * 4 ]) << 24 |
ord(ciphertext[i * 4 + 1]) << 16 |
ord(ciphertext[i * 4 + 2]) << 8 |
ord(ciphertext[i * 4 + 3]) ) ^ Kd[0][i]
# apply round transformsfor r inrange(1, ROUNDS):
for i inrange(BC):
a[i] = (T5[(t[ i ] >> 24) & 0xFF] ^
T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^
T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^
T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i]
t = copy.copy(a)
# last round is special
result = []
for i inrange(BC):
tt = Kd[ROUNDS][i]
result.append((Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
result.append((Si[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
return''.join(map(chr, result))
defencrypt(key, block):
return rijndael(key, len(block)).encrypt(block)
defdecrypt(key, block):
return rijndael(key, len(block)).decrypt(block)
Note that the rijndael.py
file only implements the block cipher. The encrypt
/ decrypt
functions only handle plaintexts that are precisely the block size. This means that the caller of these functions will have to provide the block cipher mode of operation and the zero padding himself.
Example python code (from a Java programmer, beware):
classzeropad:
def__init__(self, block_size):
assert block_size > 0and block_size < 256
self.block_size = block_size
defpad(self, pt):
ptlen = len(pt)
padsize = self.block_size - ((ptlen + self.block_size - 1) % self.block_size + 1)
return pt + "\0" * padsize
defunpad(self, ppt):
assertlen(ppt) % self.block_size == 0
offset = len(ppt)
if (offset == 0):
return''
end = offset - self.block_size + 1while (offset > end):
offset -= 1;
if (ppt[offset] != "\0"):
return ppt[:offset + 1]
assert false
classcbc:
def__init__(self, padding, cipher, iv):
assert padding.block_size == cipher.block_size;
assertlen(iv) == cipher.block_size;
self.padding = padding
self.cipher = cipher
self.iv = iv
defencrypt(self, pt):
ppt = self.padding.pad(pt)
offset = 0
ct = ''
v = self.iv
while (offset < len(ppt)):
block = ppt[offset:offset + self.cipher.block_size]
block = self.xorblock(block, v)
block = self.cipher.encrypt(block)
ct += block
offset += self.cipher.block_size
v = block
return ct;
defdecrypt(self, ct):
assertlen(ct) % self.cipher.block_size == 0
ppt = ''
offset = 0
v = self.iv
while (offset < len(ct)):
block = ct[offset:offset + self.cipher.block_size]
decrypted = self.cipher.decrypt(block)
ppt += self.xorblock(decrypted, v)
offset += self.cipher.block_size
v = block
pt = self.padding.unpad(ppt)
return pt;
defxorblock(self, b1, b2):
# sorry, not very Pythonesk
i = 0
r = '';
while (i < self.cipher.block_size):
r += chr(ord(b1[i]) ^ ord(b2[i]))
i += 1return r
Post a Comment for "Python Equivalent Of Php's Mcrypt_rijndael_256 Cbc"