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 <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(std::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(std::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(std::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(std::span<const std::byte> input, std::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 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 ChaCha20(std::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(std::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 void Seek(Nonce96 nonce, uint32_t block_counter) noexcept 105 { 106 m_aligned.Seek(nonce, block_counter); 107 m_bufleft = 0; 108 } 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(std::span<const std::byte> in_bytes, std::span<std::byte> out_bytes) noexcept; 115 116 /** outputs the keystream to out. */ 117 void Keystream(std::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(std::span<const std::byte> key, uint32_t rekey_interval) noexcept; 154 155 /** Encrypt or decrypt a chunk. */ 156 void Crypt(std::span<const std::byte> input, std::span<std::byte> output) noexcept; 157 }; 158 159 #endif // BITCOIN_CRYPTO_CHACHA20_H