From 8764d282132dacb2713584f62d2f56885dd8727d Mon Sep 17 00:00:00 2001 From: QuantumSoham Date: Sun, 27 Apr 2025 15:33:58 +0530 Subject: [PATCH 1/3] Add RC4 stream cipher implementation --- ciphers/rc4.py | 119 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 ciphers/rc4.py diff --git a/ciphers/rc4.py b/ciphers/rc4.py new file mode 100644 index 000000000000..d4a19a5936b3 --- /dev/null +++ b/ciphers/rc4.py @@ -0,0 +1,119 @@ +""" +Implementation of RC4 (Rivest Cipher 4) stream cipher algorithm. + +Reference: +- https://en.wikipedia.org/wiki/RC4 +""" + +def ksa(key: bytes) -> list[int]: + """ + Key Scheduling Algorithm (KSA) for RC4. + + Args: + key: The secret key as bytes. + + Returns: + A permutation (list) of 256 integers (S-box). + + Example: + >>> ksa(b'Key')[:5] + [0, 1, 2, 3, 4] + """ + key_length = len(key) + s_box = list(range(256)) + j = 0 + + for i in range(256): + j = (j + s_box[i] + key[i % key_length]) % 256 + s_box[i], s_box[j] = s_box[j], s_box[i] + + return s_box + + +def prga(s_box: list[int], message_length: int) -> list[int]: + """ + Pseudo-Random Generation Algorithm (PRGA) for RC4. + + Args: + s_box: The S-box after KSA. + message_length: Number of bytes to generate. + + Returns: + A keystream as a list of integers. + + Example: + >>> prga(list(range(256)), 5) + [0, 1, 2, 3, 4] + """ + i = 0 + j = 0 + keystream = [] + + for _ in range(message_length): + i = (i + 1) % 256 + j = (j + s_box[i]) % 256 + s_box[i], s_box[j] = s_box[j], s_box[i] + k = s_box[(s_box[i] + s_box[j]) % 256] + keystream.append(k) + + return keystream + + +def rc4(key: bytes, data: bytes) -> bytes: + """ + Encrypt or decrypt data using RC4 stream cipher. + + Args: + key: The secret key as bytes. + data: The plaintext or ciphertext as bytes. + + Returns: + Encrypted or decrypted output as bytes. + + Example: + >>> ciphertext = rc4(b'Key', b'Plaintext') + >>> rc4(b'Key', ciphertext) + b'Plaintext' + """ + s_box = ksa(key) + keystream = prga(s_box, len(data)) + output = bytes([ + data_byte ^ keystream_byte + for data_byte, keystream_byte in zip(data, keystream) + ]) + return output + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description="Encrypt or decrypt data using RC4 cipher." + ) + parser.add_argument( + "mode", choices=["encrypt", "decrypt"], help="Mode: encrypt or decrypt" + ) + parser.add_argument( + "key", type=str, help="Encryption/Decryption key" + ) + parser.add_argument( + "input", type=str, help="Input text" + ) + + args = parser.parse_args() + + key_bytes = args.key.encode('ascii') + input_bytes = args.input.encode('ascii') + + result_bytes = rc4(key_bytes, input_bytes) + + if args.mode == "encrypt": + print(result_bytes.hex()) + else: # decrypt mode + try: + # if user passed hex data, decode it + input_bytes = bytes.fromhex(args.input) + result_bytes = rc4(key_bytes, input_bytes) + print(result_bytes.decode('ascii')) + except ValueError: + print("Error: Input must be valid hex string when decrypting.") From 559aae6a87d46f1ad12f0930b6419452f9a9af42 Mon Sep 17 00:00:00 2001 From: QuantumSoham Date: Sun, 27 Apr 2025 10:06:27 +0000 Subject: [PATCH 2/3] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index fa731e32ff23..d7a5659e91cf 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -113,6 +113,7 @@ * [Porta Cipher](ciphers/porta_cipher.py) * [Rabin Miller](ciphers/rabin_miller.py) * [Rail Fence Cipher](ciphers/rail_fence_cipher.py) + * [Rc4](ciphers/rc4.py) * [Rot13](ciphers/rot13.py) * [Rsa Cipher](ciphers/rsa_cipher.py) * [Rsa Factorization](ciphers/rsa_factorization.py) From 81b308fa6f03da0d91a859570cd6f5921642f304 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 27 Apr 2025 10:10:03 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/rc4.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ciphers/rc4.py b/ciphers/rc4.py index d4a19a5936b3..463708fff9e9 100644 --- a/ciphers/rc4.py +++ b/ciphers/rc4.py @@ -5,6 +5,7 @@ - https://en.wikipedia.org/wiki/RC4 """ + def ksa(key: bytes) -> list[int]: """ Key Scheduling Algorithm (KSA) for RC4. @@ -77,10 +78,12 @@ def rc4(key: bytes, data: bytes) -> bytes: """ s_box = ksa(key) keystream = prga(s_box, len(data)) - output = bytes([ - data_byte ^ keystream_byte - for data_byte, keystream_byte in zip(data, keystream) - ]) + output = bytes( + [ + data_byte ^ keystream_byte + for data_byte, keystream_byte in zip(data, keystream) + ] + ) return output @@ -93,17 +96,13 @@ def rc4(key: bytes, data: bytes) -> bytes: parser.add_argument( "mode", choices=["encrypt", "decrypt"], help="Mode: encrypt or decrypt" ) - parser.add_argument( - "key", type=str, help="Encryption/Decryption key" - ) - parser.add_argument( - "input", type=str, help="Input text" - ) + parser.add_argument("key", type=str, help="Encryption/Decryption key") + parser.add_argument("input", type=str, help="Input text") args = parser.parse_args() - key_bytes = args.key.encode('ascii') - input_bytes = args.input.encode('ascii') + key_bytes = args.key.encode("ascii") + input_bytes = args.input.encode("ascii") result_bytes = rc4(key_bytes, input_bytes) @@ -114,6 +113,6 @@ def rc4(key: bytes, data: bytes) -> bytes: # if user passed hex data, decode it input_bytes = bytes.fromhex(args.input) result_bytes = rc4(key_bytes, input_bytes) - print(result_bytes.decode('ascii')) + print(result_bytes.decode("ascii")) except ValueError: print("Error: Input must be valid hex string when decrypting.")