def _check_oaep_padding(data, p, bytes): """Checks the OAEP padding on a 'bytes'-byte string.""" if len(data) != bytes: raise CryptoError("Decoding error") # This test (though required in the OAEP spec) is extraneous here. # if len(data) < 2*DIGEST_LEN+1: # raise CryptoError("Decoding error") if data[0] != "\x00": raise CryptoError("Decoding error") maskedSeed, maskedDB = data[1 : DIGEST_LEN + 1], data[DIGEST_LEN + 1 :] seed = _ml.strxor(maskedSeed, _oaep_mgf(maskedDB, DIGEST_LEN)) db = _ml.strxor(maskedDB, _oaep_mgf(seed, len(maskedDB))) m = None if db[:DIGEST_LEN] != _ml.sha1(p): raise CryptoError("Decoding error") for i in xrange(DIGEST_LEN, len(db)): if db[i] == "\x01": m = db[i + 1 :] break elif db[i] == "\x00": pass else: raise CryptoError("Decoding error") if m is None: raise CryptoError("Decoding error") return m
def _check_oaep_padding(data, p, b): '''Checks the OAEP padding on a 'bytes'-byte string.''' if len(data) != b: raise CryptoError("Decoding error") # This test (though required in the OAEP spec) is extraneous here. # if len(data) < 2*DIGEST_LEN+1: # raise CryptoError("Decoding error") if data[0] != '\x00': raise CryptoError("Decoding error") maskedSeed, maskedDB = data[1:DIGEST_LEN + 1], data[DIGEST_LEN + 1:] seed = _ml.strxor(maskedSeed, _oaep_mgf(maskedDB, DIGEST_LEN)) db = _ml.strxor(maskedDB, _oaep_mgf(seed, len(maskedDB))) m = None if db[:DIGEST_LEN] != _ml.sha1(p): raise CryptoError("Decoding error") for i in xrange(DIGEST_LEN, len(db)): if db[i] == '\x01': m = db[i + 1:] break elif db[i] == '\x00': pass else: raise CryptoError("Decoding error") if m is None: raise CryptoError("Decoding error") return m
def testLeaks1(): print "Trying to leak (sha1,aes,xor,seed,oaep)" s20k="a"*20*1024 keytxt="a"*16 key = _ml.aes_key(keytxt) while 1: _ml.aes_key(keytxt) _ml.sha1(s20k) _ml.aes_ctr128_crypt(key,s20k,0) _ml.aes_ctr128_crypt(key,s20k,2000) _ml.aes_ctr128_crypt(key,"",2000,20000) _ml.aes_ctr128_crypt(key,"",0,20000) _ml.aes_ctr128_crypt(key,s20k,0,2000) try: _ml.aes_ctr128_crypt("abc",s20k,0,2000) except: pass _ml.strxor(s20k,s20k) try: _ml.strxor(s20k,keytxt) except: pass _ml.openssl_seed(s20k) r = _ml.add_oaep_padding("Hello",OAEP_PARAMETER,128) _ml.check_oaep_padding(r,OAEP_PARAMETER,128) try: _ml.check_oaep_padding("hello",OAEP_PARAMETER,128) except: pass try: _ml.add_oaep_padding(s20k,OAEP_PARAMETER,128) except: pass try: _ml.add_oaep_padding("a"*127,OAEP_PARAMETER,128) except: pass
def _oaep_mgf(seed, bytes): """ Mask generation function specified for RSA-OAEP. Given a seed and a number of bytes, generates a mask for OAEP by computing sha1(seed + "\x00\x00\x00\x00")+sha1(seed+"\x00\x00\x00\x01)+... The mask is truncated to the specified length. LIMITATION: This implementation can only generate 5120 bytes of key material.""" assert bytes <= 5120 padding = [] nHashes = ceilDiv(bytes, DIGEST_LEN) # assert (nHashes-1)*DIGEST_LEN <= bytes <= nHashes*DIGEST_LEN padding = [_ml.sha1("%s\x00\x00\x00%c" % (seed, i)) for i in range(nHashes)] padding = "".join(padding) return padding[:bytes]
def _oaep_mgf(seed, b): ''' Mask generation function specified for RSA-OAEP. Given a seed and a number of bytes, generates a mask for OAEP by computing sha1(seed + "\x00\x00\x00\x00")+sha1(seed+"\x00\x00\x00\x01)+... The mask is truncated to the specified length. LIMITATION: This implementation can only generate 5120 bytes of key material.''' assert b <= 5120 padding = [] nHashes = ceilDiv(b, DIGEST_LEN) # assert (nHashes-1)*DIGEST_LEN <= b <= nHashes*DIGEST_LEN padding = ([ _ml.sha1("%s\x00\x00\x00%c" % (seed, i)) for i in range(nHashes) ]) padding = "".join(padding) return padding[:b]
def sha1(s): """Return the SHA1 hash of a string""" return _ml.sha1(s)
"""Given four 20-byte keys, encrypts s using the LIONESS super-pseudorandom permutation. """ assert len(key1) == len(key3) == DIGEST_LEN assert len(key2) == len(key4) == DIGEST_LEN assert len(s) > DIGEST_LEN # Split the message. left = s[:DIGEST_LEN] right = s[DIGEST_LEN:] del s # Performance note: This business with sha1("".join((key,right,key))) # may look slow, but it contributes only .7% to the total time for # LIONESS. right = _ml.aes_ctr128_crypt(_ml.aes_key(_ml.sha1("".join((key1, left, key1)))[:AES_KEY_LEN]), right, 0) left = _ml.strxor(left, _ml.sha1("".join((key2, right, key2)))) right = _ml.aes_ctr128_crypt(_ml.aes_key(_ml.sha1("".join((key3, left, key3)))[:AES_KEY_LEN]), right, 0) left = _ml.strxor(left, _ml.sha1("".join((key4, right, key4)))) # You could write the above as: # right = ctr_crypt(right, "".join((key1,left,key1))[:AES_KEY_LEN]) # left = strxor(left, sha1("".join((key2,right,key2)))) # right = ctr_crypt(right, "".join((key3,left,key3))[:AES_KEY_LEN]) # left = strxor(left, sha1("".join((key4,right,key4)))) # but that would be slower by about 10%. (Since LIONESS is in the # critical path, we care.) return left + right
super-pseudorandom permutation. """ assert len(key1) == len(key3) == DIGEST_LEN assert len(key2) == len(key4) == DIGEST_LEN assert len(s) > DIGEST_LEN # Split the message. left = s[:DIGEST_LEN] right = s[DIGEST_LEN:] del s # Performance note: This business with sha1("".join((key,right,key))) # may look slow, but it contributes only .7% to the total time for # LIONESS. right = _ml.aes_ctr128_crypt( _ml.aes_key(_ml.sha1("".join((key1, left, key1)))[:AES_KEY_LEN]), right, 0) left = _ml.strxor(left, _ml.sha1("".join((key2, right, key2)))) right = _ml.aes_ctr128_crypt( _ml.aes_key(_ml.sha1("".join((key3, left, key3)))[:AES_KEY_LEN]), right, 0) left = _ml.strxor(left, _ml.sha1("".join((key4, right, key4)))) # You could write the above as: # right = ctr_crypt(right, "".join((key1,left,key1))[:AES_KEY_LEN]) # left = strxor(left, sha1("".join((key2,right,key2)))) # right = ctr_crypt(right, "".join((key3,left,key3))[:AES_KEY_LEN]) # left = strxor(left, sha1("".join((key4,right,key4)))) # but that would be slower by about 10%. (Since LIONESS is in the # critical path, we care.) return left + right
def timeEfficiency(): print "#================= ACTUAL v. IDEAL =====================" # Here we compare the time spent in an operation with the time we think # is required for its underlying operations, in order to try to measure # its efficiency. If function X is pretty efficient, there's not much # reason to try to optimize its implementation; instead, we need to attack # the functions it uses. ##### LIONESS shakey = "z"*20 aeskey = "p"*16 # Lioness_encrypt is: # 2 28K SHA1's (keyed) # 2 20b SHA1's (keyed) # 2 20b string xors. # 2 28K aes_crypts. shastr = shakey+s28K+shakey sha1_keyed_28k = timeit_((lambda shastr=shastr: _ml.sha1(shastr)), 1000) shastr = shakey+s20b+shakey sha1_keyed_20b = timeit_((lambda shastr=shastr: _ml.sha1(shastr)), 100000) strxor_20b = timeit_((lambda s=s20b: _ml.strxor(s,s)), 100000) aes_28k = timeit_((lambda s=s28K,k=aeskey: ctr_crypt(s,k)), 100) lionesskey = ("p"*20,)*4 lioness_e = timeit_((lambda s=s28K,k=lionesskey: lioness_encrypt(s,k)),100) expected = 2*(strxor_20b+aes_28k+sha1_keyed_28k+sha1_keyed_20b) print "LIONESS TOOK:", timestr(lioness_e) print " expected:", timestr(expected) print " difference:", timestr(lioness_e-expected) print " goodness: %3.2f%%" % (100*expected/lioness_e) print " breakdown: aes: %3.1f%%" % (100*2*aes_28k/lioness_e) print " long sha1: %3.1f%%" % (100*2*sha1_keyed_28k/lioness_e) print " short sha1: %3.1f%%" % (100*2*sha1_keyed_20b/lioness_e) print " short xor: %3.1f%%" % (100*2*strxor_20b/lioness_e) ##### SERVER PROCESS pk = pk_generate(2048) # Typical (no swap) server process is: # pk_decrypt (128b) # sha1 (2K-128b) #5*sha1 (16b+~16b) [HEADER_SEC,HEADER_ENC,PRNG,PAYLOAD_ENC,REPLAY] # hashlog.seen **omit # hashlog.log **omit # ctr_crypt (2K) # lioness_D (28K) # prng (128b) # lioness_D (2K) # With swap, add: # keys_from_payload=HASH(28K) # lioness_D (2K) enc = pk_encrypt(s70b, pk) rsa_128b = timeit_((lambda pk=pk,enc=enc: pk_decrypt(enc,pk)), 100) shastr = s2K[2048-128] sha1_hdr = timeit_((lambda shastr=shastr: sha1(shastr)), 10000) shastr = s64b[:32] sha1_key = timeit_((lambda shastr=shastr: sha1(shastr)), 10000) aes_2k = timeit_((lambda k=aeskey: ctr_crypt(s2K,k)), 1000) lioness_28k = lioness_e lioness_2k = timeit_(( lambda s=s2K,k=lionesskey: lioness_encrypt(s,k)),1000) prng_128b = timeit_((lambda k=aeskey: prng(k,128)),10000) server = FakeServerInfo("127.0.0.1", 1, pk, "X"*20) sp = PacketHandler([pk], [DummyLog()]) payload = encodeMessage("Hello world",0)[0] m_noswap = buildForwardPacket(payload, SMTP_TYPE, "f@invalid", [server, server], [server, server]) sp_ns = timeit_( lambda sp=sp, m_noswap=m_noswap: sp.processPacket(m_noswap), 100) expected = rsa_128b+sha1_hdr+sha1_key*5+aes_2k+lioness_28k+prng_128b expected += lioness_2k print "SERVERPROCESS TOOK:", timestr(sp_ns) print " expected:", timestr(expected) print " difference:", timestr(sp_ns-expected) print " goodness: %3.2f%%" % (100*expected/sp_ns) print " breakdown: rsa: %3.1f%%" % (100*rsa_128b/sp_ns) print " 28K lioness: %3.1f%%" % (100*lioness_28k/sp_ns) print " 2K lioness: %3.1f%%" % (100*lioness_2k/sp_ns) print " header aes: %3.1f%%" % (100*aes_2k/sp_ns) print " header sha1: %3.1f%%" % (100*sha1_hdr/sp_ns) print " keygen sha1: %3.1f%%" % (500*sha1_key/sp_ns) print " (logs not included)"
def cryptoTiming(): print "#==================== CRYPTO =======================" print "SHA1 (short)", timeit((lambda: sha1(short)), 100000) print "SHA1 (64b)", timeit((lambda: sha1(s64b)), 100000) print "SHA1 (2K)", timeit((lambda: sha1(s2K)), 10000) print "SHA1 (8K)", timeit((lambda: sha1(s8K)), 10000) print "SHA1 (28K)", timeit((lambda: sha1(s28K)), 1000) print "SHA1 (32K)", timeit((lambda: sha1(s32K)), 1000) shakey = "8charstr"*2 print "Keyed SHA1 for lioness (28K, unoptimized)", timeit( (lambda shakey=shakey: _ml.sha1("".join((shakey,s28K,shakey)))), 1000) print "TRNG (20 byte)", timeit((lambda: trng(20)), 100) print "TRNG (128 byte)", timeit((lambda: trng(128)), 100) print "TRNG (1K)", timeit((lambda: trng(1024)), 100) print "xor (1K)", timeit((lambda: _ml.strxor(s1K,s1K)), 100000) print "xor (32K)", timeit((lambda: _ml.strxor(s32K,s32K)), 1000) key = "8charstr"*2 print "aes (short)", timeit((lambda key=key: ctr_crypt(short,key)), 100000) print "aes (1K)", timeit((lambda key=key: ctr_crypt(s1K,key)), 10000) print "aes (2K)", timeit((lambda key=key: ctr_crypt(s2K,key)), 10000) print "aes (28K)", timeit((lambda key=key: ctr_crypt(s28K,key)), 100) print "aes (32K)", timeit((lambda key=key: ctr_crypt(s32K,key)), 100) key = _ml.aes_key(key) print "aes (short,pre-key)", \ timeit((lambda key=key: ctr_crypt(short,key)), 100000) print "aes (1K,pre-key)", \ timeit((lambda key=key: ctr_crypt(s1K,key)), 10000) print "aes (28K,pre-key)", \ timeit((lambda key=key: ctr_crypt(s28K,key)), 100) print "aes (32K,pre-key)", \ timeit((lambda key=key: ctr_crypt(s32K,key)), 100) print "aes (32K,pre-key,unoptimized)", timeit( (lambda key=key: _ml.strxor(prng(key,32768),s32K)), 100) print "prng (short)", timeit((lambda key=key: prng(key,8)), 100000) print "prng (128b)", timeit(( lambda key=key: prng(key,18)), 10000) print "prng (1K)", timeit(( lambda key=key: prng(key,1024)), 10000) print "prng (2K)", timeit(( lambda key=key: prng(key,2048)), 10000) print "prng (28K)", timeit(( lambda key=key: prng(key,28678)), 100) print "prng (32K)", timeit((lambda key=key: prng(key,32768)), 100) print "prng (32K, unoptimized)", timeit( (lambda key=key: ctr_crypt('\x00'*32768, key)), 100) c = AESCounterPRNG() print "aesprng.getInt (10)", \ timeit((lambda c=c: c.getInt(10)), 10000) print "aesprng.getInt (1000)", \ timeit((lambda c=c: c.getInt(1000)), 10000) print "aesprng.getInt (513)", \ timeit((lambda c=c: c.getInt(513)), 10000) L10 = [ "x" ] * 10 L1000 = [ "x" ] * 1000 print "aesprng.shuffle (10/10)", \ timeit((lambda c=c,L=L10: c.shuffle(L)), 1000) print "aesprng.shuffle (1000/1000)", \ timeit((lambda c=c,L=L1000: c.shuffle(L)), 30) print "aesprng.shuffle (10/1000)", \ timeit((lambda c=c,L=L1000: c.shuffle(L,10)), 1000) lkey = Keyset("keymaterial foo bar baz").getLionessKeys("T") print "lioness E (1K)", timeit(( lambda lkey=lkey: lioness_encrypt(s1K, lkey)), 1000) print "lioness E (2K)", timeit(( lambda lkey=lkey: lioness_encrypt(s1K, lkey)), 1000) print "lioness E (4K)", timeit(( lambda lkey=lkey: lioness_encrypt(s4K, lkey)), 1000) print "lioness E (28K)", timeit(( lambda lkey=lkey: lioness_encrypt(s28K, lkey)), 100) print "lioness E (32K)", timeit(( lambda lkey=lkey: lioness_encrypt(s32K, lkey)), 100) print "lioness D (1K)", timeit(( lambda lkey=lkey: lioness_decrypt(s1K, lkey)), 1000) print "lioness D (2K)", timeit(( lambda lkey=lkey: lioness_decrypt(s1K, lkey)), 1000) print "lioness D (4K)", timeit(( lambda lkey=lkey: lioness_decrypt(s4K, lkey)), 1000) print "lioness D (28K)", timeit(( lambda lkey=lkey: lioness_decrypt(s28K, lkey)), 100) print "lioness D (32K)", timeit(( lambda lkey=lkey: lioness_decrypt(s32K, lkey)), 100) bkey = Keyset("keymaterial foo bar baz").getBearKeys("T") print "bear E (1K)", timeit(( lambda bkey=bkey: bear_encrypt(s1K, bkey)), 1000) print "bear E (2K)", timeit(( lambda bkey=bkey: bear_encrypt(s1K, bkey)), 1000) print "bear E (4K)", timeit(( lambda bkey=bkey: bear_encrypt(s4K, bkey)), 1000) print "bear E (28K)", timeit(( lambda bkey=bkey: bear_encrypt(s28K, bkey)), 100) print "bear E (32K)", timeit(( lambda bkey=bkey: bear_encrypt(s32K, bkey)), 100) print "bear D (1K)", timeit(( lambda bkey=bkey: bear_decrypt(s1K, bkey)), 1000) print "bear D (2K)", timeit(( lambda bkey=bkey: bear_decrypt(s1K, bkey)), 1000) print "bear D (4K)", timeit(( lambda bkey=bkey: bear_decrypt(s4K, bkey)), 1000) print "bear D (28K)", timeit(( lambda bkey=bkey: bear_decrypt(s28K, bkey)), 100) print "bear D (32K)", timeit(( lambda bkey=bkey: bear_decrypt(s32K, bkey)), 100)