Skip to content

Commit 8764d28

Browse files
committed
Add RC4 stream cipher implementation
1 parent 29afed0 commit 8764d28

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

ciphers/rc4.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""
2+
Implementation of RC4 (Rivest Cipher 4) stream cipher algorithm.
3+
4+
Reference:
5+
- https://en.wikipedia.org/wiki/RC4
6+
"""
7+
8+
def ksa(key: bytes) -> list[int]:
9+
"""
10+
Key Scheduling Algorithm (KSA) for RC4.
11+
12+
Args:
13+
key: The secret key as bytes.
14+
15+
Returns:
16+
A permutation (list) of 256 integers (S-box).
17+
18+
Example:
19+
>>> ksa(b'Key')[:5]
20+
[0, 1, 2, 3, 4]
21+
"""
22+
key_length = len(key)
23+
s_box = list(range(256))
24+
j = 0
25+
26+
for i in range(256):
27+
j = (j + s_box[i] + key[i % key_length]) % 256
28+
s_box[i], s_box[j] = s_box[j], s_box[i]
29+
30+
return s_box
31+
32+
33+
def prga(s_box: list[int], message_length: int) -> list[int]:
34+
"""
35+
Pseudo-Random Generation Algorithm (PRGA) for RC4.
36+
37+
Args:
38+
s_box: The S-box after KSA.
39+
message_length: Number of bytes to generate.
40+
41+
Returns:
42+
A keystream as a list of integers.
43+
44+
Example:
45+
>>> prga(list(range(256)), 5)
46+
[0, 1, 2, 3, 4]
47+
"""
48+
i = 0
49+
j = 0
50+
keystream = []
51+
52+
for _ in range(message_length):
53+
i = (i + 1) % 256
54+
j = (j + s_box[i]) % 256
55+
s_box[i], s_box[j] = s_box[j], s_box[i]
56+
k = s_box[(s_box[i] + s_box[j]) % 256]
57+
keystream.append(k)
58+
59+
return keystream
60+
61+
62+
def rc4(key: bytes, data: bytes) -> bytes:
63+
"""
64+
Encrypt or decrypt data using RC4 stream cipher.
65+
66+
Args:
67+
key: The secret key as bytes.
68+
data: The plaintext or ciphertext as bytes.
69+
70+
Returns:
71+
Encrypted or decrypted output as bytes.
72+
73+
Example:
74+
>>> ciphertext = rc4(b'Key', b'Plaintext')
75+
>>> rc4(b'Key', ciphertext)
76+
b'Plaintext'
77+
"""
78+
s_box = ksa(key)
79+
keystream = prga(s_box, len(data))
80+
output = bytes([
81+
data_byte ^ keystream_byte
82+
for data_byte, keystream_byte in zip(data, keystream)
83+
])
84+
return output
85+
86+
87+
if __name__ == "__main__":
88+
import argparse
89+
90+
parser = argparse.ArgumentParser(
91+
description="Encrypt or decrypt data using RC4 cipher."
92+
)
93+
parser.add_argument(
94+
"mode", choices=["encrypt", "decrypt"], help="Mode: encrypt or decrypt"
95+
)
96+
parser.add_argument(
97+
"key", type=str, help="Encryption/Decryption key"
98+
)
99+
parser.add_argument(
100+
"input", type=str, help="Input text"
101+
)
102+
103+
args = parser.parse_args()
104+
105+
key_bytes = args.key.encode('ascii')
106+
input_bytes = args.input.encode('ascii')
107+
108+
result_bytes = rc4(key_bytes, input_bytes)
109+
110+
if args.mode == "encrypt":
111+
print(result_bytes.hex())
112+
else: # decrypt mode
113+
try:
114+
# if user passed hex data, decode it
115+
input_bytes = bytes.fromhex(args.input)
116+
result_bytes = rc4(key_bytes, input_bytes)
117+
print(result_bytes.decode('ascii'))
118+
except ValueError:
119+
print("Error: Input must be valid hex string when decrypting.")

0 commit comments

Comments
 (0)