Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
285 lines (266 sloc) 10.8 KB
/*
* Copyright (c) 2017 Cossack Labs Limited
*
* This file is a part of Hermes-core.
*
* Hermes-core is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hermes-core is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hermes-core. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "themis/secure_session.h"
#include "themis/secure_session_utils.h"
#include <string.h>
#include <hermes/common/errors.h>
#include "hermes/secure_transport/transport.h"
#include "utils.h"
#define TEMP_BUFFER_SIZE (2048)
/* \brief used as user_data in secure_session_user_callbacks_t implementation
*
*/
typedef struct secure_session_callback_data_type {
uint8_t* public_key_id;
size_t public_key_id_length;
uint8_t* public_key;
size_t public_key_length;
} secure_session_callback_data_t;
void destroy_callback_data(secure_session_callback_data_t** callback_) {
if(!*callback_) {
return;
}
if((*callback_)->public_key) {
free((*callback_)->public_key);
(*callback_)->public_key = NULL;
}
if((*callback_)->public_key_id) {
free((*callback_)->public_key_id);
(*callback_)->public_key_id = NULL;
}
free(*callback_);
*callback_ = NULL;
}
void destroy_secure_session_callback(secure_session_user_callbacks_t** callback) {
secure_session_callback_data_t* callback_data = (secure_session_callback_data_t*) (*callback)->user_data;
destroy_callback_data(&callback_data);
free(*callback);
*callback = NULL;
}
// get_public_key_for_id_wrapper return public key linked with this connection if id is equals to id of public key
int get_public_key_for_id_wrapper(const void *id, size_t id_length, void *key_buffer, size_t key_buffer_length, void *user_data){
secure_session_callback_data_t* transport = (secure_session_callback_data_t*)(user_data);
if(transport->public_key_id_length == id_length && memcmp(id, transport->public_key_id, id_length) == 0){
if (key_buffer_length < transport->public_key_length){
return THEMIS_FAIL;
}
memcpy(key_buffer, transport->public_key, transport->public_key_length);
return THEMIS_SUCCESS;
}
return THEMIS_FAIL;
}
// hermes_transport_send encrypt buffer with secure session and send data with <send_data> function
// implement hermes transport interface
uint32_t hermes_transport_send(void *transport_, const uint8_t *buffer, const size_t buffer_length){
secure_transport_t* transport = (secure_transport_t*)(transport_);
size_t wrapped_buffer_size;
uint8_t* wrapped_buffer;
if (secure_session_wrap(transport->session, buffer, buffer_length, NULL, &wrapped_buffer_size) != THEMIS_BUFFER_TOO_SMALL){
return HM_FAIL;
}
wrapped_buffer = malloc(wrapped_buffer_size);
if (!wrapped_buffer){
return HM_FAIL;
}
if (secure_session_wrap(transport->session, buffer, buffer_length, wrapped_buffer, &wrapped_buffer_size) != THEMIS_SUCCESS){
free(wrapped_buffer);
return HM_FAIL;
}
if(send_data(wrapped_buffer, wrapped_buffer_size, transport->user_transport) == HM_FAIL){
free(wrapped_buffer);
return HM_FAIL;
};
free(wrapped_buffer);
return HM_SUCCESS;
}
// hermes_transport_receive read data from transport with <read_data_size> and <read_data> functions,
// unwrap with secure_session and return when read size == buffer_length
uint32_t hermes_transport_receive(void *transport_, uint8_t *buffer, size_t buffer_length){
secure_transport_t* transport = (secure_transport_t*)(transport_);
size_t total_read = 0;
do{
size_t temp_buffer_size;
uint8_t* temp_buffer;
temp_buffer_size = read_data_size(transport->user_transport);
if (temp_buffer_size == HM_FAIL){
return HM_FAIL;
}
temp_buffer = malloc(temp_buffer_size);
if(!temp_buffer){
return HM_FAIL;
}
if (read_data(temp_buffer, temp_buffer_size, transport->user_transport) == HM_FAIL){
free(temp_buffer);
temp_buffer = NULL;
return HM_FAIL;
};
size_t unwrapped_size = 0;
if (secure_session_unwrap(
transport->session, temp_buffer, temp_buffer_size, NULL, &unwrapped_size) != THEMIS_BUFFER_TOO_SMALL){
free(temp_buffer);
temp_buffer = NULL;
return HM_FAIL;
}
if (unwrapped_size + total_read > buffer_length){
free(temp_buffer);
temp_buffer = NULL;
return HM_FAIL;
}
themis_status_t status = secure_session_unwrap(
transport->session, temp_buffer, temp_buffer_size, buffer+total_read, &unwrapped_size);
// TODO: maybe rewrite with reusing buffer in loop
free(temp_buffer);
temp_buffer = NULL;
if (status != THEMIS_SUCCESS){
return HM_FAIL;
}
total_read += unwrapped_size;
}while(total_read < buffer_length);
return HM_SUCCESS;
}
// init_secure_session establish secure session using transport
hermes_status_t init_secure_session(hm_rpc_transport_t* transport, secure_session_t* session, bool is_server){
size_t buffer_size;
// size of tempBuffer took from secure_session code and examples
uint8_t tempBuffer[TEMP_BUFFER_SIZE];
// send client's connection request
if(!is_server){
if (secure_session_generate_connect_request(session, NULL, &buffer_size) != THEMIS_BUFFER_TOO_SMALL){
return HM_FAIL;
}
if (secure_session_generate_connect_request(session, tempBuffer, &buffer_size) != THEMIS_SUCCESS){
return HM_FAIL;
}
if(send_data(tempBuffer, buffer_size, transport) != HM_SUCCESS){
return HM_FAIL;
}
}
themis_status_t status;
uint32_t data_size;
uint32_t bytes_read;
while(!secure_session_is_established(session)){
data_size = read_data_size(transport);
if (data_size == HM_FAIL || data_size > TEMP_BUFFER_SIZE){
return HM_FAIL;
}
// receive response
bytes_read = read_data(tempBuffer, data_size, transport);
if(bytes_read == HM_FAIL){
return HM_FAIL;
};
buffer_size = sizeof(tempBuffer);
// calculate unwrapped size
status = secure_session_unwrap(session, tempBuffer, data_size, tempBuffer, &buffer_size);
if (status == THEMIS_SSESSION_SEND_OUTPUT_TO_PEER){
if (send_data(tempBuffer, buffer_size, transport) != HM_SUCCESS){
return HM_FAIL;
}
} else if (status == THEMIS_SUCCESS){
break;
} else {
return HM_FAIL;
}
}
return HM_SUCCESS;
}
hm_rpc_transport_t* create_secure_transport_with_callback(
const uint8_t *user_id, size_t user_id_length,
const uint8_t *private_key, size_t private_key_length,
secure_session_user_callbacks_t* callback,
hm_rpc_transport_t* user_transport,
bool is_server){
secure_transport_t* transport = calloc(1, sizeof(secure_transport_t));
transport->user_transport = user_transport;
transport->session_callback = callback;
transport->session = secure_session_create(user_id, user_id_length, private_key, private_key_length, callback);
if (!transport->session)
{
destroy_secure_transport(&transport);
return NULL;
}
if(init_secure_session(user_transport, transport->session, is_server)!=HM_SUCCESS){
destroy_secure_transport(&transport);
return NULL;
}
hm_rpc_transport_t* rpc_transport = calloc(1, sizeof(hm_rpc_transport_t));
rpc_transport->user_data = transport;
rpc_transport->recv = hermes_transport_receive;
rpc_transport->send = hermes_transport_send;
return rpc_transport;
}
// create_secure_transport establish secure session as user with <user_id> using <private_key> with host with id
// <public_key_id> that will be authenticated via <public_key> and return wrapepd <user_transport> with established
// secure session
hm_rpc_transport_t* create_secure_transport(
const uint8_t *user_id, const size_t user_id_length,
const uint8_t *private_key, const size_t private_key_length,
const uint8_t *public_key, const size_t public_key_length,
const uint8_t *public_key_id, const size_t public_key_id_length,
hm_rpc_transport_t* user_transport,
bool is_server){
secure_session_callback_data_t* callback_data = malloc(sizeof(secure_session_callback_data_t));
if(!callback_data){
return NULL;
}
callback_data->public_key = malloc(public_key_length);
if(!callback_data->public_key){
free(callback_data);
return NULL;
}
callback_data->public_key_id = malloc(public_key_id_length);
if(!callback_data->public_key_id){
free(callback_data);
return NULL;
}
memcpy(callback_data->public_key, public_key, public_key_length);
callback_data->public_key_length = public_key_length;
memcpy(callback_data->public_key_id, public_key_id, public_key_id_length);
callback_data->public_key_id_length = public_key_id_length;
secure_session_user_callbacks_t* secure_session_callback = calloc(1, sizeof(secure_session_user_callbacks_t));
if(!secure_session_callback){
destroy_callback_data(&callback_data);
return NULL;
}
secure_session_callback->user_data = callback_data;
secure_session_callback->get_public_key_for_id = get_public_key_for_id_wrapper;
return create_secure_transport_with_callback(
user_id, user_id_length, private_key, private_key_length, secure_session_callback, user_transport,
is_server);
}
uint32_t destroy_rpc_secure_transport(hm_rpc_transport_t** transport_){
if(!transport_ || !(*transport_) || !((*transport_))){
return HM_FAIL;
}
secure_transport_t* transport = (secure_transport_t*) ((*transport_)->user_data);
return destroy_secure_transport(&transport);
}
// destroy_secure_transport free memory allocated for secure transport
uint32_t destroy_secure_transport(secure_transport_t** transport_){
if(!transport_ || !(*transport_) || !((*transport_))){
return HM_FAIL;
}
secure_transport_t* transport = *transport_;
destroy_secure_session_callback(&(transport->session_callback));
secure_session_destroy(transport->session);
transport->session = NULL;
free(transport);
*transport_ = NULL;
return HM_SUCCESS;
}