account.hpp (7658B)
1 #pragma once 2 3 #include <botan/base64.h> 4 #include <botan/ed25519.h> 5 #include <botan/x25519.h> 6 #include <numeric> 7 8 #include "list.hpp" 9 10 // Define a macro to detect Emscripten 11 #ifdef __EMSCRIPTEN__ 12 #define EMSCRIPTEN_CONSTEXPR 13 #else 14 #define EMSCRIPTEN_CONSTEXPR constexpr 15 #endif 16 17 namespace spank_olm 18 { 19 /** 20 * \brief Represents identity keys containing both Ed25519 and Curve25519 key pairs. 21 * 22 * This struct aggregates an Ed25519 key pair for signing and a Curve25519 key pair 23 * for encryption and key exchange. 24 */ 25 struct IdentityKeys 26 { 27 Botan::Ed25519_PrivateKey ed25519_key; 28 ///< The Ed25519 key pair for signing. The public key can be obtained using the public_key() method. 29 Botan::X25519_PrivateKey curve25519_key; 30 ///< The Curve25519 key pair for encryption and key exchange. The public key can be obtained using the 31 ///< public_key() method. 32 }; 33 34 /** 35 * \brief Represents a one-time key used in the encryption process. 36 * 37 * This struct contains an identifier, a publication status, and a Curve25519 key pair. 38 */ 39 struct OneTimeKey 40 { 41 std::uint32_t id; ///< The unique identifier for the one-time key. 42 bool published; ///< Indicates whether the key has been published. 43 Botan::X25519_PrivateKey key; ///< The Curve25519 key pair for encryption and key exchange. 44 }; 45 46 constexpr std::size_t MAX_ONE_TIME_KEYS(100); ///< The maximum number of one-time keys. 47 48 struct Account 49 { 50 Account() : next_one_time_key_id(0) {} 51 52 Account(Account const &other) = default; 53 54 std::optional<IdentityKeys> identity_keys; ///< The identity keys for the account. 55 FixedSizeArray<OneTimeKey, MAX_ONE_TIME_KEYS> one_time_keys; ///< The one-time keys for the account. 56 std::optional<OneTimeKey> current_fallback_key; ///< The current fallback key. 57 std::optional<OneTimeKey> prev_fallback_key; ///< The previous fallback key. 58 std::uint32_t next_one_time_key_id; ///< The identifier for the next one-time key. 59 60 /** 61 * \brief Generates a new account with the given identity keys. 62 * 63 * \param rng The botan random number generator to use. 64 * \return The result of the operation. 65 * \throws SpankOlmErrorKeyGeneration if the key generation fails. 66 */ 67 void new_account(Botan::RandomNumberGenerator &rng); 68 69 /** 70 * \brief Output the identity keys for this account as JSON. 71 * 72 * The output JSON will have the following format: 73 * 74 * ```json 75 * { 76 * "curve25519": "<43 base64 characters>", 77 * "ed25519": "<43 base64 characters>" 78 * } 79 * ``` 80 * 81 * \return The JSON representation of the identity keys. 82 */ 83 [[nodiscard]] std::string get_identity_json() const; 84 85 /** 86 * \brief Signs a message using the Ed25519 key. 87 * 88 * \param rng The botan random number generator to use. 89 * \param message The message to sign. 90 * \return The signature of the message. 91 */ 92 [[nodiscard]] std::vector<uint8_t> sign(Botan::RandomNumberGenerator &rng, std::string_view message) const; 93 94 95 /** 96 * \brief Output the identity keys for this account as JSON. 97 * 98 * The output JSON will have the following format: 99 * 100 * ```json 101 * { 102 * "curve25519": [ 103 * "<6 byte key id>": "<43 base64 characters>", 104 * "<6 byte key id>": "<43 base64 characters>", 105 * ... 106 * ] 107 * } 108 * ``` 109 * 110 * @return Returns the JSON representation of the one time keys which haven't been published yet. 111 */ 112 [[nodiscard]] EMSCRIPTEN_CONSTEXPR std::string get_one_time_keys_json() 113 { 114 std::vector<std::string> stringified_keys; 115 116 for (const auto &key : one_time_keys) 117 { 118 if (!key->published) 119 { 120 auto key_base64 = Botan::base64_encode(key->key.public_key()->raw_public_key_bits()); 121 stringified_keys.push_back(R"(")" + std::to_string(key->id) + R"(": ")" + key_base64 + "\""); 122 } 123 } 124 125 const std::string keys_json = std::accumulate( 126 stringified_keys.begin(), stringified_keys.end(), std::string(), 127 [](const std::string &acc, const std::string &key) { return acc.empty() ? key : acc + ", " + key; }); 128 129 return R"({"curve25519": {)" + keys_json + "}}"; 130 } 131 132 /** 133 * \brief Mark the curent list of one_time_keys and the current_fallback_key as published. 134 * 135 * The current one time keys will no longer be returned by 136 * get_one_time_keys_json() and the current fallback key will no longer be 137 * returned by get_unpublished_fallback_key_json(). 138 * 139 * \return The count of keys marked as published. 140 */ 141 std::size_t mark_keys_as_published(); 142 143 /** 144 * \brief Returns the maximum number of one-time keys. 145 * 146 * This function provides the maximum number of one-time keys that can be stored 147 * in the account. This value is defined by the constant MAX_ONE_TIME_KEYS. 148 * 149 * \return The maximum number of one-time keys. 150 */ 151 [[nodiscard]] static constexpr std::size_t max_number_of_one_time_keys() { return MAX_ONE_TIME_KEYS; } 152 153 /** 154 * \brief Generates a number of new one-time keys. 155 * 156 * Generates a number of new one time keys. If the total number of keys 157 * stored by this account exceeds max_number_of_one_time_keys() then the 158 * old keys are discarded. 159 */ 160 void generate_one_time_keys(Botan::RandomNumberGenerator &rng, std::size_t number_of_keys); 161 162 /** 163 * \brief Generates a new fallback key. 164 */ 165 void generate_fallback_key(Botan::RandomNumberGenerator &rng); 166 167 /** 168 * \brief Output the currentöy unpublished fallback key as JSON. 169 * 170 * The output JSON will have the following format: 171 * 172 * ```json 173 * { 174 * "curve25519": [ 175 * "<6 byte key id>": "<43 base64 characters>", 176 * "<6 byte key id>": "<43 base64 characters>", 177 * ... 178 * ] 179 * } 180 * ``` 181 */ 182 [[nodiscard]] std::string EMSCRIPTEN_CONSTEXPR get_unpublished_fallback_key_json() const 183 { 184 if (!current_fallback_key || current_fallback_key->published) 185 { 186 return R"({"curve25519": {}})"; 187 } 188 189 const auto key_base64 = Botan::base64_encode(current_fallback_key->key.public_key()->raw_public_key_bits()); 190 return R"({"curve25519": {")" + std::to_string(current_fallback_key->id) + R"(": ")" + key_base64 + "\"}}"; 191 } 192 193 /** 194 * \brief Forget about the old fallback key. 195 */ 196 void forget_old_fallback_key(); 197 198 /** 199 * \brief Lookup a one time key with the given public key 200 */ 201 [[nodiscard]] std::optional<OneTimeKey const *> lookup_key(Botan::Public_Key const &key) const; 202 203 /** 204 * \brief Remove a one time key with the given public key 205 */ 206 void remove_key(Botan::Public_Key const &key); 207 208 [[nodiscard]] std::vector<uint8_t> pickle() const; 209 210 static Account unpickle(std::vector<uint8_t> const &data); 211 }; 212 } // namespace spank_olm