Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2000 - 2006 Pekka Riikonen
+ Copyright (C) 2000 - 2007 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
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_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 */
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;
}
static SilcSKEStatus silc_ske_check_version(SilcSKE ske)
{
- SilcUInt32 l_protocol_version = 0, r_protocol_version = 0;
SilcUInt32 r_software_version = 0;
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, NULL, NULL))
return SILC_SKE_STATUS_BAD_VERSION;
/* Backwards compatibility checks */
}
/* 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);
}
/* 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);
}
/* 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);
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
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 *******************************/
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)
ske->schedule = schedule;
ske->public_key = public_key;
ske->private_key = private_key;
+ ske->retry_timer = SILC_SKE_RETRY_MIN;
return 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;
}
+
+ /* 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 */
/******************************** 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)
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;
}
SilcSKEStatus status;
SilcSKEStartPayload payload;
SilcSKESecurityProperties prop;
- SilcSKEDiffieHellmanGroup group;
+ SilcSKEDiffieHellmanGroup group = NULL;
SilcBuffer packet_buf = &ske->packet->buffer;
SilcUInt16 remote_port = 0;
SilcID id;
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 */
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;
}
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
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)
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;
/* 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);
/* 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;
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 */
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)) {
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,
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;
/* 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;
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;
}
/* 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;
}
/* 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;
}
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));
- /* Call the completion callback */
- if (ske->callbacks->completed)
- ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
+ 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;
+ }
- 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"));
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)
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;
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)
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_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;
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) {
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);
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 */
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;
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. */
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,
/* 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);
}
/* 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;
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;
/* 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;
}
/* 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;
}
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;
}
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;
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;
+ }
+
+ /* 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);
+
+ 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);
+ return SILC_FSM_CONTINUE;
+ }
+ 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;
+ 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;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /* 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_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;
+ 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_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;
+ 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;
}
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);
return &ske->op;
}
+/***************************** Responder Rekey ******************************/
+
SILC_FSM_STATE(silc_ske_st_rekey_responder_start);
SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
SilcAsyncOperation
silc_ske_rekey_responder(SilcSKE ske,
SilcPacketStream stream,
- SilcBuffer ke_payload,
SilcSKERekeyMaterial rekey)
{
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;
/* 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_buffer_clear(buf);
silc_buffer_free(buf);
+ SILC_LOG_HEXDUMP(("enc"), key->send_enc_key, key->enc_key_len / 8);
+
return key;
}
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;
silc_buffer_clear(buf);
silc_buffer_free(buf);
+ if (rekey) {
+ *rekey = silc_ske_make_rekey_material(ske, key);
+ if (!(*rekey))
+ return NULL;
+ }
+
return 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,
SilcHmac *ret_hmac_receive,
SilcHash *ret_hash)
{
+ unsigned char iv[32];
+ 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),
}
/* Set key material */
+ memset(iv, 0, sizeof(iv));
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);
+ 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) {
+ memcpy(iv, ske->hash, 4);
+ memcpy(iv + 4, keymat->receive_iv, iv_included ? 4 : 8);
+ silc_cipher_set_iv(*ret_send_key, iv);
+ } else {
+ 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) {
+ memcpy(iv, ske->hash, 4);
+ memcpy(iv + 4, keymat->send_iv, iv_included ? 4 : 8);
+ silc_cipher_set_iv(*ret_receive_key, iv);
+ } else {
+ 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 {
- 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);
+ 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) {
+ memcpy(iv, ske->hash, 4);
+ memcpy(iv + 4, keymat->send_iv, iv_included ? 4 : 8);
+ silc_cipher_set_iv(*ret_send_key, iv);
+ } else {
+ 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) {
+ memcpy(iv, ske->hash, 4);
+ memcpy(iv + 4, keymat->receive_iv, iv_included ? 4 : 8);
+ silc_cipher_set_iv(*ret_receive_key, iv);
+ } else {
+ 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);
}
/* Allocate hash */
return FALSE;
}
- SILC_LOG_INFO(("Security properties: %s %s %s %s",
- ret_send_key ? silc_cipher_get_name(*ret_send_key) : "??",
- ret_hmac_send ? silc_hmac_get_name(*ret_hmac_send) : "??",
- ret_hash ? silc_hash_get_name(*ret_hash) : "??",
- ske->prop->flags & SILC_SKE_SP_FLAG_PFS ? "PFS" : ""));
-
return TRUE;
}
"Bad payload length in packet",
"Error computing signature",
"System out of memory",
+ "Key exchange timeout",
NULL
};
{
return ske->prop;
}
+
+/* Get key material */
+
+SilcSKEKeyMaterial silc_ske_get_key_material(SilcSKE ske)
+{
+ return ske->keymat;
+}