diff options
author | George Hazan <george.hazan@gmail.com> | 2016-01-26 08:28:32 +0000 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2016-01-26 08:28:32 +0000 |
commit | 2a82a9154f9d6e0b5658c82cd346051017339a1e (patch) | |
tree | 8321c2d1897d4026e68064241014eef4a57ddc9d /libs/libaxolotl/src/session_cipher.c | |
parent | 80148955f82c205cc94f0112e0fbfe8f91bc4330 (diff) |
libaxolotl - initial commit
git-svn-id: http://svn.miranda-ng.org/main/trunk@16169 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'libs/libaxolotl/src/session_cipher.c')
-rw-r--r-- | libs/libaxolotl/src/session_cipher.c | 884 |
1 files changed, 884 insertions, 0 deletions
diff --git a/libs/libaxolotl/src/session_cipher.c b/libs/libaxolotl/src/session_cipher.c new file mode 100644 index 0000000000..c2065ef5dc --- /dev/null +++ b/libs/libaxolotl/src/session_cipher.c @@ -0,0 +1,884 @@ +#include "session_cipher.h" + +#include <assert.h> +#include <string.h> +#include "axolotl_internal.h" +#include "session_builder.h" +#include "session_record.h" +#include "session_state.h" +#include "ratchet.h" +#include "protocol.h" + +struct session_cipher +{ + axolotl_store_context *store; + const axolotl_address *remote_address; + session_builder *builder; + axolotl_context *global_context; + int (*decrypt_callback)(session_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context); + int inside_callback; + void *user_data; +}; + +static int session_cipher_decrypt_from_record_and_whisper_message(session_cipher *cipher, + session_record *record, whisper_message *ciphertext, axolotl_buffer **plaintext); +static int session_cipher_decrypt_from_state_and_whisper_message(session_cipher *cipher, + session_state *state, whisper_message *ciphertext, axolotl_buffer **plaintext); + +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); +static int session_cipher_get_or_create_message_keys(ratchet_message_keys *message_keys, + session_state *state, ec_public_key *their_ephemeral, + ratchet_chain_key *chain_key, uint32_t counter, + axolotl_context *global_context); + +static int session_cipher_get_ciphertext(session_cipher *cipher, + axolotl_buffer **ciphertext, + uint32_t version, ratchet_message_keys *message_keys, + const uint8_t *plaintext, size_t plaintext_len); +static int session_cipher_get_plaintext(session_cipher *cipher, + axolotl_buffer **plaintext, + uint32_t version, ratchet_message_keys *message_keys, + const uint8_t *ciphertext, size_t ciphertext_len); + +static int session_cipher_decrypt_callback(session_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context); + +int session_cipher_create(session_cipher **cipher, + axolotl_store_context *store, const axolotl_address *remote_address, + axolotl_context *global_context) +{ + int result = 0; + session_builder *builder = 0; + session_cipher *result_cipher; + + assert(store); + assert(global_context); + + result = session_builder_create(&builder, store, remote_address, global_context); + if(result < 0) { + return result; + } + + result_cipher = malloc(sizeof(session_cipher)); + if(!result_cipher) { + return AX_ERR_NOMEM; + } + memset(result_cipher, 0, sizeof(session_cipher)); + + result_cipher->store = store; + result_cipher->remote_address = remote_address; + result_cipher->builder = builder; + result_cipher->global_context = global_context; + + *cipher = result_cipher; + return 0; +} + +void session_cipher_set_user_data(session_cipher *cipher, void *user_data) +{ + assert(cipher); + cipher->user_data = user_data; +} + +void *session_cipher_get_user_data(session_cipher *cipher) +{ + assert(cipher); + return cipher->user_data; +} + +void session_cipher_set_decryption_callback(session_cipher *cipher, + int (*callback)(session_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context)) +{ + assert(cipher); + cipher->decrypt_callback = callback; +} + +int session_cipher_encrypt(session_cipher *cipher, + const uint8_t *padded_message, size_t padded_message_len, + ciphertext_message **encrypted_message) +{ + int result = 0; + session_record *record = 0; + session_state *state = 0; + ratchet_chain_key *chain_key = 0; + ratchet_chain_key *next_chain_key = 0; + ratchet_message_keys message_keys; + ec_public_key *sender_ephemeral = 0; + uint32_t previous_counter = 0; + uint32_t session_version = 0; + axolotl_buffer *ciphertext = 0; + uint32_t chain_key_index = 0; + ec_public_key *local_identity_key = 0; + ec_public_key *remote_identity_key = 0; + whisper_message *message = 0; + pre_key_whisper_message *pre_key_message = 0; + uint8_t *ciphertext_data = 0; + size_t ciphertext_len = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + if(cipher->inside_callback == 1) { + result = AX_ERR_INVAL; + goto complete; + } + + result = axolotl_session_load_session(cipher->store, &record, cipher->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + if(!state) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + chain_key = session_state_get_sender_chain_key(state); + if(!chain_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = ratchet_chain_key_get_message_keys(chain_key, &message_keys); + if(result < 0) { + goto complete; + } + + sender_ephemeral = session_state_get_sender_ratchet_key(state); + if(!sender_ephemeral) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + previous_counter = session_state_get_previous_counter(state); + session_version = session_state_get_session_version(state); + + result = session_cipher_get_ciphertext(cipher, + &ciphertext, + session_version, &message_keys, + padded_message, padded_message_len); + if(result < 0) { + goto complete; + } + ciphertext_data = axolotl_buffer_data(ciphertext); + ciphertext_len = axolotl_buffer_len(ciphertext); + + chain_key_index = ratchet_chain_key_get_index(chain_key); + + local_identity_key = session_state_get_local_identity_key(state); + if(!local_identity_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + remote_identity_key = session_state_get_remote_identity_key(state); + if(!remote_identity_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = whisper_message_create(&message, + session_version, + message_keys.mac_key, sizeof(message_keys.mac_key), + sender_ephemeral, + chain_key_index, previous_counter, + ciphertext_data, ciphertext_len, + local_identity_key, remote_identity_key, + cipher->global_context); + if(result < 0) { + goto complete; + } + + if(session_state_has_unacknowledged_pre_key_message(state) == 1) { + uint32_t local_registration_id = session_state_get_local_registration_id(state); + int has_pre_key_id = 0; + uint32_t pre_key_id = 0; + uint32_t signed_pre_key_id; + ec_public_key *base_key; + + if(session_state_unacknowledged_pre_key_message_has_pre_key_id(state)) { + has_pre_key_id = 1; + pre_key_id = session_state_unacknowledged_pre_key_message_get_pre_key_id(state); + } + signed_pre_key_id = session_state_unacknowledged_pre_key_message_get_signed_pre_key_id(state); + base_key = session_state_unacknowledged_pre_key_message_get_base_key(state); + + if(!base_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = pre_key_whisper_message_create(&pre_key_message, + session_version, local_registration_id, (has_pre_key_id ? &pre_key_id : 0), + signed_pre_key_id, base_key, local_identity_key, + message, + cipher->global_context); + if(result < 0) { + goto complete; + } + AXOLOTL_UNREF(message); + message = 0; + } + + result = ratchet_chain_key_create_next(chain_key, &next_chain_key); + if(result < 0) { + goto complete; + } + + result = session_state_set_sender_chain_key(state, next_chain_key); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(cipher->store, cipher->remote_address, record); + +complete: + if(result >= 0) { + if(pre_key_message) { + *encrypted_message = (ciphertext_message *)pre_key_message; + } + else { + *encrypted_message = (ciphertext_message *)message; + } + } + else { + AXOLOTL_UNREF(pre_key_message); + AXOLOTL_UNREF(message); + } + axolotl_buffer_free(ciphertext); + AXOLOTL_UNREF(next_chain_key); + AXOLOTL_UNREF(record); + axolotl_explicit_bzero(&message_keys, sizeof(ratchet_message_keys)); + axolotl_unlock(cipher->global_context); + return result; +} + +int session_cipher_decrypt_pre_key_whisper_message(session_cipher *cipher, + pre_key_whisper_message *ciphertext, void *decrypt_context, + axolotl_buffer **plaintext) +{ + int result = 0; + axolotl_buffer *result_buf = 0; + session_record *record = 0; + int has_unsigned_pre_key_id = 0; + uint32_t unsigned_pre_key_id = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + if(cipher->inside_callback == 1) { + result = AX_ERR_INVAL; + goto complete; + } + + result = axolotl_session_load_session(cipher->store, &record, cipher->remote_address); + if(result < 0) { + goto complete; + } + + result = session_builder_process_pre_key_whisper_message(cipher->builder, record, ciphertext, &unsigned_pre_key_id); + if(result < 0) { + goto complete; + } + has_unsigned_pre_key_id = result; + + result = session_cipher_decrypt_from_record_and_whisper_message(cipher, record, + pre_key_whisper_message_get_whisper_message(ciphertext), + &result_buf); + if(result < 0) { + goto complete; + } + + result = session_cipher_decrypt_callback(cipher, result_buf, decrypt_context); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(cipher->store, cipher->remote_address, record); + if(result < 0) { + goto complete; + } + + if(has_unsigned_pre_key_id) { + result = axolotl_pre_key_remove_key(cipher->store, unsigned_pre_key_id); + if(result < 0) { + goto complete; + } + } + +complete: + AXOLOTL_UNREF(record); + if(result >= 0) { + *plaintext = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + axolotl_unlock(cipher->global_context); + return result; +} + +int session_cipher_decrypt_whisper_message(session_cipher *cipher, + whisper_message *ciphertext, void *decrypt_context, + axolotl_buffer **plaintext) +{ + int result = 0; + axolotl_buffer *result_buf = 0; + session_record *record = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + if(cipher->inside_callback == 1) { + result = AX_ERR_INVAL; + goto complete; + } + + result = axolotl_session_contains_session(cipher->store, cipher->remote_address); + if(result == 0) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "No session for: %s:%d", cipher->remote_address->name, cipher->remote_address->device_id); + result = AX_ERR_NO_SESSION; + goto complete; + } + else if(result < 0) { + goto complete; + } + + result = axolotl_session_load_session(cipher->store, &record, + cipher->remote_address); + if(result < 0) { + goto complete; + } + + result = session_cipher_decrypt_from_record_and_whisper_message( + cipher, record, ciphertext, &result_buf); + if(result < 0) { + goto complete; + } + + result = session_cipher_decrypt_callback(cipher, result_buf, decrypt_context); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(cipher->store, + cipher->remote_address, record); + +complete: + AXOLOTL_UNREF(record); + if(result >= 0) { + *plaintext = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + axolotl_unlock(cipher->global_context); + return result; +} + +static int session_cipher_decrypt_from_record_and_whisper_message(session_cipher *cipher, + session_record *record, whisper_message *ciphertext, axolotl_buffer **plaintext) +{ + int result = 0; + axolotl_buffer *result_buf = 0; + session_state *state = 0; + session_state *state_copy = 0; + session_record_state_node *previous_states_node = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + state = session_record_get_state(record); + if(state) { + result = session_state_copy(&state_copy, state, cipher->global_context); + if(result < 0) { + goto complete; + } + + //TODO Collect and log invalid message errors if totally unsuccessful + + result = session_cipher_decrypt_from_state_and_whisper_message(cipher, state_copy, ciphertext, &result_buf); + if(result < 0 && result != AX_ERR_INVALID_MESSAGE) { + goto complete; + } + + if(result >= AX_SUCCESS) { + session_record_set_state(record, state_copy); + goto complete; + } + AXOLOTL_UNREF(state_copy); + } + + previous_states_node = session_record_get_previous_states_head(record); + while(previous_states_node) { + state = session_record_get_previous_states_element(previous_states_node); + + result = session_state_copy(&state_copy, state, cipher->global_context); + if(result < 0) { + goto complete; + } + + result = session_cipher_decrypt_from_state_and_whisper_message(cipher, state_copy, ciphertext, &result_buf); + if(result < 0 && result != AX_ERR_INVALID_MESSAGE) { + goto complete; + } + + if(result >= AX_SUCCESS) { + session_record_get_previous_states_remove(record, previous_states_node); + result = session_record_promote_state(record, state_copy); + goto complete; + } + + AXOLOTL_UNREF(state_copy); + previous_states_node = session_record_get_previous_states_next(previous_states_node); + } + + axolotl_log(cipher->global_context, AX_LOG_WARNING, "No valid sessions"); + result = AX_ERR_INVALID_MESSAGE; + +complete: + AXOLOTL_UNREF(state_copy); + if(result >= 0) { + *plaintext = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + axolotl_unlock(cipher->global_context); + return result; +} + +static int session_cipher_decrypt_from_state_and_whisper_message(session_cipher *cipher, + session_state *state, whisper_message *ciphertext, axolotl_buffer **plaintext) +{ + int result = 0; + axolotl_buffer *result_buf = 0; + ec_public_key *their_ephemeral = 0; + uint32_t counter = 0; + ratchet_chain_key *chain_key = 0; + ratchet_message_keys message_keys; + uint8_t message_version = 0; + uint32_t session_version = 0; + ec_public_key *remote_identity_key = 0; + ec_public_key *local_identity_key = 0; + axolotl_buffer *ciphertext_body = 0; + + if(!session_state_has_sender_chain(state)) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Uninitialized session!"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + message_version = whisper_message_get_message_version(ciphertext); + session_version = session_state_get_session_version(state); + + if(message_version != session_version) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Message version %d, but session version %d", message_version, session_version); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + their_ephemeral = whisper_message_get_sender_ratchet_key(ciphertext); + if(!their_ephemeral) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + counter = whisper_message_get_counter(ciphertext); + + result = session_cipher_get_or_create_chain_key(cipher, &chain_key, state, their_ephemeral); + if(result < 0) { + goto complete; + } + + result = session_cipher_get_or_create_message_keys(&message_keys, state, + their_ephemeral, chain_key, counter, cipher->global_context); + if(result < 0) { + goto complete; + } + + remote_identity_key = session_state_get_remote_identity_key(state); + if(!remote_identity_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + local_identity_key = session_state_get_local_identity_key(state); + if(!local_identity_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = whisper_message_verify_mac(ciphertext, message_version, + remote_identity_key, local_identity_key, + message_keys.mac_key, sizeof(message_keys.mac_key), + cipher->global_context); + if(result != 1) { + if(result == 0) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Message mac not verified"); + result = AX_ERR_INVALID_MESSAGE; + } + else if(result < 0) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Error attempting to verify message mac"); + } + goto complete; + } + + ciphertext_body = whisper_message_get_body(ciphertext); + if(!ciphertext_body) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Message body does not exist"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + result = session_cipher_get_plaintext(cipher, &result_buf, message_version, &message_keys, + axolotl_buffer_data(ciphertext_body), axolotl_buffer_len(ciphertext_body)); + if(result < 0) { + goto complete; + } + + session_state_clear_unacknowledged_pre_key_message(state); + +complete: + AXOLOTL_UNREF(chain_key); + if(result >= 0) { + *plaintext = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + axolotl_explicit_bzero(&message_keys, sizeof(ratchet_message_keys)); + return result; +} + +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) { + AXOLOTL_REF(result_key); + goto complete; + } + + root_key = session_state_get_root_key(state); + if(!root_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + our_ephemeral = session_state_get_sender_ratchet_key_pair(state); + if(!our_ephemeral) { + result = AX_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 = AX_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; + AXOLOTL_REF(result_key); + +complete: + AXOLOTL_UNREF(receiver_root_key); + AXOLOTL_UNREF(receiver_chain_key); + AXOLOTL_UNREF(sender_root_key); + AXOLOTL_UNREF(sender_chain_key); + AXOLOTL_UNREF(our_new_ephemeral); + if(result >= 0) { + *chain_key = result_key; + } + else { + AXOLOTL_UNREF(result_key); + } + return result; +} + +static int session_cipher_get_or_create_message_keys(ratchet_message_keys *message_keys, + session_state *state, ec_public_key *their_ephemeral, + ratchet_chain_key *chain_key, uint32_t counter, axolotl_context *global_context) +{ + int result = 0; + ratchet_chain_key *cur_chain_key = 0; + ratchet_chain_key *next_chain_key = 0; + ratchet_message_keys message_keys_result; + + if(ratchet_chain_key_get_index(chain_key) > counter) { + result = session_state_remove_message_keys(state, &message_keys_result, their_ephemeral, counter); + if(result == 1) { + result = 0; + goto complete; + } + + axolotl_log(global_context, AX_LOG_WARNING, "Received message with old counter: %d, %d", + ratchet_chain_key_get_index(chain_key), counter); + result = AX_ERR_DUPLICATE_MESSAGE; + goto complete; + } + + if(counter - ratchet_chain_key_get_index(chain_key) > 2000) { + axolotl_log(global_context, AX_LOG_WARNING, "Over 2000 messages into the future!"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + cur_chain_key = chain_key; + AXOLOTL_REF(cur_chain_key); + + while(ratchet_chain_key_get_index(cur_chain_key) < counter) { + result = ratchet_chain_key_get_message_keys(cur_chain_key, &message_keys_result); + if(result < 0) { + goto complete; + } + + result = session_state_set_message_keys(state, their_ephemeral, &message_keys_result); + if(result < 0) { + goto complete; + } + + result = ratchet_chain_key_create_next(cur_chain_key, &next_chain_key); + if(result < 0) { + goto complete; + } + AXOLOTL_UNREF(cur_chain_key); + cur_chain_key = next_chain_key; + next_chain_key = 0; + } + + result = ratchet_chain_key_create_next(cur_chain_key, &next_chain_key); + if(result < 0) { + goto complete; + } + + result = session_state_set_receiver_chain_key(state, their_ephemeral, next_chain_key); + if(result < 0) { + goto complete; + } + + result = ratchet_chain_key_get_message_keys(cur_chain_key, &message_keys_result); + if(result < 0) { + goto complete; + } + +complete: + if(result >= 0) { + memcpy(message_keys, &message_keys_result, sizeof(ratchet_message_keys)); + } + AXOLOTL_UNREF(cur_chain_key); + AXOLOTL_UNREF(next_chain_key); + axolotl_explicit_bzero(&message_keys_result, sizeof(ratchet_message_keys)); + return result; +} + +int session_cipher_get_remote_registration_id(session_cipher *cipher, uint32_t *remote_id) +{ + int result = 0; + uint32_t id_result = 0; + session_record *record = 0; + session_state *state = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + result = axolotl_session_load_session(cipher->store, &record, cipher->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + if(!state) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + id_result = session_state_get_remote_registration_id(state); + +complete: + if(result >= 0) { + *remote_id = id_result; + } + axolotl_unlock(cipher->global_context); + return result; +} + +int session_cipher_get_session_version(session_cipher *cipher, uint32_t *version) +{ + int result = 0; + uint32_t version_result = 0; + session_record *record = 0; + session_state *state = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + result = axolotl_session_contains_session(cipher->store, cipher->remote_address); + if(result != 1) { + if(result == 0) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "No session for: %s:%d", cipher->remote_address->name, cipher->remote_address->device_id); + result = AX_ERR_NO_SESSION; + } + goto complete; + } + + result = axolotl_session_load_session(cipher->store, &record, cipher->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + if(!state) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + version_result = session_state_get_session_version(state); + +complete: + if(result >= 0) { + *version = version_result; + } + axolotl_unlock(cipher->global_context); + return result; +} + +static int session_cipher_get_ciphertext(session_cipher *cipher, + axolotl_buffer **ciphertext, + uint32_t version, ratchet_message_keys *message_keys, + const uint8_t *plaintext, size_t plaintext_len) +{ + int result = 0; + axolotl_buffer *output = 0; + + if(version >= 3) { + result = axolotl_encrypt(cipher->global_context, + &output, AX_CIPHER_AES_CBC_PKCS5, + message_keys->cipher_key, sizeof(message_keys->cipher_key), + message_keys->iv, sizeof(message_keys->iv), + plaintext, plaintext_len); + } + else { + uint8_t iv[16]; + memset(iv, 0, sizeof(iv)); + iv[3] = (uint8_t)(message_keys->counter); + iv[2] = (uint8_t)(message_keys->counter >> 8); + iv[1] = (uint8_t)(message_keys->counter >> 16); + iv[0] = (uint8_t)(message_keys->counter >> 24); + + result = axolotl_encrypt(cipher->global_context, + &output, AX_CIPHER_AES_CTR_NOPADDING, + message_keys->cipher_key, sizeof(message_keys->cipher_key), + iv, sizeof(iv), + plaintext, plaintext_len); + } + + if(result >= 0) { + *ciphertext = output; + } + + return result; +} + +static int session_cipher_get_plaintext(session_cipher *cipher, + axolotl_buffer **plaintext, + uint32_t version, ratchet_message_keys *message_keys, + const uint8_t *ciphertext, size_t ciphertext_len) +{ + int result = 0; + axolotl_buffer *output = 0; + + if(version >= 3) { + result = axolotl_decrypt(cipher->global_context, + &output, AX_CIPHER_AES_CBC_PKCS5, + message_keys->cipher_key, sizeof(message_keys->cipher_key), + message_keys->iv, sizeof(message_keys->iv), + ciphertext, ciphertext_len); + } + else { + uint8_t iv[16]; + memset(iv, 0, sizeof(iv)); + iv[3] = (uint8_t)(message_keys->counter); + iv[2] = (uint8_t)(message_keys->counter >> 8); + iv[1] = (uint8_t)(message_keys->counter >> 16); + iv[0] = (uint8_t)(message_keys->counter >> 24); + + result = axolotl_decrypt(cipher->global_context, + &output, AX_CIPHER_AES_CTR_NOPADDING, + message_keys->cipher_key, sizeof(message_keys->cipher_key), + iv, sizeof(iv), + ciphertext, ciphertext_len); + } + + if(result >= 0) { + *plaintext = output; + } + + return result; +} + +static int session_cipher_decrypt_callback(session_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context) +{ + int result = 0; + if(cipher->decrypt_callback) { + cipher->inside_callback = 1; + result = cipher->decrypt_callback(cipher, plaintext, decrypt_context); + cipher->inside_callback = 0; + } + return result; +} + +void session_cipher_free(session_cipher *cipher) +{ + if(cipher) { + session_builder_free(cipher->builder); + free(cipher); + } +} |