# # test.py : Functions used for testing the modules # # Part of the Python Cryptography Toolkit # # Distribute and use freely; there are no restrictions on further # dissemination and usage except those imposed by the laws of your # country of residence. This software is provided "as is" without # warranty of fitness for use or suitability for any purpose, express # or implied. Use at your own risk or not at all. # __revision__ = "$Id: test.py,v 1.16 2004/08/13 22:24:18 akuchling Exp $" import binascii import string import testdata from Crypto.Cipher import * def die(string): import sys print '***ERROR: ', string # sys.exit(0) # Will default to continuing onward... def print_timing (size, delta, verbose): if verbose: if delta == 0: print 'Unable to measure time -- elapsed time too small' else: print '%.2f K/sec' % (size/delta) def exerciseBlockCipher(cipher, verbose): import string, time try: ciph = eval(cipher) except NameError: print cipher, 'module not available' return None print cipher+ ':' str='1' # Build 128K of test data for i in xrange(0, 17): str=str+str if ciph.key_size==0: ciph.key_size=16 password = 'password12345678Extra text for password'[0:ciph.key_size] IV = 'Test IV Test IV Test IV Test'[0:ciph.block_size] if verbose: print ' ECB mode:', obj=ciph.new(password, ciph.MODE_ECB) if obj.block_size != ciph.block_size: die("Module and cipher object block_size don't match") text='1234567812345678'[0:ciph.block_size] c=obj.encrypt(text) if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') text='KuchlingKuchling'[0:ciph.block_size] c=obj.encrypt(text) if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') text='NotTodayNotEver!'[0:ciph.block_size] c=obj.encrypt(text) if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') start=time.time() s=obj.encrypt(str) s2=obj.decrypt(s) end=time.time() if (str!=s2): die('Error in resulting plaintext from ECB mode') print_timing(256, end-start, verbose) del obj if verbose: print ' CFB mode:', obj1=ciph.new(password, ciph.MODE_CFB, IV) obj2=ciph.new(password, ciph.MODE_CFB, IV) start=time.time() ciphertext=obj1.encrypt(str[0:65536]) plaintext=obj2.decrypt(ciphertext) end=time.time() if (plaintext!=str[0:65536]): die('Error in resulting plaintext from CFB mode') print_timing(64, end-start, verbose) del obj1, obj2 if verbose: print ' CBC mode:', obj1=ciph.new(password, ciph.MODE_CBC, IV) obj2=ciph.new(password, ciph.MODE_CBC, IV) start=time.time() ciphertext=obj1.encrypt(str) plaintext=obj2.decrypt(ciphertext) end=time.time() if (plaintext!=str): die('Error in resulting plaintext from CBC mode') print_timing(256, end-start, verbose) del obj1, obj2 if verbose: print ' PGP mode:', obj1=ciph.new(password, ciph.MODE_PGP, IV) obj2=ciph.new(password, ciph.MODE_PGP, IV) start=time.time() ciphertext=obj1.encrypt(str) plaintext=obj2.decrypt(ciphertext) end=time.time() if (plaintext!=str): die('Error in resulting plaintext from PGP mode') print_timing(256, end-start, verbose) del obj1, obj2 if verbose: print ' OFB mode:', obj1=ciph.new(password, ciph.MODE_OFB, IV) obj2=ciph.new(password, ciph.MODE_OFB, IV) start=time.time() ciphertext=obj1.encrypt(str) plaintext=obj2.decrypt(ciphertext) end=time.time() if (plaintext!=str): die('Error in resulting plaintext from OFB mode') print_timing(256, end-start, verbose) del obj1, obj2 def counter(length=ciph.block_size): return length * 'a' if verbose: print ' CTR mode:', obj1=ciph.new(password, ciph.MODE_CTR, counter=counter) obj2=ciph.new(password, ciph.MODE_CTR, counter=counter) start=time.time() ciphertext=obj1.encrypt(str) plaintext=obj2.decrypt(ciphertext) end=time.time() if (plaintext!=str): die('Error in resulting plaintext from CTR mode') print_timing(256, end-start, verbose) del obj1, obj2 # Test the IV handling if verbose: print ' Testing IV handling' obj1=ciph.new(password, ciph.MODE_CBC, IV) plaintext='Test'*(ciph.block_size/4)*3 ciphertext1=obj1.encrypt(plaintext) obj1.IV=IV ciphertext2=obj1.encrypt(plaintext) if ciphertext1!=ciphertext2: die('Error in setting IV') # Test keyword arguments obj1=ciph.new(key=password) obj1=ciph.new(password, mode=ciph.MODE_CBC) obj1=ciph.new(mode=ciph.MODE_CBC, key=password) obj1=ciph.new(IV=IV, mode=ciph.MODE_CBC, key=password) return ciph def exerciseStreamCipher(cipher, verbose): import string, time try: ciph = eval(cipher) except (NameError): print cipher, 'module not available' return None print cipher + ':', str='1' # Build 128K of test data for i in xrange(0, 17): str=str+str key_size = ciph.key_size or 16 password = 'password12345678Extra text for password'[0:key_size] obj1=ciph.new(password) obj2=ciph.new(password) if obj1.block_size != ciph.block_size: die("Module and cipher object block_size don't match") if obj1.key_size != ciph.key_size: die("Module and cipher object key_size don't match") text='1234567812345678Python' c=obj1.encrypt(text) if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') text='B1FF I2 A R3A11Y |<00L D00D!!!!!' c=obj1.encrypt(text) if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') text='SpamSpamSpamSpamSpamSpamSpamSpamSpam' c=obj1.encrypt(text) if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') start=time.time() s=obj1.encrypt(str) str=obj2.decrypt(s) end=time.time() print_timing(256, end-start, verbose) del obj1, obj2 return ciph def TestStreamModules(args=['arc4', 'XOR'], verbose=1): import sys, string args=map(string.lower, args) if 'arc4' in args: # Test ARC4 stream cipher arc4=exerciseStreamCipher('ARC4', verbose) if (arc4!=None): for entry in testdata.arc4: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=arc4.new(key) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('ARC4 failed on entry '+`entry`) if 'xor' in args: # Test XOR stream cipher XOR=exerciseStreamCipher('XOR', verbose) if (XOR!=None): for entry in testdata.xor: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=XOR.new(key) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('XOR failed on entry '+`entry`) def TestBlockModules(args=['aes', 'arc2', 'des', 'blowfish', 'cast', 'des3', 'idea', 'rc5'], verbose=1): import string args=map(string.lower, args) if 'aes' in args: ciph=exerciseBlockCipher('AES', verbose) # AES if (ciph!=None): if verbose: print ' Verifying against test suite...' for entry in testdata.aes: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=ciph.new(key, ciph.MODE_ECB) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('AES failed on entry '+`entry`) for i in ciphertext: if verbose: print hex(ord(i)), if verbose: print for entry in testdata.aes_modes: mode, key, plain, cipher, kw = entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=ciph.new(key, mode, **kw) obj2=ciph.new(key, mode, **kw) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('AES encrypt failed on entry '+`entry`) for i in ciphertext: if verbose: print hex(ord(i)), if verbose: print plain2=obj2.decrypt(ciphertext) if plain2!=plain: die('AES decrypt failed on entry '+`entry`) for i in plain2: if verbose: print hex(ord(i)), if verbose: print if 'arc2' in args: ciph=exerciseBlockCipher('ARC2', verbose) # Alleged RC2 if (ciph!=None): if verbose: print ' Verifying against test suite...' for entry in testdata.arc2: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=ciph.new(key, ciph.MODE_ECB) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('ARC2 failed on entry '+`entry`) for i in ciphertext: if verbose: print hex(ord(i)), print if 'blowfish' in args: ciph=exerciseBlockCipher('Blowfish',verbose)# Bruce Schneier's Blowfish cipher if (ciph!=None): if verbose: print ' Verifying against test suite...' for entry in testdata.blowfish: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=ciph.new(key, ciph.MODE_ECB) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('Blowfish failed on entry '+`entry`) for i in ciphertext: if verbose: print hex(ord(i)), if verbose: print if 'cast' in args: ciph=exerciseBlockCipher('CAST', verbose) # CAST-128 if (ciph!=None): if verbose: print ' Verifying against test suite...' for entry in testdata.cast: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=ciph.new(key, ciph.MODE_ECB) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('CAST failed on entry '+`entry`) for i in ciphertext: if verbose: print hex(ord(i)), if verbose: print if 0: # The full-maintenance test; it requires 4 million encryptions, # and correspondingly is quite time-consuming. I've disabled # it; it's faster to compile block/cast.c with -DTEST and run # the resulting program. a = b = '\x01\x23\x45\x67\x12\x34\x56\x78\x23\x45\x67\x89\x34\x56\x78\x9A' for i in range(0, 1000000): obj = cast.new(b, cast.MODE_ECB) a = obj.encrypt(a[:8]) + obj.encrypt(a[-8:]) obj = cast.new(a, cast.MODE_ECB) b = obj.encrypt(b[:8]) + obj.encrypt(b[-8:]) if a!="\xEE\xA9\xD0\xA2\x49\xFD\x3B\xA6\xB3\x43\x6F\xB8\x9D\x6D\xCA\x92": if verbose: print 'CAST test failed: value of "a" doesn\'t match' if b!="\xB2\xC9\x5E\xB0\x0C\x31\xAD\x71\x80\xAC\x05\xB8\xE8\x3D\x69\x6E": if verbose: print 'CAST test failed: value of "b" doesn\'t match' if 'des' in args: # Test/benchmark DES block cipher des=exerciseBlockCipher('DES', verbose) if (des!=None): # Various tests taken from the DES library packaged with Kerberos V4 obj=des.new(binascii.a2b_hex('0123456789abcdef'), des.MODE_ECB) s=obj.encrypt('Now is t') if (s!=binascii.a2b_hex('3fa40e8a984d4815')): die('DES fails test 1') obj=des.new(binascii.a2b_hex('08192a3b4c5d6e7f'), des.MODE_ECB) s=obj.encrypt('\000\000\000\000\000\000\000\000') if (s!=binascii.a2b_hex('25ddac3e96176467')): die('DES fails test 2') obj=des.new(binascii.a2b_hex('0123456789abcdef'), des.MODE_CBC, binascii.a2b_hex('1234567890abcdef')) s=obj.encrypt("Now is the time for all ") if (s!=binascii.a2b_hex('e5c7cdde872bf27c43e934008c389c0f683788499a7c05f6')): die('DES fails test 3') obj=des.new(binascii.a2b_hex('0123456789abcdef'), des.MODE_CBC, binascii.a2b_hex('fedcba9876543210')) s=obj.encrypt("7654321 Now is the time for \000\000\000\000") if (s!=binascii.a2b_hex("ccd173ffab2039f4acd8aefddfd8a1eb468e91157888ba681d269397f7fe62b4")): die('DES fails test 4') del obj,s # R. Rivest's test: see http://theory.lcs.mit.edu/~rivest/destest.txt x=binascii.a2b_hex('9474B8E8C73BCA7D') for i in range(0, 16): obj=des.new(x, des.MODE_ECB) if (i & 1): x=obj.decrypt(x) else: x=obj.encrypt(x) if x!=binascii.a2b_hex('1B1A2DDB4C642438'): die("DES fails Rivest's test") if verbose: print ' Verifying against test suite...' for entry in testdata.des: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=des.new(key, des.MODE_ECB) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('DES failed on entry '+`entry`) for entry in testdata.des_cbc: key, iv, plain, cipher=entry key, iv, cipher=binascii.a2b_hex(key),binascii.a2b_hex(iv),binascii.a2b_hex(cipher) obj1=des.new(key, des.MODE_CBC, iv) obj2=des.new(key, des.MODE_CBC, iv) ciphertext=obj1.encrypt(plain) if (ciphertext!=cipher): die('DES CBC mode failed on entry '+`entry`) if 'des3' in args: ciph=exerciseBlockCipher('DES3', verbose) # Triple DES if (ciph!=None): if verbose: print ' Verifying against test suite...' for entry in testdata.des3: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=ciph.new(key, ciph.MODE_ECB) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('DES3 failed on entry '+`entry`) for i in ciphertext: if verbose: print hex(ord(i)), if verbose: print for entry in testdata.des3_cbc: key, iv, plain, cipher=entry key, iv, cipher=binascii.a2b_hex(key),binascii.a2b_hex(iv),binascii.a2b_hex(cipher) obj1=ciph.new(key, ciph.MODE_CBC, iv) obj2=ciph.new(key, ciph.MODE_CBC, iv) ciphertext=obj1.encrypt(plain) if (ciphertext!=cipher): die('DES3 CBC mode failed on entry '+`entry`) if 'idea' in args: ciph=exerciseBlockCipher('IDEA', verbose) # IDEA block cipher if (ciph!=None): if verbose: print ' Verifying against test suite...' for entry in testdata.idea: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=ciph.new(key, ciph.MODE_ECB) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('IDEA failed on entry '+`entry`) if 'rc5' in args: # Ronald Rivest's RC5 algorithm ciph=exerciseBlockCipher('RC5', verbose) if (ciph!=None): if verbose: print ' Verifying against test suite...' for entry in testdata.rc5: key,plain,cipher=entry key=binascii.a2b_hex(key) plain=binascii.a2b_hex(plain) cipher=binascii.a2b_hex(cipher) obj=ciph.new(key[4:], ciph.MODE_ECB, version =ord(key[0]), word_size=ord(key[1]), rounds =ord(key[2]) ) ciphertext=obj.encrypt(plain) if (ciphertext!=cipher): die('RC5 failed on entry '+`entry`) for i in ciphertext: if verbose: print hex(ord(i)), if verbose: print