chacha20.h
1 // Copyright (c) 2017-present The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #ifndef BITCOIN_CRYPTO_CHACHA20_H 6 #define BITCOIN_CRYPTO_CHACHA20_H 7 8 #include <array> 9 #include <cstddef> 10 #include <cstdint> 11 #include <span> 12 #include <utility> 13 14 // classes for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein 15 // https://cr.yp.to/chacha/chacha-20080128.pdf. 16 // 17 // The 128-bit input is here implemented as a 96-bit nonce and a 32-bit block 18 // counter, as in RFC8439 Section 2.3. When the 32-bit block counter overflows 19 // the first 32-bit part of the nonce is automatically incremented, making it 20 // conceptually compatible with variants that use a 64/64 split instead. 21 22 /** ChaCha20 cipher that only operates on multiples of 64 bytes. */ 23 class ChaCha20Aligned 24 { 25 private: 26 uint32_t input[12]; 27 28 public: 29 /** Expected key length in constructor and SetKey. */ 30 static constexpr unsigned KEYLEN{32}; 31 32 /** Block size (inputs/outputs to Keystream / Crypt should be multiples of this). */ 33 static constexpr unsigned BLOCKLEN{64}; 34 35 /** For safety, disallow initialization without key. */ 36 ChaCha20Aligned() noexcept = delete; 37 38 /** Initialize a cipher with specified 32-byte key. */ 39 ChaCha20Aligned(std::span<const std::byte> key) noexcept; 40 41 /** Destructor to clean up private memory. */ 42 ~ChaCha20Aligned(); 43 44 /** Set 32-byte key, and seek to nonce 0 and block position 0. */ 45 void SetKey(std::span<const std::byte> key) noexcept; 46 47 /** Type for 96-bit nonces used by the Set function below. 48 * 49 * The first field corresponds to the LE32-encoded first 4 bytes of the nonce, also referred 50 * to as the '32-bit fixed-common part' in Example 2.8.2 of RFC8439. 51 * 52 * The second field corresponds to the LE64-encoded last 8 bytes of the nonce. 53 * 54 */ 55 using Nonce96 = std::pair<uint32_t, uint64_t>; 56 57 /** Set the 96-bit nonce and 32-bit block counter. 58 * 59 * Block_counter selects a position to seek to (to byte BLOCKLEN*block_counter). After 256 GiB, 60 * the block counter overflows, and nonce.first is incremented. 61 */ 62 void Seek(Nonce96 nonce, uint32_t block_counter) noexcept; 63 64 /** outputs the keystream into out, whose length must be a multiple of BLOCKLEN. */ 65 void Keystream(std::span<std::byte> out) noexcept; 66 67 /** en/deciphers the message <input> and write the result into <output> 68 * 69 * The size of input and output must be equal, and be a multiple of BLOCKLEN. 70 */ 71 void Crypt(std::span<const std::byte> input, std::span<std::byte> output) noexcept; 72 }; 73 74 /** Unrestricted ChaCha20 cipher. */ 75 class ChaCha20 76 { 77 private: 78 ChaCha20Aligned m_aligned; 79 std::array<std::byte, ChaCha20Aligned::BLOCKLEN> m_buffer; 80 unsigned m_bufleft{0}; 81 82 public: 83 /** Expected key length in constructor and SetKey. */ 84 static constexpr unsigned KEYLEN = ChaCha20Aligned::KEYLEN; 85 86 /** For safety, disallow initialization without key. */ 87 ChaCha20() noexcept = delete; 88 89 /** Initialize a cipher with specified 32-byte key. */ 90 ChaCha20(std::span<const std::byte> key) noexcept : m_aligned(key) {} 91 92 /** Destructor to clean up private memory. */ 93 ~ChaCha20(); 94 95 /** Set 32-byte key, and seek to nonce 0 and block position 0. */ 96 void SetKey(std::span<const std::byte> key) noexcept; 97 98 /** 96-bit nonce type. */ 99 using Nonce96 = ChaCha20Aligned::Nonce96; 100 101 /** Set the 96-bit nonce and 32-bit block counter. See ChaCha20Aligned::Seek. */ 102 void Seek(Nonce96 nonce, uint32_t block_counter) noexcept 103 { 104 m_aligned.Seek(nonce, block_counter); 105 m_bufleft = 0; 106 } 107 108 /** en/deciphers the message <in_bytes> and write the result into <out_bytes> 109 * 110 * The size of in_bytes and out_bytes must be equal. 111 */ 112 void Crypt(std::span<const std::byte> in_bytes, std::span<std::byte> out_bytes) noexcept; 113 114 /** outputs the keystream to out. */ 115 void Keystream(std::span<std::byte> out) noexcept; 116 }; 117 118 /** Forward-secure ChaCha20 119 * 120 * This implements a stream cipher that automatically transitions to a new stream with a new key 121 * and new nonce after a predefined number of encryptions or decryptions. 122 * 123 * See BIP324 for details. 124 */ 125 class FSChaCha20 126 { 127 private: 128 /** Internal stream cipher. */ 129 ChaCha20 m_chacha20; 130 131 /** The number of encryptions/decryptions before a rekey happens. */ 132 const uint32_t m_rekey_interval; 133 134 /** The number of encryptions/decryptions since the last rekey. */ 135 uint32_t m_chunk_counter{0}; 136 137 /** The number of rekey operations that have happened. */ 138 uint64_t m_rekey_counter{0}; 139 140 public: 141 /** Length of keys expected by the constructor. */ 142 static constexpr unsigned KEYLEN = 32; 143 144 // No copy or move to protect the secret. 145 FSChaCha20(const FSChaCha20&) = delete; 146 FSChaCha20(FSChaCha20&&) = delete; 147 FSChaCha20& operator=(const FSChaCha20&) = delete; 148 FSChaCha20& operator=(FSChaCha20&&) = delete; 149 150 /** Construct an FSChaCha20 cipher that rekeys every rekey_interval Crypt() calls. */ 151 FSChaCha20(std::span<const std::byte> key, uint32_t rekey_interval) noexcept; 152 153 /** Encrypt or decrypt a chunk. */ 154 void Crypt(std::span<const std::byte> input, std::span<std::byte> output) noexcept; 155 }; 156 157 #endif // BITCOIN_CRYPTO_CHACHA20_H