mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
crypto: support split plaintext in ChaCha20Poly1305 Encrypt/Decrypt
This commit is contained in:
parent
af2b44c76e
commit
c91cedf281
3 changed files with 117 additions and 44 deletions
|
@ -73,31 +73,33 @@ void ComputeTag(ChaCha20& chacha20, Span<const std::byte> aad, Span<const std::b
|
||||||
|
|
||||||
} // namespace
|
} // 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).
|
// Encrypt using ChaCha20 (starting at block 1).
|
||||||
m_chacha20.Seek64(nonce, 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.
|
// Seek to block 0, and compute tag using key drawn from there.
|
||||||
m_chacha20.Seek64(nonce, 0);
|
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).
|
// Verify tag (using key drawn from block 0).
|
||||||
m_chacha20.Seek64(nonce, 0);
|
m_chacha20.Seek64(nonce, 0);
|
||||||
std::byte expected_tag[EXPANSION];
|
std::byte expected_tag[EXPANSION];
|
||||||
ComputeTag(m_chacha20, aad, cipher.first(plain.size()), expected_tag);
|
ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), expected_tag);
|
||||||
if (timingsafe_bcmp(UCharCast(expected_tag), UCharCast(cipher.data() + plain.size()), EXPANSION)) return false;
|
if (timingsafe_bcmp(UCharCast(expected_tag), UCharCast(cipher.data() + cipher.size() - EXPANSION), EXPANSION)) return false;
|
||||||
|
|
||||||
// Decrypt (starting at block 1).
|
// 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;
|
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();
|
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();
|
NextPacket();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,13 +39,31 @@ public:
|
||||||
*
|
*
|
||||||
* Requires cipher.size() = plain.size() + EXPANSION.
|
* 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.
|
/** Decrypt a message with a specified 96-bit nonce and aad. Returns true if valid.
|
||||||
*
|
*
|
||||||
* Requires cipher.size() = plain.size() + EXPANSION.
|
* 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.
|
/** Get a number of keystream bytes from the underlying stream cipher.
|
||||||
*
|
*
|
||||||
|
@ -101,13 +119,31 @@ public:
|
||||||
*
|
*
|
||||||
* Requires cipher.size() = plain.size() + EXPANSION.
|
* 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.
|
/** Decrypt a message with a specified aad. Returns true if valid.
|
||||||
*
|
*
|
||||||
* Requires cipher.size() = plain.size() + EXPANSION.
|
* 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
|
#endif // BITCOIN_CRYPTO_CHACHA20POLY1305_H
|
||||||
|
|
|
@ -255,20 +255,37 @@ static void TestChaCha20Poly1305(const std::string& plain_hex, const std::string
|
||||||
auto key = ParseHex<std::byte>(key_hex);
|
auto key = ParseHex<std::byte>(key_hex);
|
||||||
auto expected_cipher = ParseHex<std::byte>(cipher_hex);
|
auto expected_cipher = ParseHex<std::byte>(cipher_hex);
|
||||||
|
|
||||||
std::vector<std::byte> cipher(plain.size() + AEADChaCha20Poly1305::EXPANSION);
|
for (int i = 0; i < 10; ++i) {
|
||||||
AEADChaCha20Poly1305 aead{key};
|
// During i=0, use single-plain Encrypt/Decrypt; others use a split at prefix.
|
||||||
aead.Encrypt(plain, aad, nonce, cipher);
|
size_t prefix = i ? InsecureRandRange(plain.size() + 1) : plain.size();
|
||||||
BOOST_CHECK(cipher == expected_cipher);
|
// 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);
|
// Decrypt.
|
||||||
bool ret = aead.Decrypt(cipher, aad, nonce, decipher);
|
std::vector<std::byte> decipher(cipher.size() - AEADChaCha20Poly1305::EXPANSION);
|
||||||
BOOST_CHECK(ret);
|
bool ret{false};
|
||||||
BOOST_CHECK(decipher == plain);
|
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());
|
std::vector<std::byte> keystream(plain.size());
|
||||||
|
AEADChaCha20Poly1305 aead{key};
|
||||||
aead.Keystream(nonce, keystream);
|
aead.Keystream(nonce, keystream);
|
||||||
for (size_t i = 0; i < plain.size(); ++i) {
|
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);
|
auto expected_cipher = ParseHex<std::byte>(cipher_hex);
|
||||||
std::vector<std::byte> cipher(plain.size() + FSChaCha20Poly1305::EXPANSION);
|
std::vector<std::byte> cipher(plain.size() + FSChaCha20Poly1305::EXPANSION);
|
||||||
|
|
||||||
FSChaCha20Poly1305 enc_aead{key, 224};
|
for (int it = 0; it < 10; ++it) {
|
||||||
for (uint64_t i = 0; i < msg_idx; ++i) {
|
// During it==0 we use the single-plain Encrypt/Decrypt; others use a split at prefix.
|
||||||
std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};
|
size_t prefix = it ? InsecureRandRange(plain.size() + 1) : plain.size();
|
||||||
enc_aead.Encrypt(Span{dummy_tag}.first(0), Span{dummy_tag}.first(0), dummy_tag);
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
enc_aead.Encrypt(plain, aad, cipher);
|
|
||||||
BOOST_CHECK(cipher == expected_cipher);
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::byte> decipher(cipher.size() - AEADChaCha20Poly1305::EXPANSION);
|
|
||||||
bool ret = dec_aead.Decrypt(cipher, aad, decipher);
|
|
||||||
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) {
|
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) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue