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
158