X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcske%2Fsilcske.c;h=c230e36b72ed4a7b820d58aa1296d2316c7228d2;hp=cb4713708ae0305eb360d7ec167310bee3d46f41;hb=40f8443d8d3a6577336ee66d18e04d9ac4d956bb;hpb=6210937137137fe4019e808686dd1fcf99360af6 diff --git a/lib/silcske/silcske.c b/lib/silcske/silcske.c index cb471370..c230e36b 100644 --- a/lib/silcske/silcske.c +++ b/lib/silcske/silcske.c @@ -18,23 +18,22 @@ */ /* $Id$ */ -#include "silcincludes.h" +#include "silc.h" #include "silcske.h" #include "groups_internal.h" -/* Static functions */ -static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n, - SilcUInt32 len, - SilcMPInt *rnd); -static SilcSKEStatus silc_ske_make_hash(SilcSKE ske, - unsigned char *return_hash, - SilcUInt32 *return_hash_len, - int initiator); -static SilcSKEStatus -silc_ske_select_security_properties(SilcSKE ske, - const char *version, - SilcSKEStartPayload payload, - SilcSKEStartPayload remote_payload); +/************************** Types and definitions ***************************/ + +/* Structure to hold all SKE callbacks. */ +struct SilcSKECallbacksStruct { + SilcSKEVerifyCb verify_key; + SilcSKECompletionCb completed; + void *context; +}; + + +/************************ Static utility functions **************************/ + SilcSKEKeyMaterial silc_ske_process_key_material_data(unsigned char *data, SilcUInt32 data_len, @@ -47,142 +46,27 @@ silc_ske_process_key_material(SilcSKE ske, SilcUInt32 req_iv_len, SilcUInt32 req_enc_key_len, SilcUInt32 req_hmac_key_len); -static void silc_ske_packet_receive(SilcPacketEngine engine, - SilcPacketStream stream, - SilcPacket packet, - void *callback_context, - void *app_context); -static void silc_ske_packet_eos(SilcPacketEngine engine, - SilcPacketStream stream, - void *callback_context, - void *app_context); -static void silc_ske_packet_error(SilcPacketEngine engine, - SilcPacketStream stream, - SilcPacketError error, - void *callback_context, - void *app_context); - -/* Structure to hold all SKE callbacks. */ -struct SilcSKECallbacksStruct { - SilcSKEVerifyCb verify_key; - SilcSKECheckVersionCb check_version; - SilcSKECompletionCb completed; - void *context; -}; - -/* Packet stream callbacks */ -static SilcPacketCallbacks silc_ske_stream_cbs = -{ - silc_ske_packet_receive, - silc_ske_packet_eos, - silc_ske_packet_error -}; - -/* Allocates new SKE object. */ - -SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, void *context) -{ - SilcSKE ske; - - SILC_LOG_DEBUG(("Allocating new Key Exchange object")); - - if (!rng || !schedule) - return NULL; - - ske = silc_calloc(1, sizeof(*ske)); - if (!ske) - return NULL; - ske->status = SILC_SKE_STATUS_OK; - ske->rng = rng; - ske->user_data = context; - ske->schedule = schedule; - ske->users = 1; - - return ske; -} - -/* Free's SKE object. */ - -void silc_ske_free(SilcSKE ske) -{ - ske->users--; - if (ske->users > 0) { - SILC_LOG_DEBUG(("Key Exchange set to FREED status")); - ske->status = SILC_SKE_STATUS_FREED; - return; - } - - SILC_LOG_DEBUG(("Freeing Key Exchange object")); - - if (ske) { - /* Free start payload */ - if (ske->start_payload) - silc_ske_payload_start_free(ske->start_payload); - - /* Free KE payload */ - if (ske->ke1_payload) - silc_ske_payload_ke_free(ske->ke1_payload); - if (ske->ke2_payload) - silc_ske_payload_ke_free(ske->ke2_payload); - silc_free(ske->remote_version); - - /* Free rest */ - if (ske->prop) { - if (ske->prop->group) - silc_ske_group_free(ske->prop->group); - if (ske->prop->pkcs) - silc_pkcs_free(ske->prop->pkcs); - if (ske->prop->cipher) - silc_cipher_free(ske->prop->cipher); - if (ske->prop->hash) - silc_hash_free(ske->prop->hash); - if (ske->prop->hmac) - silc_hmac_free(ske->prop->hmac); - silc_free(ske->prop); - } - if (ske->start_payload_copy) - silc_buffer_free(ske->start_payload_copy); - if (ske->x) { - silc_mp_uninit(ske->x); - silc_free(ske->x); - } - if (ske->KEY) { - silc_mp_uninit(ske->KEY); - silc_free(ske->KEY); - } - silc_free(ske->hash); - silc_free(ske->callbacks); - memset(ske, 'F', sizeof(*ske)); - silc_free(ske); - } -} -/* Return user context */ +/* Packet callback */ -void *silc_ske_get_context(SilcSKE ske) +static SilcBool silc_ske_packet_receive(SilcPacketEngine engine, + SilcPacketStream stream, + SilcPacket packet, + void *callback_context, + void *app_context) { - return ske->user_data; + SilcSKE ske = callback_context; + ske->packet = packet; + silc_fsm_continue(&ske->fsm); + return TRUE; } -/* Sets protocol callbacks */ - -void silc_ske_set_callbacks(SilcSKE ske, - SilcSKEVerifyCb verify_key, - SilcSKECheckVersionCb check_version, - SilcSKECompletionCb completed, - void *context) +/* Packet stream callbacks */ +static SilcPacketCallbacks silc_ske_stream_cbs = { - if (ske->callbacks) - silc_free(ske->callbacks); - ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks)); - if (!ske->callbacks) - return; - ske->callbacks->verify_key = verify_key; - ske->callbacks->check_version = check_version; - ske->callbacks->completed = completed; - ske->callbacks->context = context; -} + silc_ske_packet_receive, NULL, NULL +}; /* Aborts SKE protocol */ @@ -197,716 +81,831 @@ static void silc_ske_abort(SilcAsyncOperation op, void *context) static void silc_ske_pk_verified(SilcSKE ske, SilcSKEStatus status, void *completion_context) { + ske->status = status; SILC_FSM_CALL_CONTINUE(&ske->fsm); } -/* Initiator state machine */ -SILC_FSM_STATE(silc_ske_st_initiator_start); -SILC_FSM_STATE(silc_ske_st_initiator_phase1); -SILC_FSM_STATE(silc_ske_st_initiator_phase2); -SILC_FSM_STATE(silc_ske_st_initiator_phase3); -SILC_FSM_STATE(silc_ske_st_initiator_phase4); -SILC_FSM_STATE(silc_ske_st_initiator_end); -SILC_FSM_STATE(silc_ske_st_initiator_aborted); -SILC_FSM_STATE(silc_ske_st_initiator_error); - -/* Start protocol. Send our proposal */ +/* Checks remote and local versions */ -SILC_FSM_STATE(silc_ske_st_initiator_start) +static SilcSKEStatus silc_ske_check_version(SilcSKE ske) { - SilcSKE ske = fsm_context; - SilcBuffer payload_buf; - SilcStatus status; + SilcUInt32 l_protocol_version = 0, r_protocol_version = 0; - SILC_LOG_DEBUG(("Start")); - - if (ske->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - return SILC_FSM_CONTINUE; - } + if (!ske->remote_version || !ske->version) + return SILC_SKE_STATUS_BAD_VERSION; - /* Encode the payload */ - status = silc_ske_payload_start_encode(ske, ske->start_payload, - &payload_buf); - if (status != SILC_SKE_STATUS_OK) { - /** Error encoding Start Payload */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; - } + if (!silc_parse_version_string(ske->remote_version, &r_protocol_version, + NULL, NULL, NULL, NULL)) + return SILC_SKE_STATUS_BAD_VERSION; - /* Save the the payload buffer for future use. It is later used to - compute the HASH value. */ - ske->start_payload_copy = payload_buf; + if (!silc_parse_version_string(ske->version, &l_protocol_version, + NULL, NULL, NULL, NULL)) + return SILC_SKE_STATUS_BAD_VERSION; - /* Send the packet */ - /* XXX */ + /* If remote is too new, don't connect */ + if (l_protocol_version < r_protocol_version) + return SILC_SKE_STATUS_BAD_VERSION; - /** Wait for responder proposal */ - SILC_LOG_DEBUG(("Waiting for reponder proposal")); - silc_fsm_next(ske, silc_ske_st_initiator_phase1); - return SILC_FSM_WAIT; + return SILC_SKE_STATUS_OK; } -/* Phase-1. Receives responder's proposal */ +/* Selects the supported security properties from the initiator's Key + Exchange Start Payload. */ -SILC_FSM_STATE(silc_ske_st_initiator_phase1) +static SilcSKEStatus +silc_ske_select_security_properties(SilcSKE ske, + SilcSKEStartPayload payload, + SilcSKEStartPayload remote_payload) { - SilcSKE ske = fsm_context; SilcSKEStatus status; - SilcSKEStartPayload payload; - SilcSKESecurityProperties prop; - SilcSKEDiffieHellmanGroup group; + SilcSKEStartPayload rp; + char *cp; + int len; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Parsing KE Start Payload")); - if (ske->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - return SILC_FSM_CONTINUE; - } + rp = remote_payload; - /* Decode the payload */ - status = silc_ske_payload_start_decode(ske, ske->packet_buf, &payload); + /* Check version string */ + ske->remote_version = silc_memdup(rp->version, rp->version_len); + status = silc_ske_check_version(ske); if (status != SILC_SKE_STATUS_OK) { - /** Error decoding Start Payload */ ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; + return status; } - /* Check that the cookie is returned unmodified */ - if (memcmp(ske->start_payload->cookie, payload->cookie, - ske->start_payload->cookie_len)) { - /** Invalid cookie */ - SILC_LOG_ERROR(("Responder modified our cookie and it must not do it")); - ske->status = SILC_SKE_STATUS_INVALID_COOKIE; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; + /* Flags are returned unchanged. */ + payload->flags = rp->flags; + + /* Take cookie, we must return it to sender unmodified. */ + payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char)); + if (!payload->cookie) { + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + return status; } + payload->cookie_len = SILC_SKE_COOKIE_LEN; + memcpy(payload->cookie, rp->cookie, SILC_SKE_COOKIE_LEN); - /* Check version string */ - if (ske->callbacks->check_version) { - status = ske->callbacks->check_version(ske, payload->version, - payload->version_len, - ske->callbacks->context); - if (status != SILC_SKE_STATUS_OK) { - /** Version mismatch */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; - } + /* Put our version to our reply */ + payload->version = strdup(ske->version); + if (!payload->version) { + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + return status; } + payload->version_len = strlen(ske->version); - /* Free our KE Start Payload context, we don't need it anymore. */ - silc_ske_payload_start_free(ske->start_payload); - ske->start_payload = NULL; + /* Get supported Key Exchange groups */ + cp = rp->ke_grp_list; + if (cp && strchr(cp, ',')) { + while(cp) { + char *item; - /* Take the selected security properties into use while doing - the key exchange. This is used only while doing the key - exchange. */ - ske->prop = prop = silc_calloc(1, sizeof(*prop)); - if (!ske->prop) - goto err; - prop->flags = payload->flags; - status = silc_ske_group_get_by_name(payload->ke_grp_list, &group); - if (status != SILC_SKE_STATUS_OK) - goto err; + len = strcspn(cp, ","); + item = silc_calloc(len + 1, sizeof(char)); + if (!item) { + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + return status; + } + memcpy(item, cp, len); - prop->group = group; + SILC_LOG_DEBUG(("Proposed KE group `%s'", item)); - if (silc_pkcs_alloc(payload->pkcs_alg_list, &prop->pkcs) == FALSE) { - status = SILC_SKE_STATUS_UNKNOWN_PKCS; - goto err; - } - if (silc_cipher_alloc(payload->enc_alg_list, &prop->cipher) == FALSE) { - status = SILC_SKE_STATUS_UNKNOWN_CIPHER; - goto err; - } - if (silc_hash_alloc(payload->hash_alg_list, &prop->hash) == FALSE) { - status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION; - goto err; - } - if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) { - status = SILC_SKE_STATUS_UNKNOWN_HMAC; - goto err; - } + if (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) { + SILC_LOG_DEBUG(("Found KE group `%s'", item)); - /* Save remote's KE Start Payload */ - ske->start_payload = payload; + payload->ke_grp_len = len; + payload->ke_grp_list = item; + break; + } - /** Send KE Payload */ - silc_fsm_next(fsm, silc_ske_st_initiator_phase2); - return SILC_FSM_CONTINUE; + cp += len; + if (strlen(cp) == 0) + cp = NULL; + else + cp++; - err: - if (payload) - silc_ske_payload_start_free(payload); + if (item) + silc_free(item); + } - silc_ske_group_free(group); + if (!payload->ke_grp_len && !payload->ke_grp_list) { + SILC_LOG_DEBUG(("Could not find supported KE group")); + silc_free(payload); + return SILC_SKE_STATUS_UNKNOWN_GROUP; + } + } else { - if (prop->pkcs) - silc_pkcs_free(prop->pkcs); - if (prop->cipher) - silc_cipher_free(prop->cipher); - if (prop->hash) - silc_hash_free(prop->hash); - if (prop->hmac) - silc_hmac_free(prop->hmac); - silc_free(prop); - ske->prop = NULL; + if (!rp->ke_grp_len) { + SILC_LOG_DEBUG(("KE group not defined in payload")); + silc_free(payload); + return SILC_SKE_STATUS_BAD_PAYLOAD; + } - if (status == SILC_SKE_STATUS_OK) - status = SILC_SKE_STATUS_ERROR; + SILC_LOG_DEBUG(("Proposed KE group `%s'", rp->ke_grp_list)); + SILC_LOG_DEBUG(("Found KE group `%s'", rp->ke_grp_list)); - /** Error */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; -} + payload->ke_grp_len = rp->ke_grp_len; + payload->ke_grp_list = strdup(rp->ke_grp_list); + } -/* Phase-2. Send KE payload */ + /* Get supported PKCS algorithms */ + cp = rp->pkcs_alg_list; + if (cp && strchr(cp, ',')) { + while(cp) { + char *item; -SILC_FSM_STATE(silc_ske_st_initiator_phase2) -{ - SilcSKE ske = fsm_context; - SilcSKEStatus status; - SilcBuffer payload_buf; - SilcMPInt *x; - SilcSKEKEPayload payload; - SilcUInt32 pk_len; + len = strcspn(cp, ","); + item = silc_calloc(len + 1, sizeof(char)); + if (!item) { + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + return status; + } + memcpy(item, cp, len); - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", item)); - /* Create the random number x, 1 < x < q. */ - x = silc_calloc(1, sizeof(*x)); - if (!x){ - /** Out of memory */ - ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; - } - silc_mp_init(x); - status = - silc_ske_create_rnd(ske, &ske->prop->group->group_order, - silc_mp_sizeinbase(&ske->prop->group->group_order, 2), - x); - if (status != SILC_SKE_STATUS_OK) { - /** Error generating random number */ - silc_mp_uninit(x); - silc_free(x); - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; - } + if (silc_pkcs_is_supported(item) == TRUE) { + SILC_LOG_DEBUG(("Found PKCS alg `%s'", item)); - /* Encode the result to Key Exchange Payload. */ + payload->pkcs_alg_len = len; + payload->pkcs_alg_list = item; + break; + } - payload = silc_calloc(1, sizeof(*payload)); - if (!payload) { - /** Out of memory */ - silc_mp_uninit(x); - silc_free(x); - ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; - } - ske->ke1_payload = payload; + cp += len; + if (strlen(cp) == 0) + cp = NULL; + else + cp++; - SILC_LOG_DEBUG(("Computing e = g ^ x mod p")); + if (item) + silc_free(item); + } - /* Do the Diffie Hellman computation, e = g ^ x mod p */ - silc_mp_init(&payload->x); - silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x, - &ske->prop->group->group); + if (!payload->pkcs_alg_len && !payload->pkcs_alg_list) { + SILC_LOG_DEBUG(("Could not find supported PKCS alg")); + silc_free(payload->ke_grp_list); + silc_free(payload); + return SILC_SKE_STATUS_UNKNOWN_PKCS; + } + } else { - /* Get public key */ - if (ske->public_key) { - payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len); - if (!payload->pk_data) { - /** Error encoding public key */ - silc_mp_uninit(x); - silc_free(x); - silc_mp_uninit(&payload->x); + if (!rp->pkcs_alg_len) { + SILC_LOG_DEBUG(("PKCS alg not defined in payload")); + silc_free(payload->ke_grp_list); silc_free(payload); - ske->ke1_payload = NULL; - ske->status = SILC_SKE_STATUS_ERROR; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; + return SILC_SKE_STATUS_BAD_PAYLOAD; } - payload->pk_len = pk_len; + + SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list)); + SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list)); + + payload->pkcs_alg_len = rp->pkcs_alg_len; + payload->pkcs_alg_list = strdup(rp->pkcs_alg_list); } - payload->pk_type = ske->pk_type; - /* Compute signature data if we are doing mutual authentication */ - if (ske->private_key && - ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) { - unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1]; - SilcUInt32 hash_len, sign_len; + /* Get supported encryption algorithms */ + cp = rp->enc_alg_list; + if (cp && strchr(cp, ',')) { + while(cp) { + char *item; - SILC_LOG_DEBUG(("We are doing mutual authentication")); - SILC_LOG_DEBUG(("Computing HASH_i value")); + len = strcspn(cp, ","); + item = silc_calloc(len + 1, sizeof(char)); + if (!item) { + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + return status; + } + memcpy(item, cp, len); - /* Compute the hash value */ - memset(hash, 0, sizeof(hash)); - silc_ske_make_hash(ske, hash, &hash_len, TRUE); + SILC_LOG_DEBUG(("Proposed encryption alg `%s'", item)); - SILC_LOG_DEBUG(("Signing HASH_i value")); + if (silc_cipher_is_supported(item) == TRUE) { + SILC_LOG_DEBUG(("Found encryption alg `%s'", item)); - /* Sign the hash value */ - silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv, - ske->private_key->prv_len); - if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 || - !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) { - /** Error computing signature */ - silc_mp_uninit(x); - silc_free(x); - silc_mp_uninit(&payload->x); - silc_free(payload->pk_data); + payload->enc_alg_len = len; + payload->enc_alg_list = item; + break; + } + + cp += len; + if (strlen(cp) == 0) + cp = NULL; + else + cp++; + + if (item) + silc_free(item); + } + + if (!payload->enc_alg_len && !payload->enc_alg_list) { + SILC_LOG_DEBUG(("Could not find supported encryption alg")); + silc_free(payload->ke_grp_list); + silc_free(payload->pkcs_alg_list); silc_free(payload); - ske->ke1_payload = NULL; - ske->status = SILC_SKE_STATUS_SIGNATURE_ERROR; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; + return SILC_SKE_STATUS_UNKNOWN_CIPHER; } - payload->sign_data = silc_memdup(sign, sign_len); - payload->sign_len = sign_len; - memset(sign, 0, sizeof(sign)); - } + } else { - status = silc_ske_payload_ke_encode(ske, payload, &payload_buf); - if (status != SILC_SKE_STATUS_OK) { - /** Error encoding KE payload */ - silc_mp_uninit(x); - silc_free(x); - silc_mp_uninit(&payload->x); - silc_free(payload->pk_data); - silc_free(payload->sign_data); - silc_free(payload); - ske->ke1_payload = NULL; - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; - } + if (!rp->enc_alg_len) { + SILC_LOG_DEBUG(("Encryption alg not defined in payload")); + silc_free(payload->ke_grp_list); + silc_free(payload->pkcs_alg_list); + silc_free(payload); + return SILC_SKE_STATUS_BAD_PAYLOAD; + } - ske->x = x; + SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it", + rp->enc_alg_list)); - /* Send the packet. */ - /* XXX */ + payload->enc_alg_len = rp->enc_alg_len; + payload->enc_alg_list = strdup(rp->enc_alg_list); + } - silc_buffer_free(payload_buf); + /* Get supported hash algorithms */ + cp = rp->hash_alg_list; + if (cp && strchr(cp, ',')) { + while(cp) { + char *item; - /** Waiting responder's KE payload */ - silc_fsm_next(fsm, silc_ske_st_initiator_phase3); - return SILC_FSM_WAIT; -} + len = strcspn(cp, ","); + item = silc_calloc(len + 1, sizeof(char)); + if (!item) { + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + return status; + } + memcpy(item, cp, len); -/* Phase-3. Process responder's KE payload */ + SILC_LOG_DEBUG(("Proposed hash alg `%s'", item)); -SILC_FSM_STATE(silc_ske_st_initiator_phase3) -{ - SilcSKE ske = fsm_context; - SilcSKEStatus status; - SilcSKEKEPayload payload; - SilcMPInt *KEY; + if (silc_hash_is_supported(item) == TRUE) { + SILC_LOG_DEBUG(("Found hash alg `%s'", item)); - SILC_LOG_DEBUG(("Start")); + payload->hash_alg_len = len; + payload->hash_alg_list = item; + break; + } - if (ske->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - return SILC_FSM_CONTINUE; - } + cp += len; + if (strlen(cp) == 0) + cp = NULL; + else + cp++; - /* Decode the payload */ - status = silc_ske_payload_ke_decode(ske, ske->packet_buf, &payload); - if (status != SILC_SKE_STATUS_OK) { - /** Error decoding KE payload */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; - } - ske->ke2_payload = payload; + if (item) + silc_free(item); + } - if (!payload->pk_data && ske->callbacks->verify_key) { - SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), " - "even though we require it")); - ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED; - goto err; + if (!payload->hash_alg_len && !payload->hash_alg_list) { + SILC_LOG_DEBUG(("Could not find supported hash alg")); + silc_free(payload->ke_grp_list); + silc_free(payload->pkcs_alg_list); + silc_free(payload->enc_alg_list); + silc_free(payload); + return SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION; + } + } else { + + if (!rp->hash_alg_len) { + SILC_LOG_DEBUG(("Hash alg not defined in payload")); + silc_free(payload->ke_grp_list); + silc_free(payload->pkcs_alg_list); + silc_free(payload->enc_alg_list); + silc_free(payload); + return SILC_SKE_STATUS_BAD_PAYLOAD; + } + + SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it", + rp->hash_alg_list)); + + payload->hash_alg_len = rp->hash_alg_len; + payload->hash_alg_list = strdup(rp->hash_alg_list); } - SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p")); + /* Get supported HMACs */ + cp = rp->hmac_alg_list; + if (cp && strchr(cp, ',')) { + while(cp) { + char *item; - /* Compute the shared secret key */ - KEY = silc_calloc(1, sizeof(*KEY)); - silc_mp_init(KEY); - silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group); - ske->KEY = KEY; + len = strcspn(cp, ","); + item = silc_calloc(len + 1, sizeof(char)); + if (!item) { + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + return status; + } + memcpy(item, cp, len); - if (payload->pk_data && ske->callbacks->verify_key) { - SILC_LOG_DEBUG(("Verifying public key")); + SILC_LOG_DEBUG(("Proposed HMAC `%s'", item)); - /** Waiting public key verification */ - silc_fsm_next(fsm, silc_ske_st_initiator_phase4); - SILC_FSM_CALL(ske->callbacks->verify_key(ske, payload->pk_data, - payload->pk_len, - payload->pk_type, - ske->callbacks->context, - silc_ske_pk_verified, NULL)); - /* NOT REACHED */ + if (silc_hmac_is_supported(item) == TRUE) { + SILC_LOG_DEBUG(("Found HMAC `%s'", item)); + + payload->hmac_alg_len = len; + payload->hmac_alg_list = item; + break; + } + + cp += len; + if (strlen(cp) == 0) + cp = NULL; + else + cp++; + + if (item) + silc_free(item); + } + + if (!payload->hmac_alg_len && !payload->hmac_alg_list) { + SILC_LOG_DEBUG(("Could not find supported HMAC")); + silc_free(payload->ke_grp_list); + silc_free(payload->pkcs_alg_list); + silc_free(payload->enc_alg_list); + silc_free(payload->hash_alg_list); + silc_free(payload); + return SILC_SKE_STATUS_UNKNOWN_HMAC; + } + } else { + + if (!rp->hmac_alg_len) { + SILC_LOG_DEBUG(("HMAC not defined in payload")); + silc_free(payload->ke_grp_list); + silc_free(payload->pkcs_alg_list); + silc_free(payload->enc_alg_list); + silc_free(payload->hash_alg_list); + silc_free(payload); + return SILC_SKE_STATUS_BAD_PAYLOAD; + } + + SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it", + rp->hmac_alg_list)); + + payload->hmac_alg_len = rp->hmac_alg_len; + payload->hmac_alg_list = strdup(rp->hmac_alg_list); } - /** Process key material */ - silc_fsm_next(fsm, silc_ske_st_initiator_phase4); - return SILC_FSM_CONTINUE; + /* Get supported compression algorithms */ + cp = rp->comp_alg_list; + if (cp && strchr(cp, ',')) { + while(cp) { + char *item; - err: - silc_ske_payload_ke_free(payload); - ske->ke2_payload = NULL; + len = strcspn(cp, ","); + item = silc_calloc(len + 1, sizeof(char)); + if (!item) { + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + return status; + } + memcpy(item, cp, len); - silc_mp_uninit(ske->KEY); - silc_free(ske->KEY); - ske->KEY = NULL; + SILC_LOG_DEBUG(("Proposed Compression `%s'", item)); - if (status == SILC_SKE_STATUS_OK) - return SILC_SKE_STATUS_ERROR; +#if 1 + if (!strcmp(item, "none")) { + SILC_LOG_DEBUG(("Found Compression `%s'", item)); + payload->comp_alg_len = len; + payload->comp_alg_list = item; + break; + } +#else + if (silc_hmac_is_supported(item) == TRUE) { + SILC_LOG_DEBUG(("Found Compression `%s'", item)); + payload->comp_alg_len = len; + payload->comp_alg_list = item; + break; + } +#endif - /** Error */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; + cp += len; + if (strlen(cp) == 0) + cp = NULL; + else + cp++; + + if (item) + silc_free(item); + } + } + + payload->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + + 2 + payload->version_len + + 2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len + + 2 + payload->enc_alg_len + 2 + payload->hash_alg_len + + 2 + payload->hmac_alg_len + 2 + payload->comp_alg_len; + + return SILC_SKE_STATUS_OK; } -/* Process key material */ +/* Creates random number such that 1 < rnd < n and at most length + of len bits. The rnd sent as argument must be initialized. */ -SILC_FSM_STATE(silc_ske_st_initiator_phase4) +static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n, + SilcUInt32 len, + SilcMPInt *rnd) { - SilcSKE ske = fsm_context; - SilcSKEStatus status; - SilcSKEKEPayload payload; - unsigned char hash[SILC_HASH_MAXLEN]; - SilcUInt32 hash_len; - SilcPublicKey public_key = NULL; - int key_len, block_len; + SilcSKEStatus status = SILC_SKE_STATUS_OK; + unsigned char *string; + SilcUInt32 l; - if (ske->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - return SILC_FSM_CONTINUE; - } + if (!len) + return SILC_SKE_STATUS_ERROR; - /* Check result of public key verification */ - if (ske->status != SILC_SKE_STATUS_OK) { - /** Public key not verified */ - SILC_LOG_DEBUG(("Public key verification failed")); - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; - } + SILC_LOG_DEBUG(("Creating random number")); - payload = ske->ke2_payload; + l = ((len - 1) / 8); - if (payload->pk_data) { - /* Decode the public key */ - if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, - &public_key)) { - SILC_LOG_ERROR(("Unsupported/malformed public key received")); - status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY; - goto err; - } + /* Get the random number as string */ + string = silc_rng_get_rn_data(ske->rng, l); + if (!string) + return SILC_SKE_STATUS_OUT_OF_MEMORY; - SILC_LOG_DEBUG(("Public key is authentic")); + /* Decode the string into a MP integer */ + silc_mp_bin2mp(string, l, rnd); + silc_mp_mod_2exp(rnd, rnd, len); - /* Compute the hash value */ - status = silc_ske_make_hash(ske, hash, &hash_len, FALSE); - if (status != SILC_SKE_STATUS_OK) - goto err; + /* Checks */ + if (silc_mp_cmp_ui(rnd, 1) < 0) + status = SILC_SKE_STATUS_ERROR; + if (silc_mp_cmp(rnd, n) >= 0) + status = SILC_SKE_STATUS_ERROR; - ske->hash = silc_memdup(hash, hash_len); - ske->hash_len = hash_len; + memset(string, 'F', l); + silc_free(string); - SILC_LOG_DEBUG(("Verifying signature (HASH)")); + return status; +} - /* Verify signature */ - silc_pkcs_public_key_set(ske->prop->pkcs, public_key); - if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data, - payload->sign_len, hash, hash_len) == FALSE) { - SILC_LOG_ERROR(("Signature verification failed, incorrect signature")); - status = SILC_SKE_STATUS_INCORRECT_SIGNATURE; - goto err; - } +/* Creates a hash value HASH as defined in the SKE protocol. If the + `initiator' is TRUE then this function is used to create the HASH_i + hash value defined in the protocol. If it is FALSE then this is used + to create the HASH value defined by the protocol. */ - SILC_LOG_DEBUG(("Signature is Ok")); +static SilcSKEStatus silc_ske_make_hash(SilcSKE ske, + unsigned char *return_hash, + SilcUInt32 *return_hash_len, + int initiator) +{ + SilcSKEStatus status = SILC_SKE_STATUS_OK; + SilcBuffer buf; + unsigned char *e, *f, *KEY; + SilcUInt32 e_len, f_len, KEY_len; + int ret; - silc_pkcs_public_key_free(public_key); - memset(hash, 'F', hash_len); - } + SILC_LOG_DEBUG(("Start")); - ske->status = SILC_SKE_STATUS_OK; + if (initiator == FALSE) { + e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len); + f = silc_mp_mp2bin(&ske->ke2_payload->x, 0, &f_len); + KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len); - /* Process key material */ - key_len = silc_cipher_get_key_len(ske->prop->cipher); - block_len = silc_cipher_get_key_len(ske->prop->cipher); - hash_len = silc_hash_len(ske->prop->hash); - ske->keymat = silc_ske_process_key_material(ske, block_len, - key_len, hash_len); - if (!ske->keymat) { - SILC_LOG_ERROR(("Error processing key material")); - status = SILC_SKE_STATUS_ERROR; - goto err; - } + /* Format the buffer used to compute the hash value */ + buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) + + ske->ke2_payload->pk_len + + ske->ke1_payload->pk_len + + e_len + f_len + KEY_len); + if (!buf) + return SILC_SKE_STATUS_OUT_OF_MEMORY; - /* Send SUCCESS packet */ - /* XXX */ + /* Initiator is not required to send its public key */ + if (!ske->ke1_payload->pk_data) { + ret = + silc_buffer_format(buf, + SILC_STR_UI_XNSTRING( + ske->start_payload_copy->data, + silc_buffer_len(ske->start_payload_copy)), + SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data, + ske->ke2_payload->pk_len), + SILC_STR_UI_XNSTRING(e, e_len), + SILC_STR_UI_XNSTRING(f, f_len), + SILC_STR_UI_XNSTRING(KEY, KEY_len), + SILC_STR_END); + } else { + ret = + silc_buffer_format(buf, + SILC_STR_UI_XNSTRING( + ske->start_payload_copy->data, + silc_buffer_len(ske->start_payload_copy)), + SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data, + ske->ke2_payload->pk_len), + SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data, + ske->ke1_payload->pk_len), + SILC_STR_UI_XNSTRING(e, e_len), + SILC_STR_UI_XNSTRING(f, f_len), + SILC_STR_UI_XNSTRING(KEY, KEY_len), + SILC_STR_END); + } + if (ret == -1) { + silc_buffer_free(buf); + memset(e, 0, e_len); + memset(f, 0, f_len); + memset(KEY, 0, KEY_len); + silc_free(e); + silc_free(f); + silc_free(KEY); + return SILC_SKE_STATUS_ERROR; + } - /** Waiting completion */ - silc_fsm_next(fsm, silc_ske_st_initiator_end); - return SILC_FSM_WAIT; + memset(e, 0, e_len); + memset(f, 0, f_len); + memset(KEY, 0, KEY_len); + silc_free(e); + silc_free(f); + silc_free(KEY); + } else { + e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len); - err: - memset(hash, 'F', sizeof(hash)); - silc_ske_payload_ke_free(payload); - ske->ke2_payload = NULL; + buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) + + ske->ke1_payload->pk_len + e_len); + if (!buf) + return SILC_SKE_STATUS_OUT_OF_MEMORY; - silc_mp_uninit(ske->KEY); - silc_free(ske->KEY); - ske->KEY = NULL; + /* Format the buffer used to compute the hash value */ + ret = + silc_buffer_format(buf, + SILC_STR_UI_XNSTRING(ske->start_payload_copy->data, + silc_buffer_len(ske->start_payload_copy)), + SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data, + ske->ke1_payload->pk_len), + SILC_STR_UI_XNSTRING(e, e_len), + SILC_STR_END); + if (ret == -1) { + silc_buffer_free(buf); + memset(e, 0, e_len); + silc_free(e); + return SILC_SKE_STATUS_ERROR; + } + + memset(e, 0, e_len); + silc_free(e); + } - if (public_key) - silc_pkcs_public_key_free(public_key); + /* Make the hash */ + silc_hash_make(ske->prop->hash, buf->data, silc_buffer_len(buf), + return_hash); + *return_hash_len = silc_hash_len(ske->prop->hash); - if (ske->hash) { - memset(ske->hash, 'F', hash_len); - silc_free(ske->hash); - ske->hash = NULL; + if (initiator == FALSE) { + SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len); + } else { + SILC_LOG_HEXDUMP(("HASH_i"), return_hash, *return_hash_len); } - if (status == SILC_SKE_STATUS_OK) - status = SILC_SKE_STATUS_ERROR; + silc_buffer_free(buf); - /** Error */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - return SILC_FSM_CONTINUE; + return status; } -/* Protocol completed */ -SILC_FSM_STATE(silc_ske_st_initiator_end) -{ - SilcSKE ske = fsm_context; +/******************************* Protocol API *******************************/ - if (ske->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - return SILC_FSM_CONTINUE; - } +/* Allocates new SKE object. */ - /* Call the completion callback */ - if (ske->callbacks->completed) - ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL); +SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, + SilcPublicKey public_key, SilcPrivateKey private_key, + void *context) +{ + SilcSKE ske; - return SILC_FSM_FINISH; -} + SILC_LOG_DEBUG(("Allocating new Key Exchange object")); -/* Aborted by application */ + if (!rng || !schedule) + return NULL; -SILC_FSM_STATE(silc_ske_st_initiator_aborted) -{ + ske = silc_calloc(1, sizeof(*ske)); + if (!ske) + return NULL; + ske->status = SILC_SKE_STATUS_OK; + ske->rng = rng; + ske->user_data = context; + ske->schedule = schedule; + ske->public_key = public_key; + ske->private_key = private_key; + ske->pk_type = SILC_SKE_PK_TYPE_SILC; - return SILC_FSM_FINISH; + return ske; } -/* Error occurred */ +/* Free's SKE object. */ -SILC_FSM_STATE(silc_ske_st_initiator_error) +void silc_ske_free(SilcSKE ske) { + SILC_LOG_DEBUG(("Freeing Key Exchange object")); - return SILC_FSM_FINISH; -} + if (ske) { + /* Free start payload */ + if (ske->start_payload) + silc_ske_payload_start_free(ske->start_payload); + /* Free KE payload */ + if (ske->ke1_payload) + silc_ske_payload_ke_free(ske->ke1_payload); + if (ske->ke2_payload) + silc_ske_payload_ke_free(ske->ke2_payload); + silc_free(ske->remote_version); -static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context, - void *destructor_context) -{ + /* Free rest */ + if (ske->prop) { + if (ske->prop->group) + silc_ske_group_free(ske->prop->group); + if (ske->prop->pkcs) + silc_pkcs_free(ske->prop->pkcs); + if (ske->prop->cipher) + silc_cipher_free(ske->prop->cipher); + if (ske->prop->hash) + silc_hash_free(ske->prop->hash); + if (ske->prop->hmac) + silc_hmac_free(ske->prop->hmac); + silc_free(ske->prop); + } + if (ske->start_payload_copy) + silc_buffer_free(ske->start_payload_copy); + if (ske->x) { + silc_mp_uninit(ske->x); + silc_free(ske->x); + } + if (ske->KEY) { + silc_mp_uninit(ske->KEY); + silc_free(ske->KEY); + } + silc_free(ske->hash); + silc_free(ske->callbacks); + memset(ske, 'F', sizeof(*ske)); + silc_free(ske); + } } -/* Starts the protocol as initiator */ +/* Return user context */ -SilcAsyncOperation -silc_ske_initiator_start(SilcSKE ske, - SilcPacketStream stream, - SilcSKEStartPayload start_payload) +void *silc_ske_get_context(SilcSKE ske) { - SILC_LOG_DEBUG(("Start SKE as initiator")); - - if (!ske || !stream || !start_payload) - return NULL; - - if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske)) - return NULL; - - if (!silc_fsm_init(&ske->fsm, ske, silc_ske_initiator_finished, ske, - ske->schedule)) - return NULL; + return ske->user_data; +} - ske->start_payload = start_payload; +/* Sets protocol callbacks */ - /* Link to packet stream to get key exchange packets */ - ske->stream = stream; - silc_packet_stream_ref(ske->stream); - silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske); +void silc_ske_set_callbacks(SilcSKE ske, + SilcSKEVerifyCb verify_key, + SilcSKECompletionCb completed, + void *context) +{ + if (ske->callbacks) + silc_free(ske->callbacks); + ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks)); + if (!ske->callbacks) + return; + ske->callbacks->verify_key = verify_key; + ske->callbacks->completed = completed; + ske->callbacks->context = context; +} - /* Start SKE as initiator */ - silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start); - return &ske->op; -} +/******************************** Initiator *********************************/ -/* Responder state machine */ -SILC_FSM_STATE(silc_ske_st_responder_start); -SILC_FSM_STATE(silc_ske_st_responder_phase1); -SILC_FSM_STATE(silc_ske_st_responder_phase2); -SILC_FSM_STATE(silc_ske_st_responder_phase3); -SILC_FSM_STATE(silc_ske_st_responder_phase4); -SILC_FSM_STATE(silc_ske_st_responder_end); -SILC_FSM_STATE(silc_ske_st_responder_aborted); -SILC_FSM_STATE(silc_ske_st_responder_error); +/* Initiator state machine */ +SILC_FSM_STATE(silc_ske_st_initiator_start); +SILC_FSM_STATE(silc_ske_st_initiator_phase1); +SILC_FSM_STATE(silc_ske_st_initiator_phase2); +SILC_FSM_STATE(silc_ske_st_initiator_phase3); +SILC_FSM_STATE(silc_ske_st_initiator_phase4); +SILC_FSM_STATE(silc_ske_st_initiator_end); +SILC_FSM_STATE(silc_ske_st_initiator_aborted); +SILC_FSM_STATE(silc_ske_st_initiator_error); -/* Start protocol as responder. Decode initiator's start payload */ +/* Start protocol. Send our proposal */ -SILC_FSM_STATE(silc_ske_st_responder_start) +SILC_FSM_STATE(silc_ske_st_initiator_start) { SilcSKE ske = fsm_context; - SilcSKEStatus status; - SilcSKEStartPayload remote_payload = NULL, payload = NULL; + SilcBuffer payload_buf; + SilcStatus status; SILC_LOG_DEBUG(("Start")); if (ske->aborted) { /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_responder_aborted); + silc_fsm_next(fsm, silc_ske_st_initiator_aborted); return SILC_FSM_CONTINUE; } - /* Decode the payload */ - status = silc_ske_payload_start_decode(ske, ske->packet_buf, - &remote_payload); + /* Encode the payload */ + status = silc_ske_payload_start_encode(ske, ske->start_payload, + &payload_buf); if (status != SILC_SKE_STATUS_OK) { - /** Error decoding Start Payload */ + /** Error encoding Start Payload */ ske->status = status; - silc_fsm_next(fsm, silc_ske_st_responder_error); + silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; } - /* Take a copy of the payload buffer for future use. It is used to + /* Save the the payload buffer for future use. It is later used to compute the HASH value. */ - ske->start_payload_copy = silc_buffer_copy(ske->packet_buf); + ske->start_payload_copy = payload_buf; - /* Force the mutual authentication flag if we want to do it. */ - if (ske->flags & SILC_SKE_SP_FLAG_MUTUAL) { - SILC_LOG_DEBUG(("Force mutual authentication")); - remote_payload->flags |= SILC_SKE_SP_FLAG_MUTUAL; - } + /* Send the packet */ + /* XXX */ - /* Force PFS flag if we require it */ - if (ske->flags & SILC_SKE_SP_FLAG_PFS) { - SILC_LOG_DEBUG(("Force PFS")); - remote_payload->flags |= SILC_SKE_SP_FLAG_PFS; - } + /** Wait for responder proposal */ + SILC_LOG_DEBUG(("Waiting for reponder proposal")); + silc_fsm_next(ske, silc_ske_st_initiator_phase1); + return SILC_FSM_WAIT; +} - /* Disable IV Included flag if requested */ - if (remote_payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED && - !(ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)) { - SILC_LOG_DEBUG(("We do not support IV Included flag")); - remote_payload->flags &= ~SILC_SKE_SP_FLAG_IV_INCLUDED; +/* Phase-1. Receives responder's proposal */ + +SILC_FSM_STATE(silc_ske_st_initiator_phase1) +{ + SilcSKE ske = fsm_context; + SilcSKEStatus status; + SilcSKEStartPayload payload; + SilcSKESecurityProperties prop; + SilcSKEDiffieHellmanGroup group; + SilcBuffer packet_buf = &ske->packet->buffer; + + SILC_LOG_DEBUG(("Start")); + + if (ske->aborted) { + /** Aborted */ + silc_fsm_next(fsm, silc_ske_st_initiator_aborted); + return SILC_FSM_CONTINUE; } - /* Parse and select the security properties from the payload */ - payload = silc_calloc(1, sizeof(*payload)); - status = silc_ske_select_security_properties(ske, ske->version, - payload, remote_payload); + /* Decode the payload */ + status = silc_ske_payload_start_decode(ske, packet_buf, &payload); if (status != SILC_SKE_STATUS_OK) { - /** Error selecting proposal */ - if (remote_payload) - silc_ske_payload_start_free(remote_payload); - silc_free(payload); + /** Error decoding Start Payload */ ske->status = status; - silc_fsm_next(fsm, silc_ske_st_responder_error); + silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; } - ske->start_payload = payload; - - silc_ske_payload_start_free(remote_payload); - - /** Send proposal to initiator */ - silc_fsm_next(fsm, silc_ske_st_responder_phase1); - return SILC_FSM_CONTINUE; -} - -/* Phase-1. Send Start Payload */ + /* Check that the cookie is returned unmodified */ + if (memcmp(ske->start_payload->cookie, payload->cookie, + ske->start_payload->cookie_len)) { + /** Invalid cookie */ + SILC_LOG_ERROR(("Responder modified our cookie and it must not do it")); + ske->status = SILC_SKE_STATUS_INVALID_COOKIE; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } -SILC_FSM_STATE(silc_ske_st_responder_phase1) -{ - SilcSKE ske = fsm_context; - SilcSKEStatus status; - SilcBuffer payload_buf; - SilcSKESecurityProperties prop; - SilcSKEDiffieHellmanGroup group = NULL; + /* Check version string */ + ske->remote_version = silc_memdup(payload->version, payload->version_len); + status = silc_ske_check_version(ske); + if (status != SILC_SKE_STATUS_OK) { + /** Version mismatch */ + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } - SILC_LOG_DEBUG(("Start")); + /* Free our KE Start Payload context, we don't need it anymore. */ + silc_ske_payload_start_free(ske->start_payload); + ske->start_payload = NULL; - /* Allocate security properties from the payload. These are allocated - only for this negotiation and will be free'd after KE is over. */ + /* Take the selected security properties into use while doing + the key exchange. This is used only while doing the key + exchange. */ ske->prop = prop = silc_calloc(1, sizeof(*prop)); - prop->flags = ske->start_payload->flags; - status = silc_ske_group_get_by_name(ske->start_payload->ke_grp_list, &group); + if (!ske->prop) + goto err; + prop->flags = payload->flags; + status = silc_ske_group_get_by_name(payload->ke_grp_list, &group); if (status != SILC_SKE_STATUS_OK) goto err; prop->group = group; - if (silc_pkcs_alloc(ske->start_payload->pkcs_alg_list, + if (silc_pkcs_alloc(payload->pkcs_alg_list, ske->pk_type, &prop->pkcs) == FALSE) { status = SILC_SKE_STATUS_UNKNOWN_PKCS; goto err; } - if (silc_cipher_alloc(ske->start_payload->enc_alg_list, - &prop->cipher) == FALSE) { + if (silc_cipher_alloc(payload->enc_alg_list, &prop->cipher) == FALSE) { status = SILC_SKE_STATUS_UNKNOWN_CIPHER; goto err; } - if (silc_hash_alloc(ske->start_payload->hash_alg_list, - &prop->hash) == FALSE) { + if (silc_hash_alloc(payload->hash_alg_list, &prop->hash) == FALSE) { status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION; goto err; } - if (silc_hmac_alloc(ske->start_payload->hmac_alg_list, NULL, - &prop->hmac) == FALSE) { + if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) { status = SILC_SKE_STATUS_UNKNOWN_HMAC; goto err; } - /* Encode the payload */ - status = silc_ske_payload_start_encode(ske, ske->start_payload, - &payload_buf); - if (status != SILC_SKE_STATUS_OK) - goto err; - - /* Send the packet. */ - /* XXX */ - - silc_buffer_free(payload_buf); + /* Save remote's KE Start Payload */ + ske->start_payload = payload; - /** Waiting initiator's KE payload */ - silc_fsm_next(fsm, silc_ske_st_responder_phase2); - return SILC_FSM_WAIT; + /** Send KE Payload */ + silc_fsm_next(fsm, silc_ske_st_initiator_phase2); + return SILC_FSM_CONTINUE; err: - if (group) - silc_ske_group_free(group); + if (payload) + silc_ske_payload_start_free(payload); + + silc_ske_group_free(group); if (prop->pkcs) silc_pkcs_free(prop->pkcs); @@ -924,84 +923,238 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1) /** Error */ ske->status = status; - silc_fsm_next(fsm, silc_ske_st_responder_error); + silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; } -/* Phase-2. Decode initiator's KE payload */ +/* Phase-2. Send KE payload */ -SILC_FSM_STATE(silc_ske_st_responder_phase2) +SILC_FSM_STATE(silc_ske_st_initiator_phase2) { SilcSKE ske = fsm_context; SilcSKEStatus status; - SilcSKEKEPayload recv_payload; + SilcBuffer payload_buf; + SilcMPInt *x; + SilcSKEKEPayload payload; + SilcUInt32 pk_len; SILC_LOG_DEBUG(("Start")); - if (ske->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_responder_aborted); + /* Create the random number x, 1 < x < q. */ + x = silc_calloc(1, sizeof(*x)); + if (!x){ + /** Out of memory */ + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; } - - /* Decode Key Exchange Payload */ - status = silc_ske_payload_ke_decode(ske, ske->packet_buf, &recv_payload); + silc_mp_init(x); + status = + silc_ske_create_rnd(ske, &ske->prop->group->group_order, + silc_mp_sizeinbase(&ske->prop->group->group_order, 2), + x); if (status != SILC_SKE_STATUS_OK) { - /** Error decoding KE payload */ + /** Error generating random number */ + silc_mp_uninit(x); + silc_free(x); ske->status = status; - silc_fsm_next(fsm, silc_ske_st_responder_error); + silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; } - ske->ke1_payload = recv_payload; + /* Encode the result to Key Exchange Payload. */ - /* Verify the received public key and verify the signature if we are - doing mutual authentication. */ - if (ske->start_payload && + payload = silc_calloc(1, sizeof(*payload)); + if (!payload) { + /** Out of memory */ + silc_mp_uninit(x); + silc_free(x); + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + ske->ke1_payload = payload; + + SILC_LOG_DEBUG(("Computing e = g ^ x mod p")); + + /* Do the Diffie Hellman computation, e = g ^ x mod p */ + silc_mp_init(&payload->x); + silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x, + &ske->prop->group->group); + + /* Get public key */ + if (ske->public_key) { + payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len); + if (!payload->pk_data) { + /** Error encoding public key */ + silc_mp_uninit(x); + silc_free(x); + silc_mp_uninit(&payload->x); + silc_free(payload); + ske->ke1_payload = NULL; + ske->status = SILC_SKE_STATUS_ERROR; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + payload->pk_len = pk_len; + } + payload->pk_type = ske->pk_type; + + /* Compute signature data if we are doing mutual authentication */ + if (ske->private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) { + unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1]; + SilcUInt32 hash_len, sign_len; SILC_LOG_DEBUG(("We are doing mutual authentication")); + SILC_LOG_DEBUG(("Computing HASH_i value")); - if (!recv_payload->pk_data && ske->callbacks->verify_key) { - /** Public key not provided */ - SILC_LOG_ERROR(("Remote end did not send its public key (or " - "certificate), even though we require it")); - ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED; - silc_fsm_next(fsm, silc_ske_st_responder_error); + /* Compute the hash value */ + memset(hash, 0, sizeof(hash)); + silc_ske_make_hash(ske, hash, &hash_len, TRUE); + + SILC_LOG_DEBUG(("Signing HASH_i value")); + + /* Sign the hash value */ + silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv, + ske->private_key->prv_len); + if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 || + !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) { + /** Error computing signature */ + silc_mp_uninit(x); + silc_free(x); + silc_mp_uninit(&payload->x); + silc_free(payload->pk_data); + silc_free(payload); + ske->ke1_payload = NULL; + ske->status = SILC_SKE_STATUS_SIGNATURE_ERROR; + silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; } + payload->sign_data = silc_memdup(sign, sign_len); + payload->sign_len = sign_len; + memset(sign, 0, sizeof(sign)); + } - if (recv_payload->pk_data && ske->callbacks->verify_key) { - SILC_LOG_DEBUG(("Verifying public key")); + status = silc_ske_payload_ke_encode(ske, payload, &payload_buf); + if (status != SILC_SKE_STATUS_OK) { + /** Error encoding KE payload */ + silc_mp_uninit(x); + silc_free(x); + silc_mp_uninit(&payload->x); + silc_free(payload->pk_data); + silc_free(payload->sign_data); + silc_free(payload); + ske->ke1_payload = NULL; + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } - /** Waiting public key verification */ - silc_fsm_next(fsm, silc_ske_st_responder_phase3); - SILC_FSM_CALL(ske->callbacks->verify_key(ske, recv_payload->pk_data, - recv_payload->pk_len, - recv_payload->pk_type, - ske->callbacks->context, - silc_ske_pk_verified, NULL)); - /* NOT REACHED */ - } + ske->x = x; + + /* Send the packet. */ + /* XXX */ + + silc_buffer_free(payload_buf); + + /** Waiting responder's KE payload */ + silc_fsm_next(fsm, silc_ske_st_initiator_phase3); + return SILC_FSM_WAIT; +} + +/* Phase-3. Process responder's KE payload */ + +SILC_FSM_STATE(silc_ske_st_initiator_phase3) +{ + SilcSKE ske = fsm_context; + SilcSKEStatus status; + SilcSKEKEPayload payload; + SilcMPInt *KEY; + SilcBuffer packet_buf = &ske->packet->buffer; + + SILC_LOG_DEBUG(("Start")); + + if (ske->aborted) { + /** Aborted */ + silc_fsm_next(fsm, silc_ske_st_initiator_aborted); + return SILC_FSM_CONTINUE; + } + + /* Decode the payload */ + status = silc_ske_payload_ke_decode(ske, packet_buf, &payload); + if (status != SILC_SKE_STATUS_OK) { + /** Error decoding KE payload */ + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + ske->ke2_payload = payload; + + if (!payload->pk_data && ske->callbacks->verify_key) { + SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), " + "even though we require it")); + ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED; + goto err; + } + + SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p")); + + /* Compute the shared secret key */ + KEY = silc_calloc(1, sizeof(*KEY)); + silc_mp_init(KEY); + silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group); + ske->KEY = KEY; + + if (payload->pk_data && ske->callbacks->verify_key) { + SILC_LOG_DEBUG(("Verifying public key")); + + /** Waiting public key verification */ + silc_fsm_next(fsm, silc_ske_st_initiator_phase4); + SILC_FSM_CALL(ske->callbacks->verify_key(ske, payload->pk_data, + payload->pk_len, + payload->pk_type, + ske->callbacks->context, + silc_ske_pk_verified, NULL)); + /* NOT REACHED */ } - /** Generate KE2 payload */ - silc_fsm_next(fsm, silc_ske_st_responder_phase3); + /** Process key material */ + silc_fsm_next(fsm, silc_ske_st_initiator_phase4); + return SILC_FSM_CONTINUE; + + err: + silc_ske_payload_ke_free(payload); + ske->ke2_payload = NULL; + + silc_mp_uninit(ske->KEY); + silc_free(ske->KEY); + ske->KEY = NULL; + + if (status == SILC_SKE_STATUS_OK) + return SILC_SKE_STATUS_ERROR; + + /** Error */ + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; } -/* Phase-3. Generate KE2 payload */ +/* Process key material */ -SILC_FSM_STATE(silc_ske_st_responder_phase3) +SILC_FSM_STATE(silc_ske_st_initiator_phase4) { SilcSKE ske = fsm_context; SilcSKEStatus status; - SilcSKEKEPayload recv_payload, send_payload; - SilcMPInt *x, *KEY; + SilcSKEKEPayload payload; + unsigned char hash[SILC_HASH_MAXLEN]; + SilcUInt32 hash_len; + SilcPublicKey public_key = NULL; + int key_len, block_len; if (ske->aborted) { /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_responder_aborted); + silc_fsm_next(fsm, silc_ske_st_initiator_aborted); return SILC_FSM_CONTINUE; } @@ -1013,49 +1166,36 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3) return SILC_FSM_CONTINUE; } - recv_payload = ske->ke1_payload; - - /* The public key verification was performed only if the Mutual - Authentication flag is set. */ - if (ske->start_payload && - ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) { - SilcPublicKey public_key = NULL; - unsigned char hash[SILC_HASH_MAXLEN]; - SilcUInt32 hash_len; + payload = ske->ke2_payload; + if (payload->pk_data) { /* Decode the public key */ - if (!silc_pkcs_public_key_decode(recv_payload->pk_data, - recv_payload->pk_len, + if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, &public_key)) { - /** Error decoding public key */ SILC_LOG_ERROR(("Unsupported/malformed public key received")); - ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY; - silc_fsm_next(fsm, silc_ske_st_responder_error); - return SILC_FSM_CONTINUE; + status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY; + goto err; } SILC_LOG_DEBUG(("Public key is authentic")); /* Compute the hash value */ - status = silc_ske_make_hash(ske, hash, &hash_len, TRUE); - if (status != SILC_SKE_STATUS_OK) { - /** Error computing hash */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_responder_error); - return SILC_FSM_CONTINUE; - } + status = silc_ske_make_hash(ske, hash, &hash_len, FALSE); + if (status != SILC_SKE_STATUS_OK) + goto err; - SILC_LOG_DEBUG(("Verifying signature (HASH_i)")); + ske->hash = silc_memdup(hash, hash_len); + ske->hash_len = hash_len; + + SILC_LOG_DEBUG(("Verifying signature (HASH)")); /* Verify signature */ silc_pkcs_public_key_set(ske->prop->pkcs, public_key); - if (silc_pkcs_verify(ske->prop->pkcs, recv_payload->sign_data, - recv_payload->sign_len, hash, hash_len) == FALSE) { - /** Incorrect signature */ + if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data, + payload->sign_len, hash, hash_len) == FALSE) { SILC_LOG_ERROR(("Signature verification failed, incorrect signature")); - ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE; - silc_fsm_next(fsm, silc_ske_st_responder_error); - return SILC_FSM_CONTINUE; + status = SILC_SKE_STATUS_INCORRECT_SIGNATURE; + goto err; } SILC_LOG_DEBUG(("Signature is Ok")); @@ -1064,136 +1204,63 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3) memset(hash, 'F', hash_len); } - /* Create the random number x, 1 < x < q. */ - x = silc_calloc(1, sizeof(*x)); - silc_mp_init(x); - status = - silc_ske_create_rnd(ske, &ske->prop->group->group_order, - silc_mp_sizeinbase(&ske->prop->group->group_order, 2), - x); - if (status != SILC_SKE_STATUS_OK) { - /** Error generating random number */ - silc_mp_uninit(x); - silc_free(x); - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_responder_error); - return SILC_FSM_CONTINUE; - } - - /* Save the results for later processing */ - send_payload = silc_calloc(1, sizeof(*send_payload)); - ske->x = x; - ske->ke2_payload = send_payload; - - SILC_LOG_DEBUG(("Computing f = g ^ x mod p")); - - /* Do the Diffie Hellman computation, f = g ^ x mod p */ - silc_mp_init(&send_payload->x); - silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x, - &ske->prop->group->group); - - SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p")); - - /* Compute the shared secret key */ - KEY = silc_calloc(1, sizeof(*KEY)); - silc_mp_init(KEY); - silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x, - &ske->prop->group->group); - ske->KEY = KEY; - - /** Send KE2 payload */ - silc_fsm_next(fsm, silc_ske_st_responder_phase4); - return SILC_FSM_CONTINUE; -} - -/* Phase-4. Send KE2 payload */ - -SILC_FSM_STATE(silc_ske_st_responder_phase4) -{ - SilcSKE ske = fsm_context; - SilcSKEStatus status; - SilcBuffer payload_buf; - unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1], *pk; - SilcUInt32 hash_len, sign_len, pk_len; - - SILC_LOG_DEBUG(("Start")); - - if (ske->public_key && ske->private_key) { - SILC_LOG_DEBUG(("Getting public key")); + ske->status = SILC_SKE_STATUS_OK; - /* Get the public key */ - pk = silc_pkcs_public_key_encode(ske->public_key, &pk_len); - if (!pk) { - /** Error encoding public key */ - status = SILC_SKE_STATUS_OUT_OF_MEMORY; - silc_fsm_next(fsm, silc_ske_st_responder_error); - return SILC_FSM_CONTINUE; - } - ske->ke2_payload->pk_data = pk; - ske->ke2_payload->pk_len = pk_len; + /* Process key material */ + key_len = silc_cipher_get_key_len(ske->prop->cipher); + block_len = silc_cipher_get_key_len(ske->prop->cipher); + hash_len = silc_hash_len(ske->prop->hash); + ske->keymat = silc_ske_process_key_material(ske, block_len, + key_len, hash_len); + if (!ske->keymat) { + SILC_LOG_ERROR(("Error processing key material")); + status = SILC_SKE_STATUS_ERROR; + goto err; + } - SILC_LOG_DEBUG(("Computing HASH value")); + /* Send SUCCESS packet */ + /* XXX */ - /* Compute the hash value */ - memset(hash, 0, sizeof(hash)); - status = silc_ske_make_hash(ske, hash, &hash_len, FALSE); - if (status != SILC_SKE_STATUS_OK) { - /** Error computing hash */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_responder_error); - return SILC_FSM_CONTINUE; - } + /** Waiting completion */ + silc_fsm_next(fsm, silc_ske_st_initiator_end); + return SILC_FSM_WAIT; - ske->hash = silc_memdup(hash, hash_len); - ske->hash_len = hash_len; + err: + memset(hash, 'F', sizeof(hash)); + silc_ske_payload_ke_free(payload); + ske->ke2_payload = NULL; - SILC_LOG_DEBUG(("Signing HASH value")); + silc_mp_uninit(ske->KEY); + silc_free(ske->KEY); + ske->KEY = NULL; - /* Sign the hash value */ - silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv, - ske->private_key->prv_len); - if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 || - !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) { - /** Error computing signature */ - status = SILC_SKE_STATUS_SIGNATURE_ERROR; - silc_fsm_next(fsm, silc_ske_st_responder_error); - return SILC_FSM_CONTINUE; - } - ske->ke2_payload->sign_data = silc_memdup(sign, sign_len); - ske->ke2_payload->sign_len = sign_len; - memset(sign, 0, sizeof(sign)); - } - ske->ke2_payload->pk_type = ske->pk_type; + if (public_key) + silc_pkcs_public_key_free(public_key); - /* Encode the Key Exchange Payload */ - status = silc_ske_payload_ke_encode(ske, ske->ke2_payload, - &payload_buf); - if (status != SILC_SKE_STATUS_OK) { - /** Error encoding KE payload */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_responder_error); - return SILC_FSM_CONTINUE; + if (ske->hash) { + memset(ske->hash, 'F', hash_len); + silc_free(ske->hash); + ske->hash = NULL; } - /* Send the packet. */ - /* XXX */ - - silc_buffer_free(payload_buf); + if (status == SILC_SKE_STATUS_OK) + status = SILC_SKE_STATUS_ERROR; - /** Waiting completion */ - silc_fsm_next(fsm, silc_ske_st_responder_end); - return SILC_FSM_WAIT; + /** Error */ + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; } /* Protocol completed */ -SILC_FSM_STATE(silc_ske_st_responder_end) +SILC_FSM_STATE(silc_ske_st_initiator_end) { SilcSKE ske = fsm_context; if (ske->aborted) { /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_responder_aborted); + silc_fsm_next(fsm, silc_ske_st_initiator_aborted); return SILC_FSM_CONTINUE; } @@ -1206,41 +1273,35 @@ SILC_FSM_STATE(silc_ske_st_responder_end) /* Aborted by application */ -SILC_FSM_STATE(silc_ske_st_responder_aborted) +SILC_FSM_STATE(silc_ske_st_initiator_aborted) { - /* Send FAILURE */ - return SILC_FSM_FINISH; } /* Error occurred */ -SILC_FSM_STATE(silc_ske_st_responder_error) +SILC_FSM_STATE(silc_ske_st_initiator_error) { - /* Send FAILURE */ - return SILC_FSM_FINISH; } -static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context, +static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context, void *destructor_context) { } -/* Starts the protocol as responder. */ +/* Starts the protocol as initiator */ SilcAsyncOperation -silc_ske_responder_start(SilcSKE ske, - SilcPacketStream stream, - const char *version, - SilcBuffer start_payload, - SilcSKESecurityPropertyFlag flags) +silc_ske_initiator(SilcSKE ske, + SilcPacketStream stream, + SilcSKEStartPayload start_payload) { - SILC_LOG_DEBUG(("Start SKE as responder")); + SILC_LOG_DEBUG(("Start SKE as initiator")); if (!ske || !stream || !start_payload) return NULL; @@ -1248,684 +1309,800 @@ silc_ske_responder_start(SilcSKE ske, if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske)) return NULL; - if (!silc_fsm_init(&ske->fsm, ske, silc_ske_responder_finished, ske, + if (!silc_fsm_init(&ske->fsm, ske, silc_ske_initiator_finished, ske, ske->schedule)) return NULL; - ske->packet_buf = start_payload; - ske->flags = flags; - ske->version = strdup(version); + ske->start_payload = start_payload; + + /* Link to packet stream to get key exchange packets */ + ske->stream = stream; + silc_packet_stream_link(ske->stream, &silc_ske_stream_cbs, ske, 1000000, + SILC_PACKET_KEY_EXCHANGE, + SILC_PACKET_KEY_EXCHANGE_2, + SILC_PACKET_SUCCESS, + SILC_PACKET_FAILURE, -1); + + /* Start SKE as initiator */ + silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start); + + return &ske->op; +} + + +/******************************** Responder *********************************/ + +SILC_FSM_STATE(silc_ske_st_responder_start); +SILC_FSM_STATE(silc_ske_st_responder_phase1); +SILC_FSM_STATE(silc_ske_st_responder_phase2); +SILC_FSM_STATE(silc_ske_st_responder_phase3); +SILC_FSM_STATE(silc_ske_st_responder_phase4); +SILC_FSM_STATE(silc_ske_st_responder_phase5); +SILC_FSM_STATE(silc_ske_st_responder_end); +SILC_FSM_STATE(silc_ske_st_responder_aborted); +SILC_FSM_STATE(silc_ske_st_responder_failure); +SILC_FSM_STATE(silc_ske_st_responder_error); + +/* Start protocol as responder. Wait initiator's start payload */ - /* Link to packet stream to get key exchange packets */ - ske->stream = stream; - silc_packet_stream_ref(ske->stream); - silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske); +SILC_FSM_STATE(silc_ske_st_responder_start) +{ + SilcSKE ske = fsm_context; - /* Start SKE as responder */ - silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start); + SILC_LOG_DEBUG(("Start")); - return &ske->op; + if (ske->aborted) { + /** Aborted */ + silc_fsm_next(fsm, silc_ske_st_responder_aborted); + return SILC_FSM_CONTINUE; + } + + /* Start timeout */ + /* XXX */ + + /** Wait for initiator */ + silc_fsm_next(fsm, silc_ske_st_responder_phase1); + return SILC_FSM_WAIT; } -SILC_FSM_STATE(silc_ske_st_rekey_initiator_start); +/* Decode initiator's start payload */ -SILC_FSM_STATE(silc_ske_st_rekey_initiator_start) +SILC_FSM_STATE(silc_ske_st_responder_phase1) { + SilcSKE ske = fsm_context; + SilcSKEStatus status; + SilcSKEStartPayload remote_payload = NULL, payload = NULL; + SilcBuffer packet_buf = &ske->packet->buffer; -} + SILC_LOG_DEBUG(("Start")); -/* Starts rekey protocol as initiator */ + if (ske->aborted) { + /** Aborted */ + silc_fsm_next(fsm, silc_ske_st_responder_aborted); + return SILC_FSM_CONTINUE; + } -SilcAsyncOperation -silc_ske_rekey_initiator_start(SilcSKE ske, - SilcPacketStream stream, - SilcSKERekeyMaterial rekey) -{ - SILC_LOG_DEBUG(("Start SKE rekey as initator")); + /* See if received failure from remote */ + if (ske->packet->type == SILC_PACKET_FAILURE) { + silc_fsm_next(fsm, silc_ske_st_responder_failure); + return SILC_FSM_CONTINUE; + } - if (!ske || !stream || !rekey) - return NULL; + /* Decode the payload */ + status = silc_ske_payload_start_decode(ske, packet_buf, &remote_payload); + if (status != SILC_SKE_STATUS_OK) { + /** Error decoding Start Payload */ + silc_packet_free(ske->packet); + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } - if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske)) - return NULL; + /* Take a copy of the payload buffer for future use. It is used to + compute the HASH value. */ + ske->start_payload_copy = silc_buffer_copy(packet_buf); - if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule)) - return NULL; + silc_packet_free(ske->packet); - ske->rekey = rekey; + /* Force the mutual authentication flag if we want to do it. */ + if (ske->flags & SILC_SKE_SP_FLAG_MUTUAL) { + SILC_LOG_DEBUG(("Force mutual authentication")); + remote_payload->flags |= SILC_SKE_SP_FLAG_MUTUAL; + } - /* Link to packet stream to get key exchange packets */ - ske->stream = stream; - silc_packet_stream_ref(ske->stream); - silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske); + /* Force PFS flag if we require it */ + if (ske->flags & SILC_SKE_SP_FLAG_PFS) { + SILC_LOG_DEBUG(("Force PFS")); + remote_payload->flags |= SILC_SKE_SP_FLAG_PFS; + } - /* Start SKE rekey as initiator */ - silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start); + /* Disable IV Included flag if requested */ + if (remote_payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED && + !(ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)) { + SILC_LOG_DEBUG(("We do not support IV Included flag")); + remote_payload->flags &= ~SILC_SKE_SP_FLAG_IV_INCLUDED; + } - return &ske->op; -} + /* Parse and select the security properties from the payload */ + payload = silc_calloc(1, sizeof(*payload)); + status = silc_ske_select_security_properties(ske, payload, remote_payload); + if (status != SILC_SKE_STATUS_OK) { + /** Error selecting proposal */ + if (remote_payload) + silc_ske_payload_start_free(remote_payload); + silc_free(payload); + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } -SILC_FSM_STATE(silc_ske_st_rekey_responder_start); + ske->start_payload = payload; -SILC_FSM_STATE(silc_ske_st_rekey_responder_start) -{ + silc_ske_payload_start_free(remote_payload); + /** Send proposal to initiator */ + silc_fsm_next(fsm, silc_ske_st_responder_phase2); + return SILC_FSM_CONTINUE; } -/* Starts rekey protocol as responder */ +/* Phase-2. Send Start Payload */ -SilcAsyncOperation -silc_ske_rekey_responder_start(SilcSKE ske, - SilcPacketStream stream, - SilcBuffer ke_payload, - SilcSKERekeyMaterial rekey) +SILC_FSM_STATE(silc_ske_st_responder_phase2) { - SILC_LOG_DEBUG(("Start SKE rekey as responder")); + SilcSKE ske = fsm_context; + SilcSKEStatus status; + SilcBuffer payload_buf; + SilcSKESecurityProperties prop; + SilcSKEDiffieHellmanGroup group = NULL; - if (!ske || !stream || !rekey) - return NULL; - if (rekey->pfs && !ke_payload) - return NULL; + SILC_LOG_DEBUG(("Start")); - if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske)) - return NULL; + /* Allocate security properties from the payload. These are allocated + only for this negotiation and will be free'd after KE is over. */ + ske->prop = prop = silc_calloc(1, sizeof(*prop)); + if (!ske->prop) { + status = SILC_SKE_STATUS_OUT_OF_MEMORY; + goto err; + } + prop->flags = ske->start_payload->flags; + status = silc_ske_group_get_by_name(ske->start_payload->ke_grp_list, + &group); + if (status != SILC_SKE_STATUS_OK) + goto err; - if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule)) - return NULL; + prop->group = group; - ske->packet_buf = ke_payload; - ske->rekey = rekey; + /* XXX these shouldn't be allocated before we know the remote's + public key type. It's unnecessary to allocate these because the + select_security_properties has succeeded already. */ + if (silc_pkcs_alloc(ske->start_payload->pkcs_alg_list, + SILC_PKCS_SILC, &prop->pkcs) == FALSE) { + status = SILC_SKE_STATUS_UNKNOWN_PKCS; + goto err; + } + if (silc_cipher_alloc(ske->start_payload->enc_alg_list, + &prop->cipher) == FALSE) { + status = SILC_SKE_STATUS_UNKNOWN_CIPHER; + goto err; + } + if (silc_hash_alloc(ske->start_payload->hash_alg_list, + &prop->hash) == FALSE) { + status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION; + goto err; + } + if (silc_hmac_alloc(ske->start_payload->hmac_alg_list, NULL, + &prop->hmac) == FALSE) { + status = SILC_SKE_STATUS_UNKNOWN_HMAC; + goto err; + } - /* Link to packet stream to get key exchange packets */ - ske->stream = stream; - silc_packet_stream_ref(ske->stream); - silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske); + /* Encode the payload */ + status = silc_ske_payload_start_encode(ske, ske->start_payload, + &payload_buf); + if (status != SILC_SKE_STATUS_OK) + goto err; - /* Start SKE rekey as responder */ - silc_fsm_start(&ske->fsm, silc_ske_st_rekey_responder_start); + /* Send the packet. */ + if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE, 0, + payload_buf->data, silc_buffer_len(payload_buf))) + goto err; - return &ske->op; -} + silc_buffer_free(payload_buf); -/* Assembles security properties */ + /** Waiting initiator's KE payload */ + silc_fsm_next(fsm, silc_ske_st_responder_phase3); + return SILC_FSM_WAIT; -SilcSKEStartPayload -silc_ske_assemble_security_properties(SilcSKE ske, - SilcSKESecurityPropertyFlag flags, - const char *version) -{ - SilcSKEStartPayload rp; - int i; + err: + if (group) + silc_ske_group_free(group); - SILC_LOG_DEBUG(("Assembling KE Start Payload")); + if (prop->pkcs) + silc_pkcs_free(prop->pkcs); + if (prop->cipher) + silc_cipher_free(prop->cipher); + if (prop->hash) + silc_hash_free(prop->hash); + if (prop->hmac) + silc_hmac_free(prop->hmac); + silc_free(prop); + ske->prop = NULL; - rp = silc_calloc(1, sizeof(*rp)); + if (status == SILC_SKE_STATUS_OK) + status = SILC_SKE_STATUS_ERROR; - /* Set flags */ - rp->flags = (unsigned char)flags; + /** Error */ + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; +} - /* Set random cookie */ - rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(*rp->cookie)); - for (i = 0; i < SILC_SKE_COOKIE_LEN; i++) - rp->cookie[i] = silc_rng_get_byte_fast(ske->rng); - rp->cookie_len = SILC_SKE_COOKIE_LEN; +/* Phase-3. Decode initiator's KE payload */ - /* Put version */ - rp->version = strdup(version); - rp->version_len = strlen(version); +SILC_FSM_STATE(silc_ske_st_responder_phase3) +{ + SilcSKE ske = fsm_context; + SilcSKEStatus status; + SilcSKEKEPayload recv_payload; + SilcBuffer packet_buf = &ske->packet->buffer; - /* Get supported Key Exhange groups */ - rp->ke_grp_list = silc_ske_get_supported_groups(); - rp->ke_grp_len = strlen(rp->ke_grp_list); + SILC_LOG_DEBUG(("Start")); - /* Get supported PKCS algorithms */ - rp->pkcs_alg_list = silc_pkcs_get_supported(); - rp->pkcs_alg_len = strlen(rp->pkcs_alg_list); + if (ske->aborted) { + /** Aborted */ + silc_fsm_next(fsm, silc_ske_st_responder_aborted); + return SILC_FSM_CONTINUE; + } - /* Get supported encryption algorithms */ - rp->enc_alg_list = silc_cipher_get_supported(); - rp->enc_alg_len = strlen(rp->enc_alg_list); + /* See if received failure from remote */ + if (ske->packet->type == SILC_PACKET_FAILURE) { + silc_fsm_next(fsm, silc_ske_st_responder_failure); + return SILC_FSM_CONTINUE; + } - /* Get supported hash algorithms */ - rp->hash_alg_list = silc_hash_get_supported(); - rp->hash_alg_len = strlen(rp->hash_alg_list); + /* Decode Key Exchange Payload */ + status = silc_ske_payload_ke_decode(ske, packet_buf, &recv_payload); + if (status != SILC_SKE_STATUS_OK) { + /** Error decoding KE payload */ + silc_packet_free(ske->packet); + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } - /* Get supported HMACs */ - rp->hmac_alg_list = silc_hmac_get_supported(); - rp->hmac_alg_len = strlen(rp->hmac_alg_list); + ske->ke1_payload = recv_payload; - /* XXX */ - /* Get supported compression algorithms */ - rp->comp_alg_list = strdup("none"); - rp->comp_alg_len = strlen("none"); + silc_packet_free(ske->packet); + + /* Verify the received public key and verify the signature if we are + doing mutual authentication. */ + if (ske->start_payload && + ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) { + + SILC_LOG_DEBUG(("We are doing mutual authentication")); + + if (!recv_payload->pk_data && ske->callbacks->verify_key) { + /** Public key not provided */ + SILC_LOG_ERROR(("Remote end did not send its public key (or " + "certificate), even though we require it")); + ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } - rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + - 2 + rp->version_len + - 2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len + - 2 + rp->enc_alg_len + 2 + rp->hash_alg_len + - 2 + rp->hmac_alg_len + 2 + rp->comp_alg_len; + if (recv_payload->pk_data && ske->callbacks->verify_key) { + SILC_LOG_DEBUG(("Verifying public key")); - return rp; + /** Waiting public key verification */ + silc_fsm_next(fsm, silc_ske_st_responder_phase4); + SILC_FSM_CALL(ske->callbacks->verify_key(ske, recv_payload->pk_data, + recv_payload->pk_len, + recv_payload->pk_type, + ske->callbacks->context, + silc_ske_pk_verified, NULL)); + /* NOT REACHED */ + } + } + + /** Generate KE2 payload */ + silc_fsm_next(fsm, silc_ske_st_responder_phase4); + return SILC_FSM_CONTINUE; } -/* Selects the supported security properties from the remote end's Key - Exchange Start Payload. */ +/* Phase-4. Generate KE2 payload */ -static SilcSKEStatus -silc_ske_select_security_properties(SilcSKE ske, - const char *version, - SilcSKEStartPayload payload, - SilcSKEStartPayload remote_payload) +SILC_FSM_STATE(silc_ske_st_responder_phase4) { + SilcSKE ske = fsm_context; SilcSKEStatus status; - SilcSKEStartPayload rp; - char *cp; - int len; + SilcSKEKEPayload recv_payload, send_payload; + SilcMPInt *x, *KEY; - SILC_LOG_DEBUG(("Parsing KE Start Payload")); + if (ske->aborted) { + /** Aborted */ + silc_fsm_next(fsm, silc_ske_st_responder_aborted); + return SILC_FSM_CONTINUE; + } - rp = remote_payload; + /* Check result of public key verification */ + if (ske->status != SILC_SKE_STATUS_OK) { + /** Public key not verified */ + SILC_LOG_DEBUG(("Public key verification failed")); + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } - /* Check version string */ - if (ske->callbacks->check_version) { - status = ske->callbacks->check_version(ske, rp->version, - rp->version_len, - ske->callbacks->context); + recv_payload = ske->ke1_payload; + + /* The public key verification was performed only if the Mutual + Authentication flag is set. */ + if (ske->start_payload && + ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) { + SilcPublicKey public_key = NULL; + unsigned char hash[SILC_HASH_MAXLEN]; + SilcUInt32 hash_len; + + /* Decode the public key */ + if (!silc_pkcs_public_key_decode(recv_payload->pk_data, + recv_payload->pk_len, + &public_key)) { + /** Error decoding public key */ + SILC_LOG_ERROR(("Unsupported/malformed public key received")); + ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + SILC_LOG_DEBUG(("Public key is authentic")); + + /* Compute the hash value */ + status = silc_ske_make_hash(ske, hash, &hash_len, TRUE); if (status != SILC_SKE_STATUS_OK) { + /** Error computing hash */ ske->status = status; - return status; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; } - } - ske->remote_version = silc_memdup(rp->version, rp->version_len); + SILC_LOG_DEBUG(("Verifying signature (HASH_i)")); - /* Flags are returned unchanged. */ - payload->flags = rp->flags; + /* Verify signature */ + silc_pkcs_public_key_set(ske->prop->pkcs, public_key); + if (silc_pkcs_verify(ske->prop->pkcs, recv_payload->sign_data, + recv_payload->sign_len, hash, hash_len) == FALSE) { + /** Incorrect signature */ + SILC_LOG_ERROR(("Signature verification failed, incorrect signature")); + ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } - /* Take cookie, we must return it to sender unmodified. */ - payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char)); - payload->cookie_len = SILC_SKE_COOKIE_LEN; - memcpy(payload->cookie, rp->cookie, SILC_SKE_COOKIE_LEN); + SILC_LOG_DEBUG(("Signature is Ok")); - /* Put our version to our reply */ - payload->version = strdup(version); - payload->version_len = strlen(version); + silc_pkcs_public_key_free(public_key); + memset(hash, 'F', hash_len); + } - /* Get supported Key Exchange groups */ - cp = rp->ke_grp_list; - if (cp && strchr(cp, ',')) { - while(cp) { - char *item; + /* Create the random number x, 1 < x < q. */ + x = silc_calloc(1, sizeof(*x)); + silc_mp_init(x); + status = + silc_ske_create_rnd(ske, &ske->prop->group->group_order, + silc_mp_sizeinbase(&ske->prop->group->group_order, 2), + x); + if (status != SILC_SKE_STATUS_OK) { + /** Error generating random number */ + silc_mp_uninit(x); + silc_free(x); + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } - len = strcspn(cp, ","); - item = silc_calloc(len + 1, sizeof(char)); - memcpy(item, cp, len); + /* Save the results for later processing */ + send_payload = silc_calloc(1, sizeof(*send_payload)); + ske->x = x; + ske->ke2_payload = send_payload; - SILC_LOG_DEBUG(("Proposed KE group `%s'", item)); + SILC_LOG_DEBUG(("Computing f = g ^ x mod p")); - if (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) { - SILC_LOG_DEBUG(("Found KE group `%s'", item)); + /* Do the Diffie Hellman computation, f = g ^ x mod p */ + silc_mp_init(&send_payload->x); + silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x, + &ske->prop->group->group); - payload->ke_grp_len = len; - payload->ke_grp_list = item; - break; - } + SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p")); - cp += len; - if (strlen(cp) == 0) - cp = NULL; - else - cp++; + /* Compute the shared secret key */ + KEY = silc_calloc(1, sizeof(*KEY)); + silc_mp_init(KEY); + silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x, + &ske->prop->group->group); + ske->KEY = KEY; - if (item) - silc_free(item); - } + /** Send KE2 payload */ + silc_fsm_next(fsm, silc_ske_st_responder_phase5); + return SILC_FSM_CONTINUE; +} - if (!payload->ke_grp_len && !payload->ke_grp_list) { - SILC_LOG_DEBUG(("Could not find supported KE group")); - silc_free(payload); - return SILC_SKE_STATUS_UNKNOWN_GROUP; - } - } else { +/* Phase-5. Send KE2 payload */ - if (!rp->ke_grp_len) { - SILC_LOG_DEBUG(("KE group not defined in payload")); - silc_free(payload); - return SILC_SKE_STATUS_BAD_PAYLOAD; +SILC_FSM_STATE(silc_ske_st_responder_phase5) +{ + SilcSKE ske = fsm_context; + SilcSKEStatus status; + SilcBuffer payload_buf; + unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1], *pk; + SilcUInt32 hash_len, sign_len, pk_len; + + SILC_LOG_DEBUG(("Start")); + + if (ske->public_key && ske->private_key) { + SILC_LOG_DEBUG(("Getting public key")); + + /* Get the public key */ + pk = silc_pkcs_public_key_encode(ske->public_key, &pk_len); + if (!pk) { + /** Error encoding public key */ + status = SILC_SKE_STATUS_OUT_OF_MEMORY; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; } + ske->ke2_payload->pk_data = pk; + ske->ke2_payload->pk_len = pk_len; - SILC_LOG_DEBUG(("Proposed KE group `%s'", rp->ke_grp_list)); - SILC_LOG_DEBUG(("Found KE group `%s'", rp->ke_grp_list)); + SILC_LOG_DEBUG(("Computing HASH value")); - payload->ke_grp_len = rp->ke_grp_len; - payload->ke_grp_list = strdup(rp->ke_grp_list); - } + /* Compute the hash value */ + memset(hash, 0, sizeof(hash)); + status = silc_ske_make_hash(ske, hash, &hash_len, FALSE); + if (status != SILC_SKE_STATUS_OK) { + /** Error computing hash */ + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } - /* Get supported PKCS algorithms */ - cp = rp->pkcs_alg_list; - if (cp && strchr(cp, ',')) { - while(cp) { - char *item; + ske->hash = silc_memdup(hash, hash_len); + ske->hash_len = hash_len; - len = strcspn(cp, ","); - item = silc_calloc(len + 1, sizeof(char)); - memcpy(item, cp, len); + SILC_LOG_DEBUG(("Signing HASH value")); - SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", item)); + /* Sign the hash value */ + silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv, + ske->private_key->prv_len); + if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 || + !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) { + /** Error computing signature */ + status = SILC_SKE_STATUS_SIGNATURE_ERROR; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + ske->ke2_payload->sign_data = silc_memdup(sign, sign_len); + ske->ke2_payload->sign_len = sign_len; + memset(sign, 0, sizeof(sign)); + } + ske->ke2_payload->pk_type = ske->pk_type; - if (silc_pkcs_is_supported(item) == TRUE) { - SILC_LOG_DEBUG(("Found PKCS alg `%s'", item)); + /* Encode the Key Exchange Payload */ + status = silc_ske_payload_ke_encode(ske, ske->ke2_payload, + &payload_buf); + if (status != SILC_SKE_STATUS_OK) { + /** Error encoding KE payload */ + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } - payload->pkcs_alg_len = len; - payload->pkcs_alg_list = item; - break; - } + /* Send the packet. */ + if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE_2, 0, + payload_buf->data, silc_buffer_len(payload_buf))) { + ske->status = SILC_SKE_STATUS_ERROR; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } - cp += len; - if (strlen(cp) == 0) - cp = NULL; - else - cp++; + silc_buffer_free(payload_buf); - if (item) - silc_free(item); - } + /** Waiting completion */ + silc_fsm_next(fsm, silc_ske_st_responder_end); + return SILC_FSM_WAIT; +} - if (!payload->pkcs_alg_len && !payload->pkcs_alg_list) { - SILC_LOG_DEBUG(("Could not find supported PKCS alg")); - silc_free(payload->ke_grp_list); - silc_free(payload); - return SILC_SKE_STATUS_UNKNOWN_PKCS; - } - } else { +/* Protocol completed */ - if (!rp->pkcs_alg_len) { - SILC_LOG_DEBUG(("PKCS alg not defined in payload")); - silc_free(payload->ke_grp_list); - silc_free(payload); - return SILC_SKE_STATUS_BAD_PAYLOAD; - } +SILC_FSM_STATE(silc_ske_st_responder_end) +{ + SilcSKE ske = fsm_context; + unsigned char tmp[4]; + SilcUInt32 hash_len, key_len, block_len; - SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list)); - SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list)); + if (ske->aborted) { + /** Aborted */ + silc_fsm_next(fsm, silc_ske_st_responder_aborted); + return SILC_FSM_CONTINUE; + } - payload->pkcs_alg_len = rp->pkcs_alg_len; - payload->pkcs_alg_list = strdup(rp->pkcs_alg_list); + /* Check the result of the protocol */ + if (ske->packet->type == SILC_PACKET_FAILURE) { + silc_fsm_next(fsm, silc_ske_st_responder_failure); + return SILC_FSM_CONTINUE; } + silc_packet_free(ske->packet); - /* Get supported encryption algorithms */ - cp = rp->enc_alg_list; - if (cp && strchr(cp, ',')) { - while(cp) { - char *item; + /* Process key material */ + key_len = silc_cipher_get_key_len(ske->prop->cipher); + block_len = silc_cipher_get_key_len(ske->prop->cipher); + hash_len = silc_hash_len(ske->prop->hash); + ske->keymat = silc_ske_process_key_material(ske, block_len, + key_len, hash_len); + if (!ske->keymat) { + /** Error processing key material */ + ske->status = SILC_SKE_STATUS_ERROR; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } - len = strcspn(cp, ","); - item = silc_calloc(len + 1, sizeof(char)); - memcpy(item, cp, len); + /* Send SUCCESS packet */ + SILC_PUT32_MSB(SILC_SKE_STATUS_OK, tmp); + silc_packet_send(ske->stream, SILC_PACKET_SUCCESS, 0, tmp, 4); - SILC_LOG_DEBUG(("Proposed encryption alg `%s'", item)); + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); - if (silc_cipher_is_supported(item) == TRUE) { - SILC_LOG_DEBUG(("Found encryption alg `%s'", item)); + /* Call the completion callback */ + if (ske->callbacks->completed) + ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat, + ske->rekey, ske->callbacks->context); - payload->enc_alg_len = len; - payload->enc_alg_list = item; - break; - } + return SILC_FSM_FINISH; +} - cp += len; - if (strlen(cp) == 0) - cp = NULL; - else - cp++; +/* Aborted by application */ - if (item) - silc_free(item); - } +SILC_FSM_STATE(silc_ske_st_responder_aborted) +{ + SilcSKE ske = fsm_context; + unsigned char tmp[4]; - if (!payload->enc_alg_len && !payload->enc_alg_list) { - SILC_LOG_DEBUG(("Could not find supported encryption alg")); - silc_free(payload->ke_grp_list); - silc_free(payload->pkcs_alg_list); - silc_free(payload); - return SILC_SKE_STATUS_UNKNOWN_CIPHER; - } - } else { + SILC_LOG_DEBUG(("Key exchange protocol aborted")); - if (!rp->enc_alg_len) { - SILC_LOG_DEBUG(("Encryption alg not defined in payload")); - silc_free(payload->ke_grp_list); - silc_free(payload->pkcs_alg_list); - silc_free(payload); - return SILC_SKE_STATUS_BAD_PAYLOAD; - } + /* Send FAILURE packet */ + SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, tmp); + silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, tmp, 4); - SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it", - rp->enc_alg_list)); + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); - payload->enc_alg_len = rp->enc_alg_len; - payload->enc_alg_list = strdup(rp->enc_alg_list); - } + return SILC_FSM_FINISH; +} - /* Get supported hash algorithms */ - cp = rp->hash_alg_list; - if (cp && strchr(cp, ',')) { - while(cp) { - char *item; +/* Failure received from remote */ - len = strcspn(cp, ","); - item = silc_calloc(len + 1, sizeof(char)); - memcpy(item, cp, len); +SILC_FSM_STATE(silc_ske_st_responder_failure) +{ + SilcSKE ske = fsm_context; + SilcUInt32 error = SILC_SKE_STATUS_ERROR; - SILC_LOG_DEBUG(("Proposed hash alg `%s'", item)); + SILC_LOG_DEBUG(("Key exchange protocol failed")); - if (silc_hash_is_supported(item) == TRUE) { - SILC_LOG_DEBUG(("Found hash alg `%s'", item)); + if (silc_buffer_len(&ske->packet->buffer) == 4) + SILC_GET32_MSB(error, ske->packet->buffer.data); + ske->status = error; - payload->hash_alg_len = len; - payload->hash_alg_list = item; - break; - } + /* Call the completion callback */ + if (ske->callbacks->completed) + ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, + ske->callbacks->context); - cp += len; - if (strlen(cp) == 0) - cp = NULL; - else - cp++; + silc_packet_free(ske->packet); + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); - if (item) - silc_free(item); - } + return SILC_FSM_FINISH; +} - if (!payload->hash_alg_len && !payload->hash_alg_list) { - SILC_LOG_DEBUG(("Could not find supported hash alg")); - silc_free(payload->ke_grp_list); - silc_free(payload->pkcs_alg_list); - silc_free(payload->enc_alg_list); - silc_free(payload); - return SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION; - } - } else { +/* Error occurred */ - if (!rp->hash_alg_len) { - SILC_LOG_DEBUG(("Hash alg not defined in payload")); - silc_free(payload->ke_grp_list); - silc_free(payload->pkcs_alg_list); - silc_free(payload->enc_alg_list); - silc_free(payload); - return SILC_SKE_STATUS_BAD_PAYLOAD; - } +SILC_FSM_STATE(silc_ske_st_responder_error) +{ + SilcSKE ske = fsm_context; + unsigned char tmp[4]; - SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it", - rp->hash_alg_list)); + SILC_LOG_DEBUG(("Error %d (%s) during key exchange protocol", + ske->status, silc_ske_map_status(ske->status))); - payload->hash_alg_len = rp->hash_alg_len; - payload->hash_alg_list = strdup(rp->hash_alg_list); - } + /* Send FAILURE packet */ + if (ske->status > SILC_SKE_STATUS_INVALID_COOKIE) + ske->status = SILC_SKE_STATUS_BAD_PAYLOAD; + SILC_PUT32_MSB(ske->status, tmp); + silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, tmp, 4); - /* Get supported HMACs */ - cp = rp->hmac_alg_list; - if (cp && strchr(cp, ',')) { - while(cp) { - char *item; + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); - len = strcspn(cp, ","); - item = silc_calloc(len + 1, sizeof(char)); - memcpy(item, cp, len); + return SILC_FSM_FINISH; +} - SILC_LOG_DEBUG(("Proposed HMAC `%s'", item)); - if (silc_hmac_is_supported(item) == TRUE) { - SILC_LOG_DEBUG(("Found HMAC `%s'", item)); +static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context, + void *destructor_context) +{ - payload->hmac_alg_len = len; - payload->hmac_alg_list = item; - break; - } +} - cp += len; - if (strlen(cp) == 0) - cp = NULL; - else - cp++; +/* Starts the protocol as responder. */ - if (item) - silc_free(item); - } +SilcAsyncOperation +silc_ske_responder(SilcSKE ske, + SilcPacketStream stream, + const char *version, + SilcSKESecurityPropertyFlag flags) +{ + SILC_LOG_DEBUG(("Start SKE as responder")); - if (!payload->hmac_alg_len && !payload->hmac_alg_list) { - SILC_LOG_DEBUG(("Could not find supported HMAC")); - silc_free(payload->ke_grp_list); - silc_free(payload->pkcs_alg_list); - silc_free(payload->enc_alg_list); - silc_free(payload->hash_alg_list); - silc_free(payload); - return SILC_SKE_STATUS_UNKNOWN_HMAC; - } - } else { + if (!ske || !stream || !version) { + return NULL; + } - if (!rp->hmac_alg_len) { - SILC_LOG_DEBUG(("HMAC not defined in payload")); - silc_free(payload->ke_grp_list); - silc_free(payload->pkcs_alg_list); - silc_free(payload->enc_alg_list); - silc_free(payload->hash_alg_list); - silc_free(payload); - return SILC_SKE_STATUS_BAD_PAYLOAD; - } + if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske)) + return NULL; - SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it", - rp->hmac_alg_list)); + if (!silc_fsm_init(&ske->fsm, ske, silc_ske_responder_finished, ske, + ske->schedule)) + return NULL; - payload->hmac_alg_len = rp->hmac_alg_len; - payload->hmac_alg_list = strdup(rp->hmac_alg_list); - } + ske->flags = flags; + ske->version = strdup(version); + if (!ske->version) + return NULL; + ske->responder = TRUE; - /* Get supported compression algorithms */ - cp = rp->comp_alg_list; - if (cp && strchr(cp, ',')) { - while(cp) { - char *item; + /* Link to packet stream to get key exchange packets */ + ske->stream = stream; + silc_packet_stream_link(ske->stream, &silc_ske_stream_cbs, ske, 1000000, + SILC_PACKET_KEY_EXCHANGE, + SILC_PACKET_KEY_EXCHANGE_1, + SILC_PACKET_SUCCESS, + SILC_PACKET_FAILURE, -1); - len = strcspn(cp, ","); - item = silc_calloc(len + 1, sizeof(char)); - memcpy(item, cp, len); + /* Start SKE as responder */ + silc_fsm_start(&ske->fsm, silc_ske_st_responder_start); - SILC_LOG_DEBUG(("Proposed Compression `%s'", item)); + return &ske->op; +} -#if 1 - if (!strcmp(item, "none")) { - SILC_LOG_DEBUG(("Found Compression `%s'", item)); - payload->comp_alg_len = len; - payload->comp_alg_list = item; - break; - } -#else - if (silc_hmac_is_supported(item) == TRUE) { - SILC_LOG_DEBUG(("Found Compression `%s'", item)); - payload->comp_alg_len = len; - payload->comp_alg_list = item; - break; - } -#endif +SILC_FSM_STATE(silc_ske_st_rekey_initiator_start); + +SILC_FSM_STATE(silc_ske_st_rekey_initiator_start) +{ + return SILC_FSM_FINISH; +} + +/* Starts rekey protocol as initiator */ - cp += len; - if (strlen(cp) == 0) - cp = NULL; - else - cp++; +SilcAsyncOperation +silc_ske_rekey_initiator(SilcSKE ske, + SilcPacketStream stream, + SilcSKERekeyMaterial rekey) +{ + SILC_LOG_DEBUG(("Start SKE rekey as initator")); - if (item) - silc_free(item); - } - } + if (!ske || !stream || !rekey) + return NULL; - payload->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + - 2 + payload->version_len + - 2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len + - 2 + payload->enc_alg_len + 2 + payload->hash_alg_len + - 2 + payload->hmac_alg_len + 2 + payload->comp_alg_len; + if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske)) + return NULL; - return SILC_SKE_STATUS_OK; + if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule)) + return NULL; + + ske->rekey = rekey; + + /* Link to packet stream to get key exchange packets */ + ske->stream = stream; + + /* Start SKE rekey as initiator */ + silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start); + + return &ske->op; } -/* Creates random number such that 1 < rnd < n and at most length - of len bits. The rnd sent as argument must be initialized. */ +SILC_FSM_STATE(silc_ske_st_rekey_responder_start); -static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n, - SilcUInt32 len, - SilcMPInt *rnd) +SILC_FSM_STATE(silc_ske_st_rekey_responder_start) { - SilcSKEStatus status = SILC_SKE_STATUS_OK; - unsigned char *string; - SilcUInt32 l; + return SILC_FSM_FINISH; +} - if (!len) - return SILC_SKE_STATUS_ERROR; +/* Starts rekey protocol as responder */ - SILC_LOG_DEBUG(("Creating random number")); +SilcAsyncOperation +silc_ske_rekey_responder(SilcSKE ske, + SilcPacketStream stream, + SilcBuffer ke_payload, + SilcSKERekeyMaterial rekey) +{ + SILC_LOG_DEBUG(("Start SKE rekey as responder")); - l = ((len - 1) / 8); + if (!ske || !stream || !rekey) + return NULL; + if (rekey->pfs && !ke_payload) + return NULL; - /* Get the random number as string */ - string = silc_rng_get_rn_data(ske->rng, l); - if (!string) - return SILC_SKE_STATUS_OUT_OF_MEMORY; + if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske)) + return NULL; - /* Decode the string into a MP integer */ - silc_mp_bin2mp(string, l, rnd); - silc_mp_mod_2exp(rnd, rnd, len); + if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule)) + return NULL; - /* Checks */ - if (silc_mp_cmp_ui(rnd, 1) < 0) - status = SILC_SKE_STATUS_ERROR; - if (silc_mp_cmp(rnd, n) >= 0) - status = SILC_SKE_STATUS_ERROR; + // ske->packet_buf = ke_payload; + ske->rekey = rekey; - memset(string, 'F', l); - silc_free(string); + /* Link to packet stream to get key exchange packets */ + ske->stream = stream; - return status; + /* Start SKE rekey as responder */ + silc_fsm_start(&ske->fsm, silc_ske_st_rekey_responder_start); + + return &ske->op; } -/* Creates a hash value HASH as defined in the SKE protocol. If the - `initiator' is TRUE then this function is used to create the HASH_i - hash value defined in the protocol. If it is FALSE then this is used - to create the HASH value defined by the protocol. */ +/* Assembles security properties */ -static SilcSKEStatus silc_ske_make_hash(SilcSKE ske, - unsigned char *return_hash, - SilcUInt32 *return_hash_len, - int initiator) +SilcSKEStartPayload +silc_ske_assemble_security_properties(SilcSKE ske, + SilcSKESecurityPropertyFlag flags, + const char *version) { - SilcSKEStatus status = SILC_SKE_STATUS_OK; - SilcBuffer buf; - unsigned char *e, *f, *KEY; - SilcUInt32 e_len, f_len, KEY_len; - int ret; + SilcSKEStartPayload rp; + int i; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Assembling KE Start Payload")); - if (initiator == FALSE) { - e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len); - f = silc_mp_mp2bin(&ske->ke2_payload->x, 0, &f_len); - KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len); + rp = silc_calloc(1, sizeof(*rp)); - /* Format the buffer used to compute the hash value */ - buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) + - ske->ke2_payload->pk_len + - ske->ke1_payload->pk_len + - e_len + f_len + KEY_len); - if (!buf) - return SILC_SKE_STATUS_OUT_OF_MEMORY; + /* Set flags */ + rp->flags = (unsigned char)flags; - /* Initiator is not required to send its public key */ - if (!ske->ke1_payload->pk_data) { - ret = - silc_buffer_format(buf, - SILC_STR_UI_XNSTRING( - ske->start_payload_copy->data, - silc_buffer_len(ske->start_payload_copy)), - SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data, - ske->ke2_payload->pk_len), - SILC_STR_UI_XNSTRING(e, e_len), - SILC_STR_UI_XNSTRING(f, f_len), - SILC_STR_UI_XNSTRING(KEY, KEY_len), - SILC_STR_END); - } else { - ret = - silc_buffer_format(buf, - SILC_STR_UI_XNSTRING( - ske->start_payload_copy->data, - silc_buffer_len(ske->start_payload_copy)), - SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data, - ske->ke2_payload->pk_len), - SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data, - ske->ke1_payload->pk_len), - SILC_STR_UI_XNSTRING(e, e_len), - SILC_STR_UI_XNSTRING(f, f_len), - SILC_STR_UI_XNSTRING(KEY, KEY_len), - SILC_STR_END); - } - if (ret == -1) { - silc_buffer_free(buf); - memset(e, 0, e_len); - memset(f, 0, f_len); - memset(KEY, 0, KEY_len); - silc_free(e); - silc_free(f); - silc_free(KEY); - return SILC_SKE_STATUS_ERROR; - } + /* Set random cookie */ + rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(*rp->cookie)); + for (i = 0; i < SILC_SKE_COOKIE_LEN; i++) + rp->cookie[i] = silc_rng_get_byte_fast(ske->rng); + rp->cookie_len = SILC_SKE_COOKIE_LEN; - memset(e, 0, e_len); - memset(f, 0, f_len); - memset(KEY, 0, KEY_len); - silc_free(e); - silc_free(f); - silc_free(KEY); - } else { - e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len); + /* Put version */ + rp->version = strdup(version); + rp->version_len = strlen(version); - buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) + - ske->ke1_payload->pk_len + e_len); - if (!buf) - return SILC_SKE_STATUS_OUT_OF_MEMORY; + /* Get supported Key Exhange groups */ + rp->ke_grp_list = silc_ske_get_supported_groups(); + rp->ke_grp_len = strlen(rp->ke_grp_list); - /* Format the buffer used to compute the hash value */ - ret = - silc_buffer_format(buf, - SILC_STR_UI_XNSTRING(ske->start_payload_copy->data, - silc_buffer_len(ske->start_payload_copy)), - SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data, - ske->ke1_payload->pk_len), - SILC_STR_UI_XNSTRING(e, e_len), - SILC_STR_END); - if (ret == -1) { - silc_buffer_free(buf); - memset(e, 0, e_len); - silc_free(e); - return SILC_SKE_STATUS_ERROR; - } + /* Get supported PKCS algorithms */ + rp->pkcs_alg_list = silc_pkcs_get_supported(); + rp->pkcs_alg_len = strlen(rp->pkcs_alg_list); - memset(e, 0, e_len); - silc_free(e); - } + /* Get supported encryption algorithms */ + rp->enc_alg_list = silc_cipher_get_supported(); + rp->enc_alg_len = strlen(rp->enc_alg_list); - /* Make the hash */ - silc_hash_make(ske->prop->hash, buf->data, silc_buffer_len(buf), - return_hash); - *return_hash_len = silc_hash_len(ske->prop->hash); + /* Get supported hash algorithms */ + rp->hash_alg_list = silc_hash_get_supported(); + rp->hash_alg_len = strlen(rp->hash_alg_list); - if (initiator == FALSE) { - SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len); - } else { - SILC_LOG_HEXDUMP(("HASH_i"), return_hash, *return_hash_len); - } + /* Get supported HMACs */ + rp->hmac_alg_list = silc_hmac_get_supported(); + rp->hmac_alg_len = strlen(rp->hmac_alg_list); - silc_buffer_free(buf); + /* XXX */ + /* Get supported compression algorithms */ + rp->comp_alg_list = strdup("none"); + rp->comp_alg_len = strlen("none"); - return status; + rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + + 2 + rp->version_len + + 2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len + + 2 + rp->enc_alg_len + 2 + rp->hash_alg_len + + 2 + rp->hmac_alg_len + 2 + rp->comp_alg_len; + + return rp; } /* Processes the provided key material `data' as the SILC protocol @@ -1950,6 +2127,10 @@ silc_ske_process_key_material_data(unsigned char *data, if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len) return NULL; + key = silc_calloc(1, sizeof(*key)); + if (!key) + return NULL; + buf = silc_buffer_alloc_size(1 + data_len); if (!buf) return NULL; @@ -2129,7 +2310,6 @@ silc_ske_process_key_material(SilcSKE ske, SilcUInt32 req_enc_key_len, SilcUInt32 req_hmac_key_len) { - SilcSKEStatus status; SilcBuffer buf; unsigned char *tmpbuf; SilcUInt32 klen; @@ -2190,6 +2370,81 @@ void silc_ske_free_key_material(SilcSKEKeyMaterial key) silc_free(key); } +/* Set keys into use */ + +SilcBool silc_ske_set_keys(SilcSKE ske, + SilcSKEKeyMaterial keymat, + SilcSKESecurityProperties prop, + SilcCipher *ret_send_key, + SilcCipher *ret_receive_key, + SilcHmac *ret_hmac_send, + SilcHmac *ret_hmac_receive, + SilcHash *ret_hash) +{ + /* Allocate ciphers to be used in the communication */ + if (ret_send_key) { + if (!silc_cipher_alloc((char *)silc_cipher_get_name(prop->cipher), + ret_send_key)) + return FALSE; + } + if (ret_receive_key) { + if (!silc_cipher_alloc((char *)silc_cipher_get_name(prop->cipher), + ret_receive_key)) + return FALSE; + } + + /* Allocate HMACs */ + if (ret_hmac_send) { + if (!silc_hmac_alloc((char *)silc_hmac_get_name(prop->hmac), NULL, + ret_hmac_send)) + return FALSE; + } + if (ret_hmac_receive) { + if (!silc_hmac_alloc((char *)silc_hmac_get_name(prop->hmac), NULL, + ret_hmac_receive)) + return FALSE; + } + + /* Set key material */ + if (ske->responder) { + silc_cipher_set_key(*ret_send_key, keymat->receive_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(*ret_send_key, keymat->receive_iv); + silc_cipher_set_key(*ret_receive_key, keymat->send_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(*ret_receive_key, keymat->send_iv); + silc_hmac_set_key(*ret_hmac_send, keymat->receive_hmac_key, + keymat->hmac_key_len); + silc_hmac_set_key(*ret_hmac_receive, keymat->send_hmac_key, + keymat->hmac_key_len); + } else { + silc_cipher_set_key(*ret_send_key, keymat->send_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(*ret_send_key, keymat->send_iv); + silc_cipher_set_key(*ret_receive_key, keymat->receive_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(*ret_receive_key, keymat->receive_iv); + silc_hmac_set_key(*ret_hmac_send, keymat->send_hmac_key, + keymat->hmac_key_len); + silc_hmac_set_key(*ret_hmac_receive, keymat->receive_hmac_key, + keymat->hmac_key_len); + } + + /* Allocate hash */ + if (ret_hash) { + if (!silc_hash_alloc(silc_hash_get_name(prop->hash), ret_hash)) + return FALSE; + } + + SILC_LOG_INFO(("Security properties: %s %s %s %s", + ret_send_key ? silc_cipher_get_name(*ret_send_key) : "??", + ret_hmac_send ? silc_hmac_get_name(*ret_hmac_send) : "??", + ret_hash ? silc_hash_get_name(*ret_hash) : "??", + ske->prop->flags & SILC_SKE_SP_FLAG_PFS ? "PFS" : "")); + + return TRUE; +} + const char *silc_ske_status_string[] = { /* Official */ @@ -2207,9 +2462,7 @@ const char *silc_ske_status_string[] = "Invalid cookie", /* Other errors */ - "Pending", "Remote did not provide public key", - "Key exchange protocol is not active", "Bad reserved field in packet", "Bad payload length in packet", "Error computing signature", @@ -2235,11 +2488,11 @@ const char *silc_ske_map_status(SilcSKEStatus status) /* Parses remote host's version string. */ SilcBool silc_ske_parse_version(SilcSKE ske, - SilcUInt32 *protocol_version, - char **protocol_version_string, - SilcUInt32 *software_version, - char **software_version_string, - char **vendor_version) + SilcUInt32 *protocol_version, + char **protocol_version_string, + SilcUInt32 *software_version, + char **software_version_string, + char **vendor_version) { return silc_parse_version_string(ske->remote_version, protocol_version,