diff options
author | Pieter Wuille <pieter@wuille.net> | 2023-07-06 20:40:20 -0400 |
---|---|---|
committer | Pieter Wuille <pieter@wuille.net> | 2023-07-26 17:00:22 -0400 |
commit | c91cedf281e5207fb5fd2ca81feec9760f7c2ed0 (patch) | |
tree | e15e37365ba600eacb06c7e147ce4aa18cd02fde /src | |
parent | af2b44c76e5de8ce880381e5535ead37ab0b3ba9 (diff) |
crypto: support split plaintext in ChaCha20Poly1305 Encrypt/Decrypt
Diffstat (limited to 'src')
-rw-r--r-- | src/crypto/chacha20poly1305.cpp | 28 | ||||
-rw-r--r-- | src/crypto/chacha20poly1305.h | 44 | ||||
-rw-r--r-- | src/test/crypto_tests.cpp | 85 |
3 files changed, 115 insertions, 42 deletions
diff --git a/src/crypto/chacha20poly1305.cpp b/src/crypto/chacha20poly1305.cpp index c3f8fe9e64..c936dd2265 100644 --- a/src/crypto/chacha20poly1305.cpp +++ b/src/crypto/chacha20poly1305.cpp @@ -73,31 +73,33 @@ void ComputeTag(ChaCha20& chacha20, Span<const std::byte> aad, Span<const std::b } // namespace -void AEADChaCha20Poly1305::Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> cipher) noexcept +void AEADChaCha20Poly1305::Encrypt(Span<const std::byte> plain1, Span<const std::byte> plain2, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> cipher) noexcept { - assert(cipher.size() == plain.size() + EXPANSION); + assert(cipher.size() == plain1.size() + plain2.size() + EXPANSION); // Encrypt using ChaCha20 (starting at block 1). m_chacha20.Seek64(nonce, 1); - m_chacha20.Crypt(UCharCast(plain.data()), UCharCast(cipher.data()), plain.size()); + m_chacha20.Crypt(UCharCast(plain1.data()), UCharCast(cipher.data()), plain1.size()); + m_chacha20.Crypt(UCharCast(plain2.data()), UCharCast(cipher.data() + plain1.size()), plain2.size()); // Seek to block 0, and compute tag using key drawn from there. m_chacha20.Seek64(nonce, 0); - ComputeTag(m_chacha20, aad, cipher.first(plain.size()), cipher.last(EXPANSION)); + ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), cipher.last(EXPANSION)); } -bool AEADChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> plain) noexcept +bool AEADChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> plain1, Span<std::byte> plain2) noexcept { - assert(cipher.size() == plain.size() + EXPANSION); + assert(cipher.size() == plain1.size() + plain2.size() + EXPANSION); // Verify tag (using key drawn from block 0). m_chacha20.Seek64(nonce, 0); std::byte expected_tag[EXPANSION]; - ComputeTag(m_chacha20, aad, cipher.first(plain.size()), expected_tag); - if (timingsafe_bcmp(UCharCast(expected_tag), UCharCast(cipher.data() + plain.size()), EXPANSION)) return false; + ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), expected_tag); + if (timingsafe_bcmp(UCharCast(expected_tag), UCharCast(cipher.data() + cipher.size() - EXPANSION), EXPANSION)) return false; // Decrypt (starting at block 1). - m_chacha20.Crypt(UCharCast(cipher.data()), UCharCast(plain.data()), plain.size()); + m_chacha20.Crypt(UCharCast(cipher.data()), UCharCast(plain1.data()), plain1.size()); + m_chacha20.Crypt(UCharCast(cipher.data() + plain1.size()), UCharCast(plain2.data()), plain2.size()); return true; } @@ -126,15 +128,15 @@ void FSChaCha20Poly1305::NextPacket() noexcept } } -void FSChaCha20Poly1305::Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Span<std::byte> cipher) noexcept +void FSChaCha20Poly1305::Encrypt(Span<const std::byte> plain1, Span<const std::byte> plain2, Span<const std::byte> aad, Span<std::byte> cipher) noexcept { - m_aead.Encrypt(plain, aad, {m_packet_counter, m_rekey_counter}, cipher); + m_aead.Encrypt(plain1, plain2, aad, {m_packet_counter, m_rekey_counter}, cipher); NextPacket(); } -bool FSChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain) noexcept +bool FSChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain1, Span<std::byte> plain2) noexcept { - bool ret = m_aead.Decrypt(cipher, aad, {m_packet_counter, m_rekey_counter}, plain); + bool ret = m_aead.Decrypt(cipher, aad, {m_packet_counter, m_rekey_counter}, plain1, plain2); NextPacket(); return ret; } diff --git a/src/crypto/chacha20poly1305.h b/src/crypto/chacha20poly1305.h index c4ec978df8..dc8fb1cc13 100644 --- a/src/crypto/chacha20poly1305.h +++ b/src/crypto/chacha20poly1305.h @@ -39,13 +39,31 @@ public: * * Requires cipher.size() = plain.size() + EXPANSION. */ - void Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> cipher) noexcept; + void Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> cipher) noexcept + { + Encrypt(plain, {}, aad, nonce, cipher); + } + + /** Encrypt a message (given split into plain1 + plain2) with a specified 96-bit nonce and aad. + * + * Requires cipher.size() = plain1.size() + plain2.size() + EXPANSION. + */ + void Encrypt(Span<const std::byte> plain1, Span<const std::byte> plain2, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> cipher) noexcept; /** Decrypt a message with a specified 96-bit nonce and aad. Returns true if valid. * * Requires cipher.size() = plain.size() + EXPANSION. */ - bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> plain) noexcept; + bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> plain) noexcept + { + return Decrypt(cipher, aad, nonce, plain, {}); + } + + /** Decrypt a message with a specified 96-bit nonce and aad and split the result. Returns true if valid. + * + * Requires cipher.size() = plain1.size() + plain2.size() + EXPANSION. + */ + bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> plain1, Span<std::byte> plain2) noexcept; /** Get a number of keystream bytes from the underlying stream cipher. * @@ -101,13 +119,31 @@ public: * * Requires cipher.size() = plain.size() + EXPANSION. */ - void Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Span<std::byte> cipher) noexcept; + void Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Span<std::byte> cipher) noexcept + { + Encrypt(plain, {}, aad, cipher); + } + + /** Encrypt a message (given split into plain1 + plain2) with a specified aad. + * + * Requires cipher.size() = plain.size() + EXPANSION. + */ + void Encrypt(Span<const std::byte> plain1, Span<const std::byte> plain2, Span<const std::byte> aad, Span<std::byte> cipher) noexcept; /** Decrypt a message with a specified aad. Returns true if valid. * * Requires cipher.size() = plain.size() + EXPANSION. */ - bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain) noexcept; + bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain) noexcept + { + return Decrypt(cipher, aad, plain, {}); + } + + /** Decrypt a message with a specified aad and split the result. Returns true if valid. + * + * Requires cipher.size() = plain1.size() + plain2.size() + EXPANSION. + */ + bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain1, Span<std::byte> plain2) noexcept; }; #endif // BITCOIN_CRYPTO_CHACHA20POLY1305_H diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 149a8cf7bf..6663c914a9 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -255,20 +255,37 @@ static void TestChaCha20Poly1305(const std::string& plain_hex, const std::string auto key = ParseHex<std::byte>(key_hex); auto expected_cipher = ParseHex<std::byte>(cipher_hex); - std::vector<std::byte> cipher(plain.size() + AEADChaCha20Poly1305::EXPANSION); - AEADChaCha20Poly1305 aead{key}; - aead.Encrypt(plain, aad, nonce, cipher); - BOOST_CHECK(cipher == expected_cipher); + for (int i = 0; i < 10; ++i) { + // During i=0, use single-plain Encrypt/Decrypt; others use a split at prefix. + size_t prefix = i ? InsecureRandRange(plain.size() + 1) : plain.size(); + // Encrypt. + std::vector<std::byte> cipher(plain.size() + AEADChaCha20Poly1305::EXPANSION); + AEADChaCha20Poly1305 aead{key}; + if (i == 0) { + aead.Encrypt(plain, aad, nonce, cipher); + } else { + aead.Encrypt(Span{plain}.first(prefix), Span{plain}.subspan(prefix), aad, nonce, cipher); + } + BOOST_CHECK(cipher == expected_cipher); - std::vector<std::byte> decipher(cipher.size() - AEADChaCha20Poly1305::EXPANSION); - bool ret = aead.Decrypt(cipher, aad, nonce, decipher); - BOOST_CHECK(ret); - BOOST_CHECK(decipher == plain); + // Decrypt. + std::vector<std::byte> decipher(cipher.size() - AEADChaCha20Poly1305::EXPANSION); + bool ret{false}; + if (i == 0) { + ret = aead.Decrypt(cipher, aad, nonce, decipher); + } else { + ret = aead.Decrypt(cipher, aad, nonce, Span{decipher}.first(prefix), Span{decipher}.subspan(prefix)); + } + BOOST_CHECK(ret); + BOOST_CHECK(decipher == plain); + } + // Test Keystream output. std::vector<std::byte> keystream(plain.size()); + AEADChaCha20Poly1305 aead{key}; aead.Keystream(nonce, keystream); for (size_t i = 0; i < plain.size(); ++i) { - BOOST_CHECK_EQUAL(plain[i] ^ keystream[i], cipher[i]); + BOOST_CHECK_EQUAL(plain[i] ^ keystream[i], expected_cipher[i]); } } @@ -280,25 +297,43 @@ static void TestFSChaCha20Poly1305(const std::string& plain_hex, const std::stri auto expected_cipher = ParseHex<std::byte>(cipher_hex); std::vector<std::byte> cipher(plain.size() + FSChaCha20Poly1305::EXPANSION); - FSChaCha20Poly1305 enc_aead{key, 224}; - for (uint64_t i = 0; i < msg_idx; ++i) { - std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}}; - enc_aead.Encrypt(Span{dummy_tag}.first(0), Span{dummy_tag}.first(0), dummy_tag); - } + for (int it = 0; it < 10; ++it) { + // During it==0 we use the single-plain Encrypt/Decrypt; others use a split at prefix. + size_t prefix = it ? InsecureRandRange(plain.size() + 1) : plain.size(); - enc_aead.Encrypt(plain, aad, cipher); - BOOST_CHECK(cipher == expected_cipher); + // Do msg_idx dummy encryptions to seek to the correct packet. + FSChaCha20Poly1305 enc_aead{key, 224}; + for (uint64_t i = 0; i < msg_idx; ++i) { + std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}}; + enc_aead.Encrypt(Span{dummy_tag}.first(0), Span{dummy_tag}.first(0), dummy_tag); + } - FSChaCha20Poly1305 dec_aead{key, 224}; - for (uint64_t i = 0; i < msg_idx; ++i) { - std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}}; - dec_aead.Decrypt(dummy_tag, Span{dummy_tag}.first(0), Span{dummy_tag}.first(0)); - } + // Invoke single-plain or plain1/plain2 Encrypt. + if (it == 0) { + enc_aead.Encrypt(plain, aad, cipher); + } else { + enc_aead.Encrypt(Span{plain}.first(prefix), Span{plain}.subspan(prefix), aad, cipher); + } + BOOST_CHECK(cipher == expected_cipher); - std::vector<std::byte> decipher(cipher.size() - AEADChaCha20Poly1305::EXPANSION); - bool ret = dec_aead.Decrypt(cipher, aad, decipher); - BOOST_CHECK(ret); - BOOST_CHECK(decipher == plain); + // Do msg_idx dummy decryptions to seek to the correct packet. + FSChaCha20Poly1305 dec_aead{key, 224}; + for (uint64_t i = 0; i < msg_idx; ++i) { + std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}}; + dec_aead.Decrypt(dummy_tag, Span{dummy_tag}.first(0), Span{dummy_tag}.first(0)); + } + + // Invoke single-plain or plain1/plain2 Decrypt. + std::vector<std::byte> decipher(cipher.size() - AEADChaCha20Poly1305::EXPANSION); + bool ret{false}; + if (it == 0) { + ret = dec_aead.Decrypt(cipher, aad, decipher); + } else { + ret = dec_aead.Decrypt(cipher, aad, Span{decipher}.first(prefix), Span{decipher}.subspan(prefix)); + } + BOOST_CHECK(ret); + BOOST_CHECK(decipher == plain); + } } static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &salt_hex, const std::string &info_hex, const std::string &okm_check_hex) { |