chacha20poly1305.h
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 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 FSChaCha20Poly1305(Span<const std::byte> key, uint32_t rekey_interval) noexcept : 115 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