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