static int session_cipher_get_or_create_chain_key(session_cipher *cipher, ratchet_chain_key **chain_key, session_state *state, ec_public_key *their_ephemeral) { int result = 0; ratchet_chain_key *result_key = 0; ratchet_root_key *receiver_root_key = 0; ratchet_chain_key *receiver_chain_key = 0; ratchet_root_key *sender_root_key = 0; ratchet_chain_key *sender_chain_key = 0; ec_key_pair *our_new_ephemeral = 0; ratchet_root_key *root_key = 0; ec_key_pair *our_ephemeral = 0; ratchet_chain_key *previous_sender_chain_key = 0; uint32_t index = 0; result_key = session_state_get_receiver_chain_key(state, their_ephemeral); if(result_key) { SIGNAL_REF(result_key); goto complete; } root_key = session_state_get_root_key(state); if(!root_key) { result = SG_ERR_UNKNOWN; goto complete; } our_ephemeral = session_state_get_sender_ratchet_key_pair(state); if(!our_ephemeral) { result = SG_ERR_UNKNOWN; goto complete; } result = ratchet_root_key_create_chain(root_key, &receiver_root_key, &receiver_chain_key, their_ephemeral, ec_key_pair_get_private(our_ephemeral)); if(result < 0) { goto complete; } result = curve_generate_key_pair(cipher->global_context, &our_new_ephemeral); if(result < 0) { goto complete; } result = ratchet_root_key_create_chain(receiver_root_key, &sender_root_key, &sender_chain_key, their_ephemeral, ec_key_pair_get_private(our_new_ephemeral)); if(result < 0) { goto complete; } session_state_set_root_key(state, sender_root_key); result = session_state_add_receiver_chain(state, their_ephemeral, receiver_chain_key); if(result < 0) { goto complete; } previous_sender_chain_key = session_state_get_sender_chain_key(state); if(!previous_sender_chain_key) { result = SG_ERR_UNKNOWN; goto complete; } index = ratchet_chain_key_get_index(previous_sender_chain_key); if(index > 0) { --index; } session_state_set_previous_counter(state, index); session_state_set_sender_chain(state, our_new_ephemeral, sender_chain_key); result_key = receiver_chain_key; SIGNAL_REF(result_key); complete: SIGNAL_UNREF(receiver_root_key); SIGNAL_UNREF(receiver_chain_key); SIGNAL_UNREF(sender_root_key); SIGNAL_UNREF(sender_chain_key); SIGNAL_UNREF(our_new_ephemeral); if(result >= 0) { *chain_key = result_key; } else { SIGNAL_UNREF(result_key); } return result; }
int ratcheting_session_alice_initialize( session_state *state, alice_signal_protocol_parameters *parameters, signal_context *global_context) { int result = 0; uint8_t *agreement = 0; int agreement_len = 0; ec_key_pair *sending_ratchet_key = 0; ratchet_root_key *derived_root = 0; ratchet_chain_key *derived_chain = 0; ratchet_root_key *sending_chain_root = 0; ratchet_chain_key *sending_chain_key = 0; struct vpool vp; uint8_t *secret = 0; size_t secret_len = 0; uint8_t discontinuity_data[32]; assert(state); assert(parameters); assert(global_context); vpool_init(&vp, 1024, 0); result = curve_generate_key_pair(global_context, &sending_ratchet_key); if(result < 0) { goto complete; } memset(discontinuity_data, 0xFF, sizeof(discontinuity_data)); if(!vpool_insert(&vp, vpool_get_length(&vp), discontinuity_data, sizeof(discontinuity_data))) { result = SG_ERR_NOMEM; goto complete; } agreement_len = curve_calculate_agreement(&agreement, parameters->their_signed_pre_key, parameters->our_identity_key->private_key); if(agreement_len < 0) { result = agreement_len; goto complete; } if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { free(agreement); agreement = 0; agreement_len = 0; } else { result = SG_ERR_NOMEM; goto complete; } agreement_len = curve_calculate_agreement(&agreement, parameters->their_identity_key, ec_key_pair_get_private(parameters->our_base_key)); if(agreement_len < 0) { result = agreement_len; goto complete; } if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { free(agreement); agreement = 0; agreement_len = 0; } else { result = SG_ERR_NOMEM; goto complete; } agreement_len = curve_calculate_agreement(&agreement, parameters->their_signed_pre_key, ec_key_pair_get_private(parameters->our_base_key)); if(agreement_len < 0) { result = agreement_len; goto complete; } if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { free(agreement); agreement = 0; agreement_len = 0; } else { result = SG_ERR_NOMEM; goto complete; } if(parameters->their_one_time_pre_key) { agreement_len = curve_calculate_agreement(&agreement, parameters->their_one_time_pre_key, ec_key_pair_get_private(parameters->our_base_key)); if(agreement_len < 0) { result = agreement_len; goto complete; } if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { free(agreement); agreement = 0; agreement_len = 0; } else { result = SG_ERR_NOMEM; goto complete; } } if(vpool_is_empty(&vp)) { result = SG_ERR_UNKNOWN; goto complete; } secret = vpool_get_buf(&vp); secret_len = vpool_get_length(&vp); result = ratcheting_session_calculate_derived_keys(&derived_root, &derived_chain, secret, secret_len, global_context); if(result < 0) { goto complete; } result = ratchet_root_key_create_chain(derived_root, &sending_chain_root, &sending_chain_key, parameters->their_ratchet_key, ec_key_pair_get_private(sending_ratchet_key)); if(result < 0) { goto complete; } complete: if(result >= 0) { session_state_set_session_version(state, CIPHERTEXT_CURRENT_VERSION); session_state_set_remote_identity_key(state, parameters->their_identity_key); session_state_set_local_identity_key(state, parameters->our_identity_key->public_key); session_state_add_receiver_chain(state, parameters->their_ratchet_key, derived_chain); session_state_set_sender_chain(state, sending_ratchet_key, sending_chain_key); session_state_set_root_key(state, sending_chain_root); } vpool_final(&vp); if(agreement) { free(agreement); } if(sending_ratchet_key) { SIGNAL_UNREF(sending_ratchet_key); } if(derived_root) { SIGNAL_UNREF(derived_root); } if(derived_chain) { SIGNAL_UNREF(derived_chain); } if(sending_chain_root) { SIGNAL_UNREF(sending_chain_root); } if(sending_chain_key) { SIGNAL_UNREF(sending_chain_key); } return result; }