@@ -167,6 +167,52 @@ static void TestChaCha20(const std::string &hex_message, const std::string &hexk
167
167
}
168
168
}
169
169
170
+ static void TestFSChaCha20 (const std::string& hex_plaintext, const std::string& hexkey, size_t rekey_interval, const std::string& ciphertext_after_rotation)
171
+ {
172
+ auto key_vec = ParseHex (hexkey);
173
+ BOOST_CHECK_EQUAL (FSCHACHA20_KEYLEN, key_vec.size ());
174
+ std::array<std::byte, FSCHACHA20_KEYLEN> key;
175
+ memcpy (key.data (), key_vec.data (), FSCHACHA20_KEYLEN);
176
+
177
+ auto plaintext = ParseHex (hex_plaintext);
178
+
179
+ auto fsc20 = FSChaCha20{key, rekey_interval};
180
+ auto c20 = ChaCha20{reinterpret_cast <const unsigned char *>(key.data ())};
181
+
182
+ std::vector<std::byte> fsc20_output;
183
+ fsc20_output.resize (plaintext.size ());
184
+
185
+ std::vector<unsigned char > c20_output;
186
+ c20_output.resize (plaintext.size ());
187
+
188
+ for (size_t i = 0 ; i < rekey_interval; i++) {
189
+ fsc20.Crypt (MakeByteSpan (plaintext), fsc20_output);
190
+ c20.Crypt (plaintext.data (), c20_output.data (), plaintext.size ());
191
+ BOOST_CHECK_EQUAL (0 , memcmp (c20_output.data (), fsc20_output.data (), plaintext.size ()));
192
+ }
193
+
194
+ // At the rotation interval, the outputs will no longer match
195
+ fsc20.Crypt (MakeByteSpan (plaintext), fsc20_output);
196
+ auto c20_copy = c20;
197
+ c20.Crypt (plaintext.data (), c20_output.data (), plaintext.size ());
198
+ BOOST_CHECK (memcmp (c20_output.data (), fsc20_output.data (), plaintext.size ()) != 0 );
199
+
200
+ unsigned char new_key[FSCHACHA20_KEYLEN];
201
+ c20_copy.Keystream (new_key, FSCHACHA20_KEYLEN);
202
+ c20.SetKey32 (new_key);
203
+
204
+ std::array<std::byte, 12 > new_nonce;
205
+ WriteLE32 (reinterpret_cast <unsigned char *>(new_nonce.data ()), 0 );
206
+ WriteLE64 (reinterpret_cast <unsigned char *>(new_nonce.data ()) + 4 , 1 );
207
+ c20.SetRFC8439Nonce (new_nonce);
208
+
209
+ // Outputs should match again after simulating key rotation
210
+ c20.Crypt (plaintext.data (), c20_output.data (), plaintext.size ());
211
+ BOOST_CHECK_EQUAL (0 , memcmp (c20_output.data (), fsc20_output.data (), plaintext.size ()));
212
+
213
+ BOOST_CHECK_EQUAL (HexStr (fsc20_output), ciphertext_after_rotation);
214
+ }
215
+
170
216
static void TestChaCha20RFC8439 (const std::string& hex_key, const std::array<std::byte, 12 >& nonce, uint32_t seek, const std::string& hex_expected_keystream, const std::string& hex_input, const std::string& hex_expected_output)
171
217
{
172
218
auto key = ParseHex (hex_key);
@@ -658,6 +704,20 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector)
658
704
rfc8439_nonce2, 0 , " ecfa254f845f647473d3cb140da9e87606cb33066c447b87bc2666dde3fbb739" , " " , " " );
659
705
TestChaCha20RFC8439 (" 1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0" ,
660
706
rfc8439_nonce2, 0 , " 965e3bc6f9ec7ed9560808f4d229f94b137ff275ca9b3fcbdd59deaad23310ae" , " " , " " );
707
+
708
+ // Forward secure ChaCha20
709
+ TestFSChaCha20 (" 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ,
710
+ " 0000000000000000000000000000000000000000000000000000000000000000" ,
711
+ 256 ,
712
+ " a93df4ef03011f3db95f60d996e1785df5de38fc39bfcb663a47bb5561928349" );
713
+ TestFSChaCha20 (" 01" ,
714
+ " 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ,
715
+ 5 ,
716
+ " ea" );
717
+ TestFSChaCha20 (" e93fdb5c762804b9a706816aca31e35b11d2aa3080108ef46a5b1f1508819c0a" ,
718
+ " 8ec4c3ccdaea336bdeb245636970be01266509b33f3d2642504eaf412206207a" ,
719
+ 4096 ,
720
+ " 8bfaa4eacff308fdb4a94a5ff25bd9d0c1f84b77f81239f67ff39d6e1ac280c9" );
661
721
}
662
722
663
723
BOOST_AUTO_TEST_CASE (chacha20_midblock)
0 commit comments