diff options
Diffstat (limited to 'libs/libaxolotl/src/session_state.c')
-rw-r--r-- | libs/libaxolotl/src/session_state.c | 1879 |
1 files changed, 1879 insertions, 0 deletions
diff --git a/libs/libaxolotl/src/session_state.c b/libs/libaxolotl/src/session_state.c new file mode 100644 index 0000000000..db787ebf72 --- /dev/null +++ b/libs/libaxolotl/src/session_state.c @@ -0,0 +1,1879 @@ +#include "session_state.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "hkdf.h" +#include "curve.h" +#include "ratchet.h" +#include "axolotl_internal.h" +#include "LocalStorageProtocol.pb-c.h" + +#include "utlist.h" + +#define MAX_MESSAGE_KEYS 2000 + +typedef struct message_keys_node +{ + ratchet_message_keys message_key; + struct message_keys_node *prev, *next; +} message_keys_node; + +typedef struct session_state_sender_chain +{ + ec_key_pair *sender_ratchet_key_pair; + ratchet_chain_key *chain_key; + message_keys_node *message_keys_head; +} session_state_sender_chain; + +typedef struct session_state_receiver_chain +{ + ec_public_key *sender_ratchet_key; + ratchet_chain_key *chain_key; + message_keys_node *message_keys_head; + struct session_state_receiver_chain *prev, *next; +} session_state_receiver_chain; + +typedef struct session_pending_key_exchange +{ + uint32_t sequence; + ec_key_pair *local_base_key; + ec_key_pair *local_ratchet_key; + ratchet_identity_key_pair *local_identity_key; +} session_pending_key_exchange; + +typedef struct session_pending_pre_key +{ + int has_pre_key_id; + uint32_t pre_key_id; + uint32_t signed_pre_key_id; + ec_public_key *base_key; +} session_pending_pre_key; + +struct session_state +{ + axolotl_type_base base; + + uint32_t session_version; + ec_public_key *local_identity_public; + ec_public_key *remote_identity_public; + + ratchet_root_key *root_key; + uint32_t previous_counter; + + int has_sender_chain; + session_state_sender_chain sender_chain; + + session_state_receiver_chain *receiver_chain_head; + + int has_pending_key_exchange; + session_pending_key_exchange pending_key_exchange; + + int has_pending_pre_key; + session_pending_pre_key pending_pre_key; + + uint32_t remote_registration_id; + uint32_t local_registration_id; + + int needs_refresh; + ec_public_key *alice_base_key; + + axolotl_context *global_context; +}; + +static int session_state_serialize_prepare_sender_chain( + session_state_sender_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure); +static int session_state_serialize_prepare_receiver_chain( + session_state_receiver_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure); +static void session_state_serialize_prepare_chain_free( + Textsecure__SessionStructure__Chain *chain_structure); +static int session_state_serialize_prepare_chain_chain_key( + ratchet_chain_key *chain_key, + Textsecure__SessionStructure__Chain *chain_structure); +static int session_state_serialize_prepare_chain_message_keys_list( + message_keys_node *message_keys_head, + Textsecure__SessionStructure__Chain *chain_structure); +static int session_state_serialize_prepare_message_keys( + ratchet_message_keys *message_key, + Textsecure__SessionStructure__Chain__MessageKey *message_key_structure); +static void session_state_serialize_prepare_message_keys_free( + Textsecure__SessionStructure__Chain__MessageKey *message_key_structure); +static int session_state_serialize_prepare_pending_key_exchange( + session_pending_key_exchange *exchange, + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure); +static void session_state_serialize_prepare_pending_key_exchange_free( + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure); +static int session_state_serialize_prepare_pending_pre_key( + session_pending_pre_key *pre_key, + Textsecure__SessionStructure__PendingPreKey *pre_key_structure); +static void session_state_serialize_prepare_pending_pre_key_free( + Textsecure__SessionStructure__PendingPreKey *pre_key_structure); + +static int session_state_deserialize_protobuf_pending_key_exchange( + session_pending_key_exchange *result_exchange, + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure, + axolotl_context *global_context); +static int session_state_deserialize_protobuf_pending_pre_key( + session_pending_pre_key *result_pre_key, + Textsecure__SessionStructure__PendingPreKey *pre_key_structure, + axolotl_context *global_context); +static int session_state_deserialize_protobuf_sender_chain( + uint32_t session_version, + session_state_sender_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure, + axolotl_context *global_context); +static int session_state_deserialize_protobuf_receiver_chain( + uint32_t session_version, + session_state_receiver_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure, + axolotl_context *global_context); + +static void session_state_free_sender_chain(session_state *state); +static void session_state_free_receiver_chain_node(session_state_receiver_chain *node); +static void session_state_free_receiver_chain(session_state *state); +static session_state_receiver_chain *session_state_find_receiver_chain(const session_state *state, const ec_public_key *sender_ephemeral); + +int session_state_create(session_state **state, axolotl_context *global_context) +{ + session_state *result = malloc(sizeof(session_state)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(session_state)); + AXOLOTL_INIT(result, session_state_destroy); + result->session_version = 2; + result->global_context = global_context; + + *state = result; + return 0; +} + +int session_state_serialize(axolotl_buffer **buffer, session_state *state) +{ + int result = 0; + size_t result_size = 0; + Textsecure__SessionStructure *state_structure = 0; + axolotl_buffer *result_buf = 0; + size_t len = 0; + uint8_t *data = 0; + + state_structure = malloc(sizeof(Textsecure__SessionStructure)); + if(!state_structure) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__init(state_structure); + + result = session_state_serialize_prepare(state, state_structure); + if(result < 0) { + goto complete; + } + + len = textsecure__session_structure__get_packed_size(state_structure); + + result_buf = axolotl_buffer_alloc(len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + result_size = textsecure__session_structure__pack(state_structure, data); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(state_structure) { + session_state_serialize_prepare_free(state_structure); + } + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int session_state_deserialize(session_state **state, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + session_state *result_state = 0; + Textsecure__SessionStructure *session_structure = 0; + + session_structure = textsecure__session_structure__unpack(0, len, data); + if(!session_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + result = session_state_deserialize_protobuf(&result_state, session_structure, global_context); + if(result < 0) { + goto complete; + } + +complete: + if(session_structure) { + textsecure__session_structure__free_unpacked(session_structure, 0); + } + if(result_state) { + if(result < 0) { + AXOLOTL_UNREF(result_state); + } + else { + *state = result_state; + } + } + return result; +} + +int session_state_serialize_prepare(session_state *state, Textsecure__SessionStructure *session_structure) +{ + int result = 0; + + assert(state); + assert(session_structure); + + session_structure->has_sessionversion = 1; + session_structure->sessionversion = state->session_version; + + if(state->local_identity_public) { + result = ec_public_key_serialize_protobuf( + &session_structure->localidentitypublic, state->local_identity_public); + if(result < 0) { + goto complete; + } + session_structure->has_localidentitypublic = 1; + } + + if(state->remote_identity_public) { + result = ec_public_key_serialize_protobuf( + &session_structure->remoteidentitypublic, state->remote_identity_public); + if(result < 0) { + goto complete; + } + session_structure->has_remoteidentitypublic = 1; + } + + if(state->root_key) { + result = ratchet_root_key_get_key_protobuf( + state->root_key, &session_structure->rootkey); + if(result < 0) { + goto complete; + } + session_structure->has_rootkey = 1; + } + + session_structure->has_previouscounter = 1; + session_structure->previouscounter = state->previous_counter; + + + if(state->has_sender_chain) { + session_structure->senderchain = malloc(sizeof(Textsecure__SessionStructure__Chain)); + if(!session_structure->senderchain) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__chain__init(session_structure->senderchain); + result = session_state_serialize_prepare_sender_chain( + &state->sender_chain, session_structure->senderchain); + if(result < 0) { + goto complete; + } + } + + if(state->receiver_chain_head) { + size_t i = 0; + + unsigned int count; + session_state_receiver_chain *cur_node; + DL_COUNT(state->receiver_chain_head, cur_node, count); + + if(count > SIZE_MAX / sizeof(Textsecure__SessionStructure__Chain *)) { + result = AX_ERR_NOMEM; + goto complete; + } + + session_structure->receiverchains = malloc(sizeof(Textsecure__SessionStructure__Chain *) * count); + if(!session_structure->receiverchains) { + result = AX_ERR_NOMEM; + goto complete; + } + + DL_FOREACH(state->receiver_chain_head, cur_node) { + session_structure->receiverchains[i] = malloc(sizeof(Textsecure__SessionStructure__Chain)); + if(!session_structure->receiverchains[i]) { + result = AX_ERR_NOMEM; + break; + } + textsecure__session_structure__chain__init(session_structure->receiverchains[i]); + result = session_state_serialize_prepare_receiver_chain(cur_node, session_structure->receiverchains[i]); + if(result < 0) { + break; + } + i++; + } + session_structure->n_receiverchains = i; + if(result < 0) { + goto complete; + } + } + + if(state->has_pending_key_exchange) { + session_structure->pendingkeyexchange = malloc(sizeof(Textsecure__SessionStructure__PendingKeyExchange)); + if(!session_structure->pendingkeyexchange) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__pending_key_exchange__init(session_structure->pendingkeyexchange); + result = session_state_serialize_prepare_pending_key_exchange( + &state->pending_key_exchange, + session_structure->pendingkeyexchange); + if(result < 0) { + goto complete; + } + } + + if(state->has_pending_pre_key) { + session_structure->pendingprekey = malloc(sizeof(Textsecure__SessionStructure__PendingPreKey)); + if(!session_structure->pendingprekey) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__pending_pre_key__init(session_structure->pendingprekey); + result = session_state_serialize_prepare_pending_pre_key( + &state->pending_pre_key, + session_structure->pendingprekey); + if(result < 0) { + goto complete; + } + } + + session_structure->has_remoteregistrationid = 1; + session_structure->remoteregistrationid = state->remote_registration_id; + + session_structure->has_localregistrationid = 1; + session_structure->localregistrationid = state->local_registration_id; + + session_structure->has_needsrefresh = 1; + session_structure->needsrefresh = state->needs_refresh; + + if(state->alice_base_key) { + result = ec_public_key_serialize_protobuf( + &session_structure->alicebasekey, state->alice_base_key); + if(result < 0) { + goto complete; + } + session_structure->has_alicebasekey = 1; + } + +complete: + return result; +} + +static int session_state_serialize_prepare_sender_chain( + session_state_sender_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure) +{ + int result = 0; + + if(chain->sender_ratchet_key_pair) { + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + + public_key = ec_key_pair_get_public(chain->sender_ratchet_key_pair); + result = ec_public_key_serialize_protobuf(&chain_structure->senderratchetkey, public_key); + if(result < 0) { + goto complete; + } + chain_structure->has_senderratchetkey = 1; + + private_key = ec_key_pair_get_private(chain->sender_ratchet_key_pair); + result = ec_private_key_serialize_protobuf(&chain_structure->senderratchetkeyprivate, private_key); + if(result < 0) { + goto complete; + } + chain_structure->has_senderratchetkeyprivate = 1; + } + + if(chain->chain_key) { + result = session_state_serialize_prepare_chain_chain_key(chain->chain_key, chain_structure); + if(result < 0) { + goto complete; + } + } + + if(chain->message_keys_head) { + result = session_state_serialize_prepare_chain_message_keys_list(chain->message_keys_head, chain_structure); + if(result < 0) { + goto complete; + } + } + +complete: + return result; +} + +static int session_state_serialize_prepare_receiver_chain( + session_state_receiver_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure) +{ + int result = 0; + + if(chain->sender_ratchet_key) { + result = ec_public_key_serialize_protobuf(&chain_structure->senderratchetkey, chain->sender_ratchet_key); + if(result < 0) { + goto complete; + } + chain_structure->has_senderratchetkey = 1; + } + + if(chain->chain_key) { + result = session_state_serialize_prepare_chain_chain_key(chain->chain_key, chain_structure); + if(result < 0) { + goto complete; + } + } + + if(chain->message_keys_head) { + result = session_state_serialize_prepare_chain_message_keys_list(chain->message_keys_head, chain_structure); + if(result < 0) { + goto complete; + } + } + +complete: + return result; +} + +static int session_state_serialize_prepare_chain_chain_key( + ratchet_chain_key *chain_key, + Textsecure__SessionStructure__Chain *chain_structure) +{ + int result = 0; + chain_structure->chainkey = malloc(sizeof(Textsecure__SessionStructure__Chain__ChainKey)); + if(!chain_structure->chainkey) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__chain__chain_key__init(chain_structure->chainkey); + + chain_structure->chainkey->has_index = 1; + chain_structure->chainkey->index = ratchet_chain_key_get_index(chain_key); + + result = ratchet_chain_key_get_key_protobuf(chain_key, &chain_structure->chainkey->key); + if(result < 0) { + goto complete; + } + chain_structure->chainkey->has_key = 1; + +complete: + return result; +} + +static int session_state_serialize_prepare_chain_message_keys_list( + message_keys_node *message_keys_head, + Textsecure__SessionStructure__Chain *chain_structure) +{ + int result = 0; + size_t i = 0; + + unsigned int count; + message_keys_node *cur_node; + DL_COUNT(message_keys_head, cur_node, count); + + if(count == 0) { + goto complete; + } + + if(count > SIZE_MAX / sizeof(Textsecure__SessionStructure__Chain__MessageKey *)) { + result = AX_ERR_NOMEM; + goto complete; + } + + chain_structure->messagekeys = malloc(sizeof(Textsecure__SessionStructure__Chain__MessageKey *) * count); + if(!chain_structure->messagekeys) { + result = AX_ERR_NOMEM; + goto complete; + } + + DL_FOREACH(message_keys_head, cur_node) { + chain_structure->messagekeys[i] = malloc(sizeof(Textsecure__SessionStructure__Chain__MessageKey)); + if(!chain_structure->messagekeys[i]) { + result = AX_ERR_NOMEM; + break; + } + textsecure__session_structure__chain__message_key__init(chain_structure->messagekeys[i]); + + result = session_state_serialize_prepare_message_keys(&cur_node->message_key, chain_structure->messagekeys[i]); + if(result < 0) { + break; + } + i++; + } + chain_structure->n_messagekeys = i; + if(result < 0) { + goto complete; + } + +complete: + return result; +} + +static int session_state_serialize_prepare_message_keys( + ratchet_message_keys *message_key, + Textsecure__SessionStructure__Chain__MessageKey *message_key_structure) +{ + int result = 0; + + message_key_structure->has_index = 1; + message_key_structure->index = message_key->counter; + + message_key_structure->cipherkey.data = malloc(sizeof(message_key->cipher_key)); + if(!message_key_structure->cipherkey.data) { + result = AX_ERR_NOMEM; + goto complete; + } + memcpy(message_key_structure->cipherkey.data, message_key->cipher_key, sizeof(message_key->cipher_key)); + message_key_structure->cipherkey.len = sizeof(message_key->cipher_key); + message_key_structure->has_cipherkey = 1; + + message_key_structure->mackey.data = malloc(sizeof(message_key->mac_key)); + if(!message_key_structure->mackey.data) { + result = AX_ERR_NOMEM; + goto complete; + } + memcpy(message_key_structure->mackey.data, message_key->mac_key, sizeof(message_key->mac_key)); + message_key_structure->mackey.len = sizeof(message_key->mac_key); + message_key_structure->has_mackey = 1; + + message_key_structure->iv.data = malloc(sizeof(message_key->iv)); + if(!message_key_structure->iv.data) { + result = AX_ERR_NOMEM; + goto complete; + } + memcpy(message_key_structure->iv.data, message_key->iv, sizeof(message_key->iv)); + message_key_structure->iv.len = sizeof(message_key->iv); + message_key_structure->has_iv = 1; + +complete: + return result; +} + +static void session_state_serialize_prepare_message_keys_free( + Textsecure__SessionStructure__Chain__MessageKey *message_key_structure) +{ + if(message_key_structure->has_cipherkey) { + free(message_key_structure->cipherkey.data); + } + if(message_key_structure->has_mackey) { + free(message_key_structure->mackey.data); + } + if(message_key_structure->has_iv) { + free(message_key_structure->iv.data); + } + free(message_key_structure); +} + +static int session_state_serialize_prepare_pending_key_exchange( + session_pending_key_exchange *exchange, + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure) +{ + int result = 0; + + exchange_structure->has_sequence = 1; + exchange_structure->sequence = exchange->sequence; + + if(exchange->local_base_key) { + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + + public_key = ec_key_pair_get_public(exchange->local_base_key); + result = ec_public_key_serialize_protobuf(&exchange_structure->localbasekey, public_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localbasekey = 1; + + private_key = ec_key_pair_get_private(exchange->local_base_key); + result = ec_private_key_serialize_protobuf(&exchange_structure->localbasekeyprivate, private_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localbasekeyprivate = 1; + } + + if(exchange->local_ratchet_key) { + ec_public_key *public_key; + ec_private_key *private_key; + + public_key = ec_key_pair_get_public(exchange->local_ratchet_key); + result = ec_public_key_serialize_protobuf(&exchange_structure->localratchetkey, public_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localratchetkey = 1; + + private_key = ec_key_pair_get_private(exchange->local_ratchet_key); + result = ec_private_key_serialize_protobuf(&exchange_structure->localratchetkeyprivate, private_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localratchetkeyprivate = 1; + } + + if(exchange->local_identity_key) { + ec_public_key *public_key; + ec_private_key *private_key; + + public_key = ratchet_identity_key_pair_get_public(exchange->local_identity_key); + result = ec_public_key_serialize_protobuf(&exchange_structure->localidentitykey, public_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localidentitykey = 1; + + private_key = ratchet_identity_key_pair_get_private(exchange->local_identity_key); + result = ec_private_key_serialize_protobuf(&exchange_structure->localidentitykeyprivate, private_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localidentitykeyprivate = 1; + } + +complete: + return result; +} + +static int session_state_serialize_prepare_pending_pre_key( + session_pending_pre_key *pre_key, + Textsecure__SessionStructure__PendingPreKey *pre_key_structure) +{ + int result = 0; + + if(pre_key->has_pre_key_id) { + pre_key_structure->has_prekeyid = 1; + pre_key_structure->prekeyid = pre_key->pre_key_id; + } + + pre_key_structure->has_signedprekeyid = 1; + pre_key_structure->signedprekeyid = (int32_t)pre_key->signed_pre_key_id; + + if(pre_key->base_key) { + result = ec_public_key_serialize_protobuf(&pre_key_structure->basekey, pre_key->base_key); + if(result < 0) { + goto complete; + } + pre_key_structure->has_basekey = 1; + } + +complete: + return result; +} + +void session_state_serialize_prepare_free(Textsecure__SessionStructure *session_structure) +{ + assert(session_structure); + + if(session_structure->has_localidentitypublic) { + free(session_structure->localidentitypublic.data); + } + + if(session_structure->has_remoteidentitypublic) { + free(session_structure->remoteidentitypublic.data); + } + + if(session_structure->has_rootkey) { + free(session_structure->rootkey.data); + } + + if(session_structure->senderchain) { + session_state_serialize_prepare_chain_free(session_structure->senderchain); + } + + if(session_structure->receiverchains) { + unsigned int i; + for(i = 0; i < session_structure->n_receiverchains; i++) { + if(session_structure->receiverchains[i]) { + session_state_serialize_prepare_chain_free(session_structure->receiverchains[i]); + } + } + free(session_structure->receiverchains); + } + + if(session_structure->pendingkeyexchange) { + session_state_serialize_prepare_pending_key_exchange_free(session_structure->pendingkeyexchange); + } + + if(session_structure->pendingprekey) { + session_state_serialize_prepare_pending_pre_key_free(session_structure->pendingprekey); + } + + if(session_structure->has_alicebasekey) { + free(session_structure->alicebasekey.data); + } + + free(session_structure); +} + +static void session_state_serialize_prepare_chain_free( + Textsecure__SessionStructure__Chain *chain_structure) +{ + if(chain_structure->has_senderratchetkey) { + free(chain_structure->senderratchetkey.data); + } + if(chain_structure->has_senderratchetkeyprivate) { + free(chain_structure->senderratchetkeyprivate.data); + } + if(chain_structure->chainkey) { + if(chain_structure->chainkey->has_key) { + free(chain_structure->chainkey->key.data); + } + free(chain_structure->chainkey); + } + if(chain_structure->messagekeys) { + unsigned int i; + for(i = 0; i < chain_structure->n_messagekeys; i++) { + if(chain_structure->messagekeys[i]) { + session_state_serialize_prepare_message_keys_free(chain_structure->messagekeys[i]); + } + } + free(chain_structure->messagekeys); + } + free(chain_structure); +} + +static void session_state_serialize_prepare_pending_key_exchange_free( + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure) +{ + if(exchange_structure->has_localbasekey) { + free(exchange_structure->localbasekey.data); + } + if(exchange_structure->has_localbasekeyprivate) { + free(exchange_structure->localbasekeyprivate.data); + } + if(exchange_structure->has_localratchetkey) { + free(exchange_structure->localratchetkey.data); + } + if(exchange_structure->has_localratchetkeyprivate) { + free(exchange_structure->localratchetkeyprivate.data); + } + if(exchange_structure->has_localidentitykey) { + free(exchange_structure->localidentitykey.data); + } + if(exchange_structure->has_localidentitykeyprivate) { + free(exchange_structure->localidentitykeyprivate.data); + } + free(exchange_structure); +} + +static void session_state_serialize_prepare_pending_pre_key_free( + Textsecure__SessionStructure__PendingPreKey *pre_key_structure) +{ + if(pre_key_structure->has_basekey) { + free(pre_key_structure->basekey.data); + } + + free(pre_key_structure); +} + +int session_state_deserialize_protobuf(session_state **state, Textsecure__SessionStructure *session_structure, axolotl_context *global_context) +{ + int result = 0; + session_state *result_state = 0; + + result = session_state_create(&result_state, global_context); + if(result < 0) { + goto complete; + } + + if(session_structure->has_sessionversion) { + result_state->session_version = session_structure->sessionversion; + } + + if(session_structure->has_localidentitypublic) { + result = curve_decode_point( + &result_state->local_identity_public, + session_structure->localidentitypublic.data, + session_structure->localidentitypublic.len, + global_context); + if(result < 0) { + goto complete; + } + } + + if(session_structure->has_remoteidentitypublic) { + result = curve_decode_point( + &result_state->remote_identity_public, + session_structure->remoteidentitypublic.data, + session_structure->remoteidentitypublic.len, + global_context); + if(result < 0) { + goto complete; + } + } + + if(session_structure->has_rootkey) { + hkdf_context *kdf = 0; + result = hkdf_create(&kdf, (int)result_state->session_version, global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_root_key_create( + &result_state->root_key, kdf, + session_structure->rootkey.data, + session_structure->rootkey.len, global_context); + AXOLOTL_UNREF(kdf); + if(result < 0) { + goto complete; + } + } + + if(session_structure->has_previouscounter) { + result_state->previous_counter = session_structure->previouscounter; + } + + if(session_structure->senderchain) { + session_state_deserialize_protobuf_sender_chain( + result_state->session_version, + &result_state->sender_chain, session_structure->senderchain, + global_context); + if(result < 0) { + goto complete; + } + result_state->has_sender_chain = 1; + } + + if(session_structure->n_receiverchains > 0) { + unsigned int i; + for(i = 0; i < session_structure->n_receiverchains; i++) { + session_state_receiver_chain *node = malloc(sizeof(session_state_receiver_chain)); + if(!node) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(node, 0, sizeof(session_state_receiver_chain)); + + result = session_state_deserialize_protobuf_receiver_chain( + result_state->session_version, + node, session_structure->receiverchains[i], + global_context); + if(result < 0) { + free(node); + goto complete; + } + + DL_APPEND(result_state->receiver_chain_head, node); + } + } + + if(session_structure->pendingkeyexchange) { + result = session_state_deserialize_protobuf_pending_key_exchange( + &result_state->pending_key_exchange, + session_structure->pendingkeyexchange, global_context); + if(result < 0) { + goto complete; + } + result_state->has_pending_key_exchange = 1; + } + + if(session_structure->pendingprekey) { + result = session_state_deserialize_protobuf_pending_pre_key( + &result_state->pending_pre_key, + session_structure->pendingprekey, global_context); + if(result < 0) { + goto complete; + } + result_state->has_pending_pre_key = 1; + } + + if(session_structure->has_remoteregistrationid) { + result_state->remote_registration_id = session_structure->remoteregistrationid; + } + + if(session_structure->has_localregistrationid) { + result_state->local_registration_id = session_structure->localregistrationid; + } + + if(session_structure->has_needsrefresh) { + result_state->needs_refresh = session_structure->needsrefresh; + } + + if(session_structure->has_alicebasekey) { + result = curve_decode_point( + &result_state->alice_base_key, + session_structure->alicebasekey.data, + session_structure->alicebasekey.len, + global_context); + if(result < 0) { + goto complete; + } + } + +complete: + if(result >= 0) { + *state = result_state; + } + else { + if(result_state) { + AXOLOTL_UNREF(result_state); + } + } + return result; +} + +static int session_state_deserialize_protobuf_pending_key_exchange( + session_pending_key_exchange *result_exchange, + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure, + axolotl_context *global_context) +{ + int result = 0; + + ec_key_pair *local_base_key = 0; + ec_public_key *local_base_key_public = 0; + ec_private_key *local_base_key_private = 0; + + ec_key_pair *local_ratchet_key = 0; + ec_public_key *local_ratchet_key_public = 0; + ec_private_key *local_ratchet_key_private = 0; + + ratchet_identity_key_pair *local_identity_key = 0; + ec_public_key *local_identity_key_public = 0; + ec_private_key *local_identity_key_private = 0; + + if(exchange_structure->has_localbasekey && exchange_structure->has_localbasekeyprivate) { + result = curve_decode_point(&local_base_key_public, + exchange_structure->localbasekey.data, + exchange_structure->localbasekey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&local_base_key_private, + exchange_structure->localbasekeyprivate.data, + exchange_structure->localbasekeyprivate.len, + global_context); + if(result < 0) { + goto complete; + } + + result = ec_key_pair_create(&local_base_key, + local_base_key_public, local_base_key_private); + if(result < 0) { + goto complete; + } + } + + if(exchange_structure->has_localratchetkey && exchange_structure->has_localratchetkeyprivate) { + result = curve_decode_point(&local_ratchet_key_public, + exchange_structure->localratchetkey.data, + exchange_structure->localratchetkey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&local_ratchet_key_private, + exchange_structure->localratchetkeyprivate.data, + exchange_structure->localratchetkeyprivate.len, + global_context); + if(result < 0) { + goto complete; + } + + result = ec_key_pair_create(&local_ratchet_key, + local_ratchet_key_public, local_ratchet_key_private); + if(result < 0) { + goto complete; + } + } + + if(exchange_structure->has_localidentitykey && exchange_structure->has_localidentitykeyprivate) { + result = curve_decode_point(&local_identity_key_public, + exchange_structure->localidentitykey.data, + exchange_structure->localidentitykey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&local_identity_key_private, + exchange_structure->localidentitykeyprivate.data, + exchange_structure->localidentitykeyprivate.len, + global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_identity_key_pair_create(&local_identity_key, + local_identity_key_public, + local_identity_key_private); + if(result < 0) { + goto complete; + } + } + + result_exchange->sequence = exchange_structure->sequence; + result_exchange->local_base_key = local_base_key; + result_exchange->local_ratchet_key = local_ratchet_key; + result_exchange->local_identity_key = local_identity_key; + +complete: + AXOLOTL_UNREF(local_base_key_public); + AXOLOTL_UNREF(local_base_key_private); + AXOLOTL_UNREF(local_ratchet_key_public); + AXOLOTL_UNREF(local_ratchet_key_private); + AXOLOTL_UNREF(local_identity_key_public); + AXOLOTL_UNREF(local_identity_key_private); + + if(result < 0) { + AXOLOTL_UNREF(local_base_key); + AXOLOTL_UNREF(local_ratchet_key); + AXOLOTL_UNREF(local_identity_key); + } + + return result; +} + +static int session_state_deserialize_protobuf_pending_pre_key( + session_pending_pre_key *result_pre_key, + Textsecure__SessionStructure__PendingPreKey *pre_key_structure, + axolotl_context *global_context) +{ + int result = 0; + + if(pre_key_structure->has_basekey) { + ec_public_key *base_key = 0; + result = curve_decode_point(&base_key, + pre_key_structure->basekey.data, + pre_key_structure->basekey.len, + global_context); + if(result < 0) { + goto complete; + } + result_pre_key->base_key = base_key; + } + + if(pre_key_structure->has_prekeyid) { + result_pre_key->has_pre_key_id = 1; + result_pre_key->pre_key_id = pre_key_structure->prekeyid; + } + + if(pre_key_structure->has_signedprekeyid) { + result_pre_key->signed_pre_key_id = (uint32_t)pre_key_structure->signedprekeyid; + } + +complete: + return result; +} + +static int session_state_deserialize_protobuf_sender_chain( + uint32_t session_version, + session_state_sender_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure, + axolotl_context *global_context) +{ + int result = 0; + hkdf_context *kdf = 0; + ec_key_pair *sender_ratchet_key_pair = 0; + ec_public_key *sender_ratchet_key_public = 0; + ec_private_key *sender_ratchet_key_private = 0; + ratchet_chain_key *sender_chain_key = 0; + + if(chain_structure->has_senderratchetkey && chain_structure->has_senderratchetkeyprivate) { + result = curve_decode_point(&sender_ratchet_key_public, + chain_structure->senderratchetkey.data, + chain_structure->senderratchetkey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&sender_ratchet_key_private, + chain_structure->senderratchetkeyprivate.data, + chain_structure->senderratchetkeyprivate.len, + global_context); + if(result < 0) { + goto complete; + } + + result = ec_key_pair_create(&sender_ratchet_key_pair, + sender_ratchet_key_public, sender_ratchet_key_private); + if(result < 0) { + goto complete; + } + } + + if(chain_structure->chainkey && chain_structure->chainkey->has_key && chain_structure->chainkey->has_index) { + result = hkdf_create(&kdf, (int)session_version, global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_chain_key_create( + &sender_chain_key, kdf, + chain_structure->chainkey->key.data, + chain_structure->chainkey->key.len, + chain_structure->chainkey->index, + global_context); + if(result < 0) { + goto complete; + } + } + + chain->sender_ratchet_key_pair = sender_ratchet_key_pair; + chain->chain_key = sender_chain_key; + +complete: + AXOLOTL_UNREF(kdf); + AXOLOTL_UNREF(sender_ratchet_key_public); + AXOLOTL_UNREF(sender_ratchet_key_private); + if(result < 0) { + AXOLOTL_UNREF(sender_ratchet_key_pair); + AXOLOTL_UNREF(sender_chain_key); + } + return result; +} + +static int session_state_deserialize_protobuf_receiver_chain( + uint32_t session_version, + session_state_receiver_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure, + axolotl_context *global_context) +{ + int result = 0; + + hkdf_context *kdf = 0; + ec_public_key *sender_ratchet_key = 0; + ratchet_chain_key *chain_key = 0; + message_keys_node *message_keys_head = 0; + + if(chain_structure->has_senderratchetkey) { + result = curve_decode_point(&sender_ratchet_key, + chain_structure->senderratchetkey.data, + chain_structure->senderratchetkey.len, + global_context); + if(result < 0) { + goto complete; + } + } + + if(chain_structure->chainkey && chain_structure->chainkey->has_key && chain_structure->chainkey->has_index) { + result = hkdf_create(&kdf, (int)session_version, global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_chain_key_create( + &chain_key, kdf, + chain_structure->chainkey->key.data, + chain_structure->chainkey->key.len, + chain_structure->chainkey->index, + global_context); + if(result < 0) { + goto complete; + } + } + + if(chain_structure->n_messagekeys > 0) { + unsigned int i; + for(i = 0; i < chain_structure->n_messagekeys; i++) { + Textsecure__SessionStructure__Chain__MessageKey *key_structure = + chain_structure->messagekeys[i]; + + message_keys_node *node = malloc(sizeof(message_keys_node)); + if(!node) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(node, 0, sizeof(message_keys_node)); + + if(key_structure->has_index) { + node->message_key.counter = key_structure->index; + } + if(key_structure->has_cipherkey && key_structure->cipherkey.len == sizeof(node->message_key.cipher_key)) { + memcpy(node->message_key.cipher_key, key_structure->cipherkey.data, key_structure->cipherkey.len); + } + if(key_structure->has_mackey && key_structure->mackey.len == sizeof(node->message_key.mac_key)) { + memcpy(node->message_key.mac_key, key_structure->mackey.data, key_structure->mackey.len); + } + if(key_structure->has_iv && key_structure->iv.len == sizeof(node->message_key.iv)) { + memcpy(node->message_key.iv, key_structure->iv.data, key_structure->iv.len); + } + + DL_APPEND(message_keys_head, node); + } + } + + chain->sender_ratchet_key = sender_ratchet_key; + chain->chain_key = chain_key; + chain->message_keys_head = message_keys_head; + +complete: + AXOLOTL_UNREF(kdf); + if(result < 0) { + AXOLOTL_UNREF(sender_ratchet_key); + AXOLOTL_UNREF(chain_key); + if(message_keys_head) { + message_keys_node *cur_node; + message_keys_node *tmp_node; + DL_FOREACH_SAFE(message_keys_head, cur_node, tmp_node) { + DL_DELETE(message_keys_head, cur_node); + axolotl_explicit_bzero(&cur_node->message_key, sizeof(ratchet_message_keys)); + free(cur_node); + } + } + } + return result; +} + +int session_state_copy(session_state **state, session_state *other_state, axolotl_context *global_context) +{ + int result = 0; + axolotl_buffer *buffer = 0; + size_t len = 0; + uint8_t *data = 0; + + assert(other_state); + assert(global_context); + + result = session_state_serialize(&buffer, other_state); + if(result < 0) { + goto complete; + } + + data = axolotl_buffer_data(buffer); + len = axolotl_buffer_len(buffer); + + result = session_state_deserialize(state, data, len, global_context); + if(result < 0) { + goto complete; + } + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + return result; +} + +void session_state_set_session_version(session_state *state, uint32_t version) +{ + assert(state); + state->session_version = version; +} + +uint32_t session_state_get_session_version(const session_state *state) +{ + assert(state); + return state->session_version; +} + +void session_state_set_local_identity_key(session_state *state, ec_public_key *identity_key) +{ + assert(state); + assert(identity_key); + if(state->local_identity_public) { + AXOLOTL_UNREF(state->local_identity_public); + } + AXOLOTL_REF(identity_key); + state->local_identity_public = identity_key; +} + +ec_public_key *session_state_get_local_identity_key(const session_state *state) +{ + assert(state); + return state->local_identity_public; +} + +void session_state_set_remote_identity_key(session_state *state, ec_public_key *identity_key) +{ + assert(state); + assert(identity_key); + if(state->remote_identity_public) { + AXOLOTL_UNREF(state->remote_identity_public); + } + AXOLOTL_REF(identity_key); + state->remote_identity_public = identity_key; +} + +ec_public_key *session_state_get_remote_identity_key(const session_state *state) +{ + assert(state); + return state->remote_identity_public; +} + +void session_state_set_root_key(session_state *state, ratchet_root_key *root_key) +{ + assert(state); + assert(root_key); + if(state->root_key) { + AXOLOTL_UNREF(state->root_key); + } + AXOLOTL_REF(root_key); + state->root_key = root_key; +} + +ratchet_root_key *session_state_get_root_key(const session_state *state) +{ + assert(state); + return state->root_key; +} + +void session_state_set_previous_counter(session_state *state, uint32_t counter) +{ + assert(state); + state->previous_counter = counter; +} + +uint32_t session_state_get_previous_counter(const session_state *state) +{ + assert(state); + return state->previous_counter; +} + +void session_state_set_sender_chain(session_state *state, ec_key_pair *sender_ratchet_key_pair, ratchet_chain_key *chain_key) +{ + assert(state); + assert(sender_ratchet_key_pair); + assert(chain_key); + + state->has_sender_chain = 1; + + if(state->sender_chain.sender_ratchet_key_pair) { + AXOLOTL_UNREF(state->sender_chain.sender_ratchet_key_pair); + } + AXOLOTL_REF(sender_ratchet_key_pair); + state->sender_chain.sender_ratchet_key_pair = sender_ratchet_key_pair; + + if(state->sender_chain.chain_key) { + AXOLOTL_UNREF(state->sender_chain.chain_key); + } + AXOLOTL_REF(chain_key); + state->sender_chain.chain_key = chain_key; +} + +ec_public_key *session_state_get_sender_ratchet_key(const session_state *state) +{ + assert(state); + if(state->sender_chain.sender_ratchet_key_pair) { + return ec_key_pair_get_public(state->sender_chain.sender_ratchet_key_pair); + } + else { + return 0; + } +} + +ec_key_pair *session_state_get_sender_ratchet_key_pair(const session_state *state) +{ + assert(state); + return state->sender_chain.sender_ratchet_key_pair; +} + +ratchet_chain_key *session_state_get_sender_chain_key(const session_state *state) +{ + assert(state); + return state->sender_chain.chain_key; +} + +int session_state_set_sender_chain_key(session_state *state, ratchet_chain_key *chain_key) +{ + assert(state); + if(state->has_sender_chain) { + if(state->sender_chain.chain_key) { + AXOLOTL_UNREF(state->sender_chain.chain_key); + } + AXOLOTL_REF(chain_key); + state->sender_chain.chain_key = chain_key; + return 0; + } + else { + return AX_ERR_UNKNOWN; + } +} + +int session_state_has_sender_chain(const session_state *state) +{ + assert(state); + return state->has_sender_chain; +} + +int session_state_has_message_keys(session_state *state, ec_public_key *sender_ephemeral, uint32_t counter) +{ + session_state_receiver_chain *chain = 0; + message_keys_node *cur_node = 0; + + assert(state); + assert(sender_ephemeral); + + chain = session_state_find_receiver_chain(state, sender_ephemeral); + if(!chain) { + return 0; + } + + DL_FOREACH(chain->message_keys_head, cur_node) { + if(cur_node->message_key.counter == counter) { + return 1; + } + } + + return 0; +} + +int session_state_remove_message_keys(session_state *state, + ratchet_message_keys *message_keys_result, + ec_public_key *sender_ephemeral, uint32_t counter) +{ + session_state_receiver_chain *chain = 0; + message_keys_node *cur_node = 0; + message_keys_node *tmp_node = 0; + + assert(state); + assert(message_keys_result); + assert(sender_ephemeral); + + chain = session_state_find_receiver_chain(state, sender_ephemeral); + if(!chain) { + return 0; + } + + DL_FOREACH_SAFE(chain->message_keys_head, cur_node, tmp_node) { + if(cur_node->message_key.counter == counter) { + memcpy(message_keys_result, &(cur_node->message_key), sizeof(ratchet_message_keys)); + DL_DELETE(chain->message_keys_head, cur_node); + axolotl_explicit_bzero(&cur_node->message_key, sizeof(ratchet_message_keys)); + free(cur_node); + return 1; + } + } + + return 0; +} + +int session_state_set_message_keys(session_state *state, + ec_public_key *sender_ephemeral, ratchet_message_keys *message_keys) +{ + session_state_receiver_chain *chain = 0; + message_keys_node *node = 0; + int count; + + assert(state); + assert(sender_ephemeral); + assert(message_keys); + + chain = session_state_find_receiver_chain(state, sender_ephemeral); + if(!chain) { + return 0; + } + + node = malloc(sizeof(message_keys_node)); + if(!node) { + return AX_ERR_NOMEM; + } + memcpy(&(node->message_key), message_keys, sizeof(ratchet_message_keys)); + node->prev = 0; + node->next = 0; + + DL_APPEND(chain->message_keys_head, node); + + DL_COUNT(chain->message_keys_head, node, count); + while(count > MAX_MESSAGE_KEYS) { + node = chain->message_keys_head; + DL_DELETE(chain->message_keys_head, node); + axolotl_explicit_bzero(&node->message_key, sizeof(ratchet_message_keys)); + free(node); + --count; + } + + return 0; +} + +int session_state_add_receiver_chain(session_state *state, ec_public_key *sender_ratchet_key, ratchet_chain_key *chain_key) +{ + session_state_receiver_chain *node; + int count; + + assert(state); + assert(sender_ratchet_key); + assert(chain_key); + + node = malloc(sizeof(session_state_receiver_chain)); + if(!node) { + return AX_ERR_NOMEM; + } + memset(node, 0, sizeof(session_state_receiver_chain)); + + AXOLOTL_REF(sender_ratchet_key); + node->sender_ratchet_key = sender_ratchet_key; + AXOLOTL_REF(chain_key); + node->chain_key = chain_key; + + DL_APPEND(state->receiver_chain_head, node); + + DL_COUNT(state->receiver_chain_head, node, count); + while(count > 5) { + node = state->receiver_chain_head; + DL_DELETE(state->receiver_chain_head, node); + session_state_free_receiver_chain_node(node); + --count; + } + + return 0; +} + +int session_state_set_receiver_chain_key(session_state *state, ec_public_key *sender_ephemeral, ratchet_chain_key *chain_key) +{ + int result = 0; + session_state_receiver_chain *node; + + assert(state); + assert(sender_ephemeral); + assert(chain_key); + + node = session_state_find_receiver_chain(state, sender_ephemeral); + if(!node) { + axolotl_log(state->global_context, AX_LOG_WARNING, "Couldn't find receiver chain to set chain key on"); + result = AX_ERR_UNKNOWN; + goto complete; + } + + AXOLOTL_UNREF(node->chain_key); + AXOLOTL_REF(chain_key); + node->chain_key = chain_key; + +complete: + return result; +} + +static session_state_receiver_chain *session_state_find_receiver_chain(const session_state *state, const ec_public_key *sender_ephemeral) +{ + session_state_receiver_chain *result = 0; + + session_state_receiver_chain *cur_node; + DL_FOREACH(state->receiver_chain_head, cur_node) { + if(ec_public_key_compare(cur_node->sender_ratchet_key, sender_ephemeral) == 0) { + result = cur_node; + break; + } + } + + return result; +} + +ratchet_chain_key *session_state_get_receiver_chain_key(session_state *state, ec_public_key *sender_ephemeral) +{ + ratchet_chain_key *result = 0; + session_state_receiver_chain *node = session_state_find_receiver_chain(state, sender_ephemeral); + + if(node) { + result = node->chain_key; + } + + return result; +} + +void session_state_set_pending_key_exchange(session_state *state, + uint32_t sequence, + ec_key_pair *our_base_key, ec_key_pair *our_ratchet_key, + ratchet_identity_key_pair *our_identity_key) +{ + assert(state); + assert(our_base_key); + assert(our_ratchet_key); + assert(our_identity_key); + + if(state->pending_key_exchange.local_base_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_base_key); + state->pending_key_exchange.local_base_key = 0; + } + if(state->pending_key_exchange.local_ratchet_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_ratchet_key); + state->pending_key_exchange.local_ratchet_key = 0; + } + if(state->pending_key_exchange.local_identity_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_identity_key); + state->pending_key_exchange.local_identity_key = 0; + } + + AXOLOTL_REF(our_base_key); + AXOLOTL_REF(our_ratchet_key); + AXOLOTL_REF(our_identity_key); + + state->has_pending_key_exchange = 1; + state->pending_key_exchange.sequence = sequence; + state->pending_key_exchange.local_base_key = our_base_key; + state->pending_key_exchange.local_ratchet_key = our_ratchet_key; + state->pending_key_exchange.local_identity_key = our_identity_key; +} + +uint32_t session_state_get_pending_key_exchange_sequence(session_state *state) +{ + assert(state); + if(state->has_pending_key_exchange) { + return state->pending_key_exchange.sequence; + } + else { + return 0; + } +} + +ec_key_pair *session_state_get_pending_key_exchange_base_key(const session_state *state) +{ + assert(state); + if(state->has_pending_key_exchange) { + return state->pending_key_exchange.local_base_key; + } + else { + return 0; + } +} + +ec_key_pair *session_state_get_pending_key_exchange_ratchet_key(const session_state *state) +{ + assert(state); + if(state->has_pending_key_exchange) { + return state->pending_key_exchange.local_ratchet_key; + } + else { + return 0; + } +} + +ratchet_identity_key_pair *session_state_get_pending_key_exchange_identity_key(const session_state *state) +{ + assert(state); + if(state->has_pending_key_exchange) { + return state->pending_key_exchange.local_identity_key; + } + else { + return 0; + } +} + +int session_state_has_pending_key_exchange(const session_state *state) +{ + assert(state); + return state->has_pending_key_exchange; +} + +void session_state_set_unacknowledged_pre_key_message(session_state *state, + const uint32_t *pre_key_id, uint32_t signed_pre_key_id, ec_public_key *base_key) +{ + assert(state); + assert(base_key); + + if(state->pending_pre_key.base_key) { + AXOLOTL_UNREF(state->pending_pre_key.base_key); + state->pending_pre_key.base_key = 0; + } + + AXOLOTL_REF(base_key); + + state->has_pending_pre_key = 1; + if(pre_key_id) { + state->pending_pre_key.has_pre_key_id = 1; + state->pending_pre_key.pre_key_id = *pre_key_id; + } + else { + state->pending_pre_key.has_pre_key_id = 0; + state->pending_pre_key.pre_key_id = 0; + } + state->pending_pre_key.signed_pre_key_id = signed_pre_key_id; + state->pending_pre_key.base_key = base_key; +} + +int session_state_unacknowledged_pre_key_message_has_pre_key_id(const session_state *state) +{ + assert(state); + return state->pending_pre_key.has_pre_key_id; +} + +uint32_t session_state_unacknowledged_pre_key_message_get_pre_key_id(const session_state *state) +{ + assert(state); + assert(state->pending_pre_key.has_pre_key_id); + return state->pending_pre_key.pre_key_id; +} + +uint32_t session_state_unacknowledged_pre_key_message_get_signed_pre_key_id(const session_state *state) +{ + assert(state); + return state->pending_pre_key.signed_pre_key_id; +} + +ec_public_key *session_state_unacknowledged_pre_key_message_get_base_key(const session_state *state) +{ + assert(state); + return state->pending_pre_key.base_key; +} + +int session_state_has_unacknowledged_pre_key_message(const session_state *state) +{ + assert(state); + return state->has_pending_pre_key; +} + +void session_state_clear_unacknowledged_pre_key_message(session_state *state) +{ + assert(state); + if(state->pending_pre_key.base_key) { + AXOLOTL_UNREF(state->pending_pre_key.base_key); + } + memset(&state->pending_pre_key, 0, sizeof(state->pending_pre_key)); + state->has_pending_pre_key = 0; +} + +void session_state_set_remote_registration_id(session_state *state, uint32_t id) +{ + assert(state); + state->remote_registration_id = id; +} + +uint32_t session_state_get_remote_registration_id(const session_state *state) +{ + assert(state); + return state->remote_registration_id; +} + +void session_state_set_local_registration_id(session_state *state, uint32_t id) +{ + assert(state); + state->local_registration_id = id; +} + +uint32_t session_state_get_local_registration_id(const session_state *state) +{ + assert(state); + return state->local_registration_id; +} + +void session_state_set_needs_refresh(session_state *state, int value) +{ + assert(state); + assert(value == 0 || value == 1); + state->needs_refresh = value; +} + +int session_state_get_needs_refresh(const session_state *state) +{ + assert(state); + return state->needs_refresh; +} + +void session_state_set_alice_base_key(session_state *state, ec_public_key *key) +{ + assert(state); + assert(key); + + if(state->alice_base_key) { + AXOLOTL_UNREF(state->alice_base_key); + } + AXOLOTL_REF(key); + state->alice_base_key = key; +} + +ec_public_key *session_state_get_alice_base_key(const session_state *state) +{ + assert(state); + return state->alice_base_key; +} + +static void session_state_free_sender_chain(session_state *state) +{ + if(state->sender_chain.sender_ratchet_key_pair) { + AXOLOTL_UNREF(state->sender_chain.sender_ratchet_key_pair); + state->sender_chain.sender_ratchet_key_pair = 0; + } + if(state->sender_chain.chain_key) { + AXOLOTL_UNREF(state->sender_chain.chain_key); + state->sender_chain.chain_key = 0; + } + + if(state->sender_chain.message_keys_head) { + message_keys_node *cur_node; + message_keys_node *tmp_node; + DL_FOREACH_SAFE(state->sender_chain.message_keys_head, cur_node, tmp_node) { + DL_DELETE(state->sender_chain.message_keys_head, cur_node); + axolotl_explicit_bzero(&cur_node->message_key, sizeof(ratchet_message_keys)); + free(cur_node); + } + state->sender_chain.message_keys_head = 0; + } +} + +static void session_state_free_receiver_chain_node(session_state_receiver_chain *node) +{ + if(node->sender_ratchet_key) { + AXOLOTL_UNREF(node->sender_ratchet_key); + } + if(node->chain_key) { + AXOLOTL_UNREF(node->chain_key); + } + + if(node->message_keys_head) { + message_keys_node *cur_node; + message_keys_node *tmp_node; + DL_FOREACH_SAFE(node->message_keys_head, cur_node, tmp_node) { + DL_DELETE(node->message_keys_head, cur_node); + axolotl_explicit_bzero(&cur_node->message_key, sizeof(ratchet_message_keys)); + free(cur_node); + } + node->message_keys_head = 0; + } + + free(node); +} + +static void session_state_free_receiver_chain(session_state *state) +{ + session_state_receiver_chain *cur_node; + session_state_receiver_chain *tmp_node; + DL_FOREACH_SAFE(state->receiver_chain_head, cur_node, tmp_node) { + DL_DELETE(state->receiver_chain_head, cur_node); + session_state_free_receiver_chain_node(cur_node); + } + state->receiver_chain_head = 0; +} + +void session_state_destroy(axolotl_type_base *type) +{ + session_state *state = (session_state *)type; + + if(state->local_identity_public) { + AXOLOTL_UNREF(state->local_identity_public); + } + if(state->remote_identity_public) { + AXOLOTL_UNREF(state->remote_identity_public); + } + if(state->root_key) { + AXOLOTL_UNREF(state->root_key); + } + session_state_free_sender_chain(state); + session_state_free_receiver_chain(state); + if(state->has_pending_key_exchange) { + if(state->pending_key_exchange.local_base_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_base_key); + } + if(state->pending_key_exchange.local_ratchet_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_ratchet_key); + } + if(state->pending_key_exchange.local_identity_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_identity_key); + } + } + if(state->has_pending_pre_key) { + if(state->pending_pre_key.base_key) { + AXOLOTL_UNREF(state->pending_pre_key.base_key); + } + } + if(state->alice_base_key) { + AXOLOTL_UNREF(state->alice_base_key); + } + free(state); +} |