spank-olm

WIP Do not look
git clone git://archive.git.mtrnord.blog/MTRNord/spank-olm.git
Log | Files | Refs | README | LICENSE

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