Skip to content

Commit af62a14

Browse files
authored
Clean up rc4_cipher.py by removing comments
Remove detailed docstring and example usage from rc4_cipher.py for brevity.
1 parent 37c1d83 commit af62a14

1 file changed

Lines changed: 0 additions & 179 deletions

File tree

ciphers/rc4_cipher.py

Lines changed: 0 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,180 +1 @@
1-
"""
2-
RC4 (Rivest Cipher 4) Stream Cipher
31

4-
RC4 is a symmetric stream cipher designed by Ron Rivest in 1987. It was widely
5-
used in protocols such as SSL/TLS and WEP before being deprecated due to
6-
statistical biases in its keystream. Understanding RC4 remains important for
7-
security education, particularly for studying why stream cipher design matters.
8-
9-
The algorithm has two phases:
10-
1. Key Scheduling Algorithm (KSA): Initialises a 256-byte permutation using
11-
the key.
12-
2. Pseudo-Random Generation Algorithm (PRGA): Produces keystream bytes by
13-
further permuting the state array.
14-
15-
Encryption and decryption are identical: XOR the keystream with the plaintext
16-
to encrypt, or XOR with the ciphertext to decrypt.
17-
18-
Reference:
19-
https://en.wikipedia.org/wiki/RC4
20-
21-
Security note:
22-
RC4 is cryptographically broken and must NOT be used in production systems.
23-
This implementation is provided for educational purposes only.
24-
"""
25-
26-
from __future__ import annotations
27-
28-
29-
def key_scheduling(key: list[int]) -> list[int]:
30-
"""
31-
Perform the Key Scheduling Algorithm (KSA).
32-
33-
Initialises a 256-byte identity permutation and scrambles it using the
34-
provided key bytes.
35-
36-
Args:
37-
key: A list of integers (0-255) representing the key bytes.
38-
39-
Returns:
40-
A 256-element permutation list (the initial state array S).
41-
42-
>>> key_scheduling([1, 2, 3])
43-
... # doctest: +ELLIPSIS
44-
[...]
45-
46-
>>> len(key_scheduling([65, 66, 67]))
47-
256
48-
49-
>>> key_scheduling([0]) == list(range(256))
50-
False
51-
"""
52-
key_length = len(key)
53-
# Initialise the state array as the identity permutation
54-
state = list(range(256))
55-
j = 0
56-
for i in range(256):
57-
j = (j + state[i] + key[i % key_length]) % 256
58-
# Swap state[i] and state[j]
59-
state[i], state[j] = state[j], state[i]
60-
return state
61-
62-
63-
def pseudo_random_generation(state: list[int], length: int) -> list[int]:
64-
"""
65-
Perform the Pseudo-Random Generation Algorithm (PRGA).
66-
67-
Generates a keystream of the requested length from the state array
68-
produced by the KSA.
69-
70-
Args:
71-
state: A 256-element permutation list from key_scheduling().
72-
length: The number of keystream bytes to generate.
73-
74-
Returns:
75-
A list of keystream bytes (integers 0-255).
76-
77-
>>> state = list(range(256))
78-
>>> keystream = pseudo_random_generation(state, 5)
79-
>>> len(keystream)
80-
5
81-
>>> all(0 <= b <= 255 for b in keystream)
82-
True
83-
"""
84-
i = 0
85-
j = 0
86-
keystream = []
87-
for _ in range(length):
88-
i = (i + 1) % 256
89-
j = (j + state[i]) % 256
90-
# Swap state[i] and state[j]
91-
state[i], state[j] = state[j], state[i]
92-
keystream.append(state[(state[i] + state[j]) % 256])
93-
return keystream
94-
95-
96-
def encrypt(plaintext: str, key: str) -> list[int]:
97-
"""
98-
Encrypt a plaintext string using RC4 with the given key.
99-
100-
Converts the plaintext and key to byte lists, runs KSA and PRGA, then
101-
XORs the plaintext bytes with the keystream to produce ciphertext bytes.
102-
103-
Args:
104-
plaintext: The message to encrypt (ASCII string).
105-
key: The encryption key (ASCII string, 1-256 characters).
106-
107-
Returns:
108-
A list of integers representing the ciphertext bytes.
109-
110-
Raises:
111-
ValueError: If the key is empty.
112-
113-
>>> encrypt("Hello", "secret")
114-
[165, 83, 190, 112, 237]
115-
116-
>>> encrypt("", "key")
117-
[]
118-
119-
>>> encrypt("Attack at dawn", "Key")
120-
[170, 235, 3, 224, 212, 95, 234, 19, 211, 57, 46, 73, 16, 216]
121-
"""
122-
if not key:
123-
raise ValueError("Key must not be empty.")
124-
key_bytes = [ord(c) for c in key]
125-
plaintext_bytes = [ord(c) for c in plaintext]
126-
state = key_scheduling(key_bytes)
127-
keystream = pseudo_random_generation(state, len(plaintext_bytes))
128-
return [p ^ k for p, k in zip(plaintext_bytes, keystream)]
129-
130-
131-
def decrypt(ciphertext: list[int], key: str) -> str:
132-
"""
133-
Decrypt RC4 ciphertext bytes back to a plaintext string.
134-
135-
RC4 decryption is identical to encryption: generate the same keystream
136-
and XOR it with the ciphertext bytes.
137-
138-
Args:
139-
ciphertext: A list of integers (ciphertext bytes) from encrypt().
140-
key: The same key used during encryption.
141-
142-
Returns:
143-
The decrypted plaintext as a string.
144-
145-
Raises:
146-
ValueError: If the key is empty.
147-
148-
>>> decrypt([165, 83, 190, 112, 237], "secret")
149-
'Hello'
150-
151-
>>> decrypt([], "key")
152-
''
153-
154-
>>> decrypt([170, 235, 3, 224, 212, 95, 234, 19, 211, 57, 46, 73, 16, 216], "Key")
155-
'Attack at dawn'
156-
"""
157-
if not key:
158-
raise ValueError("Key must not be empty.")
159-
key_bytes = [ord(c) for c in key]
160-
state = key_scheduling(key_bytes)
161-
keystream = pseudo_random_generation(state, len(ciphertext))
162-
return "".join(chr(c ^ k) for c, k in zip(ciphertext, keystream))
163-
164-
165-
if __name__ == "__main__":
166-
import doctest
167-
168-
doctest.testmod()
169-
170-
# Example usage
171-
message = "Hello, World!"
172-
secret_key = "mysecretkey"
173-
174-
print(f"Original : {message}")
175-
encrypted = encrypt(message, secret_key)
176-
print(f"Encrypted: {encrypted}")
177-
decrypted = decrypt(encrypted, secret_key)
178-
print(f"Decrypted: {decrypted}")
179-
assert decrypted == message, "Decryption failed — output does not match original."
180-
print("Encrypt -> Decrypt round-trip successful.")

0 commit comments

Comments
 (0)