551 lines
18 KiB
Python
Executable File
551 lines
18 KiB
Python
Executable File
# -*- coding: utf-8 -*-
|
|
|
|
import StringIO
|
|
import binascii
|
|
import hashlib
|
|
from array import array
|
|
|
|
|
|
def evpKDF(passwd, salt, key_size=8, iv_size=4, iterations=1, hash_algorithm="md5"):
|
|
target_key_size = key_size + iv_size
|
|
derived_bytes = ""
|
|
number_of_derived_words = 0
|
|
block = None
|
|
hasher = hashlib.new(hash_algorithm)
|
|
while number_of_derived_words < target_key_size:
|
|
if block is not None:
|
|
hasher.update(block)
|
|
|
|
hasher.update(passwd)
|
|
hasher.update(salt)
|
|
block = hasher.digest()
|
|
hasher = hashlib.new(hash_algorithm)
|
|
|
|
for i in range(1, iterations):
|
|
hasher.update(block)
|
|
block = hasher.digest()
|
|
hasher = hashlib.new(hash_algorithm)
|
|
|
|
derived_bytes += block[0: min(len(block), (target_key_size - number_of_derived_words) * 4)]
|
|
|
|
number_of_derived_words += len(block) / 4
|
|
|
|
return {
|
|
"key": derived_bytes[0: key_size * 4],
|
|
"iv": derived_bytes[key_size * 4:]
|
|
}
|
|
|
|
|
|
class PKCS7Encoder(object):
|
|
'''
|
|
RFC 2315: PKCS#7 page 21
|
|
Some content-encryption algorithms assume the
|
|
input length is a multiple of k octets, where k > 1, and
|
|
let the application define a method for handling inputs
|
|
whose lengths are not a multiple of k octets. For such
|
|
algorithms, the method shall be to pad the input at the
|
|
trailing end with k - (l mod k) octets all having value k -
|
|
(l mod k), where l is the length of the input. In other
|
|
words, the input is padded at the trailing end with one of
|
|
the following strings:
|
|
|
|
01 -- if l mod k = k-1
|
|
02 02 -- if l mod k = k-2
|
|
.
|
|
.
|
|
.
|
|
k k ... k k -- if l mod k = 0
|
|
|
|
The padding can be removed unambiguously since all input is
|
|
padded and no padding string is a suffix of another. This
|
|
padding method is well-defined if and only if k < 256;
|
|
methods for larger k are an open issue for further study.
|
|
'''
|
|
|
|
def __init__(self, k=16):
|
|
self.k = k
|
|
|
|
## @param text The padded text for which the padding is to be removed.
|
|
# @exception ValueError Raised when the input padding is missing or corrupt.
|
|
def decode(self, text):
|
|
'''
|
|
Remove the PKCS#7 padding from a text string
|
|
'''
|
|
nl = len(text)
|
|
val = int(binascii.hexlify(text[-1]), 16)
|
|
if val > self.k:
|
|
raise ValueError('Input is not padded or padding is corrupt')
|
|
|
|
l = nl - val
|
|
return text[:l]
|
|
|
|
## @param text The text to encode.
|
|
def encode(self, text):
|
|
'''
|
|
Pad an input string according to PKCS#7
|
|
'''
|
|
l = len(text)
|
|
output = StringIO.StringIO()
|
|
val = self.k - (l % self.k)
|
|
for _ in xrange(val):
|
|
output.write('%02x' % val)
|
|
return text + binascii.unhexlify(output.getvalue())
|
|
|
|
|
|
# Pyaes file
|
|
# Globals mandated by PEP 272:
|
|
# http://www.python.org/dev/peps/pep-0272/
|
|
MODE_ECB = 1
|
|
MODE_CBC = 2
|
|
# MODE_CTR = 6
|
|
|
|
block_size = 16
|
|
key_size = None
|
|
|
|
|
|
def new(key, mode, IV=None):
|
|
if mode == MODE_ECB:
|
|
return ECBMode(AES(key))
|
|
elif mode == MODE_CBC:
|
|
if IV is None:
|
|
raise ValueError, "CBC mode needs an IV value!"
|
|
|
|
return CBCMode(AES(key), IV)
|
|
else:
|
|
raise NotImplementedError
|
|
|
|
|
|
#### AES cipher implementation
|
|
|
|
class AES(object):
|
|
block_size = 16
|
|
|
|
def __init__(self, key):
|
|
self.setkey(key)
|
|
|
|
def setkey(self, key):
|
|
"""Sets the key and performs key expansion."""
|
|
|
|
self.key = key
|
|
self.key_size = len(key)
|
|
|
|
if self.key_size == 16:
|
|
self.rounds = 10
|
|
elif self.key_size == 24:
|
|
self.rounds = 12
|
|
elif self.key_size == 32:
|
|
self.rounds = 14
|
|
else:
|
|
raise ValueError, "Key length must be 16, 24 or 32 bytes"
|
|
|
|
self.expand_key()
|
|
|
|
def expand_key(self):
|
|
"""Performs AES key expansion on self.key and stores in self.exkey"""
|
|
|
|
# The key schedule specifies how parts of the key are fed into the
|
|
# cipher's round functions. "Key expansion" means performing this
|
|
# schedule in advance. Almost all implementations do this.
|
|
#
|
|
# Here's a description of AES key schedule:
|
|
# http://en.wikipedia.org/wiki/Rijndael_key_schedule
|
|
|
|
# The expanded key starts with the actual key itself
|
|
exkey = array('B', self.key)
|
|
|
|
# extra key expansion steps
|
|
if self.key_size == 16:
|
|
extra_cnt = 0
|
|
elif self.key_size == 24:
|
|
extra_cnt = 2
|
|
else:
|
|
extra_cnt = 3
|
|
|
|
# 4-byte temporary variable for key expansion
|
|
word = exkey[-4:]
|
|
# Each expansion cycle uses 'i' once for Rcon table lookup
|
|
for i in xrange(1, 11):
|
|
|
|
#### key schedule core:
|
|
# left-rotate by 1 byte
|
|
word = word[1:4] + word[0:1]
|
|
|
|
# apply S-box to all bytes
|
|
for j in xrange(4):
|
|
word[j] = aes_sbox[word[j]]
|
|
|
|
# apply the Rcon table to the leftmost byte
|
|
word[0] = word[0] ^ aes_Rcon[i]
|
|
#### end key schedule core
|
|
|
|
for z in xrange(4):
|
|
for j in xrange(4):
|
|
# mix in bytes from the last subkey
|
|
word[j] ^= exkey[-self.key_size + j]
|
|
exkey.extend(word)
|
|
|
|
# Last key expansion cycle always finishes here
|
|
if len(exkey) >= (self.rounds + 1) * self.block_size:
|
|
break
|
|
|
|
# Special substitution step for 256-bit key
|
|
if self.key_size == 32:
|
|
for j in xrange(4):
|
|
# mix in bytes from the last subkey XORed with S-box of
|
|
# current word bytes
|
|
word[j] = aes_sbox[word[j]] ^ exkey[-self.key_size + j]
|
|
exkey.extend(word)
|
|
|
|
# Twice for 192-bit key, thrice for 256-bit key
|
|
for z in xrange(extra_cnt):
|
|
for j in xrange(4):
|
|
# mix in bytes from the last subkey
|
|
word[j] ^= exkey[-self.key_size + j]
|
|
exkey.extend(word)
|
|
|
|
self.exkey = exkey
|
|
|
|
def add_round_key(self, block, round):
|
|
"""AddRoundKey step in AES. This is where the key is mixed into plaintext"""
|
|
|
|
offset = round * 16
|
|
exkey = self.exkey
|
|
|
|
for i in xrange(16):
|
|
block[i] ^= exkey[offset + i]
|
|
|
|
# print 'AddRoundKey:', block
|
|
|
|
def sub_bytes(self, block, sbox):
|
|
"""SubBytes step, apply S-box to all bytes
|
|
|
|
Depending on whether encrypting or decrypting, a different sbox array
|
|
is passed in.
|
|
"""
|
|
|
|
for i in xrange(16):
|
|
block[i] = sbox[block[i]]
|
|
|
|
# print 'SubBytes :', block
|
|
|
|
def shift_rows(self, b):
|
|
"""ShiftRows step. Shifts 2nd row to left by 1, 3rd row by 2, 4th row by 3
|
|
|
|
Since we're performing this on a transposed matrix, cells are numbered
|
|
from top to bottom::
|
|
|
|
0 4 8 12 -> 0 4 8 12 -- 1st row doesn't change
|
|
1 5 9 13 -> 5 9 13 1 -- row shifted to left by 1 (wraps around)
|
|
2 6 10 14 -> 10 14 2 6 -- shifted by 2
|
|
3 7 11 15 -> 15 3 7 11 -- shifted by 3
|
|
"""
|
|
|
|
b[1], b[5], b[9], b[13] = b[5], b[9], b[13], b[1]
|
|
b[2], b[6], b[10], b[14] = b[10], b[14], b[2], b[6]
|
|
b[3], b[7], b[11], b[15] = b[15], b[3], b[7], b[11]
|
|
|
|
# print 'ShiftRows :', b
|
|
|
|
def shift_rows_inv(self, b):
|
|
"""Similar to shift_rows above, but performed in inverse for decryption."""
|
|
|
|
b[5], b[9], b[13], b[1] = b[1], b[5], b[9], b[13]
|
|
b[10], b[14], b[2], b[6] = b[2], b[6], b[10], b[14]
|
|
b[15], b[3], b[7], b[11] = b[3], b[7], b[11], b[15]
|
|
|
|
# print 'ShiftRows :', b
|
|
|
|
def mix_columns(self, block):
|
|
"""MixColumns step. Mixes the values in each column"""
|
|
|
|
# Cache global multiplication tables (see below)
|
|
mul_by_2 = gf_mul_by_2
|
|
mul_by_3 = gf_mul_by_3
|
|
|
|
# Since we're dealing with a transposed matrix, columns are already
|
|
# sequential
|
|
for i in xrange(4):
|
|
col = i * 4
|
|
|
|
# v0, v1, v2, v3 = block[col : col+4]
|
|
v0, v1, v2, v3 = (block[col], block[col + 1], block[col + 2],
|
|
block[col + 3])
|
|
|
|
block[col] = mul_by_2[v0] ^ v3 ^ v2 ^ mul_by_3[v1]
|
|
block[col + 1] = mul_by_2[v1] ^ v0 ^ v3 ^ mul_by_3[v2]
|
|
block[col + 2] = mul_by_2[v2] ^ v1 ^ v0 ^ mul_by_3[v3]
|
|
block[col + 3] = mul_by_2[v3] ^ v2 ^ v1 ^ mul_by_3[v0]
|
|
|
|
# print 'MixColumns :', block
|
|
|
|
def mix_columns_inv(self, block):
|
|
"""Similar to mix_columns above, but performed in inverse for decryption."""
|
|
|
|
# Cache global multiplication tables (see below)
|
|
mul_9 = gf_mul_by_9
|
|
mul_11 = gf_mul_by_11
|
|
mul_13 = gf_mul_by_13
|
|
mul_14 = gf_mul_by_14
|
|
|
|
# Since we're dealing with a transposed matrix, columns are already
|
|
# sequential
|
|
for i in xrange(4):
|
|
col = i * 4
|
|
|
|
v0, v1, v2, v3 = (block[col], block[col + 1], block[col + 2],
|
|
block[col + 3])
|
|
# v0, v1, v2, v3 = block[col:col+4]
|
|
|
|
block[col] = mul_14[v0] ^ mul_9[v3] ^ mul_13[v2] ^ mul_11[v1]
|
|
block[col + 1] = mul_14[v1] ^ mul_9[v0] ^ mul_13[v3] ^ mul_11[v2]
|
|
block[col + 2] = mul_14[v2] ^ mul_9[v1] ^ mul_13[v0] ^ mul_11[v3]
|
|
block[col + 3] = mul_14[v3] ^ mul_9[v2] ^ mul_13[v1] ^ mul_11[v0]
|
|
|
|
# print 'MixColumns :', block
|
|
|
|
def encrypt_block(self, block):
|
|
"""Encrypts a single block. This is the main AES function"""
|
|
|
|
# For efficiency reasons, the state between steps is transmitted via a
|
|
# mutable array, not returned.
|
|
self.add_round_key(block, 0)
|
|
|
|
for round in xrange(1, self.rounds):
|
|
self.sub_bytes(block, aes_sbox)
|
|
self.shift_rows(block)
|
|
self.mix_columns(block)
|
|
self.add_round_key(block, round)
|
|
|
|
self.sub_bytes(block, aes_sbox)
|
|
self.shift_rows(block)
|
|
# no mix_columns step in the last round
|
|
self.add_round_key(block, self.rounds)
|
|
|
|
def decrypt_block(self, block):
|
|
"""Decrypts a single block. This is the main AES decryption function"""
|
|
|
|
# For efficiency reasons, the state between steps is transmitted via a
|
|
# mutable array, not returned.
|
|
self.add_round_key(block, self.rounds)
|
|
|
|
# count rounds down from 15 ... 1
|
|
for round in xrange(self.rounds - 1, 0, -1):
|
|
self.shift_rows_inv(block)
|
|
self.sub_bytes(block, aes_inv_sbox)
|
|
self.add_round_key(block, round)
|
|
self.mix_columns_inv(block)
|
|
|
|
self.shift_rows_inv(block)
|
|
self.sub_bytes(block, aes_inv_sbox)
|
|
self.add_round_key(block, 0)
|
|
# no mix_columns step in the last round
|
|
|
|
|
|
#### ECB mode implementation
|
|
|
|
class ECBMode(object):
|
|
"""Electronic CodeBook (ECB) mode encryption.
|
|
|
|
Basically this mode applies the cipher function to each block individually;
|
|
no feedback is done. NB! This is insecure for almost all purposes
|
|
"""
|
|
|
|
def __init__(self, cipher):
|
|
self.cipher = cipher
|
|
self.block_size = cipher.block_size
|
|
|
|
def ecb(self, data, block_func):
|
|
"""Perform ECB mode with the given function"""
|
|
|
|
if len(data) % self.block_size != 0:
|
|
raise ValueError, "Plaintext length must be multiple of 16"
|
|
|
|
block_size = self.block_size
|
|
data = array('B', data)
|
|
|
|
for offset in xrange(0, len(data), block_size):
|
|
block = data[offset: offset + block_size]
|
|
block_func(block)
|
|
data[offset: offset + block_size] = block
|
|
|
|
return data.tostring()
|
|
|
|
def encrypt(self, data):
|
|
"""Encrypt data in ECB mode"""
|
|
|
|
return self.ecb(data, self.cipher.encrypt_block)
|
|
|
|
def decrypt(self, data):
|
|
"""Decrypt data in ECB mode"""
|
|
|
|
return self.ecb(data, self.cipher.decrypt_block)
|
|
|
|
|
|
#### CBC mode
|
|
|
|
class CBCMode(object):
|
|
"""Cipher Block Chaining (CBC) mode encryption. This mode avoids content leaks.
|
|
|
|
In CBC encryption, each plaintext block is XORed with the ciphertext block
|
|
preceding it; decryption is simply the inverse.
|
|
"""
|
|
|
|
# A better explanation of CBC can be found here:
|
|
# http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
|
|
|
|
def __init__(self, cipher, IV):
|
|
self.cipher = cipher
|
|
self.block_size = cipher.block_size
|
|
self.IV = array('B', IV)
|
|
|
|
def encrypt(self, data):
|
|
"""Encrypt data in CBC mode"""
|
|
|
|
block_size = self.block_size
|
|
if len(data) % block_size != 0:
|
|
raise ValueError, "Plaintext length must be multiple of 16"
|
|
|
|
data = array('B', data)
|
|
IV = self.IV
|
|
|
|
for offset in xrange(0, len(data), block_size):
|
|
block = data[offset: offset + block_size]
|
|
|
|
# Perform CBC chaining
|
|
for i in xrange(block_size):
|
|
block[i] ^= IV[i]
|
|
|
|
self.cipher.encrypt_block(block)
|
|
data[offset: offset + block_size] = block
|
|
IV = block
|
|
|
|
self.IV = IV
|
|
return data.tostring()
|
|
|
|
def decrypt(self, data):
|
|
"""Decrypt data in CBC mode"""
|
|
|
|
block_size = self.block_size
|
|
if len(data) % block_size != 0:
|
|
raise ValueError, "Ciphertext length must be multiple of 16"
|
|
|
|
data = array('B', data)
|
|
IV = self.IV
|
|
|
|
for offset in xrange(0, len(data), block_size):
|
|
ctext = data[offset: offset + block_size]
|
|
block = ctext[:]
|
|
self.cipher.decrypt_block(block)
|
|
|
|
# Perform CBC chaining
|
|
# for i in xrange(block_size):
|
|
# data[offset + i] ^= IV[i]
|
|
for i in xrange(block_size):
|
|
block[i] ^= IV[i]
|
|
data[offset: offset + block_size] = block
|
|
|
|
IV = ctext
|
|
# data[offset : offset+block_size] = block
|
|
|
|
self.IV = IV
|
|
return data.tostring()
|
|
|
|
|
|
####
|
|
|
|
def galois_multiply(a, b):
|
|
"""Galois Field multiplicaiton for AES"""
|
|
p = 0
|
|
while b:
|
|
if b & 1:
|
|
p ^= a
|
|
a <<= 1
|
|
if a & 0x100:
|
|
a ^= 0x1b
|
|
b >>= 1
|
|
|
|
return p & 0xff
|
|
|
|
|
|
# Precompute the multiplication tables for encryption
|
|
gf_mul_by_2 = array('B', [galois_multiply(x, 2) for x in range(256)])
|
|
gf_mul_by_3 = array('B', [galois_multiply(x, 3) for x in range(256)])
|
|
# ... for decryption
|
|
gf_mul_by_9 = array('B', [galois_multiply(x, 9) for x in range(256)])
|
|
gf_mul_by_11 = array('B', [galois_multiply(x, 11) for x in range(256)])
|
|
gf_mul_by_13 = array('B', [galois_multiply(x, 13) for x in range(256)])
|
|
gf_mul_by_14 = array('B', [galois_multiply(x, 14) for x in range(256)])
|
|
|
|
####
|
|
|
|
# The S-box is a 256-element array, that maps a single byte value to another
|
|
# byte value. Since it's designed to be reversible, each value occurs only once
|
|
# in the S-box
|
|
#
|
|
# More information: http://en.wikipedia.org/wiki/Rijndael_S-box
|
|
|
|
aes_sbox = array('B',
|
|
'637c777bf26b6fc53001672bfed7ab76'
|
|
'ca82c97dfa5947f0add4a2af9ca472c0'
|
|
'b7fd9326363ff7cc34a5e5f171d83115'
|
|
'04c723c31896059a071280e2eb27b275'
|
|
'09832c1a1b6e5aa0523bd6b329e32f84'
|
|
'53d100ed20fcb15b6acbbe394a4c58cf'
|
|
'd0efaafb434d338545f9027f503c9fa8'
|
|
'51a3408f929d38f5bcb6da2110fff3d2'
|
|
'cd0c13ec5f974417c4a77e3d645d1973'
|
|
'60814fdc222a908846eeb814de5e0bdb'
|
|
'e0323a0a4906245cc2d3ac629195e479'
|
|
'e7c8376d8dd54ea96c56f4ea657aae08'
|
|
'ba78252e1ca6b4c6e8dd741f4bbd8b8a'
|
|
'703eb5664803f60e613557b986c11d9e'
|
|
'e1f8981169d98e949b1e87e9ce5528df'
|
|
'8ca1890dbfe6426841992d0fb054bb16'.decode('hex')
|
|
)
|
|
|
|
# This is the inverse of the above. In other words:
|
|
# aes_inv_sbox[aes_sbox[val]] == val
|
|
|
|
aes_inv_sbox = array('B',
|
|
'52096ad53036a538bf40a39e81f3d7fb'
|
|
'7ce339829b2fff87348e4344c4dee9cb'
|
|
'547b9432a6c2233dee4c950b42fac34e'
|
|
'082ea16628d924b2765ba2496d8bd125'
|
|
'72f8f66486689816d4a45ccc5d65b692'
|
|
'6c704850fdedb9da5e154657a78d9d84'
|
|
'90d8ab008cbcd30af7e45805b8b34506'
|
|
'd02c1e8fca3f0f02c1afbd0301138a6b'
|
|
'3a9111414f67dcea97f2cfcef0b4e673'
|
|
'96ac7422e7ad3585e2f937e81c75df6e'
|
|
'47f11a711d29c5896fb7620eaa18be1b'
|
|
'fc563e4bc6d279209adbc0fe78cd5af4'
|
|
'1fdda8338807c731b11210592780ec5f'
|
|
'60517fa919b54a0d2de57a9f93c99cef'
|
|
'a0e03b4dae2af5b0c8ebbb3c83539961'
|
|
'172b047eba77d626e169146355210c7d'.decode('hex')
|
|
)
|
|
|
|
# The Rcon table is used in AES's key schedule (key expansion)
|
|
# It's a pre-computed table of exponentation of 2 in AES's finite field
|
|
#
|
|
# More information: http://en.wikipedia.org/wiki/Rijndael_key_schedule
|
|
|
|
aes_Rcon = array('B',
|
|
'8d01020408102040801b366cd8ab4d9a'
|
|
'2f5ebc63c697356ad4b37dfaefc59139'
|
|
'72e4d3bd61c29f254a943366cc831d3a'
|
|
'74e8cb8d01020408102040801b366cd8'
|
|
'ab4d9a2f5ebc63c697356ad4b37dfaef'
|
|
'c5913972e4d3bd61c29f254a943366cc'
|
|
'831d3a74e8cb8d01020408102040801b'
|
|
'366cd8ab4d9a2f5ebc63c697356ad4b3'
|
|
'7dfaefc5913972e4d3bd61c29f254a94'
|
|
'3366cc831d3a74e8cb8d010204081020'
|
|
'40801b366cd8ab4d9a2f5ebc63c69735'
|
|
'6ad4b37dfaefc5913972e4d3bd61c29f'
|
|
'254a943366cc831d3a74e8cb8d010204'
|
|
'08102040801b366cd8ab4d9a2f5ebc63'
|
|
'c697356ad4b37dfaefc5913972e4d3bd'
|
|
'61c29f254a943366cc831d3a74e8cb'.decode('hex')
|
|
)
|