Example #1
0
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;
}