/ src / crypto / chacha20poly1305.h
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