Added full aes-256-ige with tests

This commit is contained in:
Sammy Pfeiffer 2015-03-15 02:02:15 +01:00
parent 5a39d105fa
commit 33b5708da3
2 changed files with 108 additions and 23 deletions

View File

@ -0,0 +1,64 @@
__author__ = 'sam'
from ige import ige
# AES 256 IGE is using AES ECB internally, it implies (extract from PyCrypto.cipher.AES):
# key : byte string
# The secret key to use in the symmetric cipher.
# It must be 16 (*AES-128*), 24 (*AES-192*), or 32 (*AES-256*) bytes long.
# IV : byte string
# The initialization vector to use for encryption or decryption.
#
# It is ignored for `MODE_ECB` and `MODE_CTR`.
#
# For all other modes, it must be `block_size` bytes longs.
# message must be a multiple of 16 in size
msg_to_encrypt = "This is a secret message"
padding_needed = 16 - len(msg_to_encrypt) % 16
msg_to_encrypt_padded = msg_to_encrypt + b'0' * padding_needed
print("Encrypting: '" + str(msg_to_encrypt) + "'")
print("With padding: '" + str(msg_to_encrypt_padded) + "'")
# 32 bytes long key
aes_key = b'12345678901234567890123456789012'
print("With key for AES 256 ECB: '" + str(aes_key) + "'")
# Initialization Vector must be 32 bytes
aes_iv = b'01234567890123456789012345678901'
print("And initialization vector: '" + str(aes_iv) + "'")
encrypted_msg = ige(msg_to_encrypt_padded, aes_key, aes_iv, operation="encrypt")
print("\nEncrypted msg: '" + str(encrypted_msg) + "'")
print("In hex: " + encrypted_msg.__repr__())
decrypted_msg = ige(encrypted_msg, aes_key, aes_iv, operation="decrypt")
print("\nDecrypted msg: '" + str(decrypted_msg) + "'")
print("In hex: " + decrypted_msg.__repr__())
if msg_to_encrypt_padded == decrypted_msg:
print("Encrypt + Decrypt process, completed succesfully.")
# Let's test incorrect inputs
print("\n\nTesting incorrect inputs:")
# Message with length not multiple of 16
msg_not_multiple_of_16 = "6bytes"
print("Trying to encrypt: '" + msg_not_multiple_of_16 +
"' of size: " + str(len(msg_not_multiple_of_16)))
try:
encrypted_msg = ige(msg_not_multiple_of_16, aes_key, aes_iv, operation="encrypt")
except ValueError as ve:
print(" Correctly got ValueError: '" + str(ve) + "'")
# key not being 32 bytes
aes_key_not_32_bytes = b'0123456789'
print("Trying to use key: '" + str(aes_key_not_32_bytes) + "'")
try:
encrypted_msg = ige(msg_to_encrypt_padded, aes_key_not_32_bytes, aes_iv, operation="encrypt")
except ValueError as ve:
print(" Correctly got ValueError: '" + str(ve) + "'")
# iv not being 32 bytes
iv_key_not_32_bytes = b'0123456789'
print("Trying to use iv: '" + str(iv_key_not_32_bytes) + "'")
try:
encrypted_msg = ige(msg_to_encrypt_padded, aes_key, iv_key_not_32_bytes, operation="encrypt")
except ValueError as ve:
print(" Correctly got ValueError: '" + str(ve) + "'")

63
ige.py
View File

