diff --git a/include/olm/account.hh b/include/olm/account.hh index cf886d1..f54758d 100644 --- a/include/olm/account.hh +++ b/include/olm/account.hh @@ -31,6 +31,7 @@ struct IdentityKeys { struct OneTimeKey { std::uint32_t id; + bool published; Curve25519KeyPair key; }; @@ -39,15 +40,17 @@ static std::size_t const MAX_ONE_TIME_KEYS = 100; struct Account { + Account(); IdentityKeys identity_keys; List one_time_keys; + std::uint32_t next_one_time_key_id; ErrorCode last_error; /** Number of random bytes needed to create a new account */ std::size_t new_account_random_length(); - /** Create a new account. Returns NOT_ENOUGH_RANDOM if the number of random - * bytes is too small. */ + /** Create a new account. Returns std::size_t(-1) on error. If the number of + * random bytes is too small then last_error will be NOT_ENOUGH_RANDOM */ std::size_t new_account( uint8_t const * random, std::size_t random_length ); @@ -61,35 +64,30 @@ struct Account { ); /** Output the identity keys for this account as JSON in the following - * format. + * format: * - * 14 {"algorithms": - * 30 ["m.olm.curve25519-aes-sha256" - * 15 ],"device_id":" - * ? - * 22 ","keys":{"curve25519: - * 4 - * 3 ":" - * 43 - * 11 ","ed25519: - * 4 - * 3 ":" - * 43 - * 14 "},"user_id":" - * ? - * 19 ","valid_after_ts": - * ? - * 18 ,"valid_until_ts": - * ? - * 16 ,"signatures":{" - * ? - * 1 / - * ? - * 12 ":{"ed25519: - * 4 - * 3 ":" - * 86 - * 4 "}}} + * {"algorithms": + * ["m.olm.curve25519-aes-sha256" + * ] + * ,"device_id":"" + * ,"keys": + * {"curve25519:":"" + * ,"ed25519:":"" + * } + * ,"user_id":"" + * ,"valid_after_ts": + * ,"valid_until_ts": + * ,"signatures": + * {"/": + * {"ed25519:":"" + * } + * } + * } + * + * The user_id and device_id must not contain 0x00-0x1F, '\"' or '\\'. + * The JSON up to but not including the "signatures" key will be signed + * using the account's ed25519 key. That signature is then included under + * the "signatures" key. * * Returns the size of the JSON written or std::size_t(-1) on error. * If the buffer is too small last_error will be OUTPUT_BUFFER_TOO_SMALL. */ @@ -104,7 +102,13 @@ struct Account { /** Number of bytes needed to output the one time keys for this account */ std::size_t get_one_time_keys_json_length(); - /* + /** Output the one time keys that haven't been published yet as JSON: + * + * {"curve25519:":"" + * ,"curve25519:":"" + * ... + * } + * * Returns the size of the JSON written or std::size_t(-1) on error. * If the buffer is too small last_error will be OUTPUT_BUFFER_TOO_SMALL. */ @@ -112,6 +116,28 @@ struct Account { std::uint8_t * one_time_json, std::size_t one_time_json_length ); + /** Mark the current list of one_time_keys as being published. They + * will no longer be returned by get_one_time_keys_json_length(). */ + std::size_t mark_keys_as_published(); + + /** The largest number of one time keys this account can store. */ + std::size_t max_number_of_one_time_keys(); + + /** Returns the number of random bytes needed to generate a given number + * of new one time keys. */ + std::size_t generate_one_time_keys_random_length( + std::size_t number_of_keys + ); + + /** Generates a number of new one time keys. If the total number of keys + * stored by this account exceeds max_number_of_one_time_keys() then the + * old keys are discarded. Returns std::size_t(-1) on error. If the number + * of random bytes is too small then last_error will be NOT_ENOUGH_RANDOM */ + std::size_t generate_one_time_keys( + std::size_t number_of_keys, + std::uint8_t const * random, std::size_t random_length + ); + /** Lookup a one time key with the given public key */ OneTimeKey const * lookup_key( Curve25519PublicKey const & public_key diff --git a/src/account.cpp b/src/account.cpp index 5bbd6a6..ede327b 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -16,6 +16,11 @@ #include "olm/base64.hh" #include "olm/pickle.hh" +olm::Account::Account( +) : next_one_time_key_id(0), + last_error(olm::ErrorCode::SUCCESS) { +} + olm::OneTimeKey const * olm::Account::lookup_key( olm::Curve25519PublicKey const & public_key @@ -54,19 +59,12 @@ std::size_t olm::Account::new_account( return std::size_t(-1); } - unsigned id = 0; - olm::ed25519_generate_key(random, identity_keys.ed25519_key); random += 32; olm::curve25519_generate_key(random, identity_keys.curve25519_key); random += 32; - for (unsigned i = 0; i < 10; ++i) { - OneTimeKey & key = *one_time_keys.insert(one_time_keys.end()); - key.id = ++id; - olm::curve25519_generate_key(random, key.key); - random += 32; - } + generate_one_time_keys(10, random, random_length - 64); return 0; } @@ -236,6 +234,9 @@ std::size_t olm::Account::get_one_time_keys_json_length( ) { std::size_t length = 0; for (auto const & key : one_time_keys) { + if (key.published) { + continue; + } length += 2; /* {" */ length += sizeof(ONE_TIME_KEY_JSON_ALG) - 1; length += 1; /* : */ @@ -262,6 +263,9 @@ std::size_t olm::Account::get_one_time_keys_json( } std::uint8_t sep = '{'; for (auto const & key : one_time_keys) { + if (key.published) { + continue; + } *(pos++) = sep; *(pos++) = '\"'; pos = write_string(pos, ONE_TIME_KEY_JSON_ALG); @@ -284,6 +288,48 @@ std::size_t olm::Account::get_one_time_keys_json( } +std::size_t olm::Account::mark_keys_as_published( +) { + std::size_t count = 0; + for (auto & key : one_time_keys) { + if (!key.published) { + key.published = true; + count++; + } + } + return count; +} + + +std::size_t olm::Account::max_number_of_one_time_keys( +) { + return olm::MAX_ONE_TIME_KEYS; +} + +std::size_t olm::Account::generate_one_time_keys_random_length( + std::size_t number_of_keys +) { + return 32 * number_of_keys; +} + +std::size_t olm::Account::generate_one_time_keys( + std::size_t number_of_keys, + std::uint8_t const * random, std::size_t random_length +) { + if (random_length < generate_one_time_keys_random_length(number_of_keys)) { + last_error = olm::ErrorCode::NOT_ENOUGH_RANDOM; + return std::size_t(-1); + } + for (unsigned i = 0; i < number_of_keys; ++i) { + OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin()); + key.id = ++next_one_time_key_id; + key.published = false; + olm::curve25519_generate_key(random, key.key); + random += 32; + } + return number_of_keys; +} + namespace olm { static std::size_t pickle_length( @@ -319,7 +365,11 @@ static std::uint8_t const * unpickle( static std::size_t pickle_length( olm::OneTimeKey const & value ) { - return olm::pickle_length(value.id) + olm::pickle_length(value.key); + std::size_t length = 0; + length += olm::pickle_length(value.id); + length += olm::pickle_length(value.published); + length += olm::pickle_length(value.key); + return length; } @@ -328,6 +378,7 @@ static std::uint8_t * pickle( olm::OneTimeKey const & value ) { pos = olm::pickle(pos, value.id); + pos = olm::pickle(pos, value.published); pos = olm::pickle(pos, value.key); return pos; } @@ -338,6 +389,7 @@ static std::uint8_t const * unpickle( olm::OneTimeKey & value ) { pos = olm::unpickle(pos, end, value.id); + pos = olm::unpickle(pos, end, value.published); pos = olm::unpickle(pos, end, value.key); return pos; } @@ -351,6 +403,7 @@ std::size_t olm::pickle_length( std::size_t length = 0; length += olm::pickle_length(value.identity_keys); length += olm::pickle_length(value.one_time_keys); + length += olm::pickle_length(value.next_one_time_key_id); return length; } @@ -361,6 +414,7 @@ std::uint8_t * olm::pickle( ) { pos = olm::pickle(pos, value.identity_keys); pos = olm::pickle(pos, value.one_time_keys); + pos = olm::pickle(pos, value.next_one_time_key_id); return pos; } @@ -371,5 +425,6 @@ std::uint8_t const * olm::unpickle( ) { pos = olm::unpickle(pos, end, value.identity_keys); pos = olm::unpickle(pos, end, value.one_time_keys); + pos = olm::unpickle(pos, end, value.next_one_time_key_id); return pos; }