Merge branch 'uhoreg/fallback' into 'master'
add support for fallback keys See merge request matrix-org/olm!13
This commit is contained in:
commit
3cd6b15853
6 changed files with 227 additions and 2 deletions
|
@ -43,6 +43,8 @@ struct Account {
|
|||
Account();
|
||||
IdentityKeys identity_keys;
|
||||
List<OneTimeKey, MAX_ONE_TIME_KEYS> one_time_keys;
|
||||
OneTimeKey current_fallback_key;
|
||||
OneTimeKey prev_fallback_key;
|
||||
std::uint32_t next_one_time_key_id;
|
||||
OlmErrorCode last_error;
|
||||
|
||||
|
@ -126,6 +128,35 @@ struct Account {
|
|||
std::uint8_t const * random, std::size_t random_length
|
||||
);
|
||||
|
||||
/** The number of random bytes needed to generate a fallback key. */
|
||||
std::size_t generate_fallback_key_random_length();
|
||||
|
||||
/** Generates a new fallback key. 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_fallback_key(
|
||||
std::uint8_t const * random, std::size_t random_length
|
||||
);
|
||||
|
||||
/** Number of bytes needed to output the one time keys for this account */
|
||||
std::size_t get_fallback_key_json_length();
|
||||
|
||||
/** Output the fallback key as JSON:
|
||||
*
|
||||
* {"curve25519":
|
||||
* ["<6 byte key id>":"<43 base64 characters>"
|
||||
* ,"<6 byte key id>":"<43 base64 characters>"
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
std::size_t get_fallback_key_json(
|
||||
std::uint8_t * fallback_json, std::size_t fallback_json_length
|
||||
);
|
||||
|
||||
/** Lookup a one time key with the given public key */
|
||||
OneTimeKey const * lookup_key(
|
||||
_olm_curve25519_public_key const & public_key
|
||||
|
|
|
@ -254,6 +254,31 @@ size_t olm_account_generate_one_time_keys(
|
|||
void * random, size_t random_length
|
||||
);
|
||||
|
||||
/** The number of random bytes needed to generate a fallback key. */
|
||||
size_t olm_account_generate_fallback_key_random_length(
|
||||
OlmAccount * account
|
||||
);
|
||||
|
||||
/** Generates a new fallback key. Only one previous fallback key is
|
||||
* stored. Returns olm_error() on error. If the number of random bytes is too
|
||||
* small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */
|
||||
size_t olm_account_generate_fallback_key(
|
||||
OlmAccount * account,
|
||||
void * random, size_t random_length
|
||||
);
|
||||
|
||||
/** The number of bytes needed to hold the fallback key as returned by
|
||||
* olm_account_fallback_key. */
|
||||
size_t olm_account_fallback_key_length(
|
||||
OlmAccount * account
|
||||
);
|
||||
|
||||
size_t olm_account_fallback_key(
|
||||
OlmAccount * account,
|
||||
void * fallback_key, size_t fallback_key_size
|
||||
);
|
||||
|
||||
|
||||
/** The number of random bytes needed to create an outbound session */
|
||||
size_t olm_create_outbound_session_random_length(
|
||||
OlmSession * session
|
||||
|
|
2
javascript/index.d.ts
vendored
2
javascript/index.d.ts
vendored
|
@ -27,6 +27,8 @@ declare class Account {
|
|||
max_number_of_one_time_keys(): number;
|
||||
generate_one_time_keys(number_of_keys: number);
|
||||
remove_one_time_keys(session: Session);
|
||||
generate_fallback_key();
|
||||
fallback_key(): string;
|
||||
pickle(key: string): string;
|
||||
unpickle(key: string, pickle: string);
|
||||
}
|
||||
|
|
|
@ -146,6 +146,27 @@ Account.prototype['remove_one_time_keys'] = restore_stack(function(session) {
|
|||
);
|
||||
});
|
||||
|
||||
Account.prototype['generate_fallback_key'] = restore_stack(function() {
|
||||
var random_length = account_method(
|
||||
Module['_olm_account_generate_fallback_key_random_length']
|
||||
)(this.ptr);
|
||||
var random = random_stack(random_length);
|
||||
account_method(Module['_olm_account_generate_fallback_key'])(
|
||||
this.ptr, random, random_length
|
||||
);
|
||||
});
|
||||
|
||||
Account.prototype['fallback_key'] = restore_stack(function() {
|
||||
var keys_length = account_method(
|
||||
Module['_olm_account_fallback_key_length']
|
||||
)(this.ptr);
|
||||
var keys = stack(keys_length + NULL_BYTE_PADDING_LENGTH);
|
||||
account_method(Module['_olm_account_fallback_key'])(
|
||||
this.ptr, keys, keys_length
|
||||
);
|
||||
return UTF8ToString(keys, keys_length);
|
||||
});
|
||||
|
||||
Account.prototype['pickle'] = restore_stack(function(key) {
|
||||
var key_array = array_from_string(key);
|
||||
var pickle_length = account_method(
|
||||
|
|
112
src/account.cpp
112
src/account.cpp
|
@ -21,6 +21,11 @@
|
|||
olm::Account::Account(
|
||||
) : next_one_time_key_id(0),
|
||||
last_error(OlmErrorCode::OLM_SUCCESS) {
|
||||
// since we don't need to keep track of whether the fallback keys are
|
||||
// published, use the published flag as in indication for whether the keys
|
||||
// were generated
|
||||
current_fallback_key.published = false;
|
||||
prev_fallback_key.published = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -32,6 +37,20 @@ olm::OneTimeKey const * olm::Account::lookup_key(
|
|||
return &key;
|
||||
}
|
||||
}
|
||||
if (current_fallback_key.published
|
||||
&& olm::array_equal(
|
||||
current_fallback_key.key.public_key.public_key, public_key.public_key
|
||||
)
|
||||
) {
|
||||
return ¤t_fallback_key;
|
||||
}
|
||||
if (prev_fallback_key.published
|
||||
&& olm::array_equal(
|
||||
prev_fallback_key.key.public_key.public_key, public_key.public_key
|
||||
)
|
||||
) {
|
||||
return &prev_fallback_key;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -46,6 +65,22 @@ std::size_t olm::Account::remove_key(
|
|||
return id;
|
||||
}
|
||||
}
|
||||
// check if the key is a fallback key, to avoid returning an error, but
|
||||
// don't actually remove it
|
||||
if (current_fallback_key.published
|
||||
&& olm::array_equal(
|
||||
current_fallback_key.key.public_key.public_key, public_key.public_key
|
||||
)
|
||||
) {
|
||||
return current_fallback_key.id;
|
||||
}
|
||||
if (prev_fallback_key.published
|
||||
&& olm::array_equal(
|
||||
prev_fallback_key.key.public_key.public_key, public_key.public_key
|
||||
)
|
||||
) {
|
||||
return prev_fallback_key.id;
|
||||
}
|
||||
return std::size_t(-1);
|
||||
}
|
||||
|
||||
|
@ -260,6 +295,67 @@ std::size_t olm::Account::generate_one_time_keys(
|
|||
return number_of_keys;
|
||||
}
|
||||
|
||||
std::size_t olm::Account::generate_fallback_key_random_length() {
|
||||
return CURVE25519_RANDOM_LENGTH;
|
||||
}
|
||||
|
||||
std::size_t olm::Account::generate_fallback_key(
|
||||
std::uint8_t const * random, std::size_t random_length
|
||||
) {
|
||||
if (random_length < generate_fallback_key_random_length()) {
|
||||
last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
|
||||
return std::size_t(-1);
|
||||
}
|
||||
prev_fallback_key = current_fallback_key;
|
||||
current_fallback_key.id = ++next_one_time_key_id;
|
||||
current_fallback_key.published = true;
|
||||
_olm_crypto_curve25519_generate_key(random, ¤t_fallback_key.key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
std::size_t olm::Account::get_fallback_key_json_length(
|
||||
) {
|
||||
std::size_t length = 4 + sizeof(KEY_JSON_CURVE25519); /* {"curve25519":{}} */
|
||||
OneTimeKey & key = current_fallback_key;
|
||||
if (key.published) {
|
||||
length += 1; /* " */
|
||||
length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
|
||||
length += 3; /* ":" */
|
||||
length += olm::encode_base64_length(sizeof(key.key.public_key));
|
||||
length += 1; /* " */
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
std::size_t olm::Account::get_fallback_key_json(
|
||||
std::uint8_t * fallback_json, std::size_t fallback_json_length
|
||||
) {
|
||||
std::uint8_t * pos = fallback_json;
|
||||
if (fallback_json_length < get_fallback_key_json_length()) {
|
||||
last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
|
||||
return std::size_t(-1);
|
||||
}
|
||||
*(pos++) = '{';
|
||||
pos = write_string(pos, KEY_JSON_CURVE25519);
|
||||
*(pos++) = '{';
|
||||
OneTimeKey & key = current_fallback_key;
|
||||
if (key.published) {
|
||||
*(pos++) = '\"';
|
||||
std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
|
||||
_olm_pickle_uint32(key_id, key.id);
|
||||
pos = olm::encode_base64(key_id, sizeof(key_id), pos);
|
||||
*(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
|
||||
pos = olm::encode_base64(
|
||||
key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
|
||||
);
|
||||
*(pos++) = '\"';
|
||||
}
|
||||
*(pos++) = '}';
|
||||
*(pos++) = '}';
|
||||
return pos - fallback_json;
|
||||
}
|
||||
|
||||
namespace olm {
|
||||
|
||||
static std::size_t pickle_length(
|
||||
|
@ -329,7 +425,8 @@ static std::uint8_t const * unpickle(
|
|||
namespace {
|
||||
// pickle version 1 used only 32 bytes for the ed25519 private key.
|
||||
// Any keys thus used should be considered compromised.
|
||||
static const std::uint32_t ACCOUNT_PICKLE_VERSION = 2;
|
||||
// pickle version 2 does not have fallback keys.
|
||||
static const std::uint32_t ACCOUNT_PICKLE_VERSION = 3;
|
||||
}
|
||||
|
||||
|
||||
|
@ -340,6 +437,8 @@ std::size_t olm::pickle_length(
|
|||
length += olm::pickle_length(ACCOUNT_PICKLE_VERSION);
|
||||
length += olm::pickle_length(value.identity_keys);
|
||||
length += olm::pickle_length(value.one_time_keys);
|
||||
length += olm::pickle_length(value.current_fallback_key);
|
||||
length += olm::pickle_length(value.prev_fallback_key);
|
||||
length += olm::pickle_length(value.next_one_time_key_id);
|
||||
return length;
|
||||
}
|
||||
|
@ -352,6 +451,8 @@ std::uint8_t * olm::pickle(
|
|||
pos = olm::pickle(pos, ACCOUNT_PICKLE_VERSION);
|
||||
pos = olm::pickle(pos, value.identity_keys);
|
||||
pos = olm::pickle(pos, value.one_time_keys);
|
||||
pos = olm::pickle(pos, value.current_fallback_key);
|
||||
pos = olm::pickle(pos, value.prev_fallback_key);
|
||||
pos = olm::pickle(pos, value.next_one_time_key_id);
|
||||
return pos;
|
||||
}
|
||||
|
@ -365,6 +466,7 @@ std::uint8_t const * olm::unpickle(
|
|||
pos = olm::unpickle(pos, end, pickle_version);
|
||||
switch (pickle_version) {
|
||||
case ACCOUNT_PICKLE_VERSION:
|
||||
case 2:
|
||||
break;
|
||||
case 1:
|
||||
value.last_error = OlmErrorCode::OLM_BAD_LEGACY_ACCOUNT_PICKLE;
|
||||
|
@ -375,6 +477,14 @@ std::uint8_t const * olm::unpickle(
|
|||
}
|
||||
pos = olm::unpickle(pos, end, value.identity_keys);
|
||||
pos = olm::unpickle(pos, end, value.one_time_keys);
|
||||
if (pickle_version == 2) {
|
||||
// version 2 did not have fallback keys
|
||||
value.current_fallback_key.published = false;
|
||||
value.prev_fallback_key.published = false;
|
||||
} else {
|
||||
pos = olm::unpickle(pos, end, value.current_fallback_key);
|
||||
pos = olm::unpickle(pos, end, value.prev_fallback_key);
|
||||
}
|
||||
pos = olm::unpickle(pos, end, value.next_one_time_key_id);
|
||||
return pos;
|
||||
}
|
||||
|
|
36
src/olm.cpp
36
src/olm.cpp
|
@ -417,6 +417,42 @@ size_t olm_account_generate_one_time_keys(
|
|||
}
|
||||
|
||||
|
||||
size_t olm_account_generate_fallback_key_random_length(
|
||||
OlmAccount * account
|
||||
) {
|
||||
return from_c(account)->generate_fallback_key_random_length();
|
||||
}
|
||||
|
||||
|
||||
size_t olm_account_generate_fallback_key(
|
||||
OlmAccount * account,
|
||||
void * random, size_t random_length
|
||||
) {
|
||||
size_t result = from_c(account)->generate_fallback_key(
|
||||
from_c(random), random_length
|
||||
);
|
||||
olm::unset(random, random_length);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
size_t olm_account_fallback_key_length(
|
||||
OlmAccount * account
|
||||
) {
|
||||
return from_c(account)->get_fallback_key_json_length();
|
||||
}
|
||||
|
||||
|
||||
size_t olm_account_fallback_key(
|
||||
OlmAccount * account,
|
||||
void * fallback_key_json, size_t fallback_key_json_length
|
||||
) {
|
||||
return from_c(account)->get_fallback_key_json(
|
||||
from_c(fallback_key_json), fallback_key_json_length
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
size_t olm_create_outbound_session_random_length(
|
||||
OlmSession * session
|
||||
) {
|
||||
|
|
Loading…
Reference in a new issue