public
Last active

RC4 encryption

  • Download Gist
rc4.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
from hashlib import sha1
from itertools import islice, imap
from operator import xor, eq
 
 
DROP_N = 1536
# drop 1536 bytes is adviced in RFC 4345
# http://tools.ietf.org/html/rfc4345
 
 
def _drop(it, n):
try:
islice(it, n, n).next()
except StopIteration:
pass
return it
 
 
def _key_sched(key):
S = range(256)
j = 0
key = map(ord, key)
for i in xrange(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
 
return S
 
 
def _key_stream(key):
S = _key_sched(key)
i = 0
j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) % 256
yield S[t]
 
 
class RC4(object):
def __init__(self, shared_key, iv, drop_n=DROP_N):
key = sha1(iv + shared_key).digest()
self.stream = _drop(_key_stream(key), drop_n)
 
def __call__(self, data):
bytes = imap(ord, data)
converted_bytes = imap(xor, bytes, self.stream)
return ''.join(imap(chr, converted_bytes))
 
 
def test_official_vectors():
# from http://tools.ietf.org/html/rfc6229
 
v = {'0102030405060708':
[(0, '97ab8a1bf0afb96132f2f67258da15a8'),
(1536, '8369e1a965610be887fbd0c79162aafb'),
(4096, '3fb46e2c8f0f068edce8cdcd7dfc5862')],
'0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20':
[(0, 'eaa6bd25880bf93d3f5d1e4ca2611d91'),
(1536, '3e34135c79db010200767651cf263073'),
(4096, 'f3e4c0a2e02d1d01f7f0a74618af2b48')],
'ebb46227c6cc8b37641910833222772a':
[(0, '720c94b63edf44e131d950ca211a5a30'),
(1536, '1175da6ee756de46a53e2b075660b770'),
(4096, '5bbeb4787d59e5373fdbea6c6f75c29b')],
'c109163908ebe51debb46227c6cc8b37641910833222772a':
[(0, '54b64e6b5a20b5e2ec84593dc7989da7'),
(1536, 'baefe6d9ceb651842260e0d1e05e3b90'),
(4096, 'e4dd2e98d6960fae0b43545456743391')]
}
 
try:
all = all
except NameError:
def all(iterable):
for element in iterable:
if not element:
return False
return True
 
for key, data in v.iteritems():
key = key.decode('hex')
for drop, expected in data:
stream = _drop(_key_stream(key), drop)
expected = [ord(c) for c in expected.decode('hex')]
assert all(imap(eq, stream, expected))
 
 
def test_simple():
plain = 'ABCD123'
cipher = RC4('shared_secret', 'iv')(plain)
assert plain == RC4('shared_secret', 'iv')(cipher)
 
 
if __name__ == '__main__':
test_official_vectors()
test_simple()

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.