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 : 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 : void Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> cipher) noexcept 42 : { 43 : Encrypt(plain, {}, aad, nonce, cipher); 44 : } 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 : bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> plain) noexcept 57 : { 58 : 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 : 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 0 : uint32_t m_packet_counter{0}; 93 : 94 : /** The number of rekeys performed so far. */ 95 0 : 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 0 : FSChaCha20Poly1305(Span<const std::byte> key, uint32_t rekey_interval) noexcept : 115 0 : 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 : void Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Span<std::byte> cipher) noexcept 122 : { 123 : Encrypt(plain, {}, aad, cipher); 124 : } 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 : bool Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain) noexcept 137 : { 138 : 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