X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcske%2Fsilcske.c;h=caf2579628bf9a1a426057a5179f2949dfa5d07e;hp=021937c81df53d4da57d5d5bf850c3c4ed71d72c;hb=457becb724b71a6145d8c1ad2111b972ed67128d;hpb=bb18c1721daf2844ecc160711f3e953d45459b7e diff --git a/lib/silcske/silcske.c b/lib/silcske/silcske.c index 021937c8..caf25796 100644 --- a/lib/silcske/silcske.c +++ b/lib/silcske/silcske.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2000 - 2006 Pekka Riikonen + Copyright (C) 2000 - 2008 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,15 +31,47 @@ struct SilcSKECallbacksStruct { void *context; }; - /************************ Static utility functions **************************/ +/* States */ +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); +SILC_FSM_STATE(silc_ske_st_initiator_failure); +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_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); +SILC_FSM_STATE(silc_ske_st_rekey_initiator_start); +SILC_FSM_STATE(silc_ske_st_rekey_initiator_done); +SILC_FSM_STATE(silc_ske_st_rekey_initiator_end); +SILC_FSM_STATE(silc_ske_st_rekey_responder_wait); +SILC_FSM_STATE(silc_ske_st_rekey_responder_start); +SILC_FSM_STATE(silc_ske_st_rekey_responder_done); +SILC_FSM_STATE(silc_ske_st_rekey_responder_end); +SILC_TASK_CALLBACK(silc_ske_packet_send_retry); + SilcSKEKeyMaterial silc_ske_process_key_material(SilcSKE ske, SilcUInt32 req_iv_len, SilcUInt32 req_enc_key_len, - SilcUInt32 req_hmac_key_len); - + SilcUInt32 req_hmac_key_len, + SilcSKERekeyMaterial *rekey); +static SilcBool silc_ske_packet_send(SilcSKE ske, + SilcPacketType type, + SilcPacketFlags flags, + const unsigned char *data, + SilcUInt32 data_len); /* Packet callback */ @@ -50,8 +82,46 @@ static SilcBool silc_ske_packet_receive(SilcPacketEngine engine, void *app_context) { SilcSKE ske = callback_context; + + /* Clear retransmission */ + ske->retry_timer = SILC_SKE_RETRY_MIN; + ske->retry_count = 0; + silc_schedule_task_del_by_callback(ske->schedule, + silc_ske_packet_send_retry); + + /* Signal for new packet */ ske->packet = packet; - silc_fsm_continue(&ske->fsm); + + /* Check if we were aborted */ + if (ske->aborted) { + silc_packet_free(packet); + ske->packet = NULL; + + if (ske->responder) + silc_fsm_next(&ske->fsm, silc_ske_st_responder_aborted); + else + silc_fsm_next(&ske->fsm, silc_ske_st_initiator_aborted); + + silc_fsm_continue_sync(&ske->fsm); + return TRUE; + } + + /* See if received failure from remote */ + if (packet->type == SILC_PACKET_FAILURE) { + if (ske->responder) + silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure); + else + silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure); + } + + /* Handle rekey and SUCCESS packets synchronously. After SUCCESS packets + they keys are taken into use immediately, hence the synchronous + processing to get the keys in use as soon as possible. */ + if (ske->rekeying || packet->type == SILC_PACKET_SUCCESS) + silc_fsm_continue_sync(&ske->fsm); + else + silc_fsm_continue(&ske->fsm); + return TRUE; } @@ -112,36 +182,17 @@ static void silc_ske_skr_callback(SilcSKR repository, static SilcSKEStatus silc_ske_check_version(SilcSKE ske) { - SilcUInt32 l_protocol_version = 0, r_protocol_version = 0; SilcUInt32 r_software_version = 0; + char *r_software_string = NULL; if (!ske->remote_version || !ske->version) return SILC_SKE_STATUS_BAD_VERSION; - if (!silc_parse_version_string(ske->remote_version, &r_protocol_version, - NULL, &r_software_version, NULL, NULL)) - return SILC_SKE_STATUS_BAD_VERSION; - - if (!silc_parse_version_string(ske->version, &l_protocol_version, - NULL, NULL, NULL, NULL)) - return SILC_SKE_STATUS_BAD_VERSION; - - /* If remote is too new, don't connect */ - if (l_protocol_version < r_protocol_version) + if (!silc_parse_version_string(ske->remote_version, NULL, NULL, + &r_software_version, + &r_software_string, NULL)) return SILC_SKE_STATUS_BAD_VERSION; - /* Backwards compatibility checks */ - - /* Old server versions requires "valid" looking Source ID in the SILC - packets during initial key exchange. All version before 1.1.0. */ - if (r_software_version < 110) { - SilcClientID id; - memset(&id, 0, sizeof(id)); - id.ip.data_len = 4; - SILC_LOG_DEBUG(("Remote is old version, add dummy Source ID to packets")); - silc_packet_set_ids(ske->stream, SILC_ID_CLIENT, &id, 0, NULL); - } - return SILC_SKE_STATUS_OK; } @@ -387,8 +438,7 @@ silc_ske_select_security_properties(SilcSKE ske, } /* Save selected cipher to security properties */ - if (silc_cipher_alloc(payload->enc_alg_list, - &(*prop)->cipher) == FALSE) { + if (silc_cipher_alloc(payload->enc_alg_list, &(*prop)->cipher) == FALSE) { silc_free(payload->ke_grp_list); silc_free(payload->pkcs_alg_list); silc_free(payload); @@ -446,8 +496,7 @@ silc_ske_select_security_properties(SilcSKE ske, } /* Save selected hash algorithm to security properties */ - if (silc_hash_alloc(ske->start_payload->hash_alg_list, - &(*prop)->hash) == FALSE) { + if (silc_hash_alloc(payload->hash_alg_list, &(*prop)->hash) == FALSE) { silc_free(payload->ke_grp_list); silc_free(payload->pkcs_alg_list); silc_free(payload->enc_alg_list); @@ -507,8 +556,7 @@ silc_ske_select_security_properties(SilcSKE ske, } /* Save selected HMACc to security properties */ - 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) { silc_free(payload->ke_grp_list); silc_free(payload->pkcs_alg_list); silc_free(payload->enc_alg_list); @@ -623,19 +671,23 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske, { SilcSKEStatus status = SILC_SKE_STATUS_OK; SilcBuffer buf; - unsigned char *e, *f, *KEY; - SilcUInt32 e_len, f_len, KEY_len; + unsigned char *e, *f, *KEY, *s_data; + SilcUInt32 e_len, f_len, KEY_len, s_len; int ret; SILC_LOG_DEBUG(("Start")); if (initiator == FALSE) { + s_data = (ske->start_payload_copy ? + silc_buffer_data(ske->start_payload_copy) : NULL); + s_len = (ske->start_payload_copy ? + silc_buffer_len(ske->start_payload_copy) : 0); 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); /* Format the buffer used to compute the hash value */ - buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) + + buf = silc_buffer_alloc_size(s_len + ske->ke2_payload->pk_len + ske->ke1_payload->pk_len + e_len + f_len + KEY_len); @@ -646,28 +698,24 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske, 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_DATA(s_data, s_len), + SILC_STR_DATA(ske->ke2_payload->pk_data, + ske->ke2_payload->pk_len), + SILC_STR_DATA(e, e_len), + SILC_STR_DATA(f, f_len), + SILC_STR_DATA(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_DATA(s_data, s_len), + SILC_STR_DATA(ske->ke2_payload->pk_data, + ske->ke2_payload->pk_len), + SILC_STR_DATA(ske->ke1_payload->pk_data, + ske->ke1_payload->pk_len), + SILC_STR_DATA(e, e_len), + SILC_STR_DATA(f, f_len), + SILC_STR_DATA(KEY, KEY_len), SILC_STR_END); } if (ret == -1) { @@ -688,21 +736,23 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske, silc_free(f); silc_free(KEY); } else { + s_data = (ske->start_payload_copy ? + silc_buffer_data(ske->start_payload_copy) : NULL); + s_len = (ske->start_payload_copy ? + silc_buffer_len(ske->start_payload_copy) : 0); e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len); - buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) + - ske->ke1_payload->pk_len + e_len); + buf = silc_buffer_alloc_size(s_len + ske->ke1_payload->pk_len + e_len); if (!buf) return SILC_SKE_STATUS_OUT_OF_MEMORY; /* 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_DATA(s_data, s_len), + SILC_STR_DATA(ske->ke1_payload->pk_data, + ske->ke1_payload->pk_len), + SILC_STR_DATA(e, e_len), SILC_STR_END); if (ret == -1) { silc_buffer_free(buf); @@ -733,6 +783,42 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske, return status; } +/* Generate rekey material */ + +static SilcSKERekeyMaterial +silc_ske_make_rekey_material(SilcSKE ske, SilcSKEKeyMaterial keymat) +{ + SilcSKERekeyMaterial rekey; + const char *hash; + + /* Create rekey material */ + rekey = silc_calloc(1, sizeof(*rekey)); + if (!rekey) + return NULL; + + if (ske->prop) { + if (ske->prop->group) + rekey->ske_group = silc_ske_group_get_number(ske->prop->group); + rekey->pfs = (ske->prop->flags & SILC_SKE_SP_FLAG_PFS ? TRUE : FALSE); + hash = silc_hash_get_name(ske->prop->hash); + rekey->hash = silc_memdup(hash, strlen(hash)); + if (!rekey->hash) + return NULL; + } + + if (rekey->pfs == FALSE) { + rekey->send_enc_key = silc_memdup(keymat->send_enc_key, + keymat->enc_key_len / 8); + if (!rekey->send_enc_key) { + silc_free(rekey); + return NULL; + } + rekey->enc_key_len = keymat->enc_key_len; + } + + return rekey; +} + /* Assembles security properties */ static SilcSKEStartPayload @@ -799,6 +885,120 @@ silc_ske_assemble_security_properties(SilcSKE ske, return rp; } +/* Packet retransmission callback. */ + +SILC_TASK_CALLBACK(silc_ske_packet_send_retry) +{ + SilcSKE ske = context; + + if (ske->retry_count++ >= SILC_SKE_RETRY_COUNT || + ske->aborted) { + SILC_LOG_DEBUG(("Retransmission limit reached, packet was lost")); + ske->retry_count = 0; + ske->retry_timer = SILC_SKE_RETRY_MIN; + silc_free(ske->retrans.data); + ske->retrans.data = NULL; + ske->status = SILC_SKE_STATUS_TIMEOUT; + if (ske->responder) + silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure); + else + silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure); + silc_fsm_continue_sync(&ske->fsm); + return; + } + + SILC_LOG_DEBUG(("Retransmitting packet")); + silc_ske_packet_send(ske, ske->retrans.type, ske->retrans.flags, + ske->retrans.data, ske->retrans.data_len); +} + +/* Install retransmission timer */ + +static void silc_ske_install_retransmission(SilcSKE ske) +{ + if (!silc_packet_stream_is_udp(ske->stream)) + return; + + if (ske->retrans.data) { + SILC_LOG_DEBUG(("Installing retransmission timer %d secs", + ske->retry_timer)); + silc_schedule_task_add_timeout(ske->schedule, silc_ske_packet_send_retry, + ske, ske->retry_timer, 0); + } + ske->retry_timer = ((ske->retry_timer * SILC_SKE_RETRY_MUL) + + (silc_rng_get_rn16(ske->rng) % SILC_SKE_RETRY_RAND)); +} + +/* Sends SILC packet. Handles retransmissions with UDP streams. */ + +static SilcBool silc_ske_packet_send(SilcSKE ske, + SilcPacketType type, + SilcPacketFlags flags, + const unsigned char *data, + SilcUInt32 data_len) +{ + SilcBool ret; + + /* Send the packet */ + ret = silc_packet_send(ske->stream, type, flags, data, data_len); + + if (silc_packet_stream_is_udp(ske->stream) && + type != SILC_PACKET_FAILURE && type != SILC_PACKET_REKEY) { + silc_free(ske->retrans.data); + ske->retrans.type = type; + ske->retrans.flags = flags; + ske->retrans.data = silc_memdup(data, data_len); + ske->retrans.data_len = data_len; + silc_ske_install_retransmission(ske); + } + + return ret; +} + +/* Calls completion callback. Completion is called always in this function + and must not be called anywhere else. */ + +static void silc_ske_completion(SilcSKE ske) +{ + /* Call the completion callback */ + if (!ske->freed && !ske->aborted && ske->callbacks->completed) { + if (ske->status != SILC_SKE_STATUS_OK) + ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, + ske->callbacks->context); + else + ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat, + ske->rekey, ske->callbacks->context); + } +} + +/* SKE FSM destructor. */ + +static void silc_ske_finished(SilcFSM fsm, void *fsm_context, + void *destructor_context) +{ + SilcSKE ske = fsm_context; + ske->running = FALSE; + if (ske->freed) + silc_ske_free(ske); +} + +/* Key exchange timeout task callback */ + +SILC_TASK_CALLBACK(silc_ske_timeout) +{ + SilcSKE ske = context; + + SILC_LOG_DEBUG(("Timeout")); + + ske->packet = NULL; + ske->status = SILC_SKE_STATUS_TIMEOUT; + if (ske->responder) + silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure); + else + silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure); + + silc_fsm_continue_sync(&ske->fsm); +} /******************************* Protocol API *******************************/ @@ -812,8 +1012,13 @@ SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, SILC_LOG_DEBUG(("Allocating new Key Exchange object")); - if (!rng || !schedule || !public_key) + if (!rng || !schedule) + return NULL; + + if (!public_key) { + SILC_LOG_ERROR(("Public key must be given to silc_ske_alloc")); return NULL; + } ske = silc_calloc(1, sizeof(*ske)); if (!ske) @@ -825,6 +1030,8 @@ SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, ske->schedule = schedule; ske->public_key = public_key; ske->private_key = private_key; + ske->retry_timer = SILC_SKE_RETRY_MIN; + ske->refcnt = 1; return ske; } @@ -835,46 +1042,72 @@ void silc_ske_free(SilcSKE ske) { 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->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); + if (!ske) + return; - memset(ske, 'F', sizeof(*ske)); - silc_free(ske); + if (ske->running) { + ske->freed = TRUE; + + if (ske->aborted) { + /* If already aborted, destroy the session immediately */ + ske->packet = NULL; + ske->status = SILC_SKE_STATUS_ERROR; + if (ske->responder) + silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure); + else + silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure); + silc_fsm_continue_sync(&ske->fsm); + } + return; } + + ske->refcnt--; + if (ske->refcnt > 0) + return; + + /* 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->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); + if (ske->prop->public_key) + silc_pkcs_public_key_free(ske->prop->public_key); + silc_free(ske->prop); + } + if (ske->keymat) + silc_ske_free_key_material(ske->keymat); + 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->retrans.data); + silc_free(ske->hash); + silc_free(ske->callbacks); + + memset(ske, 'F', sizeof(*ske)); + silc_free(ske); } /* Return user context */ @@ -904,17 +1137,6 @@ void silc_ske_set_callbacks(SilcSKE ske, /******************************** Initiator *********************************/ -/* 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); -SILC_FSM_STATE(silc_ske_st_initiator_failure); - /* Start protocol. Send our proposal */ SILC_FSM_STATE(silc_ske_st_initiator_start) @@ -946,19 +1168,22 @@ SILC_FSM_STATE(silc_ske_st_initiator_start) ske->start_payload_copy = payload_buf; /* Send the packet. */ - if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE, 0, - silc_buffer_data(payload_buf), - silc_buffer_len(payload_buf))) { + if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE, 0, + silc_buffer_data(payload_buf), + silc_buffer_len(payload_buf))) { /** Error sending packet */ + SILC_LOG_DEBUG(("Error sending packet")); ske->status = SILC_SKE_STATUS_ERROR; silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; } - /* XXX timeout */ + /* Add key exchange timeout */ + silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout, + ske, ske->timeout, 0); /** Wait for responder proposal */ - SILC_LOG_DEBUG(("Waiting for reponder proposal")); + SILC_LOG_DEBUG(("Waiting for responder proposal")); silc_fsm_next(fsm, silc_ske_st_initiator_phase1); return SILC_FSM_WAIT; } @@ -971,7 +1196,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) SilcSKEStatus status; SilcSKEStartPayload payload; SilcSKESecurityProperties prop; - SilcSKEDiffieHellmanGroup group; + SilcSKEDiffieHellmanGroup group = NULL; SilcBuffer packet_buf = &ske->packet->buffer; SilcUInt16 remote_port = 0; SilcID id; @@ -979,11 +1204,12 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) SILC_LOG_DEBUG(("Start")); - if (ske->aborted) { - /** Aborted */ + if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE) { + SILC_LOG_DEBUG(("Remote retransmitted an old packet")); + silc_ske_install_retransmission(ske); silc_packet_free(ske->packet); - silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - return SILC_FSM_CONTINUE; + ske->packet = NULL; + return SILC_FSM_WAIT; } /* Decode the payload */ @@ -991,6 +1217,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) if (status != SILC_SKE_STATUS_OK) { /** Error decoding Start Payload */ silc_packet_free(ske->packet); + ske->packet = NULL; ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; @@ -1010,6 +1237,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) } silc_packet_free(ske->packet); + ske->packet = NULL; /* Check that the cookie is returned unmodified. In case IV included flag and session port has been set, the first two bytes of cookie @@ -1083,9 +1311,8 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) err: if (payload) silc_ske_payload_start_free(payload); - - silc_ske_group_free(group); - + if (group) + silc_ske_group_free(group); if (prop->cipher) silc_cipher_free(prop->cipher); if (prop->hash) @@ -1176,8 +1403,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) payload->pk_type = silc_pkcs_get_type(ske->public_key); /* Compute signature data if we are doing mutual authentication */ - if (ske->private_key && - ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) { + if (ske->private_key && ske->prop->flags & SILC_SKE_SP_FLAG_MUTUAL) { unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1]; SilcUInt32 hash_len, sign_len; @@ -1192,7 +1418,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) /* Sign the hash value */ if (!silc_pkcs_sign(ske->private_key, hash, hash_len, sign, - sizeof(sign) - 1, &sign_len, NULL)) { + sizeof(sign) - 1, &sign_len, FALSE, ske->prop->hash)) { /** Error computing signature */ silc_mp_uninit(x); silc_free(x); @@ -1230,10 +1456,11 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) /* Check for backwards compatibility */ /* Send the packet. */ - if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE_1, 0, - silc_buffer_data(payload_buf), - silc_buffer_len(payload_buf))) { + if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE_1, 0, + silc_buffer_data(payload_buf), + silc_buffer_len(payload_buf))) { /** Error sending packet */ + SILC_LOG_DEBUG(("Error sending packet")); ske->status = SILC_SKE_STATUS_ERROR; silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; @@ -1258,11 +1485,12 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3) SILC_LOG_DEBUG(("Start")); - if (ske->aborted) { - /** Aborted */ + if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE_2) { + SILC_LOG_DEBUG(("Remote retransmitted an old packet")); + silc_ske_install_retransmission(ske); silc_packet_free(ske->packet); - silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - return SILC_FSM_CONTINUE; + ske->packet = NULL; + return SILC_FSM_WAIT; } /* Decode the payload */ @@ -1270,11 +1498,13 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3) if (status != SILC_SKE_STATUS_OK) { /** Error decoding KE payload */ silc_packet_free(ske->packet); + ske->packet = NULL; ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; } silc_packet_free(ske->packet); + ske->packet = NULL; ske->ke2_payload = payload; if (!payload->pk_data && (ske->callbacks->verify_key || ske->repository)) { @@ -1324,8 +1554,8 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3) silc_skr_find_set_usage(find, SILC_SKR_USAGE_KEY_AGREEMENT); /* Find key from repository */ - SILC_FSM_CALL(silc_skr_find(ske->repository, find, - silc_ske_skr_callback, ske)); + SILC_FSM_CALL(silc_skr_find(ske->repository, silc_fsm_get_schedule(fsm), + find, silc_ske_skr_callback, ske)); } else { /* Verify from application */ SILC_FSM_CALL(ske->callbacks->verify_key(ske, ske->prop->public_key, @@ -1383,14 +1613,16 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4) payload = ske->ke2_payload; + /* Compute the HASH value */ + SILC_LOG_DEBUG(("Computing HASH value")); + status = silc_ske_make_hash(ske, hash, &hash_len, FALSE); + if (status != SILC_SKE_STATUS_OK) + goto err; + ske->hash = silc_memdup(hash, hash_len); + ske->hash_len = hash_len; + if (ske->prop->public_key) { SILC_LOG_DEBUG(("Public key is authentic")); - - /* Compute the hash value */ - status = silc_ske_make_hash(ske, hash, &hash_len, FALSE); - if (status != SILC_SKE_STATUS_OK) - goto err; - SILC_LOG_DEBUG(("Verifying signature (HASH)")); /* Verify signature */ @@ -1402,20 +1634,25 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4) } SILC_LOG_DEBUG(("Signature is Ok")); - - ske->hash = silc_memdup(hash, hash_len); - ske->hash_len = hash_len; memset(hash, 'F', hash_len); } ske->status = SILC_SKE_STATUS_OK; + /* In case we are doing rekey move to finish it. */ + if (ske->rekey) { + /** Finish rekey */ + silc_fsm_next(fsm, silc_ske_st_rekey_initiator_done); + return SILC_FSM_CONTINUE; + } + /* Process key material */ key_len = silc_cipher_get_key_len(ske->prop->cipher); - block_len = silc_cipher_get_key_len(ske->prop->cipher); + block_len = silc_cipher_get_block_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); + key_len, hash_len, + &ske->rekey); if (!ske->keymat) { SILC_LOG_ERROR(("Error processing key material")); status = SILC_SKE_STATUS_ERROR; @@ -1424,8 +1661,9 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4) /* Send SUCCESS packet */ SILC_PUT32_MSB((SilcUInt32)SILC_SKE_STATUS_OK, hash); - if (!silc_packet_send(ske->stream, SILC_PACKET_SUCCESS, 0, hash, 4)) { + if (!silc_ske_packet_send(ske, SILC_PACKET_SUCCESS, 0, hash, 4)) { /** Error sending packet */ + SILC_LOG_DEBUG(("Error sending packet")); ske->status = SILC_SKE_STATUS_ERROR; silc_fsm_next(fsm, silc_ske_st_initiator_error); return SILC_FSM_CONTINUE; @@ -1467,27 +1705,23 @@ SILC_FSM_STATE(silc_ske_st_initiator_end) SILC_LOG_DEBUG(("Start")); - if (ske->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - return SILC_FSM_CONTINUE; - } - - /* See if received failure from remote */ - if (ske->packet->type == SILC_PACKET_FAILURE) { + if (ske->packet->type != SILC_PACKET_SUCCESS) { + SILC_LOG_DEBUG(("Remote retransmitted an old packet")); + silc_ske_install_retransmission(ske); silc_packet_free(ske->packet); - silc_fsm_next(fsm, silc_ske_st_initiator_failure); - return SILC_FSM_CONTINUE; + ske->packet = NULL; + return SILC_FSM_WAIT; } SILC_LOG_DEBUG(("Key exchange completed successfully")); - /* Call the completion callback */ - if (ske->callbacks->completed) - ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat, - ske->rekey, ske->user_data); - silc_packet_free(ske->packet); + ske->packet = NULL; + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); + + /* Call completion */ + silc_ske_completion(ske); return SILC_FSM_FINISH; } @@ -1503,7 +1737,13 @@ SILC_FSM_STATE(silc_ske_st_initiator_aborted) /* Send FAILURE packet */ SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, data); - silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, data, 4); + silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, data, 4); + + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); + + /* Call completion */ + silc_ske_completion(ske); return SILC_FSM_FINISH; } @@ -1525,11 +1765,13 @@ SILC_FSM_STATE(silc_ske_st_initiator_error) /* Send FAILURE packet */ SILC_PUT32_MSB((SilcUInt32)status, data); - silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, data, 4); + silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, data, 4); - /* Call the completion callback */ - if (ske->callbacks->completed) - ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL); + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); + + /* Call completion */ + silc_ske_completion(ske); return SILC_FSM_FINISH; } @@ -1539,33 +1781,33 @@ SILC_FSM_STATE(silc_ske_st_initiator_error) SILC_FSM_STATE(silc_ske_st_initiator_failure) { SilcSKE ske = fsm_context; + SilcUInt32 error = SILC_SKE_STATUS_ERROR; + + if (ske->packet && silc_buffer_len(&ske->packet->buffer) == 4) { + SILC_GET32_MSB(error, ske->packet->buffer.data); + ske->status = error; + silc_packet_free(ske->packet); + ske->packet = NULL; + } SILC_LOG_DEBUG(("Error %s (%d) received during key exchange", silc_ske_map_status(ske->status), ske->status)); - /* Call the completion callback */ - if (ske->callbacks->completed) - ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL); - - return SILC_FSM_FINISH; -} + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); -/* FSM destructor */ + /* Call completion */ + silc_ske_completion(ske); -static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context, - void *destructor_context) -{ - SilcSKE ske = fsm_context; - silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + return SILC_FSM_FINISH; } /* Starts the protocol as initiator */ -SilcAsyncOperation -silc_ske_initiator(SilcSKE ske, - SilcPacketStream stream, - SilcSKEParams params, - SilcSKEStartPayload start_payload) +SilcAsyncOperation silc_ske_initiator(SilcSKE ske, + SilcPacketStream stream, + SilcSKEParams params, + SilcSKEStartPayload start_payload) { SILC_LOG_DEBUG(("Start SKE as initiator")); @@ -1575,8 +1817,7 @@ silc_ske_initiator(SilcSKE ske, 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)) + if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule)) return NULL; if (params->flags & SILC_SKE_SP_FLAG_IV_INCLUDED) @@ -1591,8 +1832,10 @@ silc_ske_initiator(SilcSKE ske, return NULL; } + ske->timeout = params->timeout_secs ? params->timeout_secs : 30; ske->start_payload = start_payload; ske->version = params->version; + ske->running = TRUE; /* Link to packet stream to get key exchange packets */ ske->stream = stream; @@ -1608,19 +1851,8 @@ silc_ske_initiator(SilcSKE ske, 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_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 */ SILC_FSM_STATE(silc_ske_st_responder_start) @@ -1635,8 +1867,9 @@ SILC_FSM_STATE(silc_ske_st_responder_start) return SILC_FSM_CONTINUE; } - /* Start timeout */ - /* XXX */ + /* Add key exchange timeout */ + silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout, + ske, ske->timeout, 0); /** Wait for initiator */ silc_fsm_next(fsm, silc_ske_st_responder_phase1); @@ -1652,38 +1885,40 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1) SilcSKEStatus status; SilcSKEStartPayload remote_payload = NULL; SilcBuffer packet_buf = &ske->packet->buffer; + SilcID id; SILC_LOG_DEBUG(("Start")); - if (ske->aborted) { - /** Aborted */ - silc_packet_free(ske->packet); - silc_fsm_next(fsm, silc_ske_st_responder_aborted); - return SILC_FSM_CONTINUE; - } - - /* See if received failure from remote */ - if (ske->packet->type == SILC_PACKET_FAILURE) { - silc_packet_free(ske->packet); - silc_fsm_next(fsm, silc_ske_st_responder_failure); - return SILC_FSM_CONTINUE; - } - /* 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->packet = NULL; ske->status = status; silc_fsm_next(fsm, silc_ske_st_responder_error); return SILC_FSM_CONTINUE; } + /* Get remote ID and set it to stream */ + if (ske->packet->src_id_len) { + silc_id_str2id(ske->packet->src_id, ske->packet->src_id_len, + ske->packet->src_id_type, + (ske->packet->src_id_type == SILC_ID_SERVER ? + (void *)&id.u.server_id : (void *)&id.u.client_id), + (ske->packet->src_id_type == SILC_ID_SERVER ? + sizeof(id.u.server_id) : sizeof(id.u.client_id))); + silc_packet_set_ids(ske->stream, 0, NULL, ske->packet->src_id_type, + (ske->packet->src_id_type == SILC_ID_SERVER ? + (void *)&id.u.server_id : (void *)&id.u.client_id)); + } + /* 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); silc_packet_free(ske->packet); + ske->packet = NULL; /* Force the mutual authentication flag if we want to do it. */ if (ske->flags & SILC_SKE_SP_FLAG_MUTUAL) { @@ -1724,9 +1959,9 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1) goto err; /* Send the packet. */ - if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE, 0, - silc_buffer_data(packet_buf), - silc_buffer_len(packet_buf))) + if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE, 0, + silc_buffer_data(packet_buf), + silc_buffer_len(packet_buf))) goto err; silc_buffer_free(packet_buf); @@ -1767,16 +2002,12 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2) SILC_LOG_DEBUG(("Start")); - if (ske->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_responder_aborted); - return SILC_FSM_CONTINUE; - } - - /* 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->packet->type != SILC_PACKET_KEY_EXCHANGE_1) { + SILC_LOG_DEBUG(("Remote retransmitted an old packet")); + silc_ske_install_retransmission(ske); + silc_packet_free(ske->packet); + ske->packet = NULL; + return SILC_FSM_WAIT; } /* Decode Key Exchange Payload */ @@ -1784,6 +2015,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2) if (status != SILC_SKE_STATUS_OK) { /** Error decoding KE payload */ silc_packet_free(ske->packet); + ske->packet = NULL; ske->status = status; silc_fsm_next(fsm, silc_ske_st_responder_error); return SILC_FSM_CONTINUE; @@ -1792,16 +2024,11 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2) ske->ke1_payload = recv_payload; silc_packet_free(ske->packet); + ske->packet = NULL; - /* 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 || - ske->repository)) { + /* Verify public key, except in rekey, when it is not sent */ + if (!ske->rekey) { + if (!recv_payload->pk_data) { /** Public key not provided */ SILC_LOG_ERROR(("Remote end did not send its public key (or " "certificate), even though we require it")); @@ -1811,8 +2038,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2) } /* Decode the remote's public key */ - if (recv_payload->pk_data && - !silc_pkcs_public_key_alloc(recv_payload->pk_type, + if (!silc_pkcs_public_key_alloc(recv_payload->pk_type, recv_payload->pk_data, recv_payload->pk_len, &ske->prop->public_key)) { @@ -1823,38 +2049,36 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2) return SILC_FSM_CONTINUE; } - if (ske->prop->public_key && (ske->callbacks->verify_key || - ske->repository)) { - SILC_LOG_DEBUG(("Verifying public key")); + SILC_LOG_DEBUG(("Verifying public key")); - /** Waiting public key verification */ - silc_fsm_next(fsm, silc_ske_st_responder_phase4); + /** Waiting public key verification */ + silc_fsm_next(fsm, silc_ske_st_responder_phase4); - /* If repository is provided, verify the key from there. */ - if (ske->repository) { - SilcSKRFind find; + /* If repository is provided, verify the key from there. */ + if (ske->repository) { + SilcSKRFind find; - find = silc_skr_find_alloc(); - if (!find) { - ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; - silc_fsm_next(fsm, silc_ske_st_responder_error); - return SILC_FSM_CONTINUE; - } - silc_skr_find_set_pkcs_type(find, - silc_pkcs_get_type(ske->prop->public_key)); - silc_skr_find_set_public_key(find, ske->prop->public_key); - silc_skr_find_set_usage(find, SILC_SKR_USAGE_KEY_AGREEMENT); - - /* Find key from repository */ - SILC_FSM_CALL(silc_skr_find(ske->repository, find, - silc_ske_skr_callback, ske)); - } else { - /* Verify from application */ + find = silc_skr_find_alloc(); + if (!find) { + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + silc_skr_find_set_pkcs_type(find, + silc_pkcs_get_type(ske->prop->public_key)); + silc_skr_find_set_public_key(find, ske->prop->public_key); + silc_skr_find_set_usage(find, SILC_SKR_USAGE_KEY_AGREEMENT); + + /* Find key from repository */ + SILC_FSM_CALL(silc_skr_find(ske->repository, + silc_fsm_get_schedule(fsm), find, + silc_ske_skr_callback, ske)); + } else { + /* Verify from application */ + if (ske->callbacks->verify_key) SILC_FSM_CALL(ske->callbacks->verify_key(ske, ske->prop->public_key, ske->callbacks->context, silc_ske_pk_verified, NULL)); - } - /* NOT REACHED */ } } @@ -1895,7 +2119,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase4) unsigned char hash[SILC_HASH_MAXLEN]; SilcUInt32 hash_len; - SILC_LOG_DEBUG(("Public key is authentic")); + SILC_LOG_DEBUG(("We are doing mutual authentication")); /* Compute the hash value */ status = silc_ske_make_hash(ske, hash, &hash_len, TRUE); @@ -1990,27 +2214,28 @@ SILC_FSM_STATE(silc_ske_st_responder_phase5) } ske->ke2_payload->pk_data = pk; ske->ke2_payload->pk_len = pk_len; + } - SILC_LOG_DEBUG(("Computing HASH value")); + SILC_LOG_DEBUG(("Computing HASH value")); - /* 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; - } - - ske->hash = silc_memdup(hash, hash_len); - ske->hash_len = hash_len; + /* 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; + } + ske->hash = silc_memdup(hash, hash_len); + ske->hash_len = hash_len; + if (ske->public_key && ske->private_key) { SILC_LOG_DEBUG(("Signing HASH value")); /* Sign the hash value */ if (!silc_pkcs_sign(ske->private_key, hash, hash_len, sign, - sizeof(sign) - 1, &sign_len, NULL)) { + sizeof(sign) - 1, &sign_len, FALSE, ske->prop->hash)) { /** Error computing signature */ status = SILC_SKE_STATUS_SIGNATURE_ERROR; silc_fsm_next(fsm, silc_ske_st_responder_error); @@ -2033,8 +2258,9 @@ SILC_FSM_STATE(silc_ske_st_responder_phase5) } /* Send the packet. */ - if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE_2, 0, - payload_buf->data, silc_buffer_len(payload_buf))) { + if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE_2, 0, + payload_buf->data, silc_buffer_len(payload_buf))) { + SILC_LOG_DEBUG(("Error sending packet")); ske->status = SILC_SKE_STATUS_ERROR; silc_fsm_next(fsm, silc_ske_st_responder_error); return SILC_FSM_CONTINUE; @@ -2042,6 +2268,13 @@ SILC_FSM_STATE(silc_ske_st_responder_phase5) silc_buffer_free(payload_buf); + /* In case we are doing rekey move to finish it. */ + if (ske->rekey) { + /** Finish rekey */ + silc_fsm_next(fsm, silc_ske_st_rekey_responder_done); + return SILC_FSM_CONTINUE; + } + /** Waiting completion */ silc_fsm_next(fsm, silc_ske_st_responder_end); return SILC_FSM_WAIT; @@ -2055,25 +2288,23 @@ SILC_FSM_STATE(silc_ske_st_responder_end) unsigned char tmp[4]; SilcUInt32 hash_len, key_len, block_len; - if (ske->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_ske_st_responder_aborted); - return SILC_FSM_CONTINUE; - } - - /* 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; + if (ske->packet->type != SILC_PACKET_SUCCESS) { + SILC_LOG_DEBUG(("Remote retransmitted an old packet")); + silc_ske_install_retransmission(ske); + silc_packet_free(ske->packet); + ske->packet = NULL; + return SILC_FSM_WAIT; } silc_packet_free(ske->packet); + ske->packet = NULL; /* Process key material */ key_len = silc_cipher_get_key_len(ske->prop->cipher); - block_len = silc_cipher_get_key_len(ske->prop->cipher); + block_len = silc_cipher_get_block_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); + key_len, hash_len, + &ske->rekey); if (!ske->keymat) { /** Error processing key material */ ske->status = SILC_SKE_STATUS_ERROR; @@ -2083,14 +2314,13 @@ SILC_FSM_STATE(silc_ske_st_responder_end) /* Send SUCCESS packet */ SILC_PUT32_MSB(SILC_SKE_STATUS_OK, tmp); - silc_packet_send(ske->stream, SILC_PACKET_SUCCESS, 0, tmp, 4); + silc_ske_packet_send(ske, SILC_PACKET_SUCCESS, 0, tmp, 4); silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); - /* Call the completion callback */ - if (ske->callbacks->completed) - ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat, - ske->rekey, ske->callbacks->context); + /* Call completion */ + silc_ske_completion(ske); return SILC_FSM_FINISH; } @@ -2106,9 +2336,13 @@ SILC_FSM_STATE(silc_ske_st_responder_aborted) /* Send FAILURE packet */ SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, tmp); - silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, tmp, 4); + silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, tmp, 4); silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); + + /* Call completion */ + silc_ske_completion(ske); return SILC_FSM_FINISH; } @@ -2122,17 +2356,18 @@ SILC_FSM_STATE(silc_ske_st_responder_failure) SILC_LOG_DEBUG(("Key exchange protocol failed")); - if (silc_buffer_len(&ske->packet->buffer) == 4) + if (ske->packet && silc_buffer_len(&ske->packet->buffer) == 4) { SILC_GET32_MSB(error, ske->packet->buffer.data); - ske->status = error; - - /* Call the completion callback */ - if (ske->callbacks->completed) - ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, - ske->callbacks->context); + ske->status = error; + silc_packet_free(ske->packet); + ske->packet = NULL; + } - silc_packet_free(ske->packet); silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); + + /* Call completion */ + silc_ske_completion(ske); return SILC_FSM_FINISH; } @@ -2151,47 +2386,43 @@ SILC_FSM_STATE(silc_ske_st_responder_error) 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); + silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, tmp, 4); silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); - return SILC_FSM_FINISH; -} - - -static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context, - void *destructor_context) -{ + /* Call completion */ + silc_ske_completion(ske); + return SILC_FSM_FINISH; } /* Starts the protocol as responder. */ -SilcAsyncOperation -silc_ske_responder(SilcSKE ske, - SilcPacketStream stream, - SilcSKEParams params) +SilcAsyncOperation silc_ske_responder(SilcSKE ske, + SilcPacketStream stream, + SilcSKEParams params) { SILC_LOG_DEBUG(("Start SKE as responder")); - if (!ske || !stream || !params || !params->version) { + if (!ske || !stream || !params || !params->version) return NULL; - } 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, - ske->schedule)) + if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule)) return NULL; ske->responder = TRUE; ske->flags = params->flags; + ske->timeout = params->timeout_secs ? params->timeout_secs : 30; if (ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED) ske->session_port = params->session_port; ske->version = strdup(params->version); if (!ske->version) return NULL; + ske->running = TRUE; /* Link to packet stream to get key exchange packets */ ske->stream = stream; @@ -2207,10 +2438,222 @@ silc_ske_responder(SilcSKE ske, return &ske->op; } -SILC_FSM_STATE(silc_ske_st_rekey_initiator_start); +/***************************** Initiator Rekey ******************************/ + +/* Start rekey */ SILC_FSM_STATE(silc_ske_st_rekey_initiator_start) { + SilcSKE ske = fsm_context; + SilcStatus status; + + SILC_LOG_DEBUG(("Start rekey (%s)", ske->rekey->pfs ? "PFS" : "No PFS")); + + if (ske->aborted) { + /** Aborted */ + silc_fsm_next(fsm, silc_ske_st_initiator_aborted); + return SILC_FSM_CONTINUE; + } + + /* Add rekey exchange timeout */ + silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout, + ske, 30, 0); + + ske->prop = silc_calloc(1, sizeof(*ske->prop)); + if (!ske->prop) { + /** No memory */ + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + + if (!silc_hash_alloc(ske->rekey->hash, &ske->prop->hash)) { + /** Cannot allocate hash */ + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + + /* Send REKEY packet to start rekey protocol */ + if (!silc_ske_packet_send(ske, SILC_PACKET_REKEY, 0, NULL, 0)) { + /** Error sending packet */ + SILC_LOG_DEBUG(("Error sending packet")); + ske->status = SILC_SKE_STATUS_ERROR; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + + /* If doing rekey without PFS, move directly to the end of the protocol. */ + if (!ske->rekey->pfs) { + /** Rekey without PFS */ + silc_fsm_next(fsm, silc_ske_st_rekey_initiator_done); + return SILC_FSM_CONTINUE; + } + + status = silc_ske_group_get_by_number(ske->rekey->ske_group, + &ske->prop->group); + if (status != SILC_SKE_STATUS_OK) { + /** Unknown group */ + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + + /** Rekey with PFS */ + silc_fsm_next(fsm, silc_ske_st_initiator_phase2); + return SILC_FSM_CONTINUE; +} + +/* Sends REKEY_DONE packet to finish the protocol. */ + +SILC_FSM_STATE(silc_ske_st_rekey_initiator_done) +{ + SilcSKE ske = fsm_context; + SilcCipher send_key; + SilcHmac hmac_send; + SilcHash hash; + SilcUInt32 key_len, block_len, hash_len, x_len; + unsigned char *pfsbuf; + + SILC_LOG_DEBUG(("Start")); + + silc_packet_get_keys(ske->stream, &send_key, NULL, &hmac_send, NULL); + key_len = silc_cipher_get_key_len(send_key); + block_len = silc_cipher_get_block_len(send_key); + hash = ske->prop->hash; + hash_len = silc_hash_len(hash); + + /* Process key material */ + if (ske->rekey->pfs) { + /* PFS */ + pfsbuf = silc_mp_mp2bin(ske->KEY, 0, &x_len); + if (pfsbuf) { + ske->keymat = silc_ske_process_key_material_data(pfsbuf, x_len, + block_len, key_len, + hash_len, hash); + memset(pfsbuf, 0, x_len); + silc_free(pfsbuf); + } + } else { + /* No PFS */ + ske->keymat = + silc_ske_process_key_material_data(ske->rekey->send_enc_key, + ske->rekey->enc_key_len / 8, + block_len, key_len, + hash_len, hash); + } + + if (!ske->keymat) { + SILC_LOG_ERROR(("Error processing key material")); + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + + ske->prop->cipher = send_key; + ske->prop->hmac = hmac_send; + + /* Get sending keys */ + if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, &send_key, NULL, + &hmac_send, NULL, NULL)) { + /** Cannot get keys */ + ske->status = SILC_SKE_STATUS_ERROR; + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + + /* Set the new keys into use. This will also send REKEY_DONE packet. Any + packet sent after this call will be protected with the new keys. */ + if (!silc_packet_set_keys(ske->stream, send_key, NULL, hmac_send, NULL, + TRUE)) { + /** Cannot set keys */ + SILC_LOG_DEBUG(("Cannot set new keys, error sending REKEY_DONE")); + ske->status = SILC_SKE_STATUS_ERROR; + silc_cipher_free(send_key); + silc_hmac_free(hmac_send); + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + + /** Wait for REKEY_DONE */ + silc_fsm_next(fsm, silc_ske_st_rekey_initiator_end); + return SILC_FSM_WAIT; +} + +/* Rekey protocol end */ + +SILC_FSM_STATE(silc_ske_st_rekey_initiator_end) +{ + SilcSKE ske = fsm_context; + SilcCipher receive_key; + SilcHmac hmac_receive; + SilcSKERekeyMaterial rekey; + + SILC_LOG_DEBUG(("Start")); + + if (ske->packet->type != SILC_PACKET_REKEY_DONE) { + SILC_LOG_DEBUG(("Remote retransmitted an old packet")); + silc_packet_free(ske->packet); + ske->packet = NULL; + return SILC_FSM_WAIT; + } + + silc_packet_get_keys(ske->stream, NULL, &receive_key, NULL, &hmac_receive); + ske->prop->cipher = receive_key; + ske->prop->hmac = hmac_receive; + + /* Get receiving keys */ + if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, NULL, &receive_key, + NULL, &hmac_receive, NULL)) { + /** Cannot get keys */ + ske->status = SILC_SKE_STATUS_ERROR; + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + + /* Set new receiving keys into use. All packets received after this will + be decrypted with the new keys. */ + if (!silc_packet_set_keys(ske->stream, NULL, receive_key, NULL, + hmac_receive, FALSE)) { + /** Cannot set keys */ + SILC_LOG_DEBUG(("Cannot set new keys")); + ske->status = SILC_SKE_STATUS_ERROR; + silc_cipher_free(receive_key); + silc_hmac_free(hmac_receive); + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + + SILC_LOG_DEBUG(("Rekey completed successfully")); + + /* Generate new rekey material */ + rekey = silc_ske_make_rekey_material(ske, ske->keymat); + if (!rekey) { + /** No memory */ + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; + } + rekey->pfs = ske->rekey->pfs; + ske->rekey = rekey; + + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + silc_packet_free(ske->packet); + ske->packet = NULL; + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); + + /* Call completion */ + silc_ske_completion(ske); + return SILC_FSM_FINISH; } @@ -2223,19 +2666,31 @@ silc_ske_rekey_initiator(SilcSKE ske, { SILC_LOG_DEBUG(("Start SKE rekey as initator")); - if (!ske || !stream || !rekey) + if (!ske || !stream || !rekey) { + SILC_LOG_ERROR(("Missing arguments to silc_ske_rekey_initiator")); + SILC_ASSERT(rekey); return NULL; + } if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske)) return NULL; - if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule)) + if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule)) return NULL; ske->rekey = rekey; + ske->responder = FALSE; + ske->running = TRUE; + ske->rekeying = TRUE; /* 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_REKEY, + SILC_PACKET_REKEY_DONE, + SILC_PACKET_KEY_EXCHANGE_2, + SILC_PACKET_SUCCESS, + SILC_PACKET_FAILURE, -1); /* Start SKE rekey as initiator */ silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start); @@ -2243,10 +2698,241 @@ silc_ske_rekey_initiator(SilcSKE ske, return &ske->op; } -SILC_FSM_STATE(silc_ske_st_rekey_responder_start); +/***************************** Responder Rekey ******************************/ + +/* Wait for initiator's packet */ + +SILC_FSM_STATE(silc_ske_st_rekey_responder_wait) +{ + SilcSKE ske = fsm_context; + + SILC_LOG_DEBUG(("Start rekey (%s)", ske->rekey->pfs ? "PFS" : "No PFS")); + + if (ske->aborted) { + /** Aborted */ + silc_fsm_next(fsm, silc_ske_st_responder_aborted); + return SILC_FSM_CONTINUE; + } + + /* Add rekey exchange timeout */ + silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout, + ske, 30, 0); + + silc_fsm_next(fsm, silc_ske_st_rekey_responder_start); + + /* If REKEY packet already received process it directly */ + if (ske->packet && ske->packet->type == SILC_PACKET_REKEY) + return SILC_FSM_CONTINUE; + + /* Wait for REKEY */ + return SILC_FSM_WAIT; +} + +/* Process initiator's REKEY packet */ SILC_FSM_STATE(silc_ske_st_rekey_responder_start) { + SilcSKE ske = fsm_context; + SilcSKEStatus status; + + SILC_LOG_DEBUG(("Start")); + + if (ske->packet->type != SILC_PACKET_REKEY) { + ske->status = SILC_SKE_STATUS_ERROR; + silc_packet_free(ske->packet); + ske->packet = NULL; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + ske->prop = silc_calloc(1, sizeof(*ske->prop)); + if (!ske->prop) { + /** No memory */ + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + if (!silc_hash_alloc(ske->rekey->hash, &ske->prop->hash)) { + /** Cannot allocate hash */ + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + /* If doing rekey without PFS, move directly to the end of the protocol. */ + if (!ske->rekey->pfs) { + /** Rekey without PFS */ + silc_fsm_next(fsm, silc_ske_st_rekey_responder_done); + return SILC_FSM_CONTINUE; + } + + status = silc_ske_group_get_by_number(ske->rekey->ske_group, + &ske->prop->group); + if (status != SILC_SKE_STATUS_OK) { + /** Unknown group */ + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + /** Rekey with PFS */ + silc_fsm_next(fsm, silc_ske_st_responder_phase2); + return SILC_FSM_WAIT; +} + +/* Sends REKEY_DONE packet to finish the protocol. */ + +SILC_FSM_STATE(silc_ske_st_rekey_responder_done) +{ + SilcSKE ske = fsm_context; + SilcCipher send_key; + SilcHmac hmac_send; + SilcHash hash; + SilcUInt32 key_len, block_len, hash_len, x_len; + unsigned char *pfsbuf; + + SILC_LOG_DEBUG(("Start")); + + silc_packet_get_keys(ske->stream, &send_key, NULL, &hmac_send, NULL); + key_len = silc_cipher_get_key_len(send_key); + block_len = silc_cipher_get_block_len(send_key); + hash = ske->prop->hash; + hash_len = silc_hash_len(hash); + + /* Process key material */ + if (ske->rekey->pfs) { + /* PFS */ + pfsbuf = silc_mp_mp2bin(ske->KEY, 0, &x_len); + if (pfsbuf) { + ske->keymat = silc_ske_process_key_material_data(pfsbuf, x_len, + block_len, key_len, + hash_len, hash); + memset(pfsbuf, 0, x_len); + silc_free(pfsbuf); + } + } else { + /* No PFS */ + ske->keymat = + silc_ske_process_key_material_data(ske->rekey->send_enc_key, + ske->rekey->enc_key_len / 8, + block_len, key_len, + hash_len, hash); + } + + if (!ske->keymat) { + SILC_LOG_ERROR(("Error processing key material")); + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + ske->prop->cipher = send_key; + ske->prop->hmac = hmac_send; + + /* Get sending keys */ + if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, &send_key, NULL, + &hmac_send, NULL, NULL)) { + /** Cannot get keys */ + ske->status = SILC_SKE_STATUS_ERROR; + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + + /* Set the new keys into use. This will also send REKEY_DONE packet. Any + packet sent after this call will be protected with the new keys. */ + if (!silc_packet_set_keys(ske->stream, send_key, NULL, hmac_send, NULL, + TRUE)) { + /** Cannot set keys */ + SILC_LOG_DEBUG(("Cannot set new keys, error sending REKEY_DONE")); + ske->status = SILC_SKE_STATUS_ERROR; + silc_cipher_free(send_key); + silc_hmac_free(hmac_send); + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + /** Wait for REKEY_DONE */ + silc_fsm_next(fsm, silc_ske_st_rekey_responder_end); + return SILC_FSM_WAIT; +} + +/* Rekey protocol end */ + +SILC_FSM_STATE(silc_ske_st_rekey_responder_end) +{ + SilcSKE ske = fsm_context; + SilcCipher receive_key; + SilcHmac hmac_receive; + SilcSKERekeyMaterial rekey; + + SILC_LOG_DEBUG(("Start")); + + if (ske->packet->type != SILC_PACKET_REKEY_DONE) { + SILC_LOG_DEBUG(("Remote retransmitted an old packet")); + silc_packet_free(ske->packet); + ske->packet = NULL; + return SILC_FSM_WAIT; + } + + silc_packet_get_keys(ske->stream, NULL, &receive_key, NULL, &hmac_receive); + ske->prop->cipher = receive_key; + ske->prop->hmac = hmac_receive; + + /* Get receiving keys */ + if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, NULL, &receive_key, + NULL, &hmac_receive, NULL)) { + /** Cannot get keys */ + ske->status = SILC_SKE_STATUS_ERROR; + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + /* Set new receiving keys into use. All packets received after this will + be decrypted with the new keys. */ + if (!silc_packet_set_keys(ske->stream, NULL, receive_key, NULL, + hmac_receive, FALSE)) { + /** Cannot set keys */ + SILC_LOG_DEBUG(("Cannot set new keys")); + ske->status = SILC_SKE_STATUS_ERROR; + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + silc_cipher_free(receive_key); + silc_hmac_free(hmac_receive); + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + + SILC_LOG_DEBUG(("Rekey completed successfully")); + + /* Generate new rekey material */ + rekey = silc_ske_make_rekey_material(ske, ske->keymat); + if (!rekey) { + /** No memory */ + ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + silc_fsm_next(fsm, silc_ske_st_responder_error); + return SILC_FSM_CONTINUE; + } + rekey->pfs = ske->rekey->pfs; + ske->rekey = rekey; + + ske->prop->cipher = NULL; + ske->prop->hmac = NULL; + silc_packet_free(ske->packet); + ske->packet = NULL; + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); + silc_schedule_task_del_by_context(ske->schedule, ske); + + /* Call completion */ + silc_ske_completion(ske); + return SILC_FSM_FINISH; } @@ -2255,30 +2941,37 @@ SILC_FSM_STATE(silc_ske_st_rekey_responder_start) SilcAsyncOperation silc_ske_rekey_responder(SilcSKE ske, SilcPacketStream stream, - SilcBuffer ke_payload, - SilcSKERekeyMaterial rekey) + SilcSKERekeyMaterial rekey, + SilcPacket packet) { SILC_LOG_DEBUG(("Start SKE rekey as responder")); if (!ske || !stream || !rekey) return NULL; - if (rekey->pfs && !ke_payload) - return NULL; if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske)) return NULL; - if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule)) + if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule)) return NULL; - // ske->packet_buf = ke_payload; ske->rekey = rekey; + ske->responder = TRUE; + ske->running = TRUE; + ske->rekeying = TRUE; + ske->packet = packet; /* 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_REKEY, + SILC_PACKET_REKEY_DONE, + SILC_PACKET_KEY_EXCHANGE_1, + SILC_PACKET_SUCCESS, + SILC_PACKET_FAILURE, -1); /* Start SKE rekey as responder */ - silc_fsm_start(&ske->fsm, silc_ske_st_rekey_responder_start); + silc_fsm_start_sync(&ske->fsm, silc_ske_st_rekey_responder_wait); return &ske->op; } @@ -2314,7 +3007,7 @@ silc_ske_process_key_material_data(unsigned char *data, return NULL; silc_buffer_format(buf, SILC_STR_UI_CHAR(0), - SILC_STR_UI_XNSTRING(data, data_len), + SILC_STR_DATA(data, data_len), SILC_STR_END); /* Take IVs */ @@ -2353,8 +3046,8 @@ silc_ske_process_key_material_data(unsigned char *data, if (!dist) return NULL; silc_buffer_format(dist, - SILC_STR_UI_XNSTRING(data, data_len), - SILC_STR_UI_XNSTRING(k1, hash_len), + SILC_STR_DATA(data, data_len), + SILC_STR_DATA(k1, hash_len), SILC_STR_END); memset(k2, 0, sizeof(k2)); silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2); @@ -2364,7 +3057,7 @@ silc_ske_process_key_material_data(unsigned char *data, silc_buffer_pull_tail(dist, hash_len); silc_buffer_pull(dist, data_len + hash_len); silc_buffer_format(dist, - SILC_STR_UI_XNSTRING(k2, hash_len), + SILC_STR_DATA(k2, hash_len), SILC_STR_END); silc_buffer_push(dist, data_len + hash_len); memset(k3, 0, sizeof(k3)); @@ -2416,8 +3109,8 @@ silc_ske_process_key_material_data(unsigned char *data, if (!dist) return NULL; silc_buffer_format(dist, - SILC_STR_UI_XNSTRING(data, data_len), - SILC_STR_UI_XNSTRING(k1, hash_len), + SILC_STR_DATA(data, data_len), + SILC_STR_DATA(k1, hash_len), SILC_STR_END); memset(k2, 0, sizeof(k2)); silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2); @@ -2427,7 +3120,7 @@ silc_ske_process_key_material_data(unsigned char *data, silc_buffer_pull_tail(dist, hash_len); silc_buffer_pull(dist, data_len + hash_len); silc_buffer_format(dist, - SILC_STR_UI_XNSTRING(k2, hash_len), + SILC_STR_DATA(k2, hash_len), SILC_STR_END); silc_buffer_push(dist, data_len + hash_len); memset(k3, 0, sizeof(k3)); @@ -2476,6 +3169,8 @@ silc_ske_process_key_material_data(unsigned char *data, silc_buffer_clear(buf); silc_buffer_free(buf); + SILC_LOG_HEXDUMP(("enc"), key->send_enc_key, key->enc_key_len / 8); + return key; } @@ -2486,7 +3181,8 @@ SilcSKEKeyMaterial silc_ske_process_key_material(SilcSKE ske, SilcUInt32 req_iv_len, SilcUInt32 req_enc_key_len, - SilcUInt32 req_hmac_key_len) + SilcUInt32 req_hmac_key_len, + SilcSKERekeyMaterial *rekey) { SilcBuffer buf; unsigned char *tmpbuf; @@ -2500,8 +3196,8 @@ silc_ske_process_key_material(SilcSKE ske, if (!buf) return NULL; silc_buffer_format(buf, - SILC_STR_UI_XNSTRING(tmpbuf, klen), - SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len), + SILC_STR_DATA(tmpbuf, klen), + SILC_STR_DATA(ske->hash, ske->hash_len), SILC_STR_END); /* Process the key material */ @@ -2515,6 +3211,12 @@ silc_ske_process_key_material(SilcSKE ske, silc_buffer_clear(buf); silc_buffer_free(buf); + if (rekey) { + *rekey = silc_ske_make_rekey_material(ske, key); + if (!(*rekey)) + return NULL; + } + return key; } @@ -2548,6 +3250,20 @@ void silc_ske_free_key_material(SilcSKEKeyMaterial key) silc_free(key); } +/* Free rekey material */ + +void silc_ske_free_rekey_material(SilcSKERekeyMaterial rekey) +{ + if (!rekey) + return; + if (rekey->send_enc_key) { + memset(rekey->send_enc_key, 0, rekey->enc_key_len / 8); + silc_free(rekey->send_enc_key); + } + silc_free(rekey->hash); + silc_free(rekey); +} + /* Set keys into use */ SilcBool silc_ske_set_keys(SilcSKE ske, @@ -2559,6 +3275,9 @@ SilcBool silc_ske_set_keys(SilcSKE ske, SilcHmac *ret_hmac_receive, SilcHash *ret_hash) { + unsigned char iv[SILC_HASH_MAXLEN]; + SilcBool iv_included = (prop->flags & SILC_SKE_SP_FLAG_IV_INCLUDED); + /* Allocate ciphers to be used in the communication */ if (ret_send_key) { if (!silc_cipher_alloc((char *)silc_cipher_get_name(prop->cipher), @@ -2583,42 +3302,134 @@ SilcBool silc_ske_set_keys(SilcSKE ske, 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" : "")); + /* Set key material */ + memset(iv, 0, sizeof(iv)); + if (ske->responder) { + if (ret_send_key) { + silc_cipher_set_key(*ret_send_key, keymat->receive_enc_key, + keymat->enc_key_len, TRUE); + + if (silc_cipher_get_mode(*ret_send_key) == SILC_CIPHER_MODE_CTR) { + /* Counter mode */ + if (!ske->rekeying) { + /* Set IV. */ + memcpy(iv, ske->hash, 4); + if (!iv_included) + memcpy(iv + 4, keymat->receive_iv, 8); + } else { + /* Rekey, recompute the truncated hash value. */ + silc_hash_make(prop->hash, keymat->receive_iv, 8, iv); + if (!iv_included) + memcpy(iv + 4, keymat->receive_iv, 8); + else + memset(iv + 4, 0, 12); + } + + silc_cipher_set_iv(*ret_send_key, iv); + } else { + /* Other modes */ + silc_cipher_set_iv(*ret_send_key, keymat->receive_iv); + } + } + if (ret_receive_key) { + silc_cipher_set_key(*ret_receive_key, keymat->send_enc_key, + keymat->enc_key_len, FALSE); + + if (silc_cipher_get_mode(*ret_receive_key) == SILC_CIPHER_MODE_CTR) { + /* Counter mode */ + if (!ske->rekeying) { + /* Set IV. */ + memcpy(iv, ske->hash, 4); + if (!iv_included) + memcpy(iv + 4, keymat->send_iv, 8); + } else { + /* Rekey, recompute the truncated hash value. */ + silc_hash_make(prop->hash, keymat->send_iv, 8, iv); + if (!iv_included) + memcpy(iv + 4, keymat->send_iv, 8); + else + memset(iv + 4, 0, 12); + } + + silc_cipher_set_iv(*ret_receive_key, iv); + } else { + /* Other modes */ + silc_cipher_set_iv(*ret_receive_key, keymat->send_iv); + } + } + if (ret_hmac_send) + silc_hmac_set_key(*ret_hmac_send, keymat->receive_hmac_key, + keymat->hmac_key_len); + if (ret_hmac_receive) + silc_hmac_set_key(*ret_hmac_receive, keymat->send_hmac_key, + keymat->hmac_key_len); + } else { + if (ret_send_key) { + silc_cipher_set_key(*ret_send_key, keymat->send_enc_key, + keymat->enc_key_len, TRUE); + + if (silc_cipher_get_mode(*ret_send_key) == SILC_CIPHER_MODE_CTR) { + /* Counter mode */ + if (!ske->rekeying) { + /* Set IV. */ + memcpy(iv, ske->hash, 4); + if (!iv_included) + memcpy(iv + 4, keymat->send_iv, 8); + } else { + /* Rekey, recompute the truncated hash value. */ + silc_hash_make(prop->hash, keymat->send_iv, 8, iv); + if (!iv_included) + memcpy(iv + 4, keymat->send_iv, 8); + else + memset(iv + 4, 0, 12); + } + + silc_cipher_set_iv(*ret_send_key, iv); + } else { + /* Other modes */ + silc_cipher_set_iv(*ret_send_key, keymat->send_iv); + } + } + if (ret_receive_key) { + silc_cipher_set_key(*ret_receive_key, keymat->receive_enc_key, + keymat->enc_key_len, FALSE); + + if (silc_cipher_get_mode(*ret_receive_key) == SILC_CIPHER_MODE_CTR) { + /* Counter mode */ + if (!ske->rekeying) { + /* Set IV. If IV Included flag was negotiated we only set the + truncated hash value. */ + memcpy(iv, ske->hash, 4); + if (!iv_included) + memcpy(iv + 4, keymat->receive_iv, 8); + } else { + /* Rekey, recompute the truncated hash value. */ + silc_hash_make(prop->hash, keymat->receive_iv, 8, iv); + if (!iv_included) + memcpy(iv + 4, keymat->receive_iv, 8); + else + memset(iv + 4, 0, 12); + } + + silc_cipher_set_iv(*ret_receive_key, iv); + } else { + /* Other modes */ + silc_cipher_set_iv(*ret_receive_key, keymat->receive_iv); + } + } + if (ret_hmac_send) + silc_hmac_set_key(*ret_hmac_send, keymat->send_hmac_key, + keymat->hmac_key_len); + if (ret_hmac_receive) + silc_hmac_set_key(*ret_hmac_receive, keymat->receive_hmac_key, + keymat->hmac_key_len); + } return TRUE; } @@ -2627,7 +3438,7 @@ const char *silc_ske_status_string[] = { /* Official */ "Ok", - "Unkown error occurred", + "Unexpected error occurred", "Bad payload in packet", "Unsupported group", "Unsupported cipher", @@ -2645,6 +3456,7 @@ const char *silc_ske_status_string[] = "Bad payload length in packet", "Error computing signature", "System out of memory", + "Key exchange timeout", NULL }; @@ -2686,3 +3498,10 @@ SilcSKESecurityProperties silc_ske_get_security_properties(SilcSKE ske) { return ske->prop; } + +/* Get key material */ + +SilcSKEKeyMaterial silc_ske_get_key_material(SilcSKE ske) +{ + return ske->keymat; +}