mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-03 17:54:19 +02:00
crypto: Implement RFC8439-compatible variant of ChaCha20
There are two variants of ChaCha20 in use. The original one uses a 64-bit nonce and a 64-bit block counter, while the one used in RFC8439 uses a 96-bit nonce and 32-bit block counter. This commit changes the interface to use the 96/32 split (but automatically incrementing the first 32-bit part of the nonce when the 32-bit block counter overflows, so to retain compatibility with >256 GiB output). Simultaneously, also merge the SetIV and Seek64 functions, as we almost always call both anyway. Co-authored-by: dhruv <856960+dhruv@users.noreply.github.com>
This commit is contained in:
@@ -47,16 +47,12 @@ ChaCha20Aligned::ChaCha20Aligned(const unsigned char* key32)
|
||||
SetKey32(key32);
|
||||
}
|
||||
|
||||
void ChaCha20Aligned::SetIV(uint64_t iv)
|
||||
void ChaCha20Aligned::Seek64(Nonce96 nonce, uint32_t block_counter)
|
||||
{
|
||||
input[10] = iv;
|
||||
input[11] = iv >> 32;
|
||||
}
|
||||
|
||||
void ChaCha20Aligned::Seek64(uint64_t pos)
|
||||
{
|
||||
input[8] = pos;
|
||||
input[9] = pos >> 32;
|
||||
input[8] = block_counter;
|
||||
input[9] = nonce.first;
|
||||
input[10] = nonce.second;
|
||||
input[11] = nonce.second >> 32;
|
||||
}
|
||||
|
||||
inline void ChaCha20Aligned::Keystream64(unsigned char* c, size_t blocks)
|
||||
|
||||
@@ -7,9 +7,15 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdint.h>
|
||||
#include <utility>
|
||||
|
||||
// classes for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein
|
||||
// https://cr.yp.to/chacha/chacha-20080128.pdf */
|
||||
// https://cr.yp.to/chacha/chacha-20080128.pdf.
|
||||
//
|
||||
// The 128-bit input is here implemented as a 96-bit nonce and a 32-bit block
|
||||
// counter, as in RFC8439 Section 2.3. When the 32-bit block counter overflows
|
||||
// the first 32-bit part of the nonce is automatically incremented, making it
|
||||
// conceptually compatible with variants that use a 64/64 split instead.
|
||||
|
||||
/** ChaCha20 cipher that only operates on multiples of 64 bytes. */
|
||||
class ChaCha20Aligned
|
||||
@@ -26,11 +32,22 @@ public:
|
||||
/** set 32-byte key. */
|
||||
void SetKey32(const unsigned char* key32);
|
||||
|
||||
/** set the 64-bit nonce. */
|
||||
void SetIV(uint64_t iv);
|
||||
/** Type for 96-bit nonces used by the Set function below.
|
||||
*
|
||||
* The first field corresponds to the LE32-encoded first 4 bytes of the nonce, also referred
|
||||
* to as the '32-bit fixed-common part' in Example 2.8.2 of RFC8439.
|
||||
*
|
||||
* The second field corresponds to the LE64-encoded last 8 bytes of the nonce.
|
||||
*
|
||||
*/
|
||||
using Nonce96 = std::pair<uint32_t, uint64_t>;
|
||||
|
||||
/** set the 64bit block counter (pos seeks to byte position 64*pos). */
|
||||
void Seek64(uint64_t pos);
|
||||
/** Set the 96-bit nonce and 32-bit block counter.
|
||||
*
|
||||
* Block_counter selects a position to seek to (to byte 64*block_counter). After 256 GiB, the
|
||||
* block counter overflows, and nonce.first is incremented.
|
||||
*/
|
||||
void Seek64(Nonce96 nonce, uint32_t block_counter);
|
||||
|
||||
/** outputs the keystream of size <64*blocks> into <c> */
|
||||
void Keystream64(unsigned char* c, size_t blocks);
|
||||
@@ -62,13 +79,13 @@ public:
|
||||
m_bufleft = 0;
|
||||
}
|
||||
|
||||
/** set the 64-bit nonce. */
|
||||
void SetIV(uint64_t iv) { m_aligned.SetIV(iv); }
|
||||
/** 96-bit nonce type. */
|
||||
using Nonce96 = ChaCha20Aligned::Nonce96;
|
||||
|
||||
/** set the 64bit block counter (pos seeks to byte position 64*pos). */
|
||||
void Seek64(uint64_t pos)
|
||||
/** Set the 96-bit nonce and 32-bit block counter. */
|
||||
void Seek64(Nonce96 nonce, uint32_t block_counter)
|
||||
{
|
||||
m_aligned.Seek64(pos);
|
||||
m_aligned.Seek64(nonce, block_counter);
|
||||
m_bufleft = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,12 +58,11 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int
|
||||
|
||||
unsigned char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
|
||||
memset(poly_key, 0, sizeof(poly_key));
|
||||
m_chacha_main.SetIV(seqnr_payload);
|
||||
|
||||
// block counter 0 for the poly1305 key
|
||||
// use lower 32bytes for the poly1305 key
|
||||
// (throws away 32 unused bytes (upper 32) from this ChaCha20 round)
|
||||
m_chacha_main.Seek64(0);
|
||||
m_chacha_main.Seek64({0, seqnr_payload}, 0);
|
||||
m_chacha_main.Crypt(poly_key, poly_key, sizeof(poly_key));
|
||||
|
||||
// if decrypting, verify the tag prior to decryption
|
||||
@@ -85,8 +84,7 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int
|
||||
// calculate and cache the next 64byte keystream block if requested sequence number is not yet the cache
|
||||
if (m_cached_aad_seqnr != seqnr_aad) {
|
||||
m_cached_aad_seqnr = seqnr_aad;
|
||||
m_chacha_header.SetIV(seqnr_aad);
|
||||
m_chacha_header.Seek64(0);
|
||||
m_chacha_header.Seek64({0, seqnr_aad}, 0);
|
||||
m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT);
|
||||
}
|
||||
// crypt the AAD (3 bytes message length) with given position in AAD cipher instance keystream
|
||||
@@ -95,7 +93,7 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int
|
||||
dest[2] = src[2] ^ m_aad_keystream_buffer[aad_pos + 2];
|
||||
|
||||
// Set the playload ChaCha instance block counter to 1 and crypt the payload
|
||||
m_chacha_main.Seek64(1);
|
||||
m_chacha_main.Seek64({0, seqnr_payload}, 1);
|
||||
m_chacha_main.Crypt(src + CHACHA20_POLY1305_AEAD_AAD_LEN, dest + CHACHA20_POLY1305_AEAD_AAD_LEN, src_len - CHACHA20_POLY1305_AEAD_AAD_LEN);
|
||||
|
||||
// If encrypting, calculate and append tag
|
||||
@@ -117,8 +115,7 @@ bool ChaCha20Poly1305AEAD::GetLength(uint32_t* len24_out, uint64_t seqnr_aad, in
|
||||
if (m_cached_aad_seqnr != seqnr_aad) {
|
||||
// we need to calculate the 64 keystream bytes since we reached a new aad sequence number
|
||||
m_cached_aad_seqnr = seqnr_aad;
|
||||
m_chacha_header.SetIV(seqnr_aad); // use LE for the nonce
|
||||
m_chacha_header.Seek64(0); // block counter 0
|
||||
m_chacha_header.Seek64({0, seqnr_aad}, 0); // use LE for the nonce
|
||||
m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); // write keystream to the cache
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user