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