X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcske%2Fsilcske.c;h=fece34b67a8ee872a0e4cf792f6153a0f7311afe;hp=d49d9ccb040a412e82d7abbdc6b96198bf6ae791;hb=805fddcf6431e784f9f77114782a90c9d12f9cbe;hpb=aa427d8e98d1b24952f869062536ca6c49670479 diff --git a/lib/silcske/silcske.c b/lib/silcske/silcske.c index d49d9ccb..fece34b6 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 @@ -37,8 +37,10 @@ struct SilcSKECallbacksStruct { 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_phase2_send); SILC_FSM_STATE(silc_ske_st_initiator_phase3); SILC_FSM_STATE(silc_ske_st_initiator_phase4); +SILC_FSM_STATE(silc_ske_st_initiator_phase5); SILC_FSM_STATE(silc_ske_st_initiator_end); SILC_FSM_STATE(silc_ske_st_initiator_aborted); SILC_FSM_STATE(silc_ske_st_initiator_error); @@ -48,6 +50,7 @@ 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_phase5_send); SILC_FSM_STATE(silc_ske_st_responder_end); SILC_FSM_STATE(silc_ske_st_responder_aborted); SILC_FSM_STATE(silc_ske_st_responder_failure); @@ -55,6 +58,11 @@ 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, @@ -81,7 +89,8 @@ static SilcBool silc_ske_packet_receive(SilcPacketEngine engine, /* Clear retransmission */ ske->retry_timer = SILC_SKE_RETRY_MIN; ske->retry_count = 0; - silc_schedule_task_del_by_context(ske->schedule, ske); + silc_schedule_task_del_by_callback(ske->schedule, + silc_ske_packet_send_retry); /* Signal for new packet */ ske->packet = packet; @@ -108,8 +117,10 @@ static SilcBool silc_ske_packet_receive(SilcPacketEngine engine, silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure); } - /* Handle rekey synchronously */ - if (ske->rekeying) + /* 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); @@ -128,6 +139,8 @@ static SilcPacketCallbacks silc_ske_stream_cbs = static void silc_ske_abort(SilcAsyncOperation op, void *context) { SilcSKE ske = context; + if (ske->key_op) + silc_async_abort(ske->key_op, NULL, NULL); ske->aborted = TRUE; } @@ -174,36 +187,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)) + if (!silc_parse_version_string(ske->remote_version, NULL, NULL, + &r_software_version, + &r_software_string, NULL)) return SILC_SKE_STATUS_BAD_VERSION; - /* If remote is too new, don't connect */ - if (l_protocol_version < r_protocol_version) - 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; } @@ -639,7 +633,7 @@ static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n, SilcMPInt *rnd) { SilcSKEStatus status = SILC_SKE_STATUS_OK; - unsigned char *string; + unsigned char string[2048]; SilcUInt32 l; if (!len) @@ -650,8 +644,7 @@ static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n, l = ((len - 1) / 8); /* Get the random number as string */ - string = silc_rng_get_rn_data(ske->rng, l); - if (!string) + if (!silc_rng_get_rn_data(ske->rng, l, string, sizeof(string))) return SILC_SKE_STATUS_OUT_OF_MEMORY; /* Decode the string into a MP integer */ @@ -665,7 +658,6 @@ static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n, status = SILC_SKE_STATUS_ERROR; memset(string, 'F', l); - silc_free(string); return status; } @@ -682,19 +674,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); @@ -705,28 +701,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) { @@ -747,21 +739,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); @@ -869,7 +863,7 @@ silc_ske_assemble_security_properties(SilcSKE ske, rp->pkcs_alg_len = strlen(rp->pkcs_alg_list); /* Get supported encryption algorithms */ - rp->enc_alg_list = silc_cipher_get_supported(); + rp->enc_alg_list = silc_cipher_get_supported(TRUE); rp->enc_alg_len = strlen(rp->enc_alg_list); /* Get supported hash algorithms */ @@ -964,15 +958,11 @@ static SilcBool silc_ske_packet_send(SilcSKE ske, return ret; } -/* SKE FSM destructor. We call completion callback here. All SKE - machines go here and call the completion. Completion must not be called - from any other place. */ +/* Calls completion callback. Completion is called always in this function + and must not be called anywhere else. */ -static void silc_ske_finished(SilcFSM fsm, void *fsm_context, - void *destructor_context) +static void silc_ske_completion(SilcSKE ske) { - SilcSKE ske = fsm_context; - /* Call the completion callback */ if (!ske->freed && !ske->aborted && ske->callbacks->completed) { if (ske->status != SILC_SKE_STATUS_OK) @@ -982,12 +972,117 @@ static void silc_ske_finished(SilcFSM fsm, void *fsm_context, 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); +} + +/* Initiator signature callback */ + +static void silc_ske_initiator_sign_cb(SilcBool success, + const unsigned char *signature, + SilcUInt32 signature_len, + void *context) +{ + SilcSKE ske = context; + + ske->key_op = NULL; + + if (ske->aborted) { + silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure); + SILC_FSM_CALL_CONTINUE(&ske->fsm); + return; + } + + ske->ke1_payload->sign_data = silc_memdup(signature, signature_len); + if (ske->ke1_payload->sign_data) + ske->ke1_payload->sign_len = signature_len; + + SILC_FSM_CALL_CONTINUE(&ske->fsm); +} + +/* Responder signature callback */ + +static void silc_ske_responder_sign_cb(SilcBool success, + const unsigned char *signature, + SilcUInt32 signature_len, + void *context) +{ + SilcSKE ske = context; + + ske->key_op = NULL; + + if (ske->aborted) { + silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure); + SILC_FSM_CALL_CONTINUE(&ske->fsm); + return; + } + + ske->ke2_payload->sign_data = silc_memdup(signature, signature_len); + if (ske->ke2_payload->sign_data) + ske->ke2_payload->sign_len = signature_len; + + SILC_FSM_CALL_CONTINUE(&ske->fsm); +} + +/* Verify callback */ + +static void silc_ske_verify_cb(SilcBool success, void *context) +{ + SilcSKE ske = context; + + ske->key_op = NULL; + + if (ske->aborted) { + 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_CALL_CONTINUE(&ske->fsm); + return; + } + + if (!success) { + SILC_LOG_ERROR(("Signature verification failed, incorrect signature")); + ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE; + if (ske->responder) + silc_fsm_next(&ske->fsm, silc_ske_st_responder_error); + else + silc_fsm_next(&ske->fsm, silc_ske_st_initiator_error); + SILC_FSM_CALL_CONTINUE(&ske->fsm); + return; + } + + SILC_LOG_DEBUG(("Signature is Ok")); + SILC_FSM_CALL_CONTINUE(&ske->fsm); +} + /******************************* Protocol API *******************************/ /* Allocates new SKE object. */ @@ -1019,6 +1114,7 @@ SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, ske->public_key = public_key; ske->private_key = private_key; ske->retry_timer = SILC_SKE_RETRY_MIN; + ske->refcnt = 1; return ske; } @@ -1034,9 +1130,24 @@ void silc_ske_free(SilcSKE 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); @@ -1058,6 +1169,8 @@ void silc_ske_free(SilcSKE ske) 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) @@ -1120,7 +1233,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_start) if (ske->aborted) { /** Aborted */ silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Encode the payload */ @@ -1130,7 +1243,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_start) /** Error encoding Start Payload */ ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Save the the payload buffer for future use. It is later used to @@ -1145,15 +1258,17 @@ SILC_FSM_STATE(silc_ske_st_initiator_start) SILC_LOG_DEBUG(("Error sending packet")); ske->status = SILC_SKE_STATUS_ERROR; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + 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); - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } /* Phase-1. Receives responder's proposal */ @@ -1177,7 +1292,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) silc_ske_install_retransmission(ske); silc_packet_free(ske->packet); ske->packet = NULL; - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } /* Decode the payload */ @@ -1188,7 +1303,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) ske->packet = NULL; ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Get remote ID and set it to stream */ @@ -1221,7 +1336,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) SILC_LOG_ERROR(("Invalid cookie, modified or unsupported feature")); ske->status = SILC_SKE_STATUS_INVALID_COOKIE; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Check version string */ @@ -1231,7 +1346,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) /** Version mismatch */ ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Free our KE Start Payload context, we don't need it anymore. */ @@ -1274,7 +1389,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) /** Send KE Payload */ silc_fsm_next(fsm, silc_ske_st_initiator_phase2); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; err: if (payload) @@ -1296,7 +1411,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1) /** Error */ ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Phase-2. Send KE payload */ @@ -1305,7 +1420,6 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) { SilcSKE ske = fsm_context; SilcSKEStatus status; - SilcBuffer payload_buf; SilcMPInt *x; SilcSKEKEPayload payload; SilcUInt32 pk_len; @@ -1318,7 +1432,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) /** Out of memory */ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } silc_mp_init(x); status = @@ -1331,7 +1445,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) silc_free(x); ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Encode the result to Key Exchange Payload. */ @@ -1343,7 +1457,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) silc_free(x); ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } ske->ke1_payload = payload; @@ -1353,27 +1467,33 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) silc_mp_init(&payload->x); silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x, &ske->prop->group->group); + ske->x = x; /* Get public key */ - payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len); + payload->pk_data = silc_pkcs_public_key_encode(NULL, ske->public_key, + &pk_len); if (!payload->pk_data) { /** Error encoding public key */ silc_mp_uninit(x); silc_free(x); + ske->x = NULL; 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); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } payload->pk_len = pk_len; payload->pk_type = silc_pkcs_get_type(ske->public_key); + /** Send KE1 packet */ + silc_fsm_next(fsm, silc_ske_st_initiator_phase2_send); + /* Compute signature data if we are doing mutual authentication */ 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; + unsigned char hash[SILC_HASH_MAXLEN]; + SilcUInt32 hash_len; SILC_LOG_DEBUG(("We are doing mutual authentication")); SILC_LOG_DEBUG(("Computing HASH_i value")); @@ -1385,30 +1505,32 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) SILC_LOG_DEBUG(("Signing HASH_i value")); /* Sign the hash value */ - if (!silc_pkcs_sign(ske->private_key, hash, hash_len, sign, - sizeof(sign) - 1, &sign_len, NULL)) { - /** 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); - SILC_FSM_CONTINUE; - } - payload->sign_data = silc_memdup(sign, sign_len); - if (payload->sign_data) - payload->sign_len = sign_len; - memset(sign, 0, sizeof(sign)); + SILC_FSM_CALL(ske->key_op = + silc_pkcs_sign_async(ske->private_key, hash, hash_len, FALSE, + ske->prop->hash, ske->rng, + silc_ske_initiator_sign_cb, ske)); + /* NOT REACHED */ } + return SILC_FSM_CONTINUE; +} + +/* Send KE1 packet */ + +SILC_FSM_STATE(silc_ske_st_initiator_phase2_send) +{ + SilcSKE ske = fsm_context; + SilcSKEStatus status; + SilcBuffer payload_buf; + SilcSKEKEPayload payload; + + SILC_LOG_DEBUG(("Start")); + + payload = ske->ke1_payload; + 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); @@ -1416,13 +1538,9 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) ske->ke1_payload = NULL; ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } - ske->x = x; - - /* Check for backwards compatibility */ - /* Send the packet. */ if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE_1, 0, silc_buffer_data(payload_buf), @@ -1431,14 +1549,14 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2) SILC_LOG_DEBUG(("Error sending packet")); ske->status = SILC_SKE_STATUS_ERROR; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } silc_buffer_free(payload_buf); /** Waiting responder's KE payload */ silc_fsm_next(fsm, silc_ske_st_initiator_phase3); - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } /* Phase-3. Process responder's KE payload */ @@ -1458,7 +1576,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3) silc_ske_install_retransmission(ske); silc_packet_free(ske->packet); ske->packet = NULL; - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } /* Decode the payload */ @@ -1469,7 +1587,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3) ske->packet = NULL; ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } silc_packet_free(ske->packet); ske->packet = NULL; @@ -1522,8 +1640,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, @@ -1535,7 +1653,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3) /** Process key material */ silc_fsm_next(fsm, silc_ske_st_initiator_phase4); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; err: silc_ske_payload_ke_free(payload); @@ -1551,7 +1669,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3) /** Error */ ske->status = status; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Process key material */ @@ -1563,12 +1681,11 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4) SilcSKEKEPayload payload; unsigned char hash[SILC_HASH_MAXLEN]; SilcUInt32 hash_len; - int key_len, block_len; if (ske->aborted) { /** Aborted */ silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Check result of public key verification */ @@ -1576,43 +1693,76 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4) /** Public key not verified */ SILC_LOG_DEBUG(("Public key verification failed")); silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } payload = ske->ke2_payload; - if (ske->prop->public_key) { - SILC_LOG_DEBUG(("Public key is authentic")); + /* 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; - /* Compute the hash value */ - status = silc_ske_make_hash(ske, hash, &hash_len, FALSE); - if (status != SILC_SKE_STATUS_OK) - goto err; + /** Send reply */ + silc_fsm_next(fsm, silc_ske_st_initiator_phase5); + if (ske->prop->public_key) { + SILC_LOG_DEBUG(("Public key is authentic")); SILC_LOG_DEBUG(("Verifying signature (HASH)")); /* Verify signature */ - if (!silc_pkcs_verify(ske->prop->public_key, payload->sign_data, - payload->sign_len, hash, hash_len, NULL)) { - SILC_LOG_ERROR(("Signature verification failed, incorrect signature")); - status = SILC_SKE_STATUS_INCORRECT_SIGNATURE; - goto err; - } + SILC_FSM_CALL(ske->key_op = + silc_pkcs_verify_async(ske->prop->public_key, + payload->sign_data, + payload->sign_len, hash, + hash_len, FALSE, NULL, + silc_ske_verify_cb, ske)); + /* NOT REACHED */ + } + err: + memset(hash, 'F', sizeof(hash)); + silc_ske_payload_ke_free(payload); + ske->ke2_payload = NULL; - SILC_LOG_DEBUG(("Signature is Ok")); + silc_mp_uninit(ske->KEY); + silc_free(ske->KEY); + ske->KEY = NULL; - ske->hash = silc_memdup(hash, hash_len); - ske->hash_len = hash_len; - memset(hash, 'F', hash_len); + if (ske->hash) { + memset(ske->hash, 'F', hash_len); + silc_free(ske->hash); + ske->hash = NULL; } + if (status == SILC_SKE_STATUS_OK) + status = SILC_SKE_STATUS_ERROR; + + /** Error */ + ske->status = status; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; +} + +/* Process key material */ + +SILC_FSM_STATE(silc_ske_st_initiator_phase5) +{ + SilcSKE ske = fsm_context; + SilcSKEStatus status; + unsigned char tmp[4]; + SilcUInt32 hash_len; + int key_len, block_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); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Process key material */ @@ -1625,45 +1775,23 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4) if (!ske->keymat) { SILC_LOG_ERROR(("Error processing key material")); status = SILC_SKE_STATUS_ERROR; - goto err; + silc_fsm_next(fsm, silc_ske_st_initiator_error); + return SILC_FSM_CONTINUE; } /* Send SUCCESS packet */ - SILC_PUT32_MSB((SilcUInt32)SILC_SKE_STATUS_OK, hash); - if (!silc_ske_packet_send(ske, SILC_PACKET_SUCCESS, 0, hash, 4)) { + SILC_PUT32_MSB((SilcUInt32)SILC_SKE_STATUS_OK, tmp); + if (!silc_ske_packet_send(ske, SILC_PACKET_SUCCESS, 0, tmp, 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); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /** Waiting completion */ silc_fsm_next(fsm, silc_ske_st_initiator_end); - SILC_FSM_WAIT; - - err: - memset(hash, 'F', sizeof(hash)); - silc_ske_payload_ke_free(payload); - ske->ke2_payload = NULL; - - silc_mp_uninit(ske->KEY); - silc_free(ske->KEY); - ske->KEY = NULL; - - if (ske->hash) { - memset(ske->hash, 'F', hash_len); - silc_free(ske->hash); - ske->hash = NULL; - } - - if (status == SILC_SKE_STATUS_OK) - status = SILC_SKE_STATUS_ERROR; - - /** Error */ - ske->status = status; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_WAIT; } /* Protocol completed */ @@ -1679,7 +1807,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_end) silc_ske_install_retransmission(ske); silc_packet_free(ske->packet); ske->packet = NULL; - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } SILC_LOG_DEBUG(("Key exchange completed successfully")); @@ -1689,7 +1817,10 @@ SILC_FSM_STATE(silc_ske_st_initiator_end) silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); silc_schedule_task_del_by_context(ske->schedule, ske); - SILC_FSM_FINISH; + /* Call completion */ + silc_ske_completion(ske); + + return SILC_FSM_FINISH; } /* Aborted by application */ @@ -1708,7 +1839,10 @@ SILC_FSM_STATE(silc_ske_st_initiator_aborted) silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); silc_schedule_task_del_by_context(ske->schedule, ske); - SILC_FSM_FINISH; + /* Call completion */ + silc_ske_completion(ske); + + return SILC_FSM_FINISH; } /* Error occurred. Send error to remote host */ @@ -1733,7 +1867,10 @@ SILC_FSM_STATE(silc_ske_st_initiator_error) silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); silc_schedule_task_del_by_context(ske->schedule, ske); - SILC_FSM_FINISH; + /* Call completion */ + silc_ske_completion(ske); + + return SILC_FSM_FINISH; } /* Failure received from remote */ @@ -1743,29 +1880,32 @@ SILC_FSM_STATE(silc_ske_st_initiator_failure) SilcSKE ske = fsm_context; SilcUInt32 error = SILC_SKE_STATUS_ERROR; - SILC_LOG_DEBUG(("Error %s (%d) received during key exchange", - silc_ske_map_status(ske->status), ske->status)); - - if (ske->packet && silc_buffer_len(&ske->packet->buffer) == 4) { + if (ske->packet && ske->packet->type == SILC_PACKET_FAILURE && + 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)); + silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); silc_schedule_task_del_by_context(ske->schedule, ske); - SILC_FSM_FINISH; + /* Call completion */ + silc_ske_completion(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")); @@ -1790,6 +1930,7 @@ 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; @@ -1821,15 +1962,16 @@ SILC_FSM_STATE(silc_ske_st_responder_start) if (ske->aborted) { /** Aborted */ silc_fsm_next(fsm, silc_ske_st_responder_aborted); - SILC_FSM_CONTINUE; + 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); - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } /* Decode initiator's start payload. Select the security properties from @@ -1841,6 +1983,7 @@ 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")); @@ -1852,7 +1995,20 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1) ske->packet = NULL; ske->status = status; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + 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 @@ -1889,7 +2045,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1) silc_ske_payload_start_free(remote_payload); ske->status = status; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } silc_ske_payload_start_free(remote_payload); @@ -1910,7 +2066,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1) /** Waiting initiator's KE payload */ silc_fsm_next(fsm, silc_ske_st_responder_phase2); - SILC_FSM_WAIT; + return SILC_FSM_WAIT; err: if (ske->prop->group) @@ -1930,7 +2086,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1) /** Error */ ske->status = status; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Phase-2. Decode initiator's KE payload */ @@ -1949,7 +2105,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2) silc_ske_install_retransmission(ske); silc_packet_free(ske->packet); ske->packet = NULL; - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } /* Decode Key Exchange Payload */ @@ -1960,7 +2116,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2) ske->packet = NULL; ske->status = status; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } ske->ke1_payload = recv_payload; @@ -1968,26 +2124,19 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2) 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")); ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* 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)) { @@ -1995,62 +2144,59 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2) 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); - SILC_FSM_CONTINUE; + 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); - 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 */ } } /** Generate KE2 payload */ silc_fsm_next(fsm, silc_ske_st_responder_phase4); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } -/* Phase-4. Generate KE2 payload */ +/* Phase-4. Generate KE2 payload, verify signature */ SILC_FSM_STATE(silc_ske_st_responder_phase4) { SilcSKE ske = fsm_context; SilcSKEStatus status; - SilcSKEKEPayload recv_payload, send_payload; - SilcMPInt *x, *KEY; + SilcSKEKEPayload recv_payload; if (ske->aborted) { /** Aborted */ silc_fsm_next(fsm, silc_ske_st_responder_aborted); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Check result of public key verification */ @@ -2058,11 +2204,14 @@ SILC_FSM_STATE(silc_ske_st_responder_phase4) /** Public key not verified */ SILC_LOG_DEBUG(("Public key verification failed")); silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } recv_payload = ske->ke1_payload; + /** Send KE2 packet */ + silc_fsm_next(fsm, silc_ske_st_responder_phase5); + /* The public key verification was performed only if the Mutual Authentication flag is set. */ if (ske->start_payload && @@ -2070,7 +2219,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); @@ -2078,25 +2227,36 @@ SILC_FSM_STATE(silc_ske_st_responder_phase4) /** Error computing hash */ ske->status = status; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } SILC_LOG_DEBUG(("Verifying signature (HASH_i)")); /* Verify signature */ - if (!silc_pkcs_verify(ske->prop->public_key, recv_payload->sign_data, - recv_payload->sign_len, hash, hash_len, NULL)) { - /** 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); - SILC_FSM_CONTINUE; - } + SILC_FSM_CALL(ske->key_op = + silc_pkcs_verify_async(ske->prop->public_key, + recv_payload->sign_data, + recv_payload->sign_len, + hash, hash_len, FALSE, NULL, + silc_ske_verify_cb, ske)); + /* NOT REACHED */ + } - SILC_LOG_DEBUG(("Signature is Ok")); + return SILC_FSM_CONTINUE; +} - memset(hash, 'F', hash_len); - } +/* Phase-5. Send KE2 payload */ + +SILC_FSM_STATE(silc_ske_st_responder_phase5) +{ + SilcSKE ske = fsm_context; + SilcSKEStatus status; + unsigned char hash[SILC_HASH_MAXLEN], *pk; + SilcUInt32 hash_len, pk_len; + SilcMPInt *x, *KEY; + SilcSKEKEPayload send_payload; + + SILC_LOG_DEBUG(("Start")); /* Create the random number x, 1 < x < q. */ x = silc_calloc(1, sizeof(*x)); @@ -2111,7 +2271,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase4) silc_free(x); ske->status = status; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Save the results for later processing */ @@ -2135,66 +2295,60 @@ SILC_FSM_STATE(silc_ske_st_responder_phase4) &ske->prop->group->group); ske->KEY = KEY; - /** Send KE2 payload */ - silc_fsm_next(fsm, silc_ske_st_responder_phase5); - SILC_FSM_CONTINUE; -} - -/* Phase-5. Send KE2 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); + pk = silc_pkcs_public_key_encode(NULL, 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); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } 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); - SILC_FSM_CONTINUE; - } + /* 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; - ske->hash = silc_memdup(hash, hash_len); - ske->hash_len = hash_len; + /** Send KE2 packet */ + silc_fsm_next(fsm, silc_ske_st_responder_phase5_send); + 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)) { - /** Error computing signature */ - status = SILC_SKE_STATUS_SIGNATURE_ERROR; - silc_fsm_next(fsm, silc_ske_st_responder_error); - 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)); + SILC_FSM_CALL(ske->key_op = + silc_pkcs_sign_async(ske->private_key, hash, hash_len, FALSE, + ske->prop->hash, ske->rng, + silc_ske_responder_sign_cb, ske)); + /* NOT REACHED */ } + + return SILC_FSM_CONTINUE; +} + +/* Send KE2 packet */ + +SILC_FSM_STATE(silc_ske_st_responder_phase5_send) +{ + SilcSKE ske = fsm_context; + SilcSKEStatus status; + SilcBuffer payload_buf; + ske->ke2_payload->pk_type = silc_pkcs_get_type(ske->public_key); /* Encode the Key Exchange Payload */ @@ -2204,7 +2358,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase5) /** Error encoding KE payload */ ske->status = status; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Send the packet. */ @@ -2213,14 +2367,21 @@ SILC_FSM_STATE(silc_ske_st_responder_phase5) SILC_LOG_DEBUG(("Error sending packet")); ske->status = SILC_SKE_STATUS_ERROR; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } 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); - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } /* Protocol completed */ @@ -2236,7 +2397,7 @@ SILC_FSM_STATE(silc_ske_st_responder_end) silc_ske_install_retransmission(ske); silc_packet_free(ske->packet); ske->packet = NULL; - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } silc_packet_free(ske->packet); ske->packet = NULL; @@ -2252,7 +2413,7 @@ SILC_FSM_STATE(silc_ske_st_responder_end) /** Error processing key material */ ske->status = SILC_SKE_STATUS_ERROR; silc_fsm_next(fsm, silc_ske_st_responder_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Send SUCCESS packet */ @@ -2262,7 +2423,10 @@ SILC_FSM_STATE(silc_ske_st_responder_end) silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); silc_schedule_task_del_by_context(ske->schedule, ske); - SILC_FSM_FINISH; + /* Call completion */ + silc_ske_completion(ske); + + return SILC_FSM_FINISH; } /* Aborted by application */ @@ -2281,7 +2445,10 @@ SILC_FSM_STATE(silc_ske_st_responder_aborted) silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); silc_schedule_task_del_by_context(ske->schedule, ske); - SILC_FSM_FINISH; + /* Call completion */ + silc_ske_completion(ske); + + return SILC_FSM_FINISH; } /* Failure received from remote */ @@ -2293,7 +2460,8 @@ SILC_FSM_STATE(silc_ske_st_responder_failure) SILC_LOG_DEBUG(("Key exchange protocol failed")); - if (ske->packet && silc_buffer_len(&ske->packet->buffer) == 4) { + if (ske->packet && ske->packet->type == SILC_PACKET_FAILURE && + silc_buffer_len(&ske->packet->buffer) == 4) { SILC_GET32_MSB(error, ske->packet->buffer.data); ske->status = error; silc_packet_free(ske->packet); @@ -2303,7 +2471,10 @@ SILC_FSM_STATE(silc_ske_st_responder_failure) silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); silc_schedule_task_del_by_context(ske->schedule, ske); - SILC_FSM_FINISH; + /* Call completion */ + silc_ske_completion(ske); + + return SILC_FSM_FINISH; } /* Error occurred */ @@ -2325,15 +2496,17 @@ SILC_FSM_STATE(silc_ske_st_responder_error) silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); silc_schedule_task_del_by_context(ske->schedule, ske); - SILC_FSM_FINISH; + /* 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")); @@ -2348,6 +2521,7 @@ silc_ske_responder(SilcSKE ske, 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); @@ -2383,17 +2557,26 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_start) if (ske->aborted) { /** Aborted */ silc_fsm_next(fsm, silc_ske_st_initiator_aborted); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } - /* XXX timeout */ + /* 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); - SILC_FSM_CONTINUE; + 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 */ @@ -2402,14 +2585,14 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_start) SILC_LOG_DEBUG(("Error sending packet")); ske->status = SILC_SKE_STATUS_ERROR; silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + 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); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } status = silc_ske_group_get_by_number(ske->rekey->ske_group, @@ -2417,12 +2600,12 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_start) if (status != SILC_SKE_STATUS_OK) { /** Unknown group */ silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /** Rekey with PFS */ silc_fsm_next(fsm, silc_ske_st_initiator_phase2); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Sends REKEY_DONE packet to finish the protocol. */ @@ -2441,13 +2624,7 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_done) 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); - - if (!silc_hash_alloc(ske->rekey->hash, &hash)) { - /** Cannot allocate hash */ - ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY; - silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; - } + hash = ske->prop->hash; hash_len = silc_hash_len(hash); /* Process key material */ @@ -2473,22 +2650,26 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_done) if (!ske->keymat) { SILC_LOG_ERROR(("Error processing key material")); silc_fsm_next(fsm, silc_ske_st_initiator_error); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } ske->prop->cipher = send_key; ske->prop->hmac = hmac_send; - ske->prop->hash = hash; /* 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); - SILC_FSM_CONTINUE; + 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, @@ -2496,13 +2677,15 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_done) /** 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); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /** Wait for REKEY_DONE */ silc_fsm_next(fsm, silc_ske_st_rekey_initiator_end); - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } /* Rekey protocol end */ @@ -2520,7 +2703,7 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_end) SILC_LOG_DEBUG(("Remote retransmitted an old packet")); silc_packet_free(ske->packet); ske->packet = NULL; - SILC_FSM_WAIT; + return SILC_FSM_WAIT; } silc_packet_get_keys(ske->stream, NULL, &receive_key, NULL, &hmac_receive); @@ -2532,8 +2715,10 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_end) 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); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } /* Set new receiving keys into use. All packets received after this will @@ -2543,8 +2728,10 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_end) /** 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); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } SILC_LOG_DEBUG(("Rekey completed successfully")); @@ -2554,8 +2741,10 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_end) 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); - SILC_FSM_CONTINUE; + return SILC_FSM_CONTINUE; } rekey->pfs = ske->rekey->pfs; ske->rekey = rekey; @@ -2567,7 +2756,10 @@ SILC_FSM_STATE(silc_ske_st_rekey_initiator_end) silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske); silc_schedule_task_del_by_context(ske->schedule, ske); - SILC_FSM_FINISH; + /* Call completion */ + silc_ske_completion(ske); + + return SILC_FSM_FINISH; } /* Starts rekey protocol as initiator */ @@ -2579,8 +2771,11 @@ 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; @@ -2610,11 +2805,240 @@ silc_ske_rekey_initiator(SilcSKE ske, /***************************** Responder Rekey ******************************/ -SILC_FSM_STATE(silc_ske_st_rekey_responder_start); +/* 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) { - SILC_FSM_FINISH; + 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; } /* Starts rekey protocol as responder */ @@ -2622,7 +3046,8 @@ SILC_FSM_STATE(silc_ske_st_rekey_responder_start) SilcAsyncOperation silc_ske_rekey_responder(SilcSKE ske, SilcPacketStream stream, - SilcSKERekeyMaterial rekey) + SilcSKERekeyMaterial rekey, + SilcPacket packet) { SILC_LOG_DEBUG(("Start SKE rekey as responder")); @@ -2639,6 +3064,7 @@ silc_ske_rekey_responder(SilcSKE ske, ske->responder = TRUE; ske->running = TRUE; ske->rekeying = TRUE; + ske->packet = packet; /* Link to packet stream to get key exchange packets */ ske->stream = stream; @@ -2650,7 +3076,7 @@ silc_ske_rekey_responder(SilcSKE ske, 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; } @@ -2686,7 +3112,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 */ @@ -2725,8 +3151,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); @@ -2736,7 +3162,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)); @@ -2788,8 +3214,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); @@ -2799,7 +3225,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)); @@ -2875,8 +3301,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 */ @@ -2954,7 +3380,8 @@ SilcBool silc_ske_set_keys(SilcSKE ske, SilcHmac *ret_hmac_receive, SilcHash *ret_hash) { - unsigned char iv[32]; + 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) { @@ -2980,6 +3407,12 @@ SilcBool silc_ske_set_keys(SilcSKE ske, return FALSE; } + /* Allocate hash */ + if (ret_hash) { + if (!silc_hash_alloc(silc_hash_get_name(prop->hash), ret_hash)) + return FALSE; + } + /* Set key material */ memset(iv, 0, sizeof(iv)); if (ske->responder) { @@ -2988,10 +3421,24 @@ SilcBool silc_ske_set_keys(SilcSKE ske, keymat->enc_key_len, TRUE); if (silc_cipher_get_mode(*ret_send_key) == SILC_CIPHER_MODE_CTR) { - memcpy(iv, ske->hash, 4); - memcpy(iv + 4, keymat->receive_iv, 4); + /* 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); } } @@ -3000,10 +3447,24 @@ SilcBool silc_ske_set_keys(SilcSKE ske, keymat->enc_key_len, FALSE); if (silc_cipher_get_mode(*ret_receive_key) == SILC_CIPHER_MODE_CTR) { - memcpy(iv, ske->hash, 4); - memcpy(iv + 4, keymat->send_iv, 4); + /* 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); } } @@ -3019,10 +3480,24 @@ SilcBool silc_ske_set_keys(SilcSKE ske, keymat->enc_key_len, TRUE); if (silc_cipher_get_mode(*ret_send_key) == SILC_CIPHER_MODE_CTR) { - memcpy(iv, ske->hash, 4); - memcpy(iv + 4, keymat->send_iv, 4); + /* 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); } } @@ -3031,10 +3506,25 @@ SilcBool silc_ske_set_keys(SilcSKE ske, keymat->enc_key_len, FALSE); if (silc_cipher_get_mode(*ret_receive_key) == SILC_CIPHER_MODE_CTR) { - memcpy(iv, ske->hash, 4); - memcpy(iv + 4, keymat->receive_iv, 4); + /* 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); } } @@ -3046,12 +3536,6 @@ SilcBool silc_ske_set_keys(SilcSKE ske, keymat->hmac_key_len); } - /* Allocate hash */ - if (ret_hash) { - if (!silc_hash_alloc(silc_hash_get_name(prop->hash), ret_hash)) - return FALSE; - } - return TRUE; } @@ -3059,7 +3543,7 @@ const char *silc_ske_status_string[] = { /* Official */ "Ok", - "Unkown error occurred", + "Unexpected error occurred", "Bad payload in packet", "Unsupported group", "Unsupported cipher", @@ -3077,6 +3561,7 @@ const char *silc_ske_status_string[] = "Bad payload length in packet", "Error computing signature", "System out of memory", + "Key exchange timeout", NULL };