@ -51,10 +51,15 @@ def xor_strings(a, b): # xor two strings of different lengths
else: else:
return bytes(x ^ y for x, y in zip(a, b)) return bytes(x ^ y for x, y in zip(a, b))
def ige(message, key, iv): def ige(message, key, iv, operation="decrypt"):
"""given a key, given an iv, and message, decrypt it.""" """Given a key, given an iv, and message
do whatever operation asked in the operation field.
Operation will be checked for: "decrypt" and "encrypt" strings.
Returns the message encrypted/decrypted.
message must be a multiple by 16 bytes (for division in 16 byte blocks)
key must be 32 byte
iv must be 32 byte (it's not internally used in AES 256 ECB, but it's
needed for IGE)"""
if type(message) == long: if type(message) == long:
message = number.long_to_bytes(message) message = number.long_to_bytes(message)
if type(key) == long: if type(key) == long:
@ -62,33 +67,49 @@ def ige(message, key, iv):
if type(iv) == long: if type(iv) == long:
iv = number.long_to_bytes(iv) iv = number.long_to_bytes(iv)
if len(key) != 32:
raise ValueError("key must be 32 bytes long (was " +
str(len(key)) + " bytes)")
if len(iv) != 32:
raise ValueError("iv must be 32 bytes long (was " +
str(len(iv)) + " bytes)")
cipher = AES.new(key, AES.MODE_ECB, iv) cipher = AES.new(key, AES.MODE_ECB, iv)
blocksize = cipher.block_size blocksize = cipher.block_size
if len(message) % blocksize != 0:
raise ValueError("message must be a multiple of 16 bytes (try adding " +
str(16 - len(message) % 16) + " bytes of padding)")
prev_x = iv[0:blocksize] ivp = iv[0:blocksize]
prev_y = iv[blocksize:] # len should be > 0 ivp2 = iv[blocksize:]
decrypted = None ciphered = None
for i in range(0, len(message), blocksize): for i in range(0, len(message), blocksize):
x = message[i:i+blocksize] indata = message[i:i+blocksize]
y = xor_strings(cipher.decrypt(xor_strings(x, prev_y)), prev_x) if operation == "decrypt":
outdata = xor_strings(cipher.decrypt(xor_strings(indata, ivp2)), ivp)
prev_x = x ivp = indata
prev_y = y ivp2 = outdata
elif operation == "encrypt":
# decrypted = sumBytes(decrypted, y); outdata = xor_strings(cipher.encrypt(xor_strings(indata, ivp)), ivp2)
if decrypted is None: ivp = outdata
decrypted = y ivp2 = indata
else: else:
decrypted_ba = bytearray(decrypted) raise ValueError("operation must be either 'decrypt' or 'encrypt'")
decrypted_ba.extend(y)
if ciphered is None:
ciphered = outdata
else:
ciphered_ba = bytearray(ciphered)
ciphered_ba.extend(outdata)
if version_info >= (3, 4, 0): if version_info >= (3, 4, 0):
decrypted = bytes(decrypted_ba) ciphered = bytes(ciphered_ba)
else: else:
decrypted = str(decrypted_ba) ciphered = str(ciphered_ba)
return ciphered
return decrypted
if __name__ == "__main__": if __name__ == "__main__":
encrypted_answer_str = "28A92FE20173B347A8BB324B5FAB2667C9A8BBCE6468D5B509A4CBDDC186240AC912CF7006AF8926DE606A2E74C0493CAA57741E6C82451F54D3E068F5CCC49B4444124B9666FFB405AAB564A3D01E67F6E912867C8D20D9882707DC330B17B4E0DD57CB53BFAAFA9EF5BE76AE6C1B9B6C51E2D6502A47C883095C46C81E3BE25F62427B585488BB3BF239213BF48EB8FE34C9A026CC8413934043974DB03556633038392CECB51F94824E140B98637730A4BE79A8F9DAFA39BAE81E1095849EA4C83467C92A3A17D997817C8A7AC61C3FF414DA37B7D66E949C0AEC858F048224210FCC61F11C3A910B431CCBD104CCCC8DC6D29D4A5D133BE639A4C32BBFF153E63ACA3AC52F2E4709B8AE01844B142C1EE89D075D64F69A399FEB04E656FE3675A6F8F412078F3D0B58DA15311C1A9F8E53B3CD6BB5572C294904B726D0BE337E2E21977DA26DD6E33270251C2CA29DFCC70227F0755F84CFDA9AC4B8DD5F84F1D1EB36BA45CDDC70444D8C213E4BD8F63B8AB95A2D0B4180DC91283DC063ACFB92D6A4E407CDE7C8C69689F77A007441D4A6A8384B666502D9B77FC68B5B43CC607E60A146223E110FCB43BC3C942EF981930CDC4A1D310C0B64D5E55D308D863251AB90502C3E46CC599E886A927CDA963B9EB16CE62603B68529EE98F9F5206419E03FB458EC4BD9454AA8F6BA777573CC54B328895B1DF25EAD9FB4CD5198EE022B2B81F388D281D5E5BC580107CA01A50665C32B552715F335FD76264FAD00DDD5AE45B94832AC79CE7C511D194BC42B70EFA850BB15C2012C5215CABFE97CE66B8D8734D0EE759A638AF013" encrypted_answer_str = "28A92FE20173B347A8BB324B5FAB2667C9A8BBCE6468D5B509A4CBDDC186240AC912CF7006AF8926DE606A2E74C0493CAA57741E6C82451F54D3E068F5CCC49B4444124B9666FFB405AAB564A3D01E67F6E912867C8D20D9882707DC330B17B4E0DD57CB53BFAAFA9EF5BE76AE6C1B9B6C51E2D6502A47C883095C46C81E3BE25F62427B585488BB3BF239213BF48EB8FE34C9A026CC8413934043974DB03556633038392CECB51F94824E140B98637730A4BE79A8F9DAFA39BAE81E1095849EA4C83467C92A3A17D997817C8A7AC61C3FF414DA37B7D66E949C0AEC858F048224210FCC61F11C3A910B431CCBD104CCCC8DC6D29D4A5D133BE639A4C32BBFF153E63ACA3AC52F2E4709B8AE01844B142C1EE89D075D64F69A399FEB04E656FE3675A6F8F412078F3D0B58DA15311C1A9F8E53B3CD6BB5572C294904B726D0BE337E2E21977DA26DD6E33270251C2CA29DFCC70227F0755F84CFDA9AC4B8DD5F84F1D1EB36BA45CDDC70444D8C213E4BD8F63B8AB95A2D0B4180DC91283DC063ACFB92D6A4E407CDE7C8C69689F77A007441D4A6A8384B666502D9B77FC68B5B43CC607E60A146223E110FCB43BC3C942EF981930CDC4A1D310C0B64D5E55D308D863251AB90502C3E46CC599E886A927CDA963B9EB16CE62603B68529EE98F9F5206419E03FB458EC4BD9454AA8F6BA777573CC54B328895B1DF25EAD9FB4CD5198EE022B2B81F388D281D5E5BC580107CA01A50665C32B552715F335FD76264FAD00DDD5AE45B94832AC79CE7C511D194BC42B70EFA850BB15C2012C5215CABFE97CE66B8D8734D0EE759A638AF013"