static themis_status_t secure_session_proceed_client(secure_session_t *session_ctx, const void *data, size_t data_length, void *output, size_t *output_length) { const soter_container_hdr_t *proto_message = data; const soter_container_hdr_t *peer_id; const soter_container_hdr_t *peer_ecdh_key; size_t peer_ecdh_key_length; const uint8_t *signature; size_t signature_length; themis_status_t res = THEMIS_SUCCESS; soter_status_t soter_status; uint8_t sign_key[1024]; /* Should be enough for RSA 8192 which is 512 bytes */ size_t sign_key_length; uint8_t ecdh_key[1024]; size_t ecdh_key_length = sizeof(ecdh_key); const soter_container_hdr_t *peer_sign_key; soter_kdf_context_buf_t sign_data[4]; uint8_t *data_to_send = output; size_t length_to_send; soter_container_hdr_t *container; uint8_t *mac; if (data_length < sizeof(soter_container_hdr_t)) { return THEMIS_INVALID_PARAMETER; } if (memcmp(proto_message->tag, THEMIS_SESSION_PROTO_TAG, SOTER_CONTAINER_TAG_LENGTH)) { return THEMIS_INVALID_PARAMETER; } if (data_length < (soter_container_data_size(proto_message) + sizeof(soter_container_hdr_t))) { return THEMIS_INVALID_PARAMETER; } if (THEMIS_SUCCESS != soter_verify_container_checksum(proto_message)) { return THEMIS_INVALID_PARAMETER; } peer_id = (const soter_container_hdr_t *)soter_container_const_data(proto_message); if (memcmp(peer_id->tag, THEMIS_SESSION_ID_TAG, SOTER_CONTAINER_TAG_LENGTH)) { return THEMIS_INVALID_PARAMETER; } if (THEMIS_SUCCESS != soter_verify_container_checksum(peer_id)) { return THEMIS_INVALID_PARAMETER; } peer_ecdh_key = (const soter_container_hdr_t *)(soter_container_const_data(peer_id) + soter_container_data_size(peer_id)); if (memcmp(peer_ecdh_key->tag, EC_PUB_KEY_PREF, strlen(EC_PUB_KEY_PREF))) { return THEMIS_INVALID_PARAMETER; } peer_ecdh_key_length = ntohl(peer_ecdh_key->size); signature = (const uint8_t *)peer_ecdh_key + peer_ecdh_key_length; signature_length = (const uint8_t *)data + soter_container_data_size(proto_message) + sizeof(soter_container_hdr_t) - signature; if (session_ctx->user_callbacks->get_public_key_for_id(soter_container_const_data(peer_id), soter_container_data_size(peer_id), sign_key, sizeof(sign_key), session_ctx->user_callbacks->user_data)) { return THEMIS_SSESSION_GET_PUB_FOR_ID_CALLBACK_ERROR; } peer_sign_key = (const soter_container_hdr_t *)sign_key; if (memcmp(peer_sign_key->tag, EC_PUB_KEY_PREF, strlen(EC_PUB_KEY_PREF))) { return THEMIS_INVALID_PARAMETER; } sign_key_length = ntohl(peer_sign_key->size); if (sizeof(soter_container_hdr_t) >= sign_key_length) { return THEMIS_INVALID_PARAMETER; } soter_status = soter_asym_ka_export_key(&(session_ctx->ecdh_ctx), ecdh_key, &ecdh_key_length, false); if (THEMIS_SUCCESS != soter_status) { return soter_status; } sign_data[0].data = (const uint8_t *)peer_ecdh_key; sign_data[0].length = peer_ecdh_key_length; sign_data[1].data = ecdh_key; sign_data[1].length = ecdh_key_length; sign_data[2].data = soter_container_const_data(peer_id); sign_data[2].length = soter_container_data_size(peer_id); sign_data[3].data = session_ctx->we.id; sign_data[3].length = session_ctx->we.id_length; res = verify_signature(peer_sign_key, sign_key_length, sign_data, 4, signature, signature_length); if (THEMIS_SUCCESS != res) { return res; } res = secure_session_peer_init(&(session_ctx->peer), soter_container_const_data(peer_id), soter_container_data_size(peer_id), peer_ecdh_key, peer_ecdh_key_length, peer_sign_key, sign_key_length); if (THEMIS_SUCCESS != res) { goto err; } sign_data[0].data = ecdh_key; sign_data[0].length = ecdh_key_length; sign_data[1].data = (const uint8_t *)peer_ecdh_key; sign_data[1].length = peer_ecdh_key_length; sign_data[2].data = session_ctx->we.id; sign_data[2].length = session_ctx->we.id_length; sign_data[3].data = soter_container_const_data(peer_id); sign_data[3].length = soter_container_data_size(peer_id); res = soter_kdf(NULL, 0, SESSION_ID_GENERATION_LABEL, sign_data, 4, &(session_ctx->session_id), sizeof(session_ctx->session_id)); if (THEMIS_SUCCESS != res) { goto err; } /* we will reuse sign_key buffer for shared secret computation */ sign_key_length = sizeof(sign_key); res = soter_asym_ka_derive(&(session_ctx->ecdh_ctx), peer_ecdh_key, peer_ecdh_key_length, sign_key, &sign_key_length); if (THEMIS_SUCCESS != res) { goto err; } sign_data[0].data = (const uint8_t *)(&(session_ctx->session_id)); sign_data[0].length = sizeof(session_ctx->session_id); res = soter_kdf(sign_key, sign_key_length, SESSION_MASTER_KEY_GENERATION_LABEL, sign_data, 1, session_ctx->session_master_key, sizeof(session_ctx->session_master_key)); if (THEMIS_SUCCESS != res) { goto err; } /* restore sign data for signature computation */ sign_data[0].data = ecdh_key; sign_data[0].length = ecdh_key_length; res = compute_signature(session_ctx->we.sign_key, session_ctx->we.sign_key_length, NULL, 0, NULL, &signature_length); if (THEMIS_BUFFER_TOO_SMALL != res) { goto err; } /* we will reuse sign_key_length for mac length retrieval */ res = compute_mac(session_ctx->session_master_key, sizeof(session_ctx->session_master_key), NULL, 0, NULL, &sign_key_length); if (THEMIS_BUFFER_TOO_SMALL != res) { goto err; } length_to_send = sizeof(soter_container_hdr_t) + signature_length + sign_key_length; if ((NULL == output) || (*output_length < length_to_send)) { *output_length = length_to_send; res = THEMIS_BUFFER_TOO_SMALL; goto err; } container = (soter_container_hdr_t *)data_to_send; /* Actual signature may be 1 or 2 bytes less than reported above because leading zeroes are stripped */ length_to_send -= signature_length; res = compute_signature(session_ctx->we.sign_key, session_ctx->we.sign_key_length, sign_data, 4, soter_container_data(container), &signature_length); if (THEMIS_SUCCESS != res) { goto err; } length_to_send += signature_length; mac = soter_container_data(container) + signature_length; sign_data[0].data = (const uint8_t *)(session_ctx->peer.ecdh_key); sign_data[0].length = session_ctx->peer.ecdh_key_length; sign_data[1].data = (const uint8_t *)(&(session_ctx->session_id)); sign_data[1].length = sizeof(session_ctx->session_id); res = compute_mac(session_ctx->session_master_key, sizeof(session_ctx->session_master_key), sign_data, 2, mac, &sign_key_length); if (THEMIS_SUCCESS != res) { goto err; } *output_length = length_to_send; memcpy(container->tag, THEMIS_SESSION_PROTO_TAG, SOTER_CONTAINER_TAG_LENGTH); soter_container_set_data_size(container, length_to_send - sizeof(soter_container_hdr_t)); soter_update_container_checksum(container); /* "Client mode": waiting final confirmation from server */ session_ctx->state_handler = secure_session_finish_client; err: if (THEMIS_SUCCESS != res) { secure_session_peer_cleanup(&(session_ctx->peer)); } return res; }
themis_status_t secure_session_generate_connect_request(secure_session_t *session_ctx, void *output, size_t *output_length) { uint8_t *data_to_send = output; size_t length_to_send; soter_container_hdr_t *container; size_t ecdh_key_length = 0; size_t signature_length = 0; soter_status_t soter_status; themis_status_t res = THEMIS_SUCCESS; soter_kdf_context_buf_t sign_data; soter_status = soter_asym_ka_export_key(&(session_ctx->ecdh_ctx), NULL, &ecdh_key_length, false); if (THEMIS_BUFFER_TOO_SMALL != soter_status) { return soter_status; } res = compute_signature(session_ctx->we.sign_key, session_ctx->we.sign_key_length, NULL, 0, NULL, &signature_length); if (THEMIS_BUFFER_TOO_SMALL != res) { return res; } length_to_send = 2 * sizeof(soter_container_hdr_t) + session_ctx->we.id_length + ecdh_key_length + signature_length; if ((NULL == output) || (*output_length < length_to_send)) { *output_length = length_to_send; return THEMIS_BUFFER_TOO_SMALL; } /* Storing ID in a container */ container = ((soter_container_hdr_t *)data_to_send) + 1; memcpy(container->tag, THEMIS_SESSION_ID_TAG, SOTER_CONTAINER_TAG_LENGTH); soter_container_set_data_size(container, session_ctx->we.id_length); memcpy(soter_container_data(container), session_ctx->we.id, session_ctx->we.id_length); soter_update_container_checksum(container); /* Moving back to beginning of allocated buffer */ container = (soter_container_hdr_t *)data_to_send; soter_status = soter_asym_ka_export_key(&(session_ctx->ecdh_ctx), data_to_send + (2 * sizeof(soter_container_hdr_t)) + session_ctx->we.id_length, &ecdh_key_length, false); if (THEMIS_SUCCESS != soter_status) { return soter_status; } sign_data.data = data_to_send + (2 * sizeof(soter_container_hdr_t)) + session_ctx->we.id_length; sign_data.length = ecdh_key_length; /* Actual signature may be 1 or 2 bytes less than reported above because leading zeroes are stripped */ length_to_send -= signature_length; res = compute_signature(session_ctx->we.sign_key, session_ctx->we.sign_key_length, &sign_data, 1, data_to_send + (2 * sizeof(soter_container_hdr_t)) + session_ctx->we.id_length + ecdh_key_length, &signature_length); if (THEMIS_SUCCESS != res) { return res; } length_to_send += signature_length; *output_length = length_to_send; memcpy(container->tag, THEMIS_SESSION_PROTO_TAG, SOTER_CONTAINER_TAG_LENGTH); soter_container_set_data_size(container, length_to_send - sizeof(soter_container_hdr_t)); soter_update_container_checksum(container); /* In "client mode" awaiting initial response from the server */ session_ctx->state_handler = secure_session_proceed_client; session_ctx->is_client = true; if (session_ctx->user_callbacks->state_changed) { session_ctx->user_callbacks->state_changed(STATE_NEGOTIATING, session_ctx->user_callbacks->user_data); } return THEMIS_SUCCESS; }
static themis_status_t secure_session_accept(secure_session_t *session_ctx, const void *data, size_t data_length, void *output, size_t *output_length) { const soter_container_hdr_t *proto_message = data; const soter_container_hdr_t *peer_id; const soter_container_hdr_t *peer_ecdh_key; size_t peer_ecdh_key_length; const uint8_t *signature; size_t signature_length; themis_status_t res = THEMIS_SUCCESS; soter_status_t soter_status; uint8_t sign_key[1024]; /* Should be enough for RSA 8192 which is 512 bytes */ size_t sign_key_length; const soter_container_hdr_t *peer_sign_key; soter_kdf_context_buf_t sign_data[4]; uint8_t *data_to_send = output; size_t length_to_send; size_t ecdh_key_length = 0; soter_container_hdr_t *container; if (data_length < sizeof(soter_container_hdr_t)) { return THEMIS_INVALID_PARAMETER; } if (memcmp(proto_message->tag, THEMIS_SESSION_PROTO_TAG, SOTER_CONTAINER_TAG_LENGTH)) { return THEMIS_INVALID_PARAMETER; } if (data_length < (soter_container_data_size(proto_message) + sizeof(soter_container_hdr_t))) { return THEMIS_INVALID_PARAMETER; } if (THEMIS_SUCCESS != soter_verify_container_checksum(proto_message)) { return THEMIS_INVALID_PARAMETER; } peer_id = (const soter_container_hdr_t *)soter_container_const_data(proto_message); if (memcmp(peer_id->tag, THEMIS_SESSION_ID_TAG, SOTER_CONTAINER_TAG_LENGTH)) { return THEMIS_INVALID_PARAMETER; } if (THEMIS_SUCCESS != soter_verify_container_checksum(peer_id)) { return THEMIS_INVALID_PARAMETER; } peer_ecdh_key = (const soter_container_hdr_t *)(soter_container_const_data(peer_id) + soter_container_data_size(peer_id)); if (memcmp(peer_ecdh_key->tag, EC_PUB_KEY_PREF, strlen(EC_PUB_KEY_PREF))) { return THEMIS_INVALID_PARAMETER; } peer_ecdh_key_length = soter_container_data_size(peer_ecdh_key) + sizeof(soter_container_hdr_t); signature = (const uint8_t *)peer_ecdh_key + peer_ecdh_key_length; signature_length = (const uint8_t *)data + soter_container_data_size(proto_message) + sizeof(soter_container_hdr_t) - signature; if (session_ctx->user_callbacks->get_public_key_for_id(soter_container_const_data(peer_id), soter_container_data_size(peer_id), sign_key, sizeof(sign_key), session_ctx->user_callbacks->user_data)) { return THEMIS_SSESSION_GET_PUB_FOR_ID_CALLBACK_ERROR; } peer_sign_key = (const soter_container_hdr_t *)sign_key; if (memcmp(peer_sign_key->tag, EC_PUB_KEY_PREF, strlen(EC_PUB_KEY_PREF))) { return THEMIS_INVALID_PARAMETER; } sign_key_length = ntohl(peer_sign_key->size); if (sizeof(soter_container_hdr_t) >= sign_key_length) { return THEMIS_INVALID_PARAMETER; } sign_data[0].data = (const uint8_t *)peer_ecdh_key; sign_data[0].length = peer_ecdh_key_length; res = verify_signature(peer_sign_key, sign_key_length, sign_data, 1, signature, signature_length); if (THEMIS_SUCCESS != res) { return res; } /* Preparing to send response */ res = compute_signature(session_ctx->we.sign_key, session_ctx->we.sign_key_length, NULL, 0, NULL, &signature_length); if (THEMIS_BUFFER_TOO_SMALL != res) { return res; } soter_status = soter_asym_ka_export_key(&(session_ctx->ecdh_ctx), NULL, &ecdh_key_length, false); if (THEMIS_BUFFER_TOO_SMALL != soter_status) { return soter_status; } length_to_send = 2 * sizeof(soter_container_hdr_t) + session_ctx->we.id_length + ecdh_key_length + signature_length; if ((NULL == output) || (*output_length < length_to_send)) { *output_length = length_to_send; return THEMIS_BUFFER_TOO_SMALL; } res = secure_session_peer_init(&(session_ctx->peer), soter_container_const_data(peer_id), soter_container_data_size(peer_id), peer_ecdh_key, peer_ecdh_key_length, peer_sign_key, sign_key_length); if (THEMIS_SUCCESS != res) { goto err; } /* Storing ID in a container */ container = (soter_container_hdr_t *)data_to_send + 1; memcpy(container->tag, THEMIS_SESSION_ID_TAG, SOTER_CONTAINER_TAG_LENGTH); soter_container_set_data_size(container, session_ctx->we.id_length); memcpy(soter_container_data(container), session_ctx->we.id, session_ctx->we.id_length); soter_update_container_checksum(container); /* Moving back to beginning of allocated buffer */ container = (soter_container_hdr_t *)data_to_send; soter_status = soter_asym_ka_export_key(&(session_ctx->ecdh_ctx), data_to_send + (2 * sizeof(soter_container_hdr_t)) + session_ctx->we.id_length, &ecdh_key_length, false); if (THEMIS_SUCCESS != soter_status) { res = soter_status; goto err; } sign_data[0].data = data_to_send + (2 * sizeof(soter_container_hdr_t)) + session_ctx->we.id_length; sign_data[0].length = ecdh_key_length; sign_data[1].data = session_ctx->peer.ecdh_key; sign_data[1].length = session_ctx->peer.ecdh_key_length; sign_data[2].data = session_ctx->we.id; sign_data[2].length = session_ctx->we.id_length; sign_data[3].data = session_ctx->peer.id; sign_data[3].length = session_ctx->peer.id_length; /* Actual signature may be 1 or 2 bytes less than reported above because leading zeroes are stripped */ length_to_send -= signature_length; res = compute_signature(session_ctx->we.sign_key, session_ctx->we.sign_key_length, sign_data, 4, data_to_send + (2 * sizeof(soter_container_hdr_t)) + session_ctx->we.id_length + ecdh_key_length, &signature_length); if (THEMIS_SUCCESS != res) { goto err; } length_to_send += signature_length; *output_length = length_to_send; memcpy(container->tag, THEMIS_SESSION_PROTO_TAG, SOTER_CONTAINER_TAG_LENGTH); soter_container_set_data_size(container, length_to_send - sizeof(soter_container_hdr_t)); soter_update_container_checksum(container); /* "Server mode": waiting response from the client */ session_ctx->state_handler = secure_session_finish_server; if (session_ctx->user_callbacks->state_changed) { session_ctx->user_callbacks->state_changed(STATE_NEGOTIATING, session_ctx->user_callbacks->user_data); } err: if (THEMIS_SUCCESS != res) { secure_session_peer_cleanup(&(session_ctx->peer)); } return res; }
static STSStatus compose_post_data(char* buffer, uint32_t bufferSize, CommonRequestContext* commonContext, AssumeRoleRequestContext* assumeRoleContext) { string_buffer(queryParams, 2048*3); string_buffer_initialize(queryParams); char* queryParamsArray[32]; int paramsCount = 0; int len = 0; int start = 0; int amp = 0; #define safe_append(isNewParam, name, value) \ do { \ int fit; \ start = len; \ if (amp) { start++; } \ if (isNewParam) { \ queryParamsArray[paramsCount++] = &(queryParams[start]); \ } \ if (amp) { \ string_buffer_append(queryParams, "&", 1, fit); \ if (!fit) { \ return STSStatusUriTooLong; \ } \ len++; \ } \ string_buffer_append(queryParams, name "=", \ sizeof(name "=") - 1, fit); \ if (!fit) { \ return STSStatusUriTooLong; \ } \ len += strlen(name) + 1; \ amp = 1; \ string_buffer_append(queryParams, value, strlen(value), fit); \ len += strlen(value); \ } while (0) time_t now = time(NULL); char timebuf[256]; strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); STSUUID uuid; char* uuidString; uuid_create(&uuid); uuid_to_string(&uuid, &uuidString); char durationSeconds[10]; snprintf(durationSeconds, 10, "%d", assumeRoleContext->durationSeconds); safe_append(1, "Format", "JSON"); safe_append(1, "Version", "2015-04-01"); safe_append(1, "SignatureVersion", "1.0"); safe_append(1, "SignatureMethod", "HMAC-SHA1"); safe_append(1, "SignatureNonce", uuidString); safe_append(1, "Timestamp", timebuf); safe_append(1, "Action", commonContext->action); safe_append(1, "AccessKeyId", commonContext->accessKeyId); safe_append(1, "RoleArn", assumeRoleContext->RoleArn); safe_append(1, "RoleSessionName", assumeRoleContext->RoleSessionName); safe_append(1, "Policy", assumeRoleContext->policy); safe_append(1, "DurationSeconds", durationSeconds); params_array_sort(queryParamsArray, paramsCount); string_buffer(signature, 256); string_buffer_initialize(signature); compute_signature(signature, 256, queryParamsArray, paramsCount, commonContext->accessKeySecret); compose_url_encoded_post_data(buffer, bufferSize, queryParams, strlen(queryParams), signature); free(uuidString); return STSStatusOK; }