void *context;
};
-
/************************ Static utility functions **************************/
+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);
+
SilcSKEKeyMaterial
silc_ske_process_key_material(SilcSKE ske,
SilcUInt32 req_iv_len,
SilcUInt32 req_enc_key_len,
SilcUInt32 req_hmac_key_len);
-
+static SilcBool silc_ske_packet_send(SilcSKE ske,
+ SilcPacketType type,
+ SilcPacketFlags flags,
+ const unsigned char *data,
+ SilcUInt32 data_len);
/* Packet callback */
}
/* 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 rp;
}
+/* Packet retransmission callback. */
+
+SILC_TASK_CALLBACK(silc_ske_packet_send_retry)
+{
+ SilcSKE ske = context;
+
+ if (ske->retry_count++ >= SILC_SKE_RETRY_COUNT) {
+ SILC_LOG_DEBUG(("Retry 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);
+}
+
+/* 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) {
+ 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;
+ 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));
+ }
+
+ return ret;
+}
+
/******************************* 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;
+ 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);
+ 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->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;
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"));
+ ske->retry_timer = SILC_SKE_RETRY_MIN;
+ silc_schedule_task_del_by_context(ske->schedule, ske);
+
if (ske->aborted) {
/** Aborted */
silc_packet_free(ske->packet);
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_initiator_failure);
+ return SILC_FSM_CONTINUE;
+ }
+
+ if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE) {
+ SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+ silc_packet_free(ske->packet);
+ return SILC_FSM_WAIT;
+ }
+
/* Decode the payload */
status = silc_ske_payload_start_decode(ske, packet_buf, &payload);
if (status != SILC_SKE_STATUS_OK) {
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)
/* 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_LOG_DEBUG(("Start"));
+ ske->retry_timer = SILC_SKE_RETRY_MIN;
+ silc_schedule_task_del_by_context(ske->schedule, ske);
+
if (ske->aborted) {
/** Aborted */
silc_packet_free(ske->packet);
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_initiator_failure);
+ return SILC_FSM_CONTINUE;
+ }
+
+ if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
+ SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+ silc_packet_free(ske->packet);
+ return SILC_FSM_WAIT;
+ }
+
/* Decode the payload */
status = silc_ske_payload_ke_decode(ske, packet_buf, &payload);
if (status != SILC_SKE_STATUS_OK) {
/* 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_LOG_DEBUG(("Start"));
+ ske->retry_timer = SILC_SKE_RETRY_MIN;
+ silc_schedule_task_del_by_context(ske->schedule, ske);
+
if (ske->aborted) {
/** Aborted */
silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
return SILC_FSM_CONTINUE;
}
+ if (ske->packet->type != SILC_PACKET_SUCCESS) {
+ SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+ silc_packet_free(ske->packet);
+ return SILC_FSM_WAIT;
+ }
+
SILC_LOG_DEBUG(("Key exchange completed successfully"));
/* Call the completion callback */
ske->rekey, ske->callbacks->context);
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);
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);
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,
ske->callbacks->context);
+ silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+ silc_schedule_task_del_by_context(ske->schedule, ske);
+
return SILC_FSM_FINISH;
}
ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL,
ske->callbacks->context);
+ silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+ silc_schedule_task_del_by_context(ske->schedule, ske);
+
return SILC_FSM_FINISH;
}
void *destructor_context)
{
SilcSKE ske = fsm_context;
- silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+
+ ske->running = FALSE;
+ if (ske->freed)
+ silc_ske_free(ske);
}
/* Starts the protocol as initiator */
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)
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"));
+ ske->retry_timer = SILC_SKE_RETRY_MIN;
+ silc_schedule_task_del_by_context(ske->schedule, ske);
+
if (ske->aborted) {
/** Aborted */
silc_fsm_next(fsm, silc_ske_st_responder_aborted);
return SILC_FSM_CONTINUE;
}
+ if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
+ SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+ silc_packet_free(ske->packet);
+ return SILC_FSM_WAIT;
+ }
+
/* Decode Key Exchange Payload */
status = silc_ske_payload_ke_decode(ske, packet_buf, &recv_payload);
if (status != SILC_SKE_STATUS_OK) {
}
/* 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);
unsigned char tmp[4];
SilcUInt32 hash_len, key_len, block_len;
+ ske->retry_timer = SILC_SKE_RETRY_MIN;
+ silc_schedule_task_del_by_context(ske->schedule, ske);
+
if (ske->aborted) {
/** Aborted */
silc_fsm_next(fsm, silc_ske_st_responder_aborted);
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_packet_free(ske->packet);
+ return SILC_FSM_WAIT;
+ }
silc_packet_free(ske->packet);
/* Process key material */
/* 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)
/* 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);
return SILC_FSM_FINISH;
}
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);
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)
{
+ SilcSKE ske = fsm_context;
+ ske->running = FALSE;
+ if (ske->freed)
+ silc_ske_free(ske);
}
/* Starts the protocol as responder. */
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->prop;
}
+
+/* Get key material */
+
+SilcSKEKeyMaterial silc_ske_get_key_material(SilcSKE ske)
+{
+ return ske->keymat;
+}