bip324.h raw
1 // Copyright (c) 2023 The Limenka 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 LIMENKA_BIP324_H
6 #define LIMENKA_BIP324_H
7
8 #include <array>
9 #include <cstddef>
10 #include <optional>
11
12 #include <crypto/chacha20.h>
13 #include <crypto/chacha20poly1305.h>
14 #include <key.h>
15 #include <pubkey.h>
16 #include <span.h>
17
18 /** The BIP324 packet cipher, encapsulating its key derivation, stream cipher, and AEAD. */
19 class BIP324Cipher
20 {
21 public:
22 static constexpr unsigned SESSION_ID_LEN{32};
23 static constexpr unsigned GARBAGE_TERMINATOR_LEN{16};
24 static constexpr unsigned REKEY_INTERVAL{224};
25 static constexpr unsigned LENGTH_LEN{3};
26 static constexpr unsigned HEADER_LEN{1};
27 static constexpr unsigned EXPANSION = LENGTH_LEN + HEADER_LEN + FSChaCha20Poly1305::EXPANSION;
28 static constexpr std::byte IGNORE_BIT{0x80};
29
30 private:
31 std::optional<FSChaCha20> m_send_l_cipher;
32 std::optional<FSChaCha20> m_recv_l_cipher;
33 std::optional<FSChaCha20Poly1305> m_send_p_cipher;
34 std::optional<FSChaCha20Poly1305> m_recv_p_cipher;
35
36 CKey m_key;
37 EllSwiftPubKey m_our_pubkey;
38
39 std::array<std::byte, SESSION_ID_LEN> m_session_id;
40 std::array<std::byte, GARBAGE_TERMINATOR_LEN> m_send_garbage_terminator;
41 std::array<std::byte, GARBAGE_TERMINATOR_LEN> m_recv_garbage_terminator;
42
43 public:
44 /** No default constructor; keys must be provided to create a BIP324Cipher. */
45 BIP324Cipher() = delete;
46
47 /** Initialize a BIP324 cipher with specified key and encoding entropy (testing only). */
48 BIP324Cipher(const CKey& key, Span<const std::byte> ent32) noexcept;
49
50 /** Initialize a BIP324 cipher with specified key (testing only). */
51 BIP324Cipher(const CKey& key, const EllSwiftPubKey& pubkey) noexcept;
52
53 /** Retrieve our public key. */
54 const EllSwiftPubKey& GetOurPubKey() const noexcept { return m_our_pubkey; }
55
56 /** Initialize when the other side's public key is received. Can only be called once.
57 *
58 * initiator is set to true if we are the initiator establishing the v2 P2P connection.
59 * self_decrypt is only for testing, and swaps encryption/decryption keys, so that encryption
60 * and decryption can be tested without knowing the other side's private key.
61 */
62 void Initialize(const EllSwiftPubKey& their_pubkey, bool initiator, bool self_decrypt = false) noexcept;
63
64 /** Determine whether this cipher is fully initialized. */
65 explicit operator bool() const noexcept { return m_send_l_cipher.has_value(); }
66
67 /** Encrypt a packet. Only after Initialize().
68 *
69 * It must hold that output.size() == contents.size() + EXPANSION.
70 */
71 void Encrypt(Span<const std::byte> contents, Span<const std::byte> aad, bool ignore, Span<std::byte> output) noexcept;
72
73 /** Decrypt the length of a packet. Only after Initialize().
74 *
75 * It must hold that input.size() == LENGTH_LEN.
76 */
77 unsigned DecryptLength(Span<const std::byte> input) noexcept;
78
79 /** Decrypt a packet. Only after Initialize().
80 *
81 * It must hold that input.size() + LENGTH_LEN == contents.size() + EXPANSION.
82 * Contents.size() must equal the length returned by DecryptLength.
83 */
84 bool Decrypt(Span<const std::byte> input, Span<const std::byte> aad, bool& ignore, Span<std::byte> contents) noexcept;
85
86 /** Get the Session ID. Only after Initialize(). */
87 Span<const std::byte> GetSessionID() const noexcept { return m_session_id; }
88
89 /** Get the Garbage Terminator to send. Only after Initialize(). */
90 Span<const std::byte> GetSendGarbageTerminator() const noexcept { return m_send_garbage_terminator; }
91
92 /** Get the expected Garbage Terminator to receive. Only after Initialize(). */
93 Span<const std::byte> GetReceiveGarbageTerminator() const noexcept { return m_recv_garbage_terminator; }
94 };
95
96 #endif // LIMENKA_BIP324_H
97