In [1]:
get_ipython().ast_node_interactivity = 'all'

RC4, also known as ARC4 or ARCFOUR, is a stream cipher.

In [2]:
from collections import namedtuple

class RC4:
    def __init__(self):
        self.S = list(range(256))
        self.i = 0
        self.j = 0
In [3]:
RC4()
Out [3]:
<__main__.rc4 at>
In [4]:
class RC4(RC4):
    def init_key(self, key, rounds=256):
        for i in range(256):
            self.S[i] = i
        
        j = 0
        for i in range(rounds):
            i &= 0xFF
            j = (j + self.S[i] + key[i % len(key)]) & 0xFF
            x = self.S[i]
            self.S[i] = self.S[j]
            self.S[j] = x
In [5]:
r = RC4()
r.init_key(b"testkey")
In [6]:
class RC4(RC4):
    def get_byte(self):
        self.i = (self.i + 1) & 0xFF
        self.j = (self.j + self.S[self.i]) & 0xFF
        
        x = self.S[self.i]
        self.S[self.i] = self.S[self.j]
        self.S[self.j] = x
        
        return self.S[(self.S[self.i] + self.S[self.j]) & 0xFF]
In [7]:
r = RC4()
r.init_key(b"testkey", rounds=2**16)
[r.get_byte() for _ in range(5)]
Out [7]:
[63, 174, 37, 137, 119]
In [8]:
class RC4(RC4):
    def get_buf(self, size):
        buf = bytearray(size)
        for i in range(size):
            buf[i] = self.get_byte()
        return buf
In [9]:
def check_output(key, expected):
    r = RC4()
    r.init_key(key.encode('ascii'))
    
    size = int(len(expected) / 2)
    return r.get_buf(size).hex().upper() == expected.upper()

check_output("Key", "EB9F7781B734CA72A719")
check_output("Wiki", "6044DB6D41B7")
check_output("Secret", "04D46B053CA87B59")
Out [9]:
True
Out [9]:
True
Out [9]:
True
In []: