Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2000 - 2005 Pekka Riikonen
+ Copyright (C) 2000 - 2006 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 **************************/
-SilcSKEKeyMaterial
-silc_ske_process_key_material_data(unsigned char *data,
- SilcUInt32 data_len,
- SilcUInt32 req_iv_len,
- SilcUInt32 req_enc_key_len,
- SilcUInt32 req_hmac_key_len,
- SilcHash hash);
+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 */
SILC_FSM_CALL_CONTINUE(&ske->fsm);
}
+/* SKR find callback */
+
+static void silc_ske_skr_callback(SilcSKR repository,
+ SilcSKRFind find,
+ SilcSKRStatus status,
+ SilcDList keys, void *context)
+{
+ SilcSKE ske = context;
+
+ silc_skr_find_free(find);
+
+ if (status != SILC_SKR_OK) {
+ if (ske->callbacks->verify_key) {
+ /* Verify from application */
+ ske->callbacks->verify_key(ske, ske->prop->public_key,
+ ske->callbacks->context,
+ silc_ske_pk_verified, NULL);
+ return;
+ }
+ }
+
+ if (keys)
+ silc_dlist_uninit(keys);
+
+ /* Continue */
+ ske->status = (status == SILC_SKR_OK ? SILC_SKE_STATUS_OK :
+ SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY);
+ SILC_FSM_CALL_CONTINUE(&ske->fsm);
+}
+
/* Checks remote and local versions */
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, NULL, NULL, NULL))
+ NULL, &r_software_version, NULL, NULL))
return SILC_SKE_STATUS_BAD_VERSION;
if (!silc_parse_version_string(ske->version, &l_protocol_version,
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;
}
/* Selects the supported security properties from the initiator's Key
- Exchange Start Payload. */
+ Exchange Start Payload. A responder function. Saves our reply
+ start payload to ske->start_payload. */
static SilcSKEStatus
silc_ske_select_security_properties(SilcSKE ske,
- SilcSKEStartPayload payload,
- SilcSKEStartPayload remote_payload)
+ SilcSKEStartPayload remote_payload,
+ SilcSKESecurityProperties *prop)
{
SilcSKEStatus status;
- SilcSKEStartPayload rp;
+ SilcSKEStartPayload rp, payload;
char *cp;
int len;
rp = remote_payload;
+ /* Check for mandatory fields */
+ if (!rp->ke_grp_len) {
+ SILC_LOG_DEBUG(("KE group not defined in payload"));
+ return SILC_SKE_STATUS_BAD_PAYLOAD;
+ }
+ if (!rp->pkcs_alg_len) {
+ SILC_LOG_DEBUG(("PKCS alg not defined in payload"));
+ return SILC_SKE_STATUS_BAD_PAYLOAD;
+ }
+ if (!rp->enc_alg_len) {
+ SILC_LOG_DEBUG(("Encryption alg not defined in payload"));
+ return SILC_SKE_STATUS_BAD_PAYLOAD;
+ }
+ if (!rp->hash_alg_len) {
+ SILC_LOG_DEBUG(("Hash alg not defined in payload"));
+ return SILC_SKE_STATUS_BAD_PAYLOAD;
+ }
+ if (!rp->hmac_alg_len) {
+ SILC_LOG_DEBUG(("HMAC not defined in payload"));
+ return SILC_SKE_STATUS_BAD_PAYLOAD;
+ }
+
+ /* Allocate security properties */
+ *prop = silc_calloc(1, sizeof(**prop));
+ if (!(*prop))
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
+
+ /* Allocate our reply start payload */
+ payload = silc_calloc(1, sizeof(*payload));
+ if (!payload) {
+ silc_free(*prop);
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ }
+
/* Check version string */
ske->remote_version = silc_memdup(rp->version, rp->version_len);
status = silc_ske_check_version(ske);
}
/* Flags are returned unchanged. */
- payload->flags = rp->flags;
+ (*prop)->flags = payload->flags = rp->flags;
/* Take cookie, we must return it to sender unmodified. */
payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char));
payload->cookie_len = SILC_SKE_COOKIE_LEN;
memcpy(payload->cookie, rp->cookie, SILC_SKE_COOKIE_LEN);
+ /* In case IV included flag and session port is set the first 16-bits of
+ cookie will include our session port. */
+ if (rp->flags & SILC_SKE_SP_FLAG_IV_INCLUDED && ske->session_port) {
+ /* Take remote port */
+ SILC_GET16_MSB((*prop)->remote_port, payload->cookie);
+
+ /* Put out port */
+ SILC_PUT16_MSB(ske->session_port, payload->cookie);
+ }
+
/* Put our version to our reply */
payload->version = strdup(ske->version);
if (!payload->version) {
return SILC_SKE_STATUS_UNKNOWN_GROUP;
}
} else {
-
- if (!rp->ke_grp_len) {
- SILC_LOG_DEBUG(("KE group not defined in payload"));
- silc_free(payload);
- return SILC_SKE_STATUS_BAD_PAYLOAD;
- }
-
SILC_LOG_DEBUG(("Proposed KE group `%s'", rp->ke_grp_list));
SILC_LOG_DEBUG(("Found KE group `%s'", rp->ke_grp_list));
payload->ke_grp_list = strdup(rp->ke_grp_list);
}
+ /* Save group to security properties */
+ status = silc_ske_group_get_by_name(payload->ke_grp_list, &(*prop)->group);
+ if (status != SILC_SKE_STATUS_OK) {
+ silc_free(payload);
+ return SILC_SKE_STATUS_UNKNOWN_GROUP;
+ }
+
/* Get supported PKCS algorithms */
cp = rp->pkcs_alg_list;
if (cp && strchr(cp, ',')) {
return SILC_SKE_STATUS_UNKNOWN_PKCS;
}
} else {
-
- if (!rp->pkcs_alg_len) {
- SILC_LOG_DEBUG(("PKCS alg not defined in payload"));
- silc_free(payload->ke_grp_list);
- silc_free(payload);
- return SILC_SKE_STATUS_BAD_PAYLOAD;
- }
-
SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list));
SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list));
return SILC_SKE_STATUS_UNKNOWN_CIPHER;
}
} else {
-
- if (!rp->enc_alg_len) {
- SILC_LOG_DEBUG(("Encryption alg not defined in payload"));
- silc_free(payload->ke_grp_list);
- silc_free(payload->pkcs_alg_list);
- silc_free(payload);
- return SILC_SKE_STATUS_BAD_PAYLOAD;
- }
-
SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
rp->enc_alg_list));
payload->enc_alg_list = strdup(rp->enc_alg_list);
}
+ /* Save selected cipher to security properties */
+ 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);
+ return SILC_SKE_STATUS_UNKNOWN_CIPHER;
+ }
+
/* Get supported hash algorithms */
cp = rp->hash_alg_list;
if (cp && strchr(cp, ',')) {
return SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
}
} else {
-
- if (!rp->hash_alg_len) {
- SILC_LOG_DEBUG(("Hash alg not defined in payload"));
- silc_free(payload->ke_grp_list);
- silc_free(payload->pkcs_alg_list);
- silc_free(payload->enc_alg_list);
- silc_free(payload);
- return SILC_SKE_STATUS_BAD_PAYLOAD;
- }
-
SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it",
rp->hash_alg_list));
payload->hash_alg_list = strdup(rp->hash_alg_list);
}
+ /* Save selected hash algorithm to security properties */
+ 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);
+ silc_free(payload);
+ return SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
+ }
+
/* Get supported HMACs */
cp = rp->hmac_alg_list;
if (cp && strchr(cp, ',')) {
return SILC_SKE_STATUS_UNKNOWN_HMAC;
}
} else {
-
- if (!rp->hmac_alg_len) {
- SILC_LOG_DEBUG(("HMAC not defined in payload"));
- silc_free(payload->ke_grp_list);
- silc_free(payload->pkcs_alg_list);
- silc_free(payload->enc_alg_list);
- silc_free(payload->hash_alg_list);
- silc_free(payload);
- return SILC_SKE_STATUS_BAD_PAYLOAD;
- }
-
SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
rp->hmac_alg_list));
payload->hmac_alg_list = strdup(rp->hmac_alg_list);
}
+ /* Save selected HMACc to security properties */
+ 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);
+ silc_free(payload->hash_alg_list);
+ silc_free(payload);
+ return SILC_SKE_STATUS_UNKNOWN_HMAC;
+ }
+
/* Get supported compression algorithms */
cp = rp->comp_alg_list;
if (cp && strchr(cp, ',')) {
2 + payload->enc_alg_len + 2 + payload->hash_alg_len +
2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
+ /* Save our reply payload */
+ ske->start_payload = payload;
+
return SILC_SKE_STATUS_OK;
}
return status;
}
+/* Assembles security properties */
+
+static SilcSKEStartPayload
+silc_ske_assemble_security_properties(SilcSKE ske,
+ SilcSKESecurityPropertyFlag flags,
+ const char *version)
+{
+ SilcSKEStartPayload rp;
+ int i;
+
+ SILC_LOG_DEBUG(("Assembling KE Start Payload"));
+
+ rp = silc_calloc(1, sizeof(*rp));
+
+ /* Set flags */
+ rp->flags = (unsigned char)flags;
+
+ /* Set random cookie */
+ rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(*rp->cookie));
+ for (i = 0; i < SILC_SKE_COOKIE_LEN; i++)
+ rp->cookie[i] = silc_rng_get_byte_fast(ske->rng);
+ rp->cookie_len = SILC_SKE_COOKIE_LEN;
+
+ /* In case IV included flag and session port is set the first 16-bits of
+ cookie will include our session port. */
+ if (flags & SILC_SKE_SP_FLAG_IV_INCLUDED && ske->session_port)
+ SILC_PUT16_MSB(ske->session_port, rp->cookie);
+
+ /* Put version */
+ rp->version = strdup(version);
+ rp->version_len = strlen(version);
+
+ /* Get supported Key Exhange groups */
+ rp->ke_grp_list = silc_ske_get_supported_groups();
+ rp->ke_grp_len = strlen(rp->ke_grp_list);
+
+ /* Get supported PKCS algorithms */
+ rp->pkcs_alg_list = silc_pkcs_get_supported();
+ rp->pkcs_alg_len = strlen(rp->pkcs_alg_list);
+
+ /* Get supported encryption algorithms */
+ rp->enc_alg_list = silc_cipher_get_supported();
+ rp->enc_alg_len = strlen(rp->enc_alg_list);
+
+ /* Get supported hash algorithms */
+ rp->hash_alg_list = silc_hash_get_supported();
+ rp->hash_alg_len = strlen(rp->hash_alg_list);
+
+ /* Get supported HMACs */
+ rp->hmac_alg_list = silc_hmac_get_supported();
+ rp->hmac_alg_len = strlen(rp->hmac_alg_list);
+
+ /* XXX */
+ /* Get supported compression algorithms */
+ rp->comp_alg_list = strdup("none");
+ rp->comp_alg_len = strlen("none");
+
+ rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
+ 2 + rp->version_len +
+ 2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len +
+ 2 + rp->enc_alg_len + 2 + rp->hash_alg_len +
+ 2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
+
+ return rp;
+}
+
+/* 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(("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 *******************************/
/* Allocates new SKE object. */
SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
- SilcPublicKey public_key, SilcPrivateKey private_key,
- void *context)
+ SilcSKR repository, SilcPublicKey public_key,
+ SilcPrivateKey private_key, void *context)
{
SilcSKE ske;
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)
return NULL;
ske->status = SILC_SKE_STATUS_OK;
ske->rng = rng;
+ ske->repository = repository;
ske->user_data = context;
ske->schedule = schedule;
ske->public_key = public_key;
ske->private_key = private_key;
- ske->pk_type = SILC_SKE_PK_TYPE_SILC;
+ 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);
-
/* Start protocol. Send our proposal */
SILC_FSM_STATE(silc_ske_st_initiator_start)
compute the HASH value. */
ske->start_payload_copy = payload_buf;
- /* Send the packet */
- /* XXX */
+ /* Send the packet. */
+ 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 */
/** Wait for responder proposal */
SILC_LOG_DEBUG(("Waiting for reponder proposal"));
- silc_fsm_next(ske, silc_ske_st_initiator_phase1);
+ 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;
+ int coff = 0;
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);
+ ske->packet = NULL;
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) {
+ 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);
+ ske->packet = NULL;
+ return SILC_FSM_WAIT;
+ }
+
/* Decode the payload */
status = silc_ske_payload_start_decode(ske, packet_buf, &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;
}
- /* Check that the cookie is returned unmodified */
- if (memcmp(ske->start_payload->cookie, payload->cookie,
- ske->start_payload->cookie_len)) {
+ /* 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));
+ }
+
+ 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
+ are the session port and we ignore them in this check. */
+ if (payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED && ske->session_port) {
+ /* Take remote port */
+ SILC_GET16_MSB(remote_port, ske->start_payload->cookie);
+ coff = 2;
+ }
+ if (memcmp(ske->start_payload->cookie + coff, payload->cookie + coff,
+ SILC_SKE_COOKIE_LEN - coff)) {
/** Invalid cookie */
- SILC_LOG_ERROR(("Responder modified our cookie and it must not do it"));
+ 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);
return SILC_FSM_CONTINUE;
goto err;
prop->group = group;
+ prop->remote_port = remote_port;
if (silc_pkcs_find_algorithm(payload->pkcs_alg_list, NULL) == NULL) {
status = SILC_SKE_STATUS_UNKNOWN_PKCS;
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)
&ske->prop->group->group);
/* Get public key */
- if (ske->public_key) {
- payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
- if (!payload->pk_data) {
- /** Error encoding public key */
- silc_mp_uninit(x);
- silc_free(x);
- silc_mp_uninit(&payload->x);
- silc_free(payload);
- ske->ke1_payload = NULL;
- ske->status = SILC_SKE_STATUS_ERROR;
- silc_fsm_next(fsm, silc_ske_st_initiator_error);
- return SILC_FSM_CONTINUE;
- }
- payload->pk_len = pk_len;
+ payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
+ if (!payload->pk_data) {
+ /** Error encoding public key */
+ silc_mp_uninit(x);
+ silc_free(x);
+ silc_mp_uninit(&payload->x);
+ silc_free(payload);
+ ske->ke1_payload = NULL;
+ ske->status = SILC_SKE_STATUS_ERROR;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
- payload->pk_type = ske->pk_type;
+ payload->pk_len = pk_len;
+ payload->pk_type = silc_pkcs_get_type(ske->public_key);
/* Compute signature data if we are doing mutual authentication */
if (ske->private_key &&
return SILC_FSM_CONTINUE;
}
payload->sign_data = silc_memdup(sign, sign_len);
- payload->sign_len = sign_len;
+ if (payload->sign_data)
+ payload->sign_len = sign_len;
memset(sign, 0, sizeof(sign));
}
ske->x = x;
+ /* Check for backwards compatibility */
+
/* Send the packet. */
- /* XXX */
+ 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_buffer_free(payload_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_packet_free(ske->packet);
+ ske->packet = NULL;
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) {
+ 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);
+ ske->packet = NULL;
+ return SILC_FSM_WAIT;
+ }
+
/* Decode the payload */
status = silc_ske_payload_ke_decode(ske, packet_buf, &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) {
+ if (!payload->pk_data && (ske->callbacks->verify_key || ske->repository)) {
SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), "
"even though we require it"));
ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
goto err;
}
- if (ske->prop->public_key && ske->callbacks->verify_key) {
+ if (ske->prop->public_key && (ske->callbacks->verify_key ||
+ ske->repository)) {
SILC_LOG_DEBUG(("Verifying public key"));
/** Waiting public key verification */
silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
- SILC_FSM_CALL(ske->callbacks->verify_key(ske,
- payload->pk_type,
- ske->prop->public_key,
- ske->callbacks->context,
- silc_ske_pk_verified, NULL));
+
+ /* If repository is provided, verify the key from there. */
+ if (ske->repository) {
+ SilcSKRFind find;
+
+ find = silc_skr_find_alloc();
+ if (!find) {
+ status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ goto err;
+ }
+ 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 */
+ SILC_FSM_CALL(ske->callbacks->verify_key(ske, ske->prop->public_key,
+ ske->callbacks->context,
+ silc_ske_pk_verified, NULL));
+ }
/* NOT REACHED */
}
if (status != SILC_SKE_STATUS_OK)
goto err;
- ske->hash = silc_memdup(hash, hash_len);
- ske->hash_len = hash_len;
-
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)) {
+ payload->sign_len, hash, hash_len, NULL)) {
SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
goto err;
SILC_LOG_DEBUG(("Signature is Ok"));
+ ske->hash = silc_memdup(hash, hash_len);
+ ske->hash_len = hash_len;
memset(hash, 'F', hash_len);
}
}
/* Send SUCCESS packet */
- /* XXX */
+ SILC_PUT32_MSB((SilcUInt32)SILC_SKE_STATUS_OK, hash);
+ 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;
+ }
/** Waiting completion */
silc_fsm_next(fsm, silc_ske_st_initiator_end);
{
SilcSKE ske = fsm_context;
+ 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;
}
+ /* See if received failure from remote */
+ if (ske->packet->type == SILC_PACKET_FAILURE) {
+ silc_fsm_next(fsm, silc_ske_st_initiator_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);
+ 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, NULL, NULL, NULL, NULL);
+ ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat,
+ ske->rekey, ske->callbacks->context);
+
+ 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);
return SILC_FSM_FINISH;
}
SILC_FSM_STATE(silc_ske_st_initiator_aborted)
{
+ SilcSKE ske = fsm_context;
+ unsigned char data[4];
+
+ SILC_LOG_DEBUG(("Aborted by caller"));
+
+ /* Send FAILURE packet */
+ SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, data);
+ 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;
}
-/* Error occurred */
+/* Error occurred. Send error to remote host */
SILC_FSM_STATE(silc_ske_st_initiator_error)
{
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ unsigned char data[4];
+
+ SILC_LOG_DEBUG(("Error %s (%d) occurred during key exchange",
+ silc_ske_map_status(ske->status), ske->status));
+
+ status = ske->status;
+ if (status > SILC_SKE_STATUS_INVALID_COOKIE)
+ status = SILC_SKE_STATUS_ERROR;
+
+ /* Send FAILURE packet */
+ SILC_PUT32_MSB((SilcUInt32)status, data);
+ silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, data, 4);
+
+ /* Call the completion callback */
+ if (!ske->aborted && 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;
+}
+
+/* Failure received from remote */
+
+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) {
+ SILC_GET32_MSB(error, ske->packet->buffer.data);
+ ske->status = error;
+ silc_packet_free(ske->packet);
+ ske->packet = NULL;
+ }
+
+ /* Call the completion callback */
+ if (!ske->aborted && 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;
}
+/* FSM destructor */
static void silc_ske_initiator_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 initiator */
SilcAsyncOperation
silc_ske_initiator(SilcSKE ske,
SilcPacketStream stream,
+ SilcSKEParams params,
SilcSKEStartPayload start_payload)
{
SILC_LOG_DEBUG(("Start SKE as initiator"));
- if (!ske || !stream || !start_payload)
+ if (!ske || !stream || !params || !params->version)
return NULL;
if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
ske->schedule))
return NULL;
+ if (params->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)
+ ske->session_port = params->session_port;
+
+ /* Generate security properties if not provided */
+ if (!start_payload) {
+ start_payload = silc_ske_assemble_security_properties(ske,
+ params->flags,
+ params->version);
+ if (!start_payload)
+ return NULL;
+ }
+
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_phase3);
-SILC_FSM_STATE(silc_ske_st_responder_phase4);
-SILC_FSM_STATE(silc_ske_st_responder_phase5);
-SILC_FSM_STATE(silc_ske_st_responder_end);
-SILC_FSM_STATE(silc_ske_st_responder_aborted);
-SILC_FSM_STATE(silc_ske_st_responder_failure);
-SILC_FSM_STATE(silc_ske_st_responder_error);
-
/* Start protocol as responder. Wait initiator's start payload */
SILC_FSM_STATE(silc_ske_st_responder_start)
return SILC_FSM_WAIT;
}
-/* Decode initiator's start payload */
+/* Decode initiator's start payload. Select the security properties from
+ the initiator's start payload and send our reply start payload back. */
SILC_FSM_STATE(silc_ske_st_responder_phase1)
{
SilcSKE ske = fsm_context;
SilcSKEStatus status;
- SilcSKEStartPayload remote_payload = NULL, payload = NULL;
+ SilcSKEStartPayload remote_payload = NULL;
SilcBuffer packet_buf = &ske->packet->buffer;
SILC_LOG_DEBUG(("Start"));
if (ske->aborted) {
/** Aborted */
+ silc_packet_free(ske->packet);
+ ske->packet = NULL;
silc_fsm_next(fsm, silc_ske_st_responder_aborted);
return SILC_FSM_CONTINUE;
}
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) {
remote_payload->flags &= ~SILC_SKE_SP_FLAG_IV_INCLUDED;
}
- /* Parse and select the security properties from the payload */
- payload = silc_calloc(1, sizeof(*payload));
- status = silc_ske_select_security_properties(ske, payload, remote_payload);
+ /* Check and select security properties */
+ status = silc_ske_select_security_properties(ske, remote_payload,
+ &ske->prop);
if (status != SILC_SKE_STATUS_OK) {
/** Error selecting proposal */
- if (remote_payload)
- silc_ske_payload_start_free(remote_payload);
- silc_free(payload);
+ silc_ske_payload_start_free(remote_payload);
ske->status = status;
silc_fsm_next(fsm, silc_ske_st_responder_error);
return SILC_FSM_CONTINUE;
}
- ske->start_payload = payload;
-
silc_ske_payload_start_free(remote_payload);
- /** Send proposal to initiator */
- silc_fsm_next(fsm, silc_ske_st_responder_phase2);
- return SILC_FSM_CONTINUE;
-}
-
-/* Phase-2. Send Start Payload */
-
-SILC_FSM_STATE(silc_ske_st_responder_phase2)
-{
- SilcSKE ske = fsm_context;
- SilcSKEStatus status;
- SilcBuffer payload_buf;
- SilcSKESecurityProperties prop;
- SilcSKEDiffieHellmanGroup group = NULL;
-
- SILC_LOG_DEBUG(("Start"));
-
- /* Allocate security properties from the payload. These are allocated
- only for this negotiation and will be free'd after KE is over. */
- ske->prop = prop = silc_calloc(1, sizeof(*prop));
- if (!ske->prop) {
- status = SILC_SKE_STATUS_OUT_OF_MEMORY;
- goto err;
- }
- prop->flags = ske->start_payload->flags;
- status = silc_ske_group_get_by_name(ske->start_payload->ke_grp_list,
- &group);
- if (status != SILC_SKE_STATUS_OK)
- goto err;
-
- prop->group = group;
-
- /* XXX these shouldn't be allocated before we know the remote's
- public key type. It's unnecessary to allocate these because the
- select_security_properties has succeeded already. */
- if (!silc_pkcs_find_algorithm(ske->start_payload->pkcs_alg_list, NULL)) {
- status = SILC_SKE_STATUS_UNKNOWN_PKCS;
- goto err;
- }
- if (silc_cipher_alloc(ske->start_payload->enc_alg_list,
- &prop->cipher) == FALSE) {
- status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
- goto err;
- }
- if (silc_hash_alloc(ske->start_payload->hash_alg_list,
- &prop->hash) == FALSE) {
- status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
- goto err;
- }
- if (silc_hmac_alloc(ske->start_payload->hmac_alg_list, NULL,
- &prop->hmac) == FALSE) {
- status = SILC_SKE_STATUS_UNKNOWN_HMAC;
- goto err;
- }
-
- /* Encode the payload */
+ /* Encode our reply payload to send the selected security properties */
status = silc_ske_payload_start_encode(ske, ske->start_payload,
- &payload_buf);
+ &packet_buf);
if (status != SILC_SKE_STATUS_OK)
goto err;
/* Send the packet. */
- if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE, 0,
- payload_buf->data, silc_buffer_len(payload_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(payload_buf);
+ silc_buffer_free(packet_buf);
/** Waiting initiator's KE payload */
- silc_fsm_next(fsm, silc_ske_st_responder_phase3);
+ silc_fsm_next(fsm, silc_ske_st_responder_phase2);
return SILC_FSM_WAIT;
err:
- if (group)
- silc_ske_group_free(group);
-
- if (prop->cipher)
- silc_cipher_free(prop->cipher);
- if (prop->hash)
- silc_hash_free(prop->hash);
- if (prop->hmac)
- silc_hmac_free(prop->hmac);
- silc_free(prop);
+ 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);
ske->prop = NULL;
if (status == SILC_SKE_STATUS_OK)
return SILC_FSM_CONTINUE;
}
-/* Phase-3. Decode initiator's KE payload */
+/* Phase-2. Decode initiator's KE payload */
-SILC_FSM_STATE(silc_ske_st_responder_phase3)
+SILC_FSM_STATE(silc_ske_st_responder_phase2)
{
SilcSKE ske = fsm_context;
SilcSKEStatus status;
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);
+ ske->packet = NULL;
+ 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) {
/** 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_LOG_DEBUG(("We are doing mutual authentication"));
- if (!recv_payload->pk_data && ske->callbacks->verify_key) {
+ if (!recv_payload->pk_data && (ske->callbacks->verify_key ||
+ ske->repository)) {
/** Public key not provided */
SILC_LOG_ERROR(("Remote end did not send its public key (or "
"certificate), even though we require it"));
return SILC_FSM_CONTINUE;
}
- if (ske->prop->public_key && ske->callbacks->verify_key) {
+ if (ske->prop->public_key && (ske->callbacks->verify_key ||
+ ske->repository)) {
SILC_LOG_DEBUG(("Verifying public key"));
/** Waiting public key verification */
silc_fsm_next(fsm, silc_ske_st_responder_phase4);
- SILC_FSM_CALL(ske->callbacks->verify_key(ske,
- recv_payload->pk_type,
- ske->prop->public_key,
- ske->callbacks->context,
- silc_ske_pk_verified, NULL));
+
+ /* If repository is provided, verify the key from there. */
+ if (ske->repository) {
+ SilcSKRFind find;
+
+ find = silc_skr_find_alloc();
+ if (!find) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+ silc_skr_find_set_pkcs_type(find,
+ silc_pkcs_get_type(ske->prop->public_key));
+ silc_skr_find_set_public_key(find, ske->prop->public_key);
+ silc_skr_find_set_usage(find, SILC_SKR_USAGE_KEY_AGREEMENT);
+
+ /* Find key from repository */
+ SILC_FSM_CALL(silc_skr_find(ske->repository, find,
+ silc_ske_skr_callback, ske));
+ } else {
+ /* Verify from application */
+ SILC_FSM_CALL(ske->callbacks->verify_key(ske, ske->prop->public_key,
+ ske->callbacks->context,
+ silc_ske_pk_verified, NULL));
+ }
/* NOT REACHED */
}
}
ske->ke2_payload->sign_len = sign_len;
memset(sign, 0, sizeof(sign));
}
- ske->ke2_payload->pk_type = ske->pk_type;
+ ske->ke2_payload->pk_type = silc_pkcs_get_type(ske->public_key);
/* Encode the Key Exchange Payload */
status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
}
/* 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;
+ 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);
+ 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);
/* 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_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;
+ ske->status = error;
+ silc_packet_free(ske->packet);
+ ske->packet = NULL;
+ }
/* Call the completion callback */
- if (ske->callbacks->completed)
+ if (!ske->aborted && ske->callbacks->completed)
ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL,
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;
}
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. */
SilcAsyncOperation
silc_ske_responder(SilcSKE ske,
SilcPacketStream stream,
- const char *version,
- SilcSKESecurityPropertyFlag flags)
+ SilcSKEParams params)
{
SILC_LOG_DEBUG(("Start SKE as responder"));
- if (!ske || !stream || !version) {
+ if (!ske || !stream || !params || !params->version) {
return NULL;
}
ske->schedule))
return NULL;
- ske->flags = flags;
- ske->version = strdup(version);
+ ske->responder = TRUE;
+ ske->flags = params->flags;
+ 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->responder = TRUE;
+ ske->running = TRUE;
/* Link to packet stream to get key exchange packets */
ske->stream = stream;
return &ske->op;
}
-/* Assembles security properties */
-
-SilcSKEStartPayload
-silc_ske_assemble_security_properties(SilcSKE ske,
- SilcSKESecurityPropertyFlag flags,
- const char *version)
-{
- SilcSKEStartPayload rp;
- int i;
-
- SILC_LOG_DEBUG(("Assembling KE Start Payload"));
-
- rp = silc_calloc(1, sizeof(*rp));
-
- /* Set flags */
- rp->flags = (unsigned char)flags;
-
- /* Set random cookie */
- rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(*rp->cookie));
- for (i = 0; i < SILC_SKE_COOKIE_LEN; i++)
- rp->cookie[i] = silc_rng_get_byte_fast(ske->rng);
- rp->cookie_len = SILC_SKE_COOKIE_LEN;
-
- /* Put version */
- rp->version = strdup(version);
- rp->version_len = strlen(version);
-
- /* Get supported Key Exhange groups */
- rp->ke_grp_list = silc_ske_get_supported_groups();
- rp->ke_grp_len = strlen(rp->ke_grp_list);
-
- /* Get supported PKCS algorithms */
- rp->pkcs_alg_list = silc_pkcs_get_supported();
- rp->pkcs_alg_len = strlen(rp->pkcs_alg_list);
-
- /* Get supported encryption algorithms */
- rp->enc_alg_list = silc_cipher_get_supported();
- rp->enc_alg_len = strlen(rp->enc_alg_list);
-
- /* Get supported hash algorithms */
- rp->hash_alg_list = silc_hash_get_supported();
- rp->hash_alg_len = strlen(rp->hash_alg_list);
-
- /* Get supported HMACs */
- rp->hmac_alg_list = silc_hmac_get_supported();
- rp->hmac_alg_len = strlen(rp->hmac_alg_list);
-
- /* XXX */
- /* Get supported compression algorithms */
- rp->comp_alg_list = strdup("none");
- rp->comp_alg_len = strlen("none");
-
- rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
- 2 + rp->version_len +
- 2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len +
- 2 + rp->enc_alg_len + 2 + rp->hash_alg_len +
- 2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
-
- return rp;
-}
-
/* Processes the provided key material `data' as the SILC protocol
specification defines. */
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;
}
software_version_string,
vendor_version);
}
+
+/* Get security properties */
+
+SilcSKESecurityProperties silc_ske_get_security_properties(SilcSKE ske)
+{
+ return ske->prop;
+}
+
+/* Get key material */
+
+SilcSKEKeyMaterial silc_ske_get_key_material(SilcSKE ske)
+{
+ return ske->keymat;
+}