| 591 | } |
| 592 | |
| 593 | static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aad_length, const std::string& hex_m, const std::string& hex_k1, const std::string& hex_k2, const std::string& hex_aad_keystream, const std::string& hex_encrypted_message, const std::string& hex_encrypted_message_seq_999) |
| 594 | { |
| 595 | // we need two sequence numbers, one for the payload cipher instance... |
| 596 | uint32_t seqnr_payload = 0; |
| 597 | // ... and one for the AAD (length) cipher instance |
| 598 | uint32_t seqnr_aad = 0; |
| 599 | // we need to keep track of the position in the AAD cipher instance |
| 600 | // keystream since we use the same 64byte output 21 times |
| 601 | // (21 times 3 bytes length < 64) |
| 602 | int aad_pos = 0; |
| 603 | |
| 604 | std::vector<unsigned char> aead_K_1 = ParseHex(hex_k1); |
| 605 | std::vector<unsigned char> aead_K_2 = ParseHex(hex_k2); |
| 606 | std::vector<unsigned char> plaintext_buf = ParseHex(hex_m); |
| 607 | std::vector<unsigned char> expected_aad_keystream = ParseHex(hex_aad_keystream); |
| 608 | std::vector<unsigned char> expected_ciphertext_and_mac = ParseHex(hex_encrypted_message); |
| 609 | std::vector<unsigned char> expected_ciphertext_and_mac_sequence999 = ParseHex(hex_encrypted_message_seq_999); |
| 610 | |
| 611 | std::vector<unsigned char> ciphertext_buf(plaintext_buf.size() + POLY1305_TAGLEN, 0); |
| 612 | std::vector<unsigned char> plaintext_buf_new(plaintext_buf.size(), 0); |
| 613 | std::vector<unsigned char> cmp_ctx_buffer(64); |
| 614 | uint32_t out_len = 0; |
| 615 | |
| 616 | // create the AEAD instance |
| 617 | ChaCha20Poly1305AEAD aead(aead_K_1.data(), aead_K_1.size(), aead_K_2.data(), aead_K_2.size()); |
| 618 | |
| 619 | // create a chacha20 instance to compare against |
| 620 | ChaCha20 cmp_ctx(aead_K_1.data(), 32); |
| 621 | |
| 622 | // encipher |
| 623 | bool res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true); |
| 624 | // make sure the operation succeeded if expected to succeed |
| 625 | BOOST_CHECK_EQUAL(res, must_succeed); |
| 626 | if (!res) return; |
| 627 | |
| 628 | // verify ciphertext & mac against the test vector |
| 629 | BOOST_CHECK_EQUAL(expected_ciphertext_and_mac.size(), ciphertext_buf.size()); |
| 630 | BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac.data(), ciphertext_buf.size()) == 0); |
| 631 | |
| 632 | // manually construct the AAD keystream |
| 633 | cmp_ctx.SetIV(seqnr_aad); |
| 634 | cmp_ctx.Seek(0); |
| 635 | cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64); |
| 636 | BOOST_CHECK(memcmp(expected_aad_keystream.data(), cmp_ctx_buffer.data(), expected_aad_keystream.size()) == 0); |
| 637 | // crypt the 3 length bytes and compare the length |
| 638 | uint32_t len_cmp = 0; |
| 639 | len_cmp = (ciphertext_buf[0] ^ cmp_ctx_buffer[aad_pos + 0]) | |
| 640 | (ciphertext_buf[1] ^ cmp_ctx_buffer[aad_pos + 1]) << 8 | |
| 641 | (ciphertext_buf[2] ^ cmp_ctx_buffer[aad_pos + 2]) << 16; |
| 642 | BOOST_CHECK_EQUAL(len_cmp, expected_aad_length); |
| 643 | |
| 644 | // encrypt / decrypt 1000 packets |
| 645 | for (size_t i = 0; i < 1000; ++i) { |
| 646 | res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true); |
| 647 | BOOST_CHECK(res); |
| 648 | BOOST_CHECK(aead.GetLength(&out_len, seqnr_aad, aad_pos, ciphertext_buf.data())); |
| 649 | BOOST_CHECK_EQUAL(out_len, expected_aad_length); |
| 650 | res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, plaintext_buf_new.data(), plaintext_buf_new.size(), ciphertext_buf.data(), ciphertext_buf.size(), false); |