diff options
Diffstat (limited to 'libs/libaxolotl/src/session_builder.c')
-rw-r--r-- | libs/libaxolotl/src/session_builder.c | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/libs/libaxolotl/src/session_builder.c b/libs/libaxolotl/src/session_builder.c new file mode 100644 index 0000000000..b962c21b5f --- /dev/null +++ b/libs/libaxolotl/src/session_builder.c @@ -0,0 +1,867 @@ +#include "session_builder.h" + +#include <assert.h> +#include <string.h> +#include "axolotl_internal.h" +#include "session_pre_key.h" +#include "session_record.h" +#include "session_state.h" +#include "ratchet.h" +#include "protocol.h" +#include "key_helper.h" + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +struct session_builder +{ + axolotl_store_context *store; + const axolotl_address *remote_address; + axolotl_context *global_context; +}; + +static int session_builder_process_pre_key_whisper_message_v2(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id); +static int session_builder_process_pre_key_whisper_message_v3(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id); +static int session_builder_process_initiate(session_builder *builder, + key_exchange_message *message, key_exchange_message **response_message); +static int session_builder_process_response(session_builder *builder, + key_exchange_message *message); + +int session_builder_create(session_builder **builder, + axolotl_store_context *store, const axolotl_address *remote_address, + axolotl_context *global_context) +{ + session_builder *result = 0; + + assert(store); + assert(global_context); + + result = malloc(sizeof(session_builder)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(session_builder)); + + result->store = store; + result->remote_address = remote_address; + result->global_context = global_context; + + *builder = result; + return 0; +} + +int session_builder_process_pre_key_whisper_message(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id) +{ + int result = 0; + int has_unsigned_pre_key_id_result = 0; + uint32_t unsigned_pre_key_id_result = 0; + int message_version = pre_key_whisper_message_get_message_version(message); + ec_public_key *their_identity_key = pre_key_whisper_message_get_identity_key(message); + + result = axolotl_identity_is_trusted_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + their_identity_key); + if(result < 0) { + goto complete; + } + if(result == 0) { + result = AX_ERR_UNTRUSTED_IDENTITY; + goto complete; + } + + if(message_version == 2) { + result = session_builder_process_pre_key_whisper_message_v2(builder, record, message, &unsigned_pre_key_id_result); + } + else if(message_version == 3) { + result = session_builder_process_pre_key_whisper_message_v3(builder, record, message, &unsigned_pre_key_id_result); + } + else { + axolotl_log(builder->global_context, AX_LOG_WARNING, "Unknown version: %d", message_version); + result = AX_ERR_INVALID_VERSION; + } + if(result < 0) { + goto complete; + } + has_unsigned_pre_key_id_result = result; + + result = axolotl_identity_save_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + their_identity_key); + if(result < 0) { + goto complete; + } + + result = has_unsigned_pre_key_id_result; + +complete: + if(result >= 0) { + *unsigned_pre_key_id = unsigned_pre_key_id_result; + } + return result; +} + +static int session_builder_process_pre_key_whisper_message_v2(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id) +{ + int result = 0; + uint32_t unsigned_pre_key_id_result = 0; + session_pre_key *our_pre_key = 0; + ratchet_identity_key_pair *our_identity_key = 0; + bob_axolotl_parameters *parameters = 0; + uint32_t pre_key_id = 0; + int contains_session = 0; + session_state *state = 0; + int contains_key = 0; + uint32_t local_registration_id = 0; + + if(!pre_key_whisper_message_has_pre_key_id(message)) { + axolotl_log(builder->global_context, AX_LOG_WARNING, "V2 message requires one time pre key ID!"); + result = AX_ERR_INVALID_KEY_ID; + goto complete; + } + + pre_key_id = pre_key_whisper_message_get_pre_key_id(message); + + contains_key = axolotl_pre_key_contains_key(builder->store, pre_key_id); + if(contains_key < 0) { + result = contains_key; + goto complete; + } + + contains_session = axolotl_session_contains_session(builder->store, builder->remote_address); + if(contains_session < 0) { + result = contains_session; + goto complete; + } + + if(!contains_key && contains_session) { + axolotl_log(builder->global_context, AX_LOG_INFO, "We've already processed the prekey part of this V2 session, letting bundled message fall through..."); + result = 0; + goto complete; + } + + result = axolotl_pre_key_load_key(builder->store, &our_pre_key, pre_key_id); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_key_pair(builder->store, &our_identity_key); + if(result < 0) { + goto complete; + } + + result = bob_axolotl_parameters_create( + ¶meters, + our_identity_key, + session_pre_key_get_key_pair(our_pre_key), + 0, + session_pre_key_get_key_pair(our_pre_key), + pre_key_whisper_message_get_identity_key(message), + pre_key_whisper_message_get_base_key(message)); + if(result < 0) { + goto complete; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + + result = ratcheting_session_bob_initialize( + state, + pre_key_whisper_message_get_message_version(message), + parameters, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_local_registration_id(builder->store, &local_registration_id); + if(result < 0) { + goto complete; + } + + session_state_set_local_registration_id(state, local_registration_id); + session_state_set_remote_registration_id(state, + pre_key_whisper_message_get_registration_id(message)); + session_state_set_alice_base_key(state, + pre_key_whisper_message_get_base_key(message));; + + if(pre_key_whisper_message_get_pre_key_id(message) != PRE_KEY_MEDIUM_MAX_VALUE) { + unsigned_pre_key_id_result = pre_key_whisper_message_get_pre_key_id(message); + result = 1; + } + else { + result = 0; + } + +complete: + AXOLOTL_UNREF(parameters); + AXOLOTL_UNREF(our_identity_key); + AXOLOTL_UNREF(our_pre_key); + if(result >= 0) { + *unsigned_pre_key_id = unsigned_pre_key_id_result; + } + return result; +} + +static int session_builder_process_pre_key_whisper_message_v3(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id) +{ + int result = 0; + uint32_t unsigned_pre_key_id_result = 0; + session_signed_pre_key *our_signed_pre_key = 0; + ratchet_identity_key_pair *our_identity_key = 0; + bob_axolotl_parameters *parameters = 0; + session_pre_key *session_our_one_time_pre_key = 0; + ec_key_pair *our_one_time_pre_key = 0; + session_state *state = 0; + uint32_t local_registration_id = 0; + + int has_session_state = session_record_has_session_state(record, + pre_key_whisper_message_get_message_version(message), + pre_key_whisper_message_get_base_key(message)); + if(has_session_state) { + axolotl_log(builder->global_context, AX_LOG_INFO, "We've already setup a session for this V3 message, letting bundled message fall through..."); + result = 0; + goto complete; + } + + result = axolotl_signed_pre_key_load_key(builder->store, + &our_signed_pre_key, + pre_key_whisper_message_get_signed_pre_key_id(message)); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_key_pair(builder->store, &our_identity_key); + if(result < 0) { + goto complete; + } + + if(pre_key_whisper_message_has_pre_key_id(message)) { + result = axolotl_pre_key_load_key(builder->store, + &session_our_one_time_pre_key, + pre_key_whisper_message_get_pre_key_id(message)); + if(result < 0) { + goto complete; + } + our_one_time_pre_key = session_pre_key_get_key_pair(session_our_one_time_pre_key); + } + + result = bob_axolotl_parameters_create( + ¶meters, + our_identity_key, + session_signed_pre_key_get_key_pair(our_signed_pre_key), + our_one_time_pre_key, + session_signed_pre_key_get_key_pair(our_signed_pre_key), + pre_key_whisper_message_get_identity_key(message), + pre_key_whisper_message_get_base_key(message)); + if(result < 0) { + goto complete; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + + result = ratcheting_session_bob_initialize( + state, + pre_key_whisper_message_get_message_version(message), + parameters, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_local_registration_id(builder->store, &local_registration_id); + if(result < 0) { + goto complete; + } + + session_state_set_local_registration_id(state, local_registration_id); + session_state_set_remote_registration_id(state, + pre_key_whisper_message_get_registration_id(message)); + session_state_set_alice_base_key(state, + pre_key_whisper_message_get_base_key(message));; + + if(pre_key_whisper_message_has_pre_key_id(message) && + pre_key_whisper_message_get_pre_key_id(message) != PRE_KEY_MEDIUM_MAX_VALUE) { + unsigned_pre_key_id_result = pre_key_whisper_message_get_pre_key_id(message); + result = 1; + } + else { + result = 0; + } + +complete: + AXOLOTL_UNREF(parameters); + AXOLOTL_UNREF(our_identity_key); + AXOLOTL_UNREF(our_signed_pre_key); + AXOLOTL_UNREF(session_our_one_time_pre_key); + if(result >= 0) { + *unsigned_pre_key_id = unsigned_pre_key_id_result; + } + return result; +} + +int session_builder_process_pre_key_bundle(session_builder *builder, session_pre_key_bundle *bundle) +{ + int result = 0; + session_record *record = 0; + ec_key_pair *our_base_key = 0; + ratchet_identity_key_pair *our_identity_key = 0; + alice_axolotl_parameters *parameters = 0; + ec_public_key *signed_pre_key = 0; + ec_public_key *pre_key = 0; + int supports_v3 = 0; + ec_public_key *their_identity_key = 0; + ec_public_key *their_signed_pre_key = 0; + ec_public_key *their_one_time_pre_key = 0; + int has_their_one_time_pre_key_id = 0; + uint32_t their_one_time_pre_key_id = 0; + session_state *state = 0; + uint32_t local_registration_id = 0; + + assert(builder); + assert(builder->store); + assert(bundle); + axolotl_lock(builder->global_context); + + result = axolotl_identity_is_trusted_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + session_pre_key_bundle_get_identity_key(bundle)); + if(result < 0) { + goto complete; + } + if(result == 0) { + result = AX_ERR_UNTRUSTED_IDENTITY; + goto complete; + } + + signed_pre_key = session_pre_key_bundle_get_signed_pre_key(bundle); + pre_key = session_pre_key_bundle_get_pre_key(bundle); + + if(signed_pre_key) { + ec_public_key *identity_key = session_pre_key_bundle_get_identity_key(bundle); + axolotl_buffer *signature = session_pre_key_bundle_get_signed_pre_key_signature(bundle); + + axolotl_buffer *serialized_signed_pre_key = 0; + result = ec_public_key_serialize(&serialized_signed_pre_key, signed_pre_key); + if(result < 0) { + goto complete; + } + + result = curve_verify_signature(identity_key, + axolotl_buffer_data(serialized_signed_pre_key), + axolotl_buffer_len(serialized_signed_pre_key), + axolotl_buffer_data(signature), + axolotl_buffer_len(signature)); + + axolotl_buffer_free(serialized_signed_pre_key); + + if(result == 0) { + axolotl_log(builder->global_context, AX_LOG_WARNING, "invalid signature on device key!"); + result = AX_ERR_INVALID_KEY; + } + if(result < 0) { + goto complete; + } + } + + if(!signed_pre_key && !pre_key) { + result = AX_ERR_INVALID_KEY; + axolotl_log(builder->global_context, AX_LOG_WARNING, "both signed and unsigned pre keys are absent!"); + goto complete; + } + + supports_v3 = (signed_pre_key != 0); + + result = axolotl_session_load_session(builder->store, &record, builder->remote_address); + if(result < 0) { + goto complete; + } + + result = curve_generate_key_pair(builder->global_context, &our_base_key); + if(result < 0) { + goto complete; + } + + their_identity_key = session_pre_key_bundle_get_identity_key(bundle); + their_signed_pre_key = supports_v3 ? signed_pre_key : pre_key; + + their_one_time_pre_key = pre_key; + + if(their_one_time_pre_key) { + has_their_one_time_pre_key_id = 1; + their_one_time_pre_key_id = session_pre_key_bundle_get_pre_key_id(bundle); + } + + result = axolotl_identity_get_key_pair(builder->store, &our_identity_key); + if(result < 0) { + goto complete; + } + + result = alice_axolotl_parameters_create(¶meters, + our_identity_key, + our_base_key, + their_identity_key, + their_signed_pre_key, + supports_v3 ? their_one_time_pre_key : 0, + their_signed_pre_key); + if(result < 0) { + goto complete; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + + result = ratcheting_session_alice_initialize( + state, + supports_v3 ? 3 : 2, parameters, + builder->global_context); + if(result < 0) { + goto complete; + } + + session_state_set_unacknowledged_pre_key_message(state, + has_their_one_time_pre_key_id ? &their_one_time_pre_key_id : 0, + session_pre_key_bundle_get_signed_pre_key_id(bundle), + ec_key_pair_get_public(our_base_key)); + + result = axolotl_identity_get_local_registration_id(builder->store, &local_registration_id); + if(result < 0) { + goto complete; + } + + session_state_set_local_registration_id(state, local_registration_id); + session_state_set_remote_registration_id(state, + session_pre_key_bundle_get_registration_id(bundle)); + session_state_set_alice_base_key(state, ec_key_pair_get_public(our_base_key)); + + axolotl_session_store_session(builder->store, builder->remote_address, record); + axolotl_identity_save_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + their_identity_key); + +complete: + AXOLOTL_UNREF(record); + AXOLOTL_UNREF(our_base_key); + AXOLOTL_UNREF(our_identity_key); + AXOLOTL_UNREF(parameters); + axolotl_unlock(builder->global_context); + return result; +} + +int session_builder_process_key_exchange_message(session_builder *builder, key_exchange_message *message, key_exchange_message **response_message) +{ + int result = 0; + key_exchange_message *result_response_message = 0; + + assert(builder); + assert(builder->store); + axolotl_lock(builder->global_context); + + result = axolotl_identity_is_trusted_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + if(result == 0) { + result = AX_ERR_UNTRUSTED_IDENTITY; + goto complete; + } + result = 0; + + if(key_exchange_message_is_initiate(message)) { + result = session_builder_process_initiate(builder, message, &result_response_message); + } + else { + result = session_builder_process_response(builder, message); + } + +complete: + if(result >= 0) { + *response_message = result_response_message; + } + else { + AXOLOTL_UNREF(result_response_message); + } + axolotl_unlock(builder->global_context); + return result; +} + +static int session_builder_process_initiate(session_builder *builder, key_exchange_message *message, key_exchange_message **response_message) +{ + int result = 0; + key_exchange_message *result_response_message = 0; + + uint32_t flags = KEY_EXCHANGE_RESPONSE_FLAG; + session_record *record = 0; + session_state *state = 0; + ratchet_identity_key_pair *identity_key_pair = 0; + ec_key_pair *our_base_key = 0; + ec_key_pair *our_ratchet_key = 0; + symmetric_axolotl_parameters *parameters = 0; + ratchet_identity_key_pair *parameters_identity_key = 0; + ec_key_pair *parameters_base_key = 0; + axolotl_buffer *parameters_public_base_key_serialized = 0; + ec_key_pair *parameters_ratchet_key = 0; + axolotl_buffer *base_key_signature = 0; + + result = axolotl_session_load_session(builder->store, &record, builder->remote_address); + if(result < 0) { + goto complete; + } + + if(key_exchange_message_get_version(message) >= 3) { + ec_public_key *message_identity_key = key_exchange_message_get_identity_key(message); + ec_public_key *message_base_key = key_exchange_message_get_base_key(message); + axolotl_buffer *message_base_key_serialized = 0; + uint8_t *message_base_key_signature = 0; + + ec_public_key_serialize(&message_base_key_serialized, message_base_key); + + message_base_key_signature = key_exchange_message_get_base_key_signature(message); + + result = curve_verify_signature(message_identity_key, + axolotl_buffer_data(message_base_key_serialized), + axolotl_buffer_len(message_base_key_serialized), + message_base_key_signature, CURVE_SIGNATURE_LEN); + axolotl_buffer_free(message_base_key_serialized); + if(result < 0) { + goto complete; + } + if(result != 1) { + axolotl_log(builder->global_context, AX_LOG_WARNING, "Bad signature!"); + result = AX_ERR_INVALID_KEY; + goto complete; + } + } + + state = session_record_get_state(record); + if(!session_state_has_pending_key_exchange(state)) { + result = axolotl_identity_get_key_pair(builder->store, &identity_key_pair); + if(result < 0) { + goto complete; + } + + result = curve_generate_key_pair(builder->global_context, &our_base_key); + if(result < 0) { + goto complete; + } + + result = curve_generate_key_pair(builder->global_context, &our_ratchet_key); + if(result < 0) { + goto complete; + } + + result = symmetric_axolotl_parameters_create( + ¶meters, + identity_key_pair, + our_base_key, + our_ratchet_key, + key_exchange_message_get_base_key(message), + key_exchange_message_get_ratchet_key(message), + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + } + else { + result = symmetric_axolotl_parameters_create( + ¶meters, + session_state_get_pending_key_exchange_identity_key(state), + session_state_get_pending_key_exchange_base_key(state), + session_state_get_pending_key_exchange_ratchet_key(state), + key_exchange_message_get_base_key(message), + key_exchange_message_get_ratchet_key(message), + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + + flags |= KEY_EXCHANGE_SIMULTAENOUS_INITIATE_FLAG; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + result = ratcheting_session_symmetric_initialize(state, + MIN(key_exchange_message_get_max_version(message), (uint32_t)CIPHERTEXT_CURRENT_VERSION), + parameters, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(builder->store, builder->remote_address, record); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_save_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + + parameters_identity_key = symmetric_axolotl_parameters_get_our_identity_key(parameters); + parameters_base_key = symmetric_axolotl_parameters_get_our_base_key(parameters); + parameters_ratchet_key = symmetric_axolotl_parameters_get_our_ratchet_key(parameters); + + result = ec_public_key_serialize(¶meters_public_base_key_serialized, + ec_key_pair_get_public(parameters_base_key)); + if(result < 0) { + goto complete; + } + + result = curve_calculate_signature(builder->global_context, + &base_key_signature, + ratchet_identity_key_pair_get_private(parameters_identity_key), + axolotl_buffer_data(parameters_public_base_key_serialized), + axolotl_buffer_len(parameters_public_base_key_serialized)); + if(result < 0) { + goto complete; + } + + result = key_exchange_message_create(&result_response_message, + session_state_get_session_version(state), + key_exchange_message_get_sequence(message), + flags, + ec_key_pair_get_public(parameters_base_key), + axolotl_buffer_data(base_key_signature), + ec_key_pair_get_public(parameters_ratchet_key), + ratchet_identity_key_pair_get_public(parameters_identity_key)); + +complete: + axolotl_buffer_free(parameters_public_base_key_serialized); + axolotl_buffer_free(base_key_signature); + AXOLOTL_UNREF(our_base_key); + AXOLOTL_UNREF(our_ratchet_key); + AXOLOTL_UNREF(identity_key_pair); + AXOLOTL_UNREF(parameters); + AXOLOTL_UNREF(record); + if(result >= 0) { + *response_message = result_response_message; + } + else { + AXOLOTL_UNREF(result_response_message); + } + return result; +} + +static int session_builder_process_response(session_builder *builder, key_exchange_message *message) +{ + int result = 0; + session_record *record = 0; + session_state *state = 0; + int has_pending_key_exchange = 0; + int is_simultaneous_initiate_response = 0; + symmetric_axolotl_parameters *parameters = 0; + + result = axolotl_session_load_session(builder->store, &record, builder->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + has_pending_key_exchange = session_state_has_pending_key_exchange(state); + is_simultaneous_initiate_response = key_exchange_message_is_response_for_simultaneous_initiate(message); + + if(!has_pending_key_exchange || + session_state_get_pending_key_exchange_sequence(state) != key_exchange_message_get_sequence(message)) { + axolotl_log(builder->global_context, AX_LOG_INFO, "No matching sequence for response. Is simultaneous initiate response: %d", is_simultaneous_initiate_response); + if(!is_simultaneous_initiate_response) { + result = AX_ERR_STALE_KEY_EXCHANGE; + goto complete; + } + else { + result = AX_SUCCESS; + goto complete; + } + } + + result = symmetric_axolotl_parameters_create( + ¶meters, + session_state_get_pending_key_exchange_identity_key(state), + session_state_get_pending_key_exchange_base_key(state), + session_state_get_pending_key_exchange_ratchet_key(state), + key_exchange_message_get_base_key(message), + key_exchange_message_get_ratchet_key(message), + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + result = ratcheting_session_symmetric_initialize(state, + MIN(key_exchange_message_get_max_version(message), (uint32_t)CIPHERTEXT_CURRENT_VERSION), + parameters, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(builder->store, builder->remote_address, record); + if(result < 0) { + goto complete; + } + + if(session_state_get_session_version(state) >= 3) { + ec_public_key *message_identity_key = key_exchange_message_get_identity_key(message); + ec_public_key *message_base_key = key_exchange_message_get_base_key(message); + axolotl_buffer *message_base_key_serialized = 0; + uint8_t *message_base_key_signature = 0; + + ec_public_key_serialize(&message_base_key_serialized, message_base_key); + + message_base_key_signature = key_exchange_message_get_base_key_signature(message); + + result = curve_verify_signature(message_identity_key, + axolotl_buffer_data(message_base_key_serialized), + axolotl_buffer_len(message_base_key_serialized), + message_base_key_signature, CURVE_SIGNATURE_LEN); + axolotl_buffer_free(message_base_key_serialized); + if(result < 0) { + goto complete; + } + if(result != 1) { + axolotl_log(builder->global_context, AX_LOG_WARNING, "Base key signature doesn't match!"); + result = AX_ERR_INVALID_KEY; + goto complete; + } + } + + result = axolotl_session_store_session(builder->store, builder->remote_address, record); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_save_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + +complete: + AXOLOTL_UNREF(parameters); + AXOLOTL_UNREF(record); + return result; +} + +int session_builder_process(session_builder *builder, key_exchange_message **message) +{ + int result = 0; + key_exchange_message *result_message = 0; + int random_value = 0; + uint32_t sequence = 0; + uint32_t flags = KEY_EXCHANGE_INITIATE_FLAG; + ec_key_pair *base_key = 0; + ec_key_pair *ratchet_key = 0; + ratchet_identity_key_pair *identity_key = 0; + axolotl_buffer *base_key_public_serialized = 0; + axolotl_buffer *base_key_signature = 0; + session_record *record = 0; + session_state *state = 0; + + assert(builder); + assert(builder->store); + axolotl_lock(builder->global_context); + + result = axolotl_key_helper_get_random_sequence(&random_value, 65534, builder->global_context); + if(result < 0) { + goto complete; + } + sequence = ((uint32_t)random_value) + 1; + + result = curve_generate_key_pair(builder->global_context, &base_key); + if(result < 0) { + goto complete; + } + + result = curve_generate_key_pair(builder->global_context, &ratchet_key); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_key_pair(builder->store, &identity_key); + if(result < 0) { + goto complete; + } + + result = ec_public_key_serialize(&base_key_public_serialized, ec_key_pair_get_public(base_key)); + if(result < 0) { + goto complete; + } + + result = curve_calculate_signature(builder->global_context, &base_key_signature, + ratchet_identity_key_pair_get_private(identity_key), + axolotl_buffer_data(base_key_public_serialized), + axolotl_buffer_len(base_key_public_serialized)); + if(result < 0) { + goto complete; + } + + if(axolotl_buffer_len(base_key_signature) != CURVE_SIGNATURE_LEN) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = axolotl_session_load_session(builder->store, &record, builder->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + session_state_set_pending_key_exchange(state, sequence, base_key, ratchet_key, identity_key); + + result = axolotl_session_store_session(builder->store, builder->remote_address, record); + if(result < 0) { + goto complete; + } + + result = key_exchange_message_create(&result_message, + 2, sequence, flags, + ec_key_pair_get_public(base_key), + axolotl_buffer_data(base_key_signature), + ec_key_pair_get_public(ratchet_key), + ratchet_identity_key_pair_get_public(identity_key)); + +complete: + AXOLOTL_UNREF(record); + axolotl_buffer_free(base_key_signature); + axolotl_buffer_free(base_key_public_serialized); + AXOLOTL_UNREF(identity_key); + AXOLOTL_UNREF(ratchet_key); + AXOLOTL_UNREF(base_key); + if(result >= 0) { + *message = result_message; + } + else { + result = AX_ERR_INVALID_KEY; + AXOLOTL_UNREF(result_message); + } + axolotl_unlock(builder->global_context); + return result; +} + +void session_builder_free(session_builder *builder) +{ + if(builder) { + free(builder); + } +} |