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