Branch data Line data Source code
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 [ - + ]: 2854603 : 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 : 2044608 : void Seek(Nonce96 nonce, uint32_t block_counter) noexcept
103 : : {
104 : 2050631 : m_aligned.Seek(nonce, block_counter);
105 [ - + - + : 2050631 : 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 : 827 : 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
|