Branch data 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 : : 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 [ + + ]: 2966853 : 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 : 2103744 : void Seek(Nonce96 nonce, uint32_t block_counter) noexcept
105 : : {
106 : 2103744 : m_aligned.Seek(nonce, block_counter);
107 [ + - + - : 2103744 : 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(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 : 873 : 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
|