Beispiel #1
0
static void get_data_to_compute(struct crypto_cipher *tfm,
			       struct crypto_ccm_req_priv_ctx *pctx,
			       struct scatterlist *sg, unsigned int len)
{
	struct scatter_walk walk;
	u8 *data_src;
	int n;

	scatterwalk_start(&walk, sg);

	while (len) {
		n = scatterwalk_clamp(&walk, len);
		if (!n) {
			scatterwalk_start(&walk, sg_next(walk.sg));
			n = scatterwalk_clamp(&walk, len);
		}
		data_src = scatterwalk_map(&walk);

		compute_mac(tfm, data_src, n, pctx);
		len -= n;

		scatterwalk_unmap(data_src);
		scatterwalk_advance(&walk, n);
		scatterwalk_done(&walk, 0, len);
		if (len)
			crypto_yield(pctx->flags);
	}

	/* any leftover needs padding and then encrypted */
	if (pctx->ilen) {
		int padlen;
		u8 *odata = pctx->odata;
		u8 *idata = pctx->idata;

		padlen = 16 - pctx->ilen;
		memset(idata + pctx->ilen, 0, padlen);
		crypto_xor(odata, idata, 16);
		crypto_cipher_encrypt_one(tfm, odata, odata);
		pctx->ilen = 0;
	}
}
Beispiel #2
0
static themis_status_t secure_session_finish_server(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;
	soter_container_hdr_t *response_message;
	themis_status_t res;

	const uint8_t *mac;
	size_t mac_length = 0;

	const uint8_t *signature;
	size_t signature_length;

	soter_kdf_context_buf_t sign_data[4];

	uint8_t ecdh_key[1024];
	size_t ecdh_key_length = sizeof(ecdh_key);

	uint8_t shared_secret[1024];
	size_t shared_secret_length = sizeof(shared_secret);


	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;
	}

	/* Get length of used mac */
	res = compute_mac(session_ctx->peer.ecdh_key, session_ctx->peer.ecdh_key_length, NULL, 0, NULL, &mac_length);
	if (THEMIS_BUFFER_TOO_SMALL != res)
	{
		return res;
	}

	signature_length = soter_container_data_size(proto_message) - mac_length;
	signature = soter_container_const_data(proto_message);
	mac = signature + signature_length;

	res = soter_asym_ka_export_key(&(session_ctx->ecdh_ctx), ecdh_key, &ecdh_key_length, false);
	if (THEMIS_SUCCESS != res)
	{
		return res;
	}

	sign_data[0].data = session_ctx->peer.ecdh_key;
	sign_data[0].length = session_ctx->peer.ecdh_key_length;

	sign_data[1].data = ecdh_key;
	sign_data[1].length = ecdh_key_length;

	sign_data[2].data = session_ctx->peer.id;
	sign_data[2].length = session_ctx->peer.id_length;

	sign_data[3].data = session_ctx->we.id;
	sign_data[3].length = session_ctx->we.id_length;

	res = verify_signature(session_ctx->peer.sign_key, session_ctx->peer.sign_key_length, sign_data, 4, signature, signature_length);
	if (THEMIS_SUCCESS != res)
	{
		return res;
	}

	res = soter_asym_ka_derive(&(session_ctx->ecdh_ctx), session_ctx->peer.ecdh_key, session_ctx->peer.ecdh_key_length, shared_secret, &shared_secret_length);
	if (THEMIS_SUCCESS != res)
	{
		return res;
	}

	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)
	{
		return res;
	}

	sign_data[0].data = (const uint8_t *)(&(session_ctx->session_id));
	sign_data[0].length = sizeof(session_ctx->session_id);

	res = soter_kdf(shared_secret, shared_secret_length, SESSION_MASTER_KEY_GENERATION_LABEL, sign_data, 1, session_ctx->session_master_key, sizeof(session_ctx->session_master_key));
	if (THEMIS_SUCCESS != res)
	{
		return res;
	}

	sign_data[0].data = ecdh_key;
	sign_data[0].length = ecdh_key_length;

	sign_data[1].data = (const uint8_t *)(&(session_ctx->session_id));
	sign_data[1].length = sizeof(session_ctx->session_id);

	res = verify_mac(session_ctx->session_master_key, sizeof(session_ctx->session_master_key), sign_data, 2, mac, mac_length);
	if (THEMIS_SUCCESS != res)
	{
		return res;
	}

	sign_data[0].data = session_ctx->peer.ecdh_key;
	sign_data[0].length = session_ctx->peer.ecdh_key_length;

	/* we will reuse ecdh_key buffer for mac response computation */
	ecdh_key_length = sizeof(ecdh_key) - sizeof(soter_container_hdr_t);
	response_message = (soter_container_hdr_t *)ecdh_key;

	res = compute_mac(session_ctx->session_master_key, sizeof(session_ctx->session_master_key), sign_data, 2, soter_container_data(response_message), &ecdh_key_length);
	if (THEMIS_SUCCESS != res)
	{
		return res;
	}

	if ((NULL == output) || (*output_length < (ecdh_key_length + sizeof(soter_container_hdr_t))))
	{
		*output_length = ecdh_key_length + sizeof(soter_container_hdr_t);
		return THEMIS_BUFFER_TOO_SMALL;
	}

	*output_length = ecdh_key_length + sizeof(soter_container_hdr_t);

	res = secure_session_derive_message_keys(session_ctx);
	if (THEMIS_SUCCESS != res)
	{
		return res;
	}

	memcpy(response_message->tag, THEMIS_SESSION_PROTO_TAG, SOTER_CONTAINER_TAG_LENGTH);
	soter_container_set_data_size(response_message, ecdh_key_length);
	soter_update_container_checksum(response_message);

	memcpy(output, ecdh_key, soter_container_data_size(response_message) + sizeof(soter_container_hdr_t));

	/* "Server mode": negotiation completed */
	session_ctx->state_handler = NULL;

	if (session_ctx->user_callbacks->state_changed)
	{
		session_ctx->user_callbacks->state_changed(STATE_ESTABLISHED, session_ctx->user_callbacks->user_data);
	}

	return res;
}
Beispiel #3
0
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;
}