@@ -167,6 +167,57 @@ 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, const std::string& hex_rekey_salt, 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 salt_vec = ParseHex (hex_rekey_salt);
178
+ BOOST_CHECK_EQUAL (FSCHACHA20_REKEY_SALT_LEN, salt_vec.size ());
179
+ std::array<std::byte, FSCHACHA20_REKEY_SALT_LEN> rekey_salt;
180
+ memcpy (rekey_salt.data (), salt_vec.data (), FSCHACHA20_REKEY_SALT_LEN);
181
+
182
+ auto plaintext = ParseHex (hex_plaintext);
183
+
184
+ auto fsc20 = FSChaCha20{key, rekey_salt, rekey_interval};
185
+ auto c20 = ChaCha20{reinterpret_cast <const unsigned char *>(key.data ())};
186
+
187
+ std::vector<std::byte> fsc20_output;
188
+ fsc20_output.resize (plaintext.size ());
189
+
190
+ std::vector<unsigned char > c20_output;
191
+ c20_output.resize (plaintext.size ());
192
+
193
+ for (size_t i = 0 ; i < rekey_interval; i++) {
194
+ fsc20.Crypt (MakeByteSpan (plaintext), fsc20_output);
195
+ c20.Crypt (plaintext.data (), c20_output.data (), plaintext.size ());
196
+ BOOST_CHECK_EQUAL (0 , memcmp (c20_output.data (), fsc20_output.data (), plaintext.size ()));
197
+ }
198
+
199
+ // At the rotation interval, the outputs will no longer match
200
+ fsc20.Crypt (MakeByteSpan (plaintext), fsc20_output);
201
+ c20.Crypt (plaintext.data (), c20_output.data (), plaintext.size ());
202
+ BOOST_CHECK (memcmp (c20_output.data (), fsc20_output.data (), plaintext.size ()) != 0 );
203
+
204
+ unsigned char new_key[FSCHACHA20_KEYLEN];
205
+ auto hasher = CSHA256 ().Write (UCharCast (rekey_salt.data ()), rekey_salt.size ());
206
+ hasher.Write (UCharCast (key.data ()), key.size ()).Finalize (new_key);
207
+ c20.SetKey32 (reinterpret_cast <unsigned char *>(new_key));
208
+
209
+ std::array<std::byte, 12 > new_nonce;
210
+ WriteLE32 (reinterpret_cast <unsigned char *>(new_nonce.data ()), 0 );
211
+ WriteLE64 (reinterpret_cast <unsigned char *>(new_nonce.data ()) + 4 , 1 );
212
+ c20.SetRFC8439Nonce (new_nonce);
213
+
214
+ // Outputs should match again after simulating key rotation
215
+ c20.Crypt (plaintext.data (), c20_output.data (), plaintext.size ());
216
+ BOOST_CHECK_EQUAL (0 , memcmp (c20_output.data (), fsc20_output.data (), plaintext.size ()));
217
+
218
+ BOOST_CHECK_EQUAL (HexStr (fsc20_output), ciphertext_after_rotation);
219
+ }
220
+
170
221
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
222
{
172
223
auto key = ParseHex (hex_key);
@@ -658,6 +709,18 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector)
658
709
rfc8439_nonce2, 0 , " ecfa254f845f647473d3cb140da9e87606cb33066c447b87bc2666dde3fbb739" , " " , " " );
659
710
TestChaCha20RFC8439 (" 1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0" ,
660
711
rfc8439_nonce2, 0 , " 965e3bc6f9ec7ed9560808f4d229f94b137ff275ca9b3fcbdd59deaad23310ae" , " " , " " );
712
+
713
+ // Forward secure ChaCha20
714
+ TestFSChaCha20 (" 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ,
715
+ " 0000000000000000000000000000000000000000000000000000000000000000" ,
716
+ " 0000000000000000000000000000000000000000000000" , 256 ,
717
+ " 4cf63894c8507adffa163a742db5fdcc9a861187b1c94a5a4c002d1bb7f2223c" );
718
+ TestFSChaCha20 (" 01" , " 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ,
719
+ " 0000000000000000000000000000000000000000000000" , 5 , " e0" );
720
+ TestFSChaCha20 (" e93fdb5c762804b9a706816aca31e35b11d2aa3080108ef46a5b1f1508819c0a" ,
721
+ " 8ec4c3ccdaea336bdeb245636970be01266509b33f3d2642504eaf412206207a" ,
722
+ " 8bb571662db12d38ee4e2630d4434f6f626cb0e6007e3c" , 4096 ,
723
+ " 30a36b4833331bf83bc16fcff408c771044e239b80472edd2e89ba9eb1845f34" );
661
724
}
662
725
663
726
BOOST_AUTO_TEST_CASE (chacha20_midblock)
0 commit comments