static int session_manager_ecc_establish_session()

in core/cmd_interface/session_manager_ecc.c [18:195]


static int session_manager_ecc_establish_session (struct session_manager *session,
	struct cmd_interface_msg *request)
{
	struct session_manager_ecc *session_mgr = (struct session_manager_ecc*) session;
	struct cerberus_protocol_key_exchange_type_0 *rq;
	struct cerberus_protocol_key_exchange_response_type_0 *rsp;
	struct session_manager_entry *curr_session;
	struct ecc_private_key session_priv_key;
	struct ecc_public_key session_pub_key;
	struct ecc_public_key device_pub_key;
	const struct riot_keys *keys;
	uint8_t *session_pub_key_der;
	uint8_t *shared_secret;
	uint16_t hash_len;
	size_t session_pub_key_der_len;
	int shared_secret_len;
	int sig_len;
	int status;

	if ((session_mgr == NULL) || (request == NULL)) {
		return SESSION_MANAGER_INVALID_ARGUMENT;
	}

	if (request->length <= cerberus_protocol_key_exchange_type_0_length (0)) {
		return SESSION_MANAGER_INVALID_REQUEST;
	}

	if (request->max_response <= sizeof (struct cerberus_protocol_key_exchange_response)) {
		return SESSION_MANAGER_BUF_TOO_SMALL;
	}

	curr_session = (struct session_manager_entry*) session_manager_get_session (&session_mgr->base,
		request->source_eid);
	if (curr_session != NULL) {
		if (curr_session->session_state != SESSION_STATE_SETUP) {
			return SESSION_MANAGER_INVALID_ORDER;
		}
	}
	else {
		return SESSION_MANAGER_UNEXPECTED_EID;
	}

	rq = (struct cerberus_protocol_key_exchange_type_0*) request->data;
	rsp = (struct cerberus_protocol_key_exchange_response_type_0*) request->data;

	if (rq->hmac_type == CERBERUS_PROTOCOL_HMAC_SHA256) {
		curr_session->hmac_hash_type = HMAC_SHA256;
		hash_len = SHA256_HASH_LENGTH;
	}
	else {
		return SESSION_MANAGER_OPERATION_UNSUPPORTED;
	}

	status = session_mgr->ecc->init_public_key (session_mgr->ecc,
		cerberus_protocol_key_exchange_type_0_key_data (rq),
		cerberus_protocol_key_exchange_type_0_key_len (request), &device_pub_key);
	if (status != 0) {
		return status;
	}

	status = session_mgr->ecc->generate_key_pair (session_mgr->ecc, ECC_KEY_LENGTH_256,
		&session_priv_key, &session_pub_key);
	if (status != 0) {
		goto free_device_key;
	}

	status = session_mgr->ecc->get_public_key_der (session_mgr->ecc, &session_pub_key,
		&session_pub_key_der, &session_pub_key_der_len);
	if (status != 0) {
		goto free_session_keys;
	}

	status = session_manager_generate_keys_digest (&session_mgr->base,
		cerberus_protocol_key_exchange_type_0_key_data (rq),
		cerberus_protocol_key_exchange_type_0_key_len (request), session_pub_key_der,
		session_pub_key_der_len);
	if (status != 0) {
		goto free_session_pub_der;
	}

	rsp->reserved = 0;

	if (CERBERUS_PROTOCOL_KEY_EXCHANGE_TYPE_0_RESPONSE_MAX_KEY_DATA (request) <=
		session_pub_key_der_len) {
		status = SESSION_MANAGER_BUF_TOO_SMALL;
		goto free_session_pub_der;
	}

	memcpy (cerberus_protocol_key_exchange_type_0_response_key_data (rsp), session_pub_key_der,
		session_pub_key_der_len);

	rsp->key_len = (uint16_t) session_pub_key_der_len;

	keys = riot_key_manager_get_riot_keys (session_mgr->base.riot);
	if (keys == NULL) {
		status = SESSION_MANAGER_INVALID_ARGUMENT;
		goto free_session_pub_der;
	}

	sig_len = ecdsa_sign_hash_and_finish (session_mgr->ecc, session_mgr->base.hash, NULL,
		keys->alias_key, keys->alias_key_length,
		cerberus_protocol_key_exchange_type_0_response_sig_data (rsp),
		CERBERUS_PROTOCOL_KEY_EXCHANGE_TYPE_0_RESPONSE_MAX_SIG_DATA (request));

	riot_key_manager_release_riot_keys (session_mgr->base.riot, keys);
	if (ROT_IS_ERROR (sig_len)) {
		status = sig_len;
		goto free_session_pub_der;
	}

	cerberus_protocol_key_exchange_type_0_response_sig_len (rsp) = (uint16_t) sig_len;

	status = session_mgr->ecc->get_shared_secret_max_length (session_mgr->ecc, &session_priv_key);
	if (ROT_IS_ERROR (status)) {
		goto free_session_pub_der;
	}

	shared_secret = platform_malloc (status);
	if (shared_secret == NULL) {
		status = SESSION_MANAGER_NO_MEMORY;
		goto free_session_pub_der;
	}

	shared_secret_len = session_mgr->ecc->compute_shared_secret (session_mgr->ecc,
		&session_priv_key, &device_pub_key, shared_secret, status);
	if (ROT_IS_ERROR (shared_secret_len)) {
		status = shared_secret_len;
		goto free_shared_secret;
	}

	status = kdf_nist800_108_counter_mode (session_mgr->base.hash, HMAC_SHA256, shared_secret,
		shared_secret_len, curr_session->device_nonce, sizeof (curr_session->device_nonce),
		curr_session->cerberus_nonce, sizeof (curr_session->cerberus_nonce),
		curr_session->session_key, sizeof (curr_session->session_key));
	if (status != 0) {
		goto free_shared_secret;
	}

	status = kdf_nist800_108_counter_mode (session_mgr->base.hash, HMAC_SHA256, shared_secret,
		shared_secret_len, curr_session->cerberus_nonce, sizeof (curr_session->cerberus_nonce),
		curr_session->device_nonce, sizeof (curr_session->device_nonce), curr_session->hmac_key,
		sizeof (curr_session->hmac_key));
	if (status != 0) {
		goto free_shared_secret;
	}

	status = hash_generate_hmac (session_mgr->base.hash, curr_session->hmac_key,
		sizeof (curr_session->hmac_key), keys->alias_cert, keys->alias_cert_length,
		curr_session->hmac_hash_type,
		cerberus_protocol_key_exchange_type_0_response_hmac_data (rsp),
		CERBERUS_PROTOCOL_KEY_EXCHANGE_TYPE_0_RESPONSE_MAX_HMAC_DATA (request));
	if (status != 0) {
		goto free_shared_secret;
	}

	cerberus_protocol_key_exchange_type_0_response_hmac_len (rsp) = hash_len;

	curr_session->session_state = SESSION_STATE_ESTABLISHED;

	request->length =
		cerberus_protocol_key_exchange_type_0_response_length (session_pub_key_der_len, sig_len,
		hash_len);
	request->crypto_timeout = true;

free_shared_secret:
	platform_free (shared_secret);

free_session_pub_der:
	platform_free (session_pub_key_der);

free_session_keys:
	session_mgr->ecc->release_key_pair (session_mgr->ecc, &session_priv_key, &session_pub_key);

free_device_key:
	session_mgr->ecc->release_key_pair (session_mgr->ecc, NULL, &device_pub_key);

	return status;
}