Implement pickling/unpickling for outbound group sessions
This commit is contained in:
parent
caaed796ad
commit
c058554132
5 changed files with 191 additions and 8 deletions
|
@ -61,6 +61,21 @@ const struct _olm_cipher *megolm_cipher();
|
|||
*/
|
||||
void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter);
|
||||
|
||||
/** Returns the number of bytes needed to store a megolm */
|
||||
size_t megolm_pickle_length(const Megolm *megolm);
|
||||
|
||||
/**
|
||||
* Pickle the megolm. Returns a pointer to the next free space in the buffer.
|
||||
*/
|
||||
uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos);
|
||||
|
||||
/**
|
||||
* Unpickle the megolm. Returns a pointer to the next item in the buffer.
|
||||
*/
|
||||
const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
|
||||
const uint8_t *end);
|
||||
|
||||
|
||||
/** advance the ratchet by one step */
|
||||
void megolm_advance(Megolm *megolm);
|
||||
|
||||
|
|
|
@ -48,6 +48,42 @@ size_t olm_clear_outbound_group_session(
|
|||
OlmOutboundGroupSession *session
|
||||
);
|
||||
|
||||
/** Returns the number of bytes needed to store an outbound group session */
|
||||
size_t olm_pickle_outbound_group_session_length(
|
||||
const OlmOutboundGroupSession *session
|
||||
);
|
||||
|
||||
/**
|
||||
* Stores a group session as a base64 string. Encrypts the session using the
|
||||
* supplied key. Returns the length of the session on success.
|
||||
*
|
||||
* Returns olm_error() on failure. If the pickle output buffer
|
||||
* is smaller than olm_pickle_outbound_group_session_length() then
|
||||
* olm_outbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
|
||||
*/
|
||||
size_t olm_pickle_outbound_group_session(
|
||||
OlmOutboundGroupSession *session,
|
||||
void const * key, size_t key_length,
|
||||
void * pickled, size_t pickled_length
|
||||
);
|
||||
|
||||
/**
|
||||
* Loads a group session from a pickled base64 string. Decrypts the session
|
||||
* using the supplied key.
|
||||
*
|
||||
* Returns olm_error() on failure. If the key doesn't match the one used to
|
||||
* encrypt the account then olm_outbound_group_session_last_error() will be
|
||||
* "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
|
||||
* olm_outbound_group_session_last_error() will be "INVALID_BASE64". The input
|
||||
* pickled buffer is destroyed
|
||||
*/
|
||||
size_t olm_unpickle_outbound_group_session(
|
||||
OlmOutboundGroupSession *session,
|
||||
void const * key, size_t key_length,
|
||||
void * pickled, size_t pickled_length
|
||||
);
|
||||
|
||||
|
||||
/** The number of random bytes needed to create an outbound group session */
|
||||
size_t olm_init_outbound_group_session_random_length(
|
||||
const OlmOutboundGroupSession *session
|
||||
|
|
25
src/megolm.c
25
src/megolm.c
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "olm/cipher.h"
|
||||
#include "olm/crypto.h"
|
||||
#include "olm/pickle.h"
|
||||
|
||||
const struct _olm_cipher *megolm_cipher() {
|
||||
static const uint8_t CIPHER_KDF_INFO[] = "MEGOLM_KEYS";
|
||||
|
@ -59,12 +60,32 @@ static void rehash_part(
|
|||
|
||||
|
||||
|
||||
void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter)
|
||||
{
|
||||
void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter) {
|
||||
megolm->counter = counter;
|
||||
memcpy(megolm->data, random_data, MEGOLM_RATCHET_LENGTH);
|
||||
}
|
||||
|
||||
size_t megolm_pickle_length(const Megolm *megolm) {
|
||||
size_t length = 0;
|
||||
length += _olm_pickle_bytes_length(megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
|
||||
length += _olm_pickle_uint32_length(megolm->counter);
|
||||
return length;
|
||||
|
||||
}
|
||||
|
||||
uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos) {
|
||||
pos = _olm_pickle_bytes(pos, megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
|
||||
pos = _olm_pickle_uint32(pos, megolm->counter);
|
||||
return pos;
|
||||
}
|
||||
|
||||
const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
|
||||
const uint8_t *end) {
|
||||
pos = _olm_unpickle_bytes(pos, end, (uint8_t *)(megolm->data),
|
||||
MEGOLM_RATCHET_LENGTH);
|
||||
pos = _olm_unpickle_uint32(pos, end, &megolm->counter);
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* simplistic implementation for a single step */
|
||||
void megolm_advance(Megolm *megolm) {
|
||||
|
|
|
@ -23,10 +23,13 @@
|
|||
#include "olm/error.h"
|
||||
#include "olm/megolm.h"
|
||||
#include "olm/message.h"
|
||||
#include "olm/pickle.h"
|
||||
#include "olm/pickle_encoding.h"
|
||||
|
||||
#define OLM_PROTOCOL_VERSION 3
|
||||
#define SESSION_ID_RANDOM_BYTES 4
|
||||
#define GROUP_SESSION_ID_LENGTH (sizeof(struct timeval) + SESSION_ID_RANDOM_BYTES)
|
||||
#define PICKLE_VERSION 1
|
||||
|
||||
struct OlmOutboundGroupSession {
|
||||
/** the Megolm ratchet providing the encryption keys */
|
||||
|
@ -64,6 +67,80 @@ size_t olm_clear_outbound_group_session(
|
|||
return sizeof(OlmOutboundGroupSession);
|
||||
}
|
||||
|
||||
static size_t raw_pickle_length(
|
||||
const OlmOutboundGroupSession *session
|
||||
) {
|
||||
size_t length = 0;
|
||||
length += _olm_pickle_uint32_length(PICKLE_VERSION);
|
||||
length += megolm_pickle_length(&(session->ratchet));
|
||||
length += _olm_pickle_bytes_length(session->session_id,
|
||||
GROUP_SESSION_ID_LENGTH);
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t olm_pickle_outbound_group_session_length(
|
||||
const OlmOutboundGroupSession *session
|
||||
) {
|
||||
return _olm_enc_output_length(raw_pickle_length(session));
|
||||
}
|
||||
|
||||
size_t olm_pickle_outbound_group_session(
|
||||
OlmOutboundGroupSession *session,
|
||||
void const * key, size_t key_length,
|
||||
void * pickled, size_t pickled_length
|
||||
) {
|
||||
size_t raw_length = raw_pickle_length(session);
|
||||
uint8_t *pos;
|
||||
|
||||
if (pickled_length < _olm_enc_output_length(raw_length)) {
|
||||
session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
pos = _olm_enc_output_pos(pickled, raw_length);
|
||||
pos = _olm_pickle_uint32(pos, PICKLE_VERSION);
|
||||
pos = megolm_pickle(&(session->ratchet), pos);
|
||||
pos = _olm_pickle_bytes(pos, session->session_id, GROUP_SESSION_ID_LENGTH);
|
||||
|
||||
return _olm_enc_output(key, key_length, pickled, raw_length);
|
||||
}
|
||||
|
||||
size_t olm_unpickle_outbound_group_session(
|
||||
OlmOutboundGroupSession *session,
|
||||
void const * key, size_t key_length,
|
||||
void * pickled, size_t pickled_length
|
||||
) {
|
||||
const uint8_t *pos;
|
||||
const uint8_t *end;
|
||||
uint32_t pickle_version;
|
||||
|
||||
size_t raw_length = _olm_enc_input(
|
||||
key, key_length, pickled, pickled_length, &(session->last_error)
|
||||
);
|
||||
if (raw_length == (size_t)-1) {
|
||||
return raw_length;
|
||||
}
|
||||
|
||||
pos = pickled;
|
||||
end = pos + raw_length;
|
||||
pos = _olm_unpickle_uint32(pos, end, &pickle_version);
|
||||
if (pickle_version != PICKLE_VERSION) {
|
||||
session->last_error = OLM_UNKNOWN_PICKLE_VERSION;
|
||||
return (size_t)-1;
|
||||
}
|
||||
pos = megolm_unpickle(&(session->ratchet), pos, end);
|
||||
pos = _olm_unpickle_bytes(pos, end, session->session_id, GROUP_SESSION_ID_LENGTH);
|
||||
|
||||
if (end != pos) {
|
||||
/* We had the wrong number of bytes in the input. */
|
||||
session->last_error = OLM_CORRUPTED_PICKLE;
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
return pickled_length;
|
||||
}
|
||||
|
||||
|
||||
size_t olm_init_outbound_group_session_random_length(
|
||||
const OlmOutboundGroupSession *session
|
||||
) {
|
||||
|
|
|
@ -18,16 +18,50 @@
|
|||
|
||||
int main() {
|
||||
|
||||
uint8_t random_bytes[] =
|
||||
"0123456789ABDEF0123456789ABCDEF"
|
||||
"0123456789ABDEF0123456789ABCDEF"
|
||||
"0123456789ABDEF0123456789ABCDEF"
|
||||
"0123456789ABDEF0123456789ABCDEF"
|
||||
"0123456789ABDEF0123456789ABCDEF";
|
||||
{
|
||||
|
||||
TestCase test_case("Pickle outbound group");
|
||||
|
||||
size_t size = olm_outbound_group_session_size();
|
||||
void *memory = alloca(size);
|
||||
OlmOutboundGroupSession *session = olm_outbound_group_session(memory);
|
||||
|
||||
size_t pickle_length = olm_pickle_outbound_group_session_length(session);
|
||||
uint8_t pickle1[pickle_length];
|
||||
olm_pickle_outbound_group_session(session,
|
||||
"secret_key", 10,
|
||||
pickle1, pickle_length);
|
||||
uint8_t pickle2[pickle_length];
|
||||
memcpy(pickle2, pickle1, pickle_length);
|
||||
|
||||
uint8_t buffer2[size];
|
||||
OlmOutboundGroupSession *session2 = olm_outbound_group_session(buffer2);
|
||||
size_t res = olm_unpickle_outbound_group_session(session2,
|
||||
"secret_key", 10,
|
||||
pickle2, pickle_length);
|
||||
assert_not_equals((size_t)-1, res);
|
||||
assert_equals(pickle_length,
|
||||
olm_pickle_outbound_group_session_length(session2));
|
||||
olm_pickle_outbound_group_session(session2,
|
||||
"secret_key", 10,
|
||||
pickle2, pickle_length);
|
||||
|
||||
assert_equals(pickle1, pickle2, pickle_length);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
TestCase test_case("Group message send/receive");
|
||||
|
||||
uint8_t random_bytes[] =
|
||||
"0123456789ABDEF0123456789ABCDEF"
|
||||
"0123456789ABDEF0123456789ABCDEF"
|
||||
"0123456789ABDEF0123456789ABCDEF"
|
||||
"0123456789ABDEF0123456789ABCDEF"
|
||||
"0123456789ABDEF0123456789ABCDEF";
|
||||
|
||||
|
||||
|
||||
size_t size = olm_outbound_group_session_size();
|
||||
void *memory = alloca(size);
|
||||
OlmOutboundGroupSession *session = olm_outbound_group_session(memory);
|
||||
|
|
Loading…
Reference in a new issue