@@ -66,4 +66,40 @@ def _chacha20_block(self, counter: int) -> bytes:
6666 for _ in range (10 ):
6767 self ._double_round (working_state )
6868 final_state = (working_state + state ) % (2 ** 32 )
69- return struct .pack ('<16I' , * final_state .flatten ())
69+ return struct .pack ('<16I' , * final_state .flatten ())
70+
71+ def _apply_keystream (self , data : bytes ) -> bytes :
72+ """
73+ Applies the ChaCha20 keystream to the input data (plaintext or ciphertext)
74+ to perform encryption or decryption.
75+
76+ This method processes the input data in 64-byte blocks. For each block:
77+ - A 64-byte keystream is generated using the `_chacha20_block()` function.
78+ - Each byte of the input block is XORed with the corresponding keystream byte.
79+ - The XORed result is appended to the output.
80+
81+ The same function is used for both encryption and decryption because
82+ XORing the ciphertext with the same keystream returns the original plaintext.
83+
84+ Args:
85+ data (bytes): The input data to be encrypted or decrypted (plaintext or ciphertext).
86+
87+ Returns:
88+ bytes: The result of XORing the input data with the ChaCha20 keystream
89+ (ciphertext if plaintext was provided, plaintext if ciphertext was provided).
90+ """
91+ result = b""
92+ chunk_size = 64
93+ start = 0
94+ while start < len (data ):
95+ chunk = data [start :start + chunk_size ]
96+ start += chunk_size
97+ keystream = self ._chacha20_block (self .counter )
98+ self .counter += 1
99+ xor_block = []
100+ for idx in range (len (chunk )):
101+ input_byte = chunk [idx ]
102+ keystream_byte = keystream [idx ]
103+ xor_block .append (input_byte ^ keystream_byte )
104+ result += bytes (xor_block )
105+ return result
0 commit comments