Line data Source code
1 : // Copyright (c) 2023 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_CHACHA20POLY1305_H
6 : #define BITCOIN_CRYPTO_CHACHA20POLY1305_H
7 :
8 : #include <cstddef>
9 : #include <stdint.h>
10 :
11 : #include <crypto/chacha20.h>
12 : #include <crypto/poly1305.h>
13 : #include <span.h>
14 :
15 : /** The AEAD_CHACHA20_POLY1305 authenticated encryption algorithm from RFC8439 section 2.8. */
16 1784 : class AEADChaCha20Poly1305
17 : {
18 : /** Internal stream cipher. */
19 : ChaCha20 m_chacha20;
20 :
21 : public:
22 : /** Expected size of key argument in constructor. */
23 : static constexpr unsigned KEYLEN = 32;
24 :
25 : /** Expansion when encrypting. */
26 : static constexpr unsigned EXPANSION = Poly1305::TAGLEN;
27 :
28 : /** Initialize an AEAD instance with a specified 32-byte key. */
29 : AEADChaCha20Poly1305(Span<const std::byte> key) noexcept;
30 :
31 : /** Switch to another 32-byte key. */
32 : void SetKey(Span<const std::byte> key) noexcept;
33 :
34 : /** 96-bit nonce type. */
35 : using Nonce96 = ChaCha20::Nonce96;
36 :
37 : /** Encrypt a message with a specified 96-bit nonce and aad.
38 : *
39 : * Requires cipher.size() = plain.size() + EXPANSION.
40 : */
41 4 : void Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> cipher) noexcept
42 : {
43 4 : Encrypt(plain, {}, aad, nonce, cipher);
44 4 : }
45 :
46 : /** Encrypt a message (given split into plain1 + plain2) with a specified 96-bit nonce and aad.
47 : *
48 : * Requires cipher.size() = plain1.size() + plain2.size() + EXPANSION.
49 : */
50 : void Encrypt(Span<const std::byte> plain1, Span<const std::byte> plain2, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> cipher) noexcept;
51 :
52 : /** Decrypt a message with a specified 96-bit nonce and aad. Returns true if valid.
53 : *
54 : * Requires cipher.size() = plain.size() + EXPANSION.
55 : */
56 4 : bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> plain) noexcept
57 : {
58 4 : return Decrypt(cipher, aad, nonce, plain, {});
59 : }
60 :
61 : /** Decrypt a message with a specified 96-bit nonce and aad and split the result. Returns true if valid.
62 : *
63 : * Requires cipher.size() = plain1.size() + plain2.size() + EXPANSION.
64 : */
65 : bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> plain1, Span<std::byte> plain2) noexcept;
66 :
67 : /** Get a number of keystream bytes from the underlying stream cipher.
68 : *
69 : * This is equivalent to Encrypt() with plain set to that many zero bytes, and dropping the
70 : * last EXPANSION bytes off the result.
71 : */
72 : void Keystream(Nonce96 nonce, Span<std::byte> keystream) noexcept;
73 : };
74 :
75 : /** Forward-secure wrapper around AEADChaCha20Poly1305.
76 : *
77 : * This implements an AEAD which automatically increments the nonce on every encryption or
78 : * decryption, and cycles keys after a predetermined number of encryptions or decryptions.
79 : *
80 : * See BIP324 for details.
81 : */
82 890 : class FSChaCha20Poly1305
83 : {
84 : private:
85 : /** Internal AEAD. */
86 : AEADChaCha20Poly1305 m_aead;
87 :
88 : /** Every how many iterations this cipher rekeys. */
89 : const uint32_t m_rekey_interval;
90 :
91 : /** The number of encryptions/decryptions since the last rekey. */
92 : uint32_t m_packet_counter{0};
93 :
94 : /** The number of rekeys performed so far. */
95 : uint64_t m_rekey_counter{0};
96 :
97 : /** Update counters (and if necessary, key) to transition to the next message. */
98 : void NextPacket() noexcept;
99 :
100 : public:
101 : /** Length of keys expected by the constructor. */
102 : static constexpr auto KEYLEN = AEADChaCha20Poly1305::KEYLEN;
103 :
104 : /** Expansion when encrypting. */
105 : static constexpr auto EXPANSION = AEADChaCha20Poly1305::EXPANSION;
106 :
107 : // No copy or move to protect the secret.
108 : FSChaCha20Poly1305(const FSChaCha20Poly1305&) = delete;
109 : FSChaCha20Poly1305(FSChaCha20Poly1305&&) = delete;
110 : FSChaCha20Poly1305& operator=(const FSChaCha20Poly1305&) = delete;
111 : FSChaCha20Poly1305& operator=(FSChaCha20Poly1305&&) = delete;
112 :
113 : /** Construct an FSChaCha20Poly1305 cipher that rekeys every rekey_interval operations. */
114 910 : FSChaCha20Poly1305(Span<const std::byte> key, uint32_t rekey_interval) noexcept :
115 910 : m_aead(key), m_rekey_interval(rekey_interval) {}
116 :
117 : /** Encrypt a message with a specified aad.
118 : *
119 : * Requires cipher.size() = plain.size() + EXPANSION.
120 : */
121 605002 : void Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Span<std::byte> cipher) noexcept
122 : {
123 605002 : Encrypt(plain, {}, aad, cipher);
124 2 : }
125 :
126 : /** Encrypt a message (given split into plain1 + plain2) with a specified aad.
127 : *
128 : * Requires cipher.size() = plain.size() + EXPANSION.
129 : */
130 : void Encrypt(Span<const std::byte> plain1, Span<const std::byte> plain2, Span<const std::byte> aad, Span<std::byte> cipher) noexcept;
131 :
132 : /** Decrypt a message with a specified aad. Returns true if valid.
133 : *
134 : * Requires cipher.size() = plain.size() + EXPANSION.
135 : */
136 605002 : bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain) noexcept
137 : {
138 605002 : return Decrypt(cipher, aad, plain, {});
139 : }
140 :
141 : /** Decrypt a message with a specified aad and split the result. Returns true if valid.
142 : *
143 : * Requires cipher.size() = plain1.size() + plain2.size() + EXPANSION.
144 : */
145 : bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain1, Span<std::byte> plain2) noexcept;
146 : };
147 :
148 : #endif // BITCOIN_CRYPTO_CHACHA20POLY1305_H
|