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