Skip to content

Commit c4746f4

Browse files
Add test case ChaCha20 key reuse vulnerability
1 parent 9eddec4 commit c4746f4

File tree

1 file changed

+53
-0
lines changed

1 file changed

+53
-0
lines changed

crypto/tests/test_chacha20.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,56 @@ def test_encrypt_decrypt():
5959
decrypted = cipher.decrypt(ciphertext)
6060

6161
assert decrypted == plaintext, "Decryption failed. Plaintext does not match."
62+
63+
def test_key_reuse_simple():
64+
"""
65+
Test the vulnerability of key reuse in ChaCha20 encryption.
66+
67+
This test demonstrates the security flaw of reusing the same key and nonce
68+
for different plaintexts in stream ciphers. It exploits the property that
69+
XORing two ciphertexts from the same keystream cancels out the keystream,
70+
revealing the XOR of the plaintexts.
71+
72+
Encrypt two different plaintexts with the same key and nonce.
73+
XOR the resulting ciphertexts to remove the keystream, leaving only the XOR of plaintexts.
74+
XOR the result with the first plaintext to recover the second plaintext.
75+
Assert that the recovered plaintext matches the original second plaintext.
76+
77+
Expected Behavior:
78+
- If the ChaCha20 implementation is correct, reusing the same key and nonce
79+
will expose the XOR relationship between plaintexts.
80+
- The test should successfully recover the second plaintext using XOR operations.
81+
82+
Assertion:
83+
- Raises an AssertionError if the recovered plaintext does not match the
84+
original second plaintext, indicating a failure in the XOR recovery logic.
85+
86+
Output:
87+
- Prints the original second plaintext.
88+
- Prints the recovered plaintext (should be identical to the original).
89+
- Displays the XOR result (hexadecimal format) for inspection.
90+
91+
Security Note:
92+
- This test highlights why it is critical never to reuse the same key and nonce
93+
in stream ciphers like ChaCha20.
94+
"""
95+
96+
97+
cipher1 = ChaCha20(VALID_KEY, VALID_NONCE)
98+
cipher2 = ChaCha20(VALID_KEY, VALID_NONCE)
99+
100+
plaintext1 = b"Hello, this is message one!"
101+
plaintext2 = b"Hi there, this is message two!"
102+
103+
ciphertext1 = cipher1.encrypt(plaintext1)
104+
ciphertext2 = cipher2.encrypt(plaintext2)
105+
106+
xor_result = []
107+
for c1_byte, c2_byte in zip(ciphertext1, ciphertext2):
108+
xor_result.append(c1_byte ^ c2_byte)
109+
xor_bytes = bytes(xor_result)
110+
recovered = []
111+
for xor_byte, p1_byte in zip(xor_bytes, plaintext1):
112+
recovered.append(xor_byte ^ p1_byte)
113+
recovered_plaintext = bytes(recovered)
114+
assert recovered_plaintext == plaintext2, "Failed to recover second plaintext from XOR pattern"

0 commit comments

Comments
 (0)