/*
- silcske.c
+ silcske.c
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2000 - 2002 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
*/
/* $Id$ */
-#include "silcincludes.h"
+#include "silc.h"
#include "silcske.h"
#include "groups_internal.h"
-/* Static functions */
-static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
- SilcUInt32 len,
- SilcMPInt *rnd);
-static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
+/************************** Types and definitions ***************************/
+
+/* Structure to hold all SKE callbacks. */
+struct SilcSKECallbacksStruct {
+ SilcSKEVerifyCb verify_key;
+ SilcSKECompletionCb completed;
+ void *context;
+};
+
+/************************ Static utility functions **************************/
+
+/* States */
+SILC_FSM_STATE(silc_ske_st_initiator_start);
+SILC_FSM_STATE(silc_ske_st_initiator_phase1);
+SILC_FSM_STATE(silc_ske_st_initiator_phase2);
+SILC_FSM_STATE(silc_ske_st_initiator_phase3);
+SILC_FSM_STATE(silc_ske_st_initiator_phase4);
+SILC_FSM_STATE(silc_ske_st_initiator_end);
+SILC_FSM_STATE(silc_ske_st_initiator_aborted);
+SILC_FSM_STATE(silc_ske_st_initiator_error);
+SILC_FSM_STATE(silc_ske_st_initiator_failure);
+SILC_FSM_STATE(silc_ske_st_responder_start);
+SILC_FSM_STATE(silc_ske_st_responder_phase1);
+SILC_FSM_STATE(silc_ske_st_responder_phase2);
+SILC_FSM_STATE(silc_ske_st_responder_phase4);
+SILC_FSM_STATE(silc_ske_st_responder_phase5);
+SILC_FSM_STATE(silc_ske_st_responder_end);
+SILC_FSM_STATE(silc_ske_st_responder_aborted);
+SILC_FSM_STATE(silc_ske_st_responder_failure);
+SILC_FSM_STATE(silc_ske_st_responder_error);
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start);
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_done);
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_end);
+SILC_FSM_STATE(silc_ske_st_rekey_responder_wait);
+SILC_FSM_STATE(silc_ske_st_rekey_responder_start);
+SILC_FSM_STATE(silc_ske_st_rekey_responder_done);
+SILC_FSM_STATE(silc_ske_st_rekey_responder_end);
+SILC_TASK_CALLBACK(silc_ske_packet_send_retry);
+
+SilcSKEKeyMaterial
+silc_ske_process_key_material(SilcSKE ske,
+ SilcUInt32 req_iv_len,
+ SilcUInt32 req_enc_key_len,
+ SilcUInt32 req_hmac_key_len,
+ SilcSKERekeyMaterial *rekey);
+static SilcBool silc_ske_packet_send(SilcSKE ske,
+ SilcPacketType type,
+ SilcPacketFlags flags,
+ const unsigned char *data,
+ SilcUInt32 data_len);
+
+/* Packet callback */
+
+static SilcBool silc_ske_packet_receive(SilcPacketEngine engine,
+ SilcPacketStream stream,
+ SilcPacket packet,
+ void *callback_context,
+ 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;
+
+ /* 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;
+}
+
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_ske_stream_cbs =
+{
+ silc_ske_packet_receive, NULL, NULL
+};
+
+/* Aborts SKE protocol */
+
+static void silc_ske_abort(SilcAsyncOperation op, void *context)
+{
+ SilcSKE ske = context;
+ ske->aborted = TRUE;
+}
+
+/* Public key verification completion callback */
+
+static void silc_ske_pk_verified(SilcSKE ske, SilcSKEStatus status,
+ void *completion_context)
+{
+ ske->status = status;
+ 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 r_software_version = 0;
+ char *r_software_string = NULL;
+
+ if (!ske->remote_version || !ske->version)
+ return SILC_SKE_STATUS_BAD_VERSION;
+
+ if (!silc_parse_version_string(ske->remote_version, NULL, NULL,
+ &r_software_version,
+ &r_software_string, NULL))
+ return SILC_SKE_STATUS_BAD_VERSION;
+
+ return SILC_SKE_STATUS_OK;
+}
+
+/* Selects the supported security properties from the initiator's Key
+ 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 remote_payload,
+ SilcSKESecurityProperties *prop)
+{
+ SilcSKEStatus status;
+ SilcSKEStartPayload rp, payload;
+ char *cp;
+ int len;
+
+ SILC_LOG_DEBUG(("Parsing KE Start Payload"));
+
+ 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);
+ if (status != SILC_SKE_STATUS_OK) {
+ ske->status = status;
+ return status;
+ }
+
+ /* Flags are returned unchanged. */
+ (*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));
+ if (!payload->cookie) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return status;
+ }
+ 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) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return status;
+ }
+ payload->version_len = strlen(ske->version);
+
+ /* Get supported Key Exchange groups */
+ cp = rp->ke_grp_list;
+ if (cp && strchr(cp, ',')) {
+ while(cp) {
+ char *item;
+
+ len = strcspn(cp, ",");
+ item = silc_calloc(len + 1, sizeof(char));
+ if (!item) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return status;
+ }
+ memcpy(item, cp, len);
+
+ SILC_LOG_DEBUG(("Proposed KE group `%s'", item));
+
+ if (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
+ SILC_LOG_DEBUG(("Found KE group `%s'", item));
+
+ payload->ke_grp_len = len;
+ payload->ke_grp_list = item;
+ break;
+ }
+
+ cp += len;
+ if (strlen(cp) == 0)
+ cp = NULL;
+ else
+ cp++;
+
+ if (item)
+ silc_free(item);
+ }
+
+ if (!payload->ke_grp_len && !payload->ke_grp_list) {
+ SILC_LOG_DEBUG(("Could not find supported KE group"));
+ silc_free(payload);
+ return SILC_SKE_STATUS_UNKNOWN_GROUP;
+ }
+ } else {
+ 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_len = rp->ke_grp_len;
+ 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, ',')) {
+ while(cp) {
+ char *item;
+
+ len = strcspn(cp, ",");
+ item = silc_calloc(len + 1, sizeof(char));
+ if (!item) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return status;
+ }
+ memcpy(item, cp, len);
+
+ SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", item));
+
+ if (silc_pkcs_find_algorithm(item, NULL)) {
+ SILC_LOG_DEBUG(("Found PKCS alg `%s'", item));
+
+ payload->pkcs_alg_len = len;
+ payload->pkcs_alg_list = item;
+ break;
+ }
+
+ cp += len;
+ if (strlen(cp) == 0)
+ cp = NULL;
+ else
+ cp++;
+
+ if (item)
+ silc_free(item);
+ }
+
+ if (!payload->pkcs_alg_len && !payload->pkcs_alg_list) {
+ SILC_LOG_DEBUG(("Could not find supported PKCS alg"));
+ silc_free(payload->ke_grp_list);
+ silc_free(payload);
+ return SILC_SKE_STATUS_UNKNOWN_PKCS;
+ }
+ } else {
+ SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list));
+ SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list));
+
+ payload->pkcs_alg_len = rp->pkcs_alg_len;
+ payload->pkcs_alg_list = strdup(rp->pkcs_alg_list);
+ }
+
+ /* Get supported encryption algorithms */
+ cp = rp->enc_alg_list;
+ if (cp && strchr(cp, ',')) {
+ while(cp) {
+ char *item;
+
+ len = strcspn(cp, ",");
+ item = silc_calloc(len + 1, sizeof(char));
+ if (!item) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return status;
+ }
+ memcpy(item, cp, len);
+
+ SILC_LOG_DEBUG(("Proposed encryption alg `%s'", item));
+
+ if (silc_cipher_is_supported(item) == TRUE) {
+ SILC_LOG_DEBUG(("Found encryption alg `%s'", item));
+
+ payload->enc_alg_len = len;
+ payload->enc_alg_list = item;
+ break;
+ }
+
+ cp += len;
+ if (strlen(cp) == 0)
+ cp = NULL;
+ else
+ cp++;
+
+ if (item)
+ silc_free(item);
+ }
+
+ if (!payload->enc_alg_len && !payload->enc_alg_list) {
+ SILC_LOG_DEBUG(("Could not find supported encryption alg"));
+ silc_free(payload->ke_grp_list);
+ silc_free(payload->pkcs_alg_list);
+ silc_free(payload);
+ return SILC_SKE_STATUS_UNKNOWN_CIPHER;
+ }
+ } else {
+ SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
+ rp->enc_alg_list));
+
+ payload->enc_alg_len = rp->enc_alg_len;
+ 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, ',')) {
+ while(cp) {
+ char *item;
+
+ len = strcspn(cp, ",");
+ item = silc_calloc(len + 1, sizeof(char));
+ if (!item) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return status;
+ }
+ memcpy(item, cp, len);
+
+ SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
+
+ if (silc_hash_is_supported(item) == TRUE) {
+ SILC_LOG_DEBUG(("Found hash alg `%s'", item));
+
+ payload->hash_alg_len = len;
+ payload->hash_alg_list = item;
+ break;
+ }
+
+ cp += len;
+ if (strlen(cp) == 0)
+ cp = NULL;
+ else
+ cp++;
+
+ if (item)
+ silc_free(item);
+ }
+
+ if (!payload->hash_alg_len && !payload->hash_alg_list) {
+ SILC_LOG_DEBUG(("Could not find supported hash alg"));
+ 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;
+ }
+ } else {
+ SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it",
+ rp->hash_alg_list));
+
+ payload->hash_alg_len = rp->hash_alg_len;
+ 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, ',')) {
+ while(cp) {
+ char *item;
+
+ len = strcspn(cp, ",");
+ item = silc_calloc(len + 1, sizeof(char));
+ if (!item) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return status;
+ }
+ memcpy(item, cp, len);
+
+ SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
+
+ if (silc_hmac_is_supported(item) == TRUE) {
+ SILC_LOG_DEBUG(("Found HMAC `%s'", item));
+
+ payload->hmac_alg_len = len;
+ payload->hmac_alg_list = item;
+ break;
+ }
+
+ cp += len;
+ if (strlen(cp) == 0)
+ cp = NULL;
+ else
+ cp++;
+
+ if (item)
+ silc_free(item);
+ }
+
+ if (!payload->hmac_alg_len && !payload->hmac_alg_list) {
+ SILC_LOG_DEBUG(("Could not find supported HMAC"));
+ 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;
+ }
+ } else {
+ SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
+ rp->hmac_alg_list));
+
+ payload->hmac_alg_len = rp->hmac_alg_len;
+ 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, ',')) {
+ while(cp) {
+ char *item;
+
+ len = strcspn(cp, ",");
+ item = silc_calloc(len + 1, sizeof(char));
+ if (!item) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return status;
+ }
+ memcpy(item, cp, len);
+
+ SILC_LOG_DEBUG(("Proposed Compression `%s'", item));
+
+#if 1
+ if (!strcmp(item, "none")) {
+ SILC_LOG_DEBUG(("Found Compression `%s'", item));
+ payload->comp_alg_len = len;
+ payload->comp_alg_list = item;
+ break;
+ }
+#else
+ if (silc_hmac_is_supported(item) == TRUE) {
+ SILC_LOG_DEBUG(("Found Compression `%s'", item));
+ payload->comp_alg_len = len;
+ payload->comp_alg_list = item;
+ break;
+ }
+#endif
+
+ cp += len;
+ if (strlen(cp) == 0)
+ cp = NULL;
+ else
+ cp++;
+
+ if (item)
+ silc_free(item);
+ }
+ }
+
+ payload->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
+ 2 + payload->version_len +
+ 2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len +
+ 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;
+}
+
+/* Creates random number such that 1 < rnd < n and at most length
+ of len bits. The rnd sent as argument must be initialized. */
+
+static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
+ SilcUInt32 len,
+ SilcMPInt *rnd)
+{
+ SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ unsigned char *string;
+ SilcUInt32 l;
+
+ if (!len)
+ return SILC_SKE_STATUS_ERROR;
+
+ SILC_LOG_DEBUG(("Creating random number"));
+
+ l = ((len - 1) / 8);
+
+ /* Get the random number as string */
+ string = silc_rng_get_rn_data(ske->rng, l);
+ if (!string)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
+
+ /* Decode the string into a MP integer */
+ silc_mp_bin2mp(string, l, rnd);
+ silc_mp_mod_2exp(rnd, rnd, len);
+
+ /* Checks */
+ if (silc_mp_cmp_ui(rnd, 1) < 0)
+ status = SILC_SKE_STATUS_ERROR;
+ if (silc_mp_cmp(rnd, n) >= 0)
+ status = SILC_SKE_STATUS_ERROR;
+
+ memset(string, 'F', l);
+ silc_free(string);
+
+ return status;
+}
+
+/* Creates a hash value HASH as defined in the SKE protocol. If the
+ `initiator' is TRUE then this function is used to create the HASH_i
+ hash value defined in the protocol. If it is FALSE then this is used
+ to create the HASH value defined by the protocol. */
+
+static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
unsigned char *return_hash,
SilcUInt32 *return_hash_len,
- int initiator);
+ int initiator)
+{
+ SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ SilcBuffer buf;
+ unsigned char *e, *f, *KEY, *s_data;
+ SilcUInt32 e_len, f_len, KEY_len, s_len;
+ int ret;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (initiator == FALSE) {
+ s_data = (ske->start_payload_copy ?
+ silc_buffer_data(ske->start_payload_copy) : NULL);
+ s_len = (ske->start_payload_copy ?
+ silc_buffer_len(ske->start_payload_copy) : 0);
+ e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
+ f = silc_mp_mp2bin(&ske->ke2_payload->x, 0, &f_len);
+ KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
+
+ /* Format the buffer used to compute the hash value */
+ buf = silc_buffer_alloc_size(s_len +
+ ske->ke2_payload->pk_len +
+ ske->ke1_payload->pk_len +
+ e_len + f_len + KEY_len);
+ if (!buf)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
+
+ /* Initiator is not required to send its public key */
+ if (!ske->ke1_payload->pk_data) {
+ ret =
+ silc_buffer_format(buf,
+ SILC_STR_DATA(s_data, s_len),
+ SILC_STR_DATA(ske->ke2_payload->pk_data,
+ ske->ke2_payload->pk_len),
+ SILC_STR_DATA(e, e_len),
+ SILC_STR_DATA(f, f_len),
+ SILC_STR_DATA(KEY, KEY_len),
+ SILC_STR_END);
+ } else {
+ ret =
+ silc_buffer_format(buf,
+ SILC_STR_DATA(s_data, s_len),
+ SILC_STR_DATA(ske->ke2_payload->pk_data,
+ ske->ke2_payload->pk_len),
+ SILC_STR_DATA(ske->ke1_payload->pk_data,
+ ske->ke1_payload->pk_len),
+ SILC_STR_DATA(e, e_len),
+ SILC_STR_DATA(f, f_len),
+ SILC_STR_DATA(KEY, KEY_len),
+ SILC_STR_END);
+ }
+ if (ret == -1) {
+ silc_buffer_free(buf);
+ memset(e, 0, e_len);
+ memset(f, 0, f_len);
+ memset(KEY, 0, KEY_len);
+ silc_free(e);
+ silc_free(f);
+ silc_free(KEY);
+ return SILC_SKE_STATUS_ERROR;
+ }
+
+ memset(e, 0, e_len);
+ memset(f, 0, f_len);
+ memset(KEY, 0, KEY_len);
+ silc_free(e);
+ silc_free(f);
+ silc_free(KEY);
+ } else {
+ s_data = (ske->start_payload_copy ?
+ silc_buffer_data(ske->start_payload_copy) : NULL);
+ s_len = (ske->start_payload_copy ?
+ silc_buffer_len(ske->start_payload_copy) : 0);
+ e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
+
+ buf = silc_buffer_alloc_size(s_len + ske->ke1_payload->pk_len + e_len);
+ if (!buf)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
+
+ /* Format the buffer used to compute the hash value */
+ ret =
+ silc_buffer_format(buf,
+ SILC_STR_DATA(s_data, s_len),
+ SILC_STR_DATA(ske->ke1_payload->pk_data,
+ ske->ke1_payload->pk_len),
+ SILC_STR_DATA(e, e_len),
+ SILC_STR_END);
+ if (ret == -1) {
+ silc_buffer_free(buf);
+ memset(e, 0, e_len);
+ silc_free(e);
+ return SILC_SKE_STATUS_ERROR;
+ }
+
+ SILC_LOG_HEXDUMP(("hash buf"), buf->data, silc_buffer_len(buf));
+
+ memset(e, 0, e_len);
+ silc_free(e);
+ }
+
+ /* Make the hash */
+ silc_hash_make(ske->prop->hash, buf->data, silc_buffer_len(buf),
+ return_hash);
+ *return_hash_len = silc_hash_len(ske->prop->hash);
+
+ if (initiator == FALSE) {
+ SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len);
+ } else {
+ SILC_LOG_HEXDUMP(("HASH_i"), return_hash, *return_hash_len);
+ }
+
+ silc_buffer_free(buf);
+
+ 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
+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(("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;
-/* Structure to hold all SKE callbacks. */
-struct SilcSKECallbacksStruct {
- SilcSKESendPacketCb send_packet;
- SilcSKECb payload_receive;
- SilcSKEVerifyCb verify_key;
- SilcSKECb proto_continue;
- SilcSKECheckVersion check_version;
- void *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 *******************************/
/* Allocates new SKE object. */
-SilcSKE silc_ske_alloc(SilcRng rng, void *context)
+SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
+ SilcSKR repository, SilcPublicKey public_key,
+ SilcPrivateKey private_key, void *context)
{
SilcSKE ske;
SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
+ 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->users = 1;
+ ske->schedule = schedule;
+ ske->public_key = public_key;
+ ske->private_key = private_key;
+ ske->retry_timer = SILC_SKE_RETRY_MIN;
+ ske->refcnt = 1;
return ske;
}
void silc_ske_free(SilcSKE ske)
{
- ske->users--;
- if (ske->users > 0) {
- SILC_LOG_DEBUG(("Key Exchange set to FREED status"));
- ske->status = SILC_SKE_STATUS_FREED;
+ SILC_LOG_DEBUG(("Freeing Key Exchange object"));
+
+ if (!ske)
return;
- }
- SILC_LOG_DEBUG(("Freeing Key Exchange object"));
+ if (ske->running) {
+ ske->freed = TRUE;
- 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->pkcs)
- silc_pkcs_free(ske->prop->pkcs);
- 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);
+ 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);
}
- silc_free(ske->hash);
- silc_free(ske->callbacks);
- silc_free(ske);
+ return;
+ }
+
+ ske->refcnt--;
+ if (ske->refcnt > 0)
+ return;
+
+ /* Free start payload */
+ if (ske->start_payload)
+ silc_ske_payload_start_free(ske->start_payload);
+
+ /* Free KE payload */
+ if (ske->ke1_payload)
+ silc_ske_payload_ke_free(ske->ke1_payload);
+ if (ske->ke2_payload)
+ silc_ske_payload_ke_free(ske->ke2_payload);
+ silc_free(ske->remote_version);
+
+ /* Free rest */
+ if (ske->prop) {
+ if (ske->prop->group)
+ silc_ske_group_free(ske->prop->group);
+ if (ske->prop->cipher)
+ silc_cipher_free(ske->prop->cipher);
+ if (ske->prop->hash)
+ silc_hash_free(ske->prop->hash);
+ if (ske->prop->hmac)
+ silc_hmac_free(ske->prop->hmac);
+ if (ske->prop->public_key)
+ silc_pkcs_public_key_free(ske->prop->public_key);
+ silc_free(ske->prop);
}
+ if (ske->keymat)
+ silc_ske_free_key_material(ske->keymat);
+ if (ske->start_payload_copy)
+ silc_buffer_free(ske->start_payload_copy);
+ if (ske->x) {
+ silc_mp_uninit(ske->x);
+ silc_free(ske->x);
+ }
+ if (ske->KEY) {
+ silc_mp_uninit(ske->KEY);
+ silc_free(ske->KEY);
+ }
+ silc_free(ske->retrans.data);
+ silc_free(ske->hash);
+ silc_free(ske->callbacks);
+
+ memset(ske, 'F', sizeof(*ske));
+ silc_free(ske);
+}
+
+/* Return user context */
+
+void *silc_ske_get_context(SilcSKE ske)
+{
+ return ske->user_data;
}
-/* Sets the callback functions for the SKE session.
-
- The `send_packet' callback is a function that sends the packet to
- network. The SKE library will call it at any time packet needs to
- be sent to the remote host.
-
- The `payload_receive' callback is called when the remote host's Key
- Exchange Start Payload has been processed. The payload is saved
- to ske->start_payload if the application would need it. The application
- must also provide the payload to the next state of the SKE.
-
- The `verify_key' callback is called to verify the received public key
- or certificate. The verification process is most likely asynchronous.
- That is why the application must call the completion callback when the
- verification process has been completed. The library then calls the user
- callback (`proto_continue'), if it is provided to indicate that the SKE
- protocol may continue.
-
- The `proto_continue' callback is called to indicate that it is
- safe to continue the execution of the SKE protocol after executing
- an asynchronous operation, such as calling the `verify_key' callback
- function, which is asynchronous. The application should check the
- ske->status in this function to check whether it is Ok to continue
- the execution of the protocol.
-
- The `check_version' callback is called to verify the remote host's
- version. The application may check its own version against the remote
- host's version and determine whether supporting the remote host
- is possible.
-
- The `context' is passed as argument to all of the above callback
- functions. */
+/* Sets protocol callbacks */
void silc_ske_set_callbacks(SilcSKE ske,
- SilcSKESendPacketCb send_packet,
- SilcSKECb payload_receive,
SilcSKEVerifyCb verify_key,
- SilcSKECb proto_continue,
- SilcSKECheckVersion check_version,
+ SilcSKECompletionCb completed,
void *context)
{
if (ske->callbacks)
ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks));
if (!ske->callbacks)
return;
- ske->callbacks->send_packet = send_packet;
- ske->callbacks->payload_receive = payload_receive;
ske->callbacks->verify_key = verify_key;
- ske->callbacks->proto_continue = proto_continue;
- ske->callbacks->check_version = check_version;
+ ske->callbacks->completed = completed;
ske->callbacks->context = context;
}
-/* Starts the SILC Key Exchange protocol for initiator. The connection
- to the remote end must be established before calling this function
- and the connecting socket must be sent as argument. This function
- creates the Key Exchange Start Payload which includes all our
- configured security properties. This payload is then sent to the
- remote end for further processing. This payload must be sent as
- argument to the function, however, it must not be encoded
- already, it is done by this function. The caller must not free
- the `start_payload' since the SKE library will save it.
-
- The packet sending is done by calling a callback function. Caller
- must provide a routine to send the packet. */
-
-SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
- SilcSocketConnection sock,
- SilcSKEStartPayload *start_payload)
+
+/******************************** Initiator *********************************/
+
+/* Start protocol. Send our proposal */
+
+SILC_FSM_STATE(silc_ske_st_initiator_start)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ SilcSKE ske = fsm_context;
SilcBuffer payload_buf;
+ SilcStatus status;
SILC_LOG_DEBUG(("Start"));
- ske->sock = sock;
- ske->rng = rng;
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+ return SILC_FSM_CONTINUE;
+ }
/* Encode the payload */
- status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
- if (status != SILC_SKE_STATUS_OK)
- return status;
+ status = silc_ske_payload_start_encode(ske, ske->start_payload,
+ &payload_buf);
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Error encoding Start Payload */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
- /* Take a copy of the payload buffer for future use. It is used to
+ /* Save the the payload buffer for future use. It is later used to
compute the HASH value. */
- ske->start_payload_copy = silc_buffer_copy(payload_buf);
- ske->start_payload = start_payload;
+ ske->start_payload_copy = payload_buf;
/* Send the packet. */
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE,
- ske->callbacks->context);
+ 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;
+ }
- silc_buffer_free(payload_buf);
+ /* Add key exchange timeout */
+ silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout,
+ ske, ske->timeout, 0);
- return status;
+ /** Wait for responder proposal */
+ SILC_LOG_DEBUG(("Waiting for responder proposal"));
+ silc_fsm_next(fsm, silc_ske_st_initiator_phase1);
+ return SILC_FSM_WAIT;
}
-/* Function called after ske_initiator_start fuction. This receives
- the remote ends Key Exchange Start payload which includes the
- security properties selected by the responder from our payload
- sent in the silc_ske_initiator_start function. */
+/* Phase-1. Receives responder's proposal */
-SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
- SilcBuffer start_payload)
+SILC_FSM_STATE(silc_ske_st_initiator_phase1)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcSKEStartPayload *payload;
+ SilcSKE ske = fsm_context;
+ 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"));
+ 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);
+ ske->packet = NULL;
+ return SILC_FSM_WAIT;
+ }
+
/* Decode the payload */
- status = silc_ske_payload_start_decode(ske, start_payload, &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_ske_payload_start_free(ske->start_payload);
- return 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)) {
- SILC_LOG_ERROR(("Responder modified our cookie and it must not do it"));
+ /* 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(("Invalid cookie, modified or unsupported feature"));
ske->status = SILC_SKE_STATUS_INVALID_COOKIE;
- silc_ske_payload_start_free(ske->start_payload);
- return status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
/* Check version string */
- if (ske->callbacks->check_version) {
- status = ske->callbacks->check_version(ske, payload->version,
- payload->version_len,
- ske->callbacks->context);
- if (status != SILC_SKE_STATUS_OK) {
- ske->status = status;
- silc_ske_payload_start_free(ske->start_payload);
- return status;
- }
+ ske->remote_version = silc_memdup(payload->version, payload->version_len);
+ status = silc_ske_check_version(ske);
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Version mismatch */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
/* Free our KE Start Payload context, we don't need it anymore. */
silc_ske_payload_start_free(ske->start_payload);
+ ske->start_payload = NULL;
/* Take the selected security properties into use while doing
- the key exchange. This is used only while doing the key
- exchange. The same data is returned to upper levels by calling
- the callback function. */
+ the key exchange. This is used only while doing the key
+ exchange. */
ske->prop = prop = silc_calloc(1, sizeof(*prop));
if (!ske->prop)
goto err;
goto err;
prop->group = group;
+ prop->remote_port = remote_port;
- if (silc_pkcs_alloc(payload->pkcs_alg_list, &prop->pkcs) == FALSE) {
+ if (silc_pkcs_find_algorithm(payload->pkcs_alg_list, NULL) == NULL) {
status = SILC_SKE_STATUS_UNKNOWN_PKCS;
goto err;
}
-
if (silc_cipher_alloc(payload->enc_alg_list, &prop->cipher) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
goto err;
}
-
if (silc_hash_alloc(payload->hash_alg_list, &prop->hash) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
goto err;
}
-
if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_HMAC;
goto err;
/* Save remote's KE Start Payload */
ske->start_payload = payload;
- /* Return the received payload by calling the callback function. */
- if (ske->callbacks->payload_receive)
- (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
-
- return status;
+ /** Send KE Payload */
+ silc_fsm_next(fsm, silc_ske_st_initiator_phase2);
+ return SILC_FSM_CONTINUE;
err:
if (payload)
silc_ske_payload_start_free(payload);
-
- silc_ske_group_free(group);
-
- if (prop->pkcs)
- silc_pkcs_free(prop->pkcs);
+ if (group)
+ silc_ske_group_free(group);
if (prop->cipher)
silc_cipher_free(prop->cipher);
if (prop->hash)
ske->prop = NULL;
if (status == SILC_SKE_STATUS_OK)
- return SILC_SKE_STATUS_ERROR;
+ status = SILC_SKE_STATUS_ERROR;
+ /** Error */
ske->status = status;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
-/* This function creates random number x, such that 1 < x < q and
- computes e = g ^ x mod p and sends the result to the remote end in
- Key Exchange Payload. */
+/* Phase-2. Send KE payload */
-SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
- SilcPublicKey public_key,
- SilcPrivateKey private_key,
- SilcSKEPKType pk_type)
+SILC_FSM_STATE(silc_ske_st_initiator_phase2)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
SilcBuffer payload_buf;
SilcMPInt *x;
- SilcSKEKEPayload *payload;
+ SilcSKEKEPayload payload;
SilcUInt32 pk_len;
SILC_LOG_DEBUG(("Start"));
/* Create the random number x, 1 < x < q. */
x = silc_calloc(1, sizeof(*x));
if (!x){
+ /** Out of memory */
ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
- return ske->status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
silc_mp_init(x);
- status =
+ status =
silc_ske_create_rnd(ske, &ske->prop->group->group_order,
silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
x);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error generating random number */
silc_mp_uninit(x);
silc_free(x);
ske->status = status;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
/* Encode the result to Key Exchange Payload. */
payload = silc_calloc(1, sizeof(*payload));
if (!payload) {
+ /** Out of memory */
silc_mp_uninit(x);
silc_free(x);
ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
- return ske->status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
ske->ke1_payload = payload;
/* Do the Diffie Hellman computation, e = g ^ x mod p */
silc_mp_init(&payload->x);
- silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x,
+ silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x,
&ske->prop->group->group);
/* Get public key */
- if (public_key) {
- payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
- if (!payload->pk_data) {
- silc_mp_uninit(x);
- silc_free(x);
- silc_mp_uninit(&payload->x);
- silc_free(payload);
- ske->status = SILC_SKE_STATUS_OK;
- return ske->status;
- }
- 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 = 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 (private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
- unsigned char hash[32], sign[2048];
+ 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;
SILC_LOG_DEBUG(("We are doing mutual authentication"));
silc_ske_make_hash(ske, hash, &hash_len, TRUE);
SILC_LOG_DEBUG(("Signing HASH_i value"));
-
+
/* Sign the hash value */
- silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv,
- private_key->prv_len);
- if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
- !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
+ if (!silc_pkcs_sign(ske->private_key, hash, hash_len, sign,
+ sizeof(sign) - 1, &sign_len, FALSE, ske->prop->hash)) {
+ /** Error computing signature */
silc_mp_uninit(x);
silc_free(x);
silc_mp_uninit(&payload->x);
silc_free(payload->pk_data);
silc_free(payload);
+ ske->ke1_payload = NULL;
ske->status = SILC_SKE_STATUS_SIGNATURE_ERROR;
- return ske->status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
- payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
- memcpy(payload->sign_data, sign, sign_len);
+ payload->sign_data = silc_memdup(sign, sign_len);
+ if (payload->sign_data)
+ payload->sign_len = sign_len;
memset(sign, 0, sizeof(sign));
- payload->sign_len = sign_len;
}
status = silc_ske_payload_ke_encode(ske, payload, &payload_buf);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error encoding KE payload */
silc_mp_uninit(x);
silc_free(x);
silc_mp_uninit(&payload->x);
silc_free(payload->pk_data);
+ silc_free(payload->sign_data);
silc_free(payload);
+ ske->ke1_payload = NULL;
ske->status = status;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
ske->x = x;
+ /* Check for backwards compatibility */
+
/* Send the packet. */
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, payload_buf,
- SILC_PACKET_KEY_EXCHANGE_1,
- ske->callbacks->context);
+ 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);
- return status;
+ /** Waiting responder's KE payload */
+ silc_fsm_next(fsm, silc_ske_st_initiator_phase3);
+ return SILC_FSM_WAIT;
}
-/* An initiator finish final callback that is called to indicate that
- the SKE protocol may continue. */
+/* Phase-3. Process responder's KE payload */
-static void silc_ske_initiator_finish_final(SilcSKE ske,
- SilcSKEStatus status,
- void *context)
+SILC_FSM_STATE(silc_ske_st_initiator_phase3)
{
- SilcSKEKEPayload *payload;
- unsigned char hash[32];
- SilcUInt32 hash_len;
- SilcPublicKey public_key = NULL;
-
- /* If the SKE was freed during the async call then free it really now,
- otherwise just decrement the reference counter. */
- if (ske->status == SILC_SKE_STATUS_FREED) {
- silc_ske_free(ske);
- return;
- }
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEKEPayload payload;
+ SilcMPInt *KEY;
+ SilcBuffer packet_buf = &ske->packet->buffer;
- /* If the caller returns PENDING status SKE library will assume that
- the caller will re-call this callback when it is not anymore in
- PENDING status. */
- if (status == SILC_SKE_STATUS_PENDING)
- return;
+ SILC_LOG_DEBUG(("Start"));
- ske->users--;
- payload = ske->ke2_payload;
+ 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);
+ ske->packet = NULL;
+ return SILC_FSM_WAIT;
+ }
- /* If the status is an error then the public key that was verified
- by the caller is not authentic. */
+ /* 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;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
+ 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_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 (payload->pk_data) {
- /* Decode the public key */
- if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len,
- &public_key)) {
- status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
+ SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+
+ /* Compute the shared secret key */
+ KEY = silc_calloc(1, sizeof(*KEY));
+ silc_mp_init(KEY);
+ silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group);
+ ske->KEY = KEY;
+
+ /* Decode the remote's public key */
+ if (payload->pk_data &&
+ !silc_pkcs_public_key_alloc(payload->pk_type,
+ payload->pk_data, payload->pk_len,
+ &ske->prop->public_key)) {
+ SILC_LOG_ERROR(("Unsupported/malformed public key received"));
+ status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+ goto err;
+ }
+
+ 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);
+
+ /* 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, silc_fsm_get_schedule(fsm),
+ find, silc_ske_skr_callback, ske));
+ } else {
+ /* Verify from application */
+ SILC_FSM_CALL(ske->callbacks->verify_key(ske, ske->prop->public_key,
+ ske->callbacks->context,
+ silc_ske_pk_verified, NULL));
}
+ /* NOT REACHED */
+ }
- SILC_LOG_DEBUG(("Public key is authentic"));
+ /** Process key material */
+ silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
+ return SILC_FSM_CONTINUE;
- /* Compute the hash value */
- status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
- if (status != SILC_SKE_STATUS_OK)
- goto err;
+ err:
+ silc_ske_payload_ke_free(payload);
+ ske->ke2_payload = NULL;
+
+ silc_mp_uninit(ske->KEY);
+ silc_free(ske->KEY);
+ ske->KEY = NULL;
+
+ if (status == SILC_SKE_STATUS_OK)
+ return SILC_SKE_STATUS_ERROR;
+
+ /** Error */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+}
+
+/* Process key material */
+
+SILC_FSM_STATE(silc_ske_st_initiator_phase4)
+{
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEKEPayload payload;
+ unsigned char hash[SILC_HASH_MAXLEN];
+ SilcUInt32 hash_len;
+ int key_len, block_len;
+
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+ return SILC_FSM_CONTINUE;
+ }
- ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
- memcpy(ske->hash, hash, hash_len);
- ske->hash_len = hash_len;
+ /* Check result of public key verification */
+ if (ske->status != SILC_SKE_STATUS_OK) {
+ /** Public key not verified */
+ SILC_LOG_DEBUG(("Public key verification failed"));
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ payload = ske->ke2_payload;
+
+ /* Compute the HASH value */
+ SILC_LOG_DEBUG(("Computing HASH value"));
+ status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+ if (status != SILC_SKE_STATUS_OK)
+ goto err;
+ ske->hash = silc_memdup(hash, hash_len);
+ ske->hash_len = hash_len;
+ if (ske->prop->public_key) {
+ SILC_LOG_DEBUG(("Public key is authentic"));
SILC_LOG_DEBUG(("Verifying signature (HASH)"));
/* Verify signature */
- silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
- if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data,
- payload->sign_len, hash, hash_len) == FALSE) {
-
- SILC_LOG_DEBUG(("Signature don't match"));
+ if (!silc_pkcs_verify(ske->prop->public_key, payload->sign_data,
+ payload->sign_len, hash, hash_len, NULL)) {
+ SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
goto err;
}
SILC_LOG_DEBUG(("Signature is Ok"));
-
- silc_pkcs_public_key_free(public_key);
memset(hash, 'F', hash_len);
}
ske->status = SILC_SKE_STATUS_OK;
- /* Call the callback. The caller may now continue the SKE protocol. */
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
+ /* 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_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,
+ &ske->rekey);
+ if (!ske->keymat) {
+ SILC_LOG_ERROR(("Error processing key material"));
+ status = SILC_SKE_STATUS_ERROR;
+ goto err;
+ }
+
+ /* Send SUCCESS packet */
+ 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;
+ }
- return;
+ /** Waiting completion */
+ silc_fsm_next(fsm, silc_ske_st_initiator_end);
+ return SILC_FSM_WAIT;
err:
memset(hash, 'F', sizeof(hash));
silc_free(ske->KEY);
ske->KEY = NULL;
- if (public_key)
- silc_pkcs_public_key_free(public_key);
-
if (ske->hash) {
memset(ske->hash, 'F', hash_len);
silc_free(ske->hash);
}
if (status == SILC_SKE_STATUS_OK)
- ske->status = SILC_SKE_STATUS_ERROR;
+ status = SILC_SKE_STATUS_ERROR;
+ /** Error */
ske->status = status;
-
- /* Call the callback. */
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
-/* Receives Key Exchange Payload from responder consisting responders
- public key, f, and signature. This function verifies the public key,
- computes the secret shared key and verifies the signature.
-
- The `proto_continue' will be called to indicate that the caller may
- continue with the SKE protocol. The caller must not continue
- before the SKE libary has called that callback. If this function
- returns an error the callback will not be called. It is called
- if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
- However, note that when the library calls the callback the ske->status
- may be error.
-
- This calls the `verify_key' callback to verify the received public
- key or certificate. If the `verify_key' is provided then the remote
- must send public key and it is considered to be an error if remote
- does not send its public key. If caller is performing a re-key with
- SKE then the `verify_key' is usually not provided when it is not also
- required for the remote to send its public key. */
-
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
- SilcBuffer ke_payload)
+/* Protocol completed */
+
+SILC_FSM_STATE(silc_ske_st_initiator_end)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcSKEKEPayload *payload;
- SilcMPInt *KEY;
+ SilcSKE ske = fsm_context;
SILC_LOG_DEBUG(("Start"));
- /* Decode the payload */
- status = silc_ske_payload_ke_decode(ske, ke_payload, &payload);
- if (status != SILC_SKE_STATUS_OK) {
- ske->status = status;
- return status;
+ 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;
}
- ske->ke2_payload = payload;
- if (!payload->pk_data && ske->callbacks->verify_key) {
- 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;
+ SILC_LOG_DEBUG(("Key exchange completed successfully"));
+
+ 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;
+}
+
+/* Aborted by application */
+
+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);
+
+ /* Call completion */
+ silc_ske_completion(ske);
+
+ return SILC_FSM_FINISH;
+}
+
+/* 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);
+
+ 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;
+}
+
+/* Failure received from remote */
+
+SILC_FSM_STATE(silc_ske_st_initiator_failure)
+{
+ SilcSKE ske = fsm_context;
+ SilcUInt32 error = SILC_SKE_STATUS_ERROR;
+
+ if (ske->packet && silc_buffer_len(&ske->packet->buffer) == 4) {
+ SILC_GET32_MSB(error, ske->packet->buffer.data);
+ ske->status = error;
+ silc_packet_free(ske->packet);
+ ske->packet = NULL;
}
- SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+ SILC_LOG_DEBUG(("Error %s (%d) received during key exchange",
+ silc_ske_map_status(ske->status), ske->status));
- /* Compute the shared secret key */
- KEY = silc_calloc(1, sizeof(*KEY));
- silc_mp_init(KEY);
- silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group);
- ske->KEY = KEY;
+ silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+ silc_schedule_task_del_by_context(ske->schedule, ske);
- if (payload->pk_data && ske->callbacks->verify_key) {
- SILC_LOG_DEBUG(("Verifying public key"));
-
- ske->users++;
- (*ske->callbacks->verify_key)(ske, payload->pk_data, payload->pk_len,
- payload->pk_type, ske->callbacks->context,
- silc_ske_initiator_finish_final, NULL);
-
- /* We will continue to the final state after the public key has
- been verified by the caller. */
- return SILC_SKE_STATUS_PENDING;
+ /* Call completion */
+ silc_ske_completion(ske);
+
+ return SILC_FSM_FINISH;
+}
+
+/* Starts the protocol as initiator */
+
+SilcAsyncOperation silc_ske_initiator(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcSKEParams params,
+ SilcSKEStartPayload start_payload)
+{
+ SILC_LOG_DEBUG(("Start SKE as initiator"));
+
+ 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_finished, 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;
}
- /* Continue to final state */
- ske->users++;
- silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, NULL);
+ ske->timeout = params->timeout_secs ? params->timeout_secs : 30;
+ ske->start_payload = start_payload;
+ ske->version = params->version;
+ ske->running = TRUE;
- return SILC_SKE_STATUS_OK;
+ /* 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_KEY_EXCHANGE,
+ SILC_PACKET_KEY_EXCHANGE_2,
+ SILC_PACKET_SUCCESS,
+ SILC_PACKET_FAILURE, -1);
+
+ /* Start SKE as initiator */
+ silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start);
+
+ return &ske->op;
+}
+
+/******************************** Responder *********************************/
+
+/* Start protocol as responder. Wait initiator's start payload */
+
+SILC_FSM_STATE(silc_ske_st_responder_start)
+{
+ SilcSKE ske = fsm_context;
- err:
- silc_ske_payload_ke_free(payload);
- ske->ke2_payload = NULL;
+ SILC_LOG_DEBUG(("Start"));
- silc_mp_uninit(ske->KEY);
- silc_free(ske->KEY);
- ske->KEY = NULL;
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+ return SILC_FSM_CONTINUE;
+ }
- if (status == SILC_SKE_STATUS_OK)
- return SILC_SKE_STATUS_ERROR;
+ /* Add key exchange timeout */
+ silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout,
+ ske, ske->timeout, 0);
- ske->status = status;
- return status;
+ /** Wait for initiator */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase1);
+ return SILC_FSM_WAIT;
}
-/* Starts Key Exchange protocol for responder. Responder receives
- Key Exchange Start Payload from initiator consisting of all the
- security properties the initiator supports. This function decodes
- the payload and parses the payload further and selects the right
- security properties. */
-
-SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
- SilcSocketConnection sock,
- const char *version,
- SilcBuffer start_payload,
- SilcSKESecurityPropertyFlag flags)
+/* 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)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcSKEStartPayload *remote_payload = NULL, *payload = NULL;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEStartPayload remote_payload = NULL;
+ SilcBuffer packet_buf = &ske->packet->buffer;
+ SilcID id;
SILC_LOG_DEBUG(("Start"));
- ske->sock = sock;
- ske->rng = rng;
-
/* Decode the payload */
- status = silc_ske_payload_start_decode(ske, start_payload, &remote_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;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /* Get remote ID and set it to stream */
+ if (ske->packet->src_id_len) {
+ silc_id_str2id(ske->packet->src_id, ske->packet->src_id_len,
+ ske->packet->src_id_type,
+ (ske->packet->src_id_type == SILC_ID_SERVER ?
+ (void *)&id.u.server_id : (void *)&id.u.client_id),
+ (ske->packet->src_id_type == SILC_ID_SERVER ?
+ sizeof(id.u.server_id) : sizeof(id.u.client_id)));
+ silc_packet_set_ids(ske->stream, 0, NULL, ske->packet->src_id_type,
+ (ske->packet->src_id_type == SILC_ID_SERVER ?
+ (void *)&id.u.server_id : (void *)&id.u.client_id));
}
/* Take a copy of the payload buffer for future use. It is used to
compute the HASH value. */
- ske->start_payload_copy = silc_buffer_copy(start_payload);
+ 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 (flags & SILC_SKE_SP_FLAG_MUTUAL) {
+ if (ske->flags & SILC_SKE_SP_FLAG_MUTUAL) {
SILC_LOG_DEBUG(("Force mutual authentication"));
remote_payload->flags |= SILC_SKE_SP_FLAG_MUTUAL;
}
/* Force PFS flag if we require it */
- if (flags & SILC_SKE_SP_FLAG_PFS) {
+ if (ske->flags & SILC_SKE_SP_FLAG_PFS) {
SILC_LOG_DEBUG(("Force PFS"));
remote_payload->flags |= SILC_SKE_SP_FLAG_PFS;
}
- /* Parse and select the security properties from the payload */
- payload = silc_calloc(1, sizeof(*payload));
- status = silc_ske_select_security_properties(ske, version,
- payload, remote_payload);
+ /* Disable IV Included flag if requested */
+ if (remote_payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED &&
+ !(ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)) {
+ SILC_LOG_DEBUG(("We do not support IV Included flag"));
+ remote_payload->flags &= ~SILC_SKE_SP_FLAG_IV_INCLUDED;
+ }
+
+ /* 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 */
+ silc_ske_payload_start_free(remote_payload);
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ silc_ske_payload_start_free(remote_payload);
+
+ /* Encode our reply payload to send the selected security properties */
+ status = silc_ske_payload_start_encode(ske, ske->start_payload,
+ &packet_buf);
if (status != SILC_SKE_STATUS_OK)
goto err;
- ske->start_payload = payload;
-
- /* Call the callback function. */
- if (ske->callbacks->payload_receive)
- (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
+ /* Send the packet. */
+ if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE, 0,
+ silc_buffer_data(packet_buf),
+ silc_buffer_len(packet_buf)))
+ goto err;
- silc_ske_payload_start_free(remote_payload);
+ silc_buffer_free(packet_buf);
- return status;
+ /** Waiting initiator's KE payload */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase2);
+ return SILC_FSM_WAIT;
err:
- if (remote_payload)
- silc_ske_payload_start_free(remote_payload);
- if (payload)
- silc_free(payload);
+ 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_SKE_STATUS_ERROR;
+ status = SILC_SKE_STATUS_ERROR;
+ /** Error */
ske->status = status;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
-/* The selected security properties from the initiator payload is now
- encoded into Key Exchange Start Payload and sent to the initiator. */
+/* Phase-2. Decode initiator's KE payload */
-SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske)
+SILC_FSM_STATE(silc_ske_st_responder_phase2)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcBuffer payload_buf;
- SilcSKESecurityProperties prop;
- SilcSKEDiffieHellmanGroup group = NULL;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEKEPayload recv_payload;
+ SilcBuffer packet_buf = &ske->packet->buffer;
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));
- 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;
-
- if (silc_pkcs_alloc(ske->start_payload->pkcs_alg_list,
- &prop->pkcs) == FALSE) {
- 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 (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;
}
- if (silc_hash_alloc(ske->start_payload->hash_alg_list,
- &prop->hash) == FALSE) {
- status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
- goto err;
+ /* 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;
}
- if (silc_hmac_alloc(ske->start_payload->hmac_alg_list, NULL,
- &prop->hmac) == FALSE) {
- status = SILC_SKE_STATUS_UNKNOWN_HMAC;
- goto err;
- }
+ ske->ke1_payload = recv_payload;
- /* Encode the payload */
- status = silc_ske_payload_start_encode(ske, ske->start_payload,
- &payload_buf);
- if (status != SILC_SKE_STATUS_OK)
- goto err;
+ silc_packet_free(ske->packet);
+ ske->packet = NULL;
- /* Send the packet. */
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE,
- ske->callbacks->context);
+ /* Verify the received public key and verify the signature if we are
+ doing mutual authentication. */
+ if (ske->start_payload &&
+ ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
- silc_buffer_free(payload_buf);
+ SILC_LOG_DEBUG(("We are doing mutual authentication"));
- return status;
+ 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"));
+ ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
- err:
- if (group)
- silc_ske_group_free(group);
+ /* Decode the remote's public key */
+ if (recv_payload->pk_data &&
+ !silc_pkcs_public_key_alloc(recv_payload->pk_type,
+ recv_payload->pk_data,
+ recv_payload->pk_len,
+ &ske->prop->public_key)) {
+ /** Error decoding public key */
+ SILC_LOG_ERROR(("Unsupported/malformed public key received"));
+ ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
- if (prop->pkcs)
- silc_pkcs_free(prop->pkcs);
- 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);
- ske->prop = NULL;
+ if (ske->prop->public_key && (ske->callbacks->verify_key ||
+ ske->repository)) {
+ SILC_LOG_DEBUG(("Verifying public key"));
- if (status == SILC_SKE_STATUS_OK)
- return SILC_SKE_STATUS_ERROR;
+ /** Waiting public key verification */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase4);
+
+ /* If repository is provided, verify the key from there. */
+ if (ske->repository) {
+ SilcSKRFind find;
+
+ find = silc_skr_find_alloc();
+ if (!find) {
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+ silc_skr_find_set_pkcs_type(find,
+ silc_pkcs_get_type(ske->prop->public_key));
+ silc_skr_find_set_public_key(find, ske->prop->public_key);
+ silc_skr_find_set_usage(find, SILC_SKR_USAGE_KEY_AGREEMENT);
+
+ /* Find key from repository */
+ SILC_FSM_CALL(silc_skr_find(ske->repository,
+ silc_fsm_get_schedule(fsm), find,
+ silc_ske_skr_callback, ske));
+ } else {
+ /* Verify from application */
+ SILC_FSM_CALL(ske->callbacks->verify_key(ske, ske->prop->public_key,
+ ske->callbacks->context,
+ silc_ske_pk_verified, NULL));
+ }
+ /* NOT REACHED */
+ }
+ }
- ske->status = status;
- return status;
+ /** Generate KE2 payload */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase4);
+ return SILC_FSM_CONTINUE;
}
-/* An responder phase 2 final callback that is called to indicate that
- the SKE protocol may continue. */
+/* Phase-4. Generate KE2 payload */
-static void silc_ske_responder_phase2_final(SilcSKE ske,
- SilcSKEStatus status,
- void *context)
+SILC_FSM_STATE(silc_ske_st_responder_phase4)
{
- SilcSKEKEPayload *recv_payload, *send_payload;
- SilcMPInt *x;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEKEPayload recv_payload, send_payload;
+ SilcMPInt *x, *KEY;
- /* If the SKE was freed during the async call then free it really now,
- otherwise just decrement the reference counter. */
- if (ske->status == SILC_SKE_STATUS_FREED) {
- silc_ske_free(ske);
- return;
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+ return SILC_FSM_CONTINUE;
}
- /* If the caller returns PENDING status SKE library will assume that
- the caller will re-call this callback when it is not anymore in
- PENDING status. */
- if (status == SILC_SKE_STATUS_PENDING)
- return;
+ /* Check result of public key verification */
+ if (ske->status != SILC_SKE_STATUS_OK) {
+ /** Public key not verified */
+ SILC_LOG_DEBUG(("Public key verification failed"));
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
- ske->users--;
recv_payload = ske->ke1_payload;
- /* If the status is an error then the public key that was verified
- by the caller is not authentic. */
- if (status != SILC_SKE_STATUS_OK) {
- ske->status = status;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
- }
-
/* The public key verification was performed only if the Mutual
Authentication flag is set. */
- if (ske->start_payload &&
+ if (ske->start_payload &&
ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
- SilcPublicKey public_key = NULL;
- unsigned char hash[32];
+ unsigned char hash[SILC_HASH_MAXLEN];
SilcUInt32 hash_len;
- /* Decode the public key */
- if (!silc_pkcs_public_key_decode(recv_payload->pk_data,
- recv_payload->pk_len,
- &public_key)) {
- ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
- }
-
SILC_LOG_DEBUG(("Public key is authentic"));
/* Compute the hash value */
status = silc_ske_make_hash(ske, hash, &hash_len, TRUE);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error computing hash */
ske->status = status;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
-
+
/* Verify signature */
- silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
- if (silc_pkcs_verify(ske->prop->pkcs, recv_payload->sign_data,
- recv_payload->sign_len, hash, hash_len) == FALSE) {
-
- SILC_LOG_DEBUG(("Signature don't match"));
-
+ if (!silc_pkcs_verify(ske->prop->public_key, recv_payload->sign_data,
+ recv_payload->sign_len, hash, hash_len, NULL)) {
+ /** Incorrect signature */
+ SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
-
+
SILC_LOG_DEBUG(("Signature is Ok"));
-
- silc_pkcs_public_key_free(public_key);
+
memset(hash, 'F', hash_len);
}
/* Create the random number x, 1 < x < q. */
x = silc_calloc(1, sizeof(*x));
silc_mp_init(x);
- status =
+ status =
silc_ske_create_rnd(ske, &ske->prop->group->group_order,
silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
x);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error generating random number */
silc_mp_uninit(x);
silc_free(x);
ske->status = status;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
/* Save the results for later processing */
/* Do the Diffie Hellman computation, f = g ^ x mod p */
silc_mp_init(&send_payload->x);
- silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x,
+ silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x,
&ske->prop->group->group);
-
- /* Call the callback. The caller may now continue with the SKE protocol. */
- ske->status = SILC_SKE_STATUS_OK;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
-}
-
-/* This function receives the Key Exchange Payload from the initiator.
- This also performs the mutual authentication if required. Then, this
- function first generated a random number x, such that 1 < x < q
- and computes f = g ^ x mod p. This then puts the result f to a Key
- Exchange Payload.
-
- The `proto_continue' will be called to indicate that the caller may
- continue with the SKE protocol. The caller must not continue
- before the SKE libary has called that callback. If this function
- returns an error the callback will not be called. It is called
- if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
- However, note that when the library calls the callback the ske->status
- may be error.
-
- This calls the `verify_key' callback to verify the received public
- key or certificate if the Mutual Authentication flag is set. If the
- `verify_key' is provided then the remote must send public key and it
- is considered to be an error if remote does not send its public key. */
-
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
- SilcBuffer ke_payload)
-{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcSKEKEPayload *recv_payload;
-
- SILC_LOG_DEBUG(("Start"));
-
- /* Decode Key Exchange Payload */
- status = silc_ske_payload_ke_decode(ske, ke_payload, &recv_payload);
- if (status != SILC_SKE_STATUS_OK) {
- ske->status = status;
- return status;
- }
-
- ske->ke1_payload = recv_payload;
-
- /* Verify the received public key and verify the signature if we are
- doing mutual authentication. */
- if (ske->start_payload &&
- ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
-
- SILC_LOG_DEBUG(("We are doing mutual authentication"));
-
- if (!recv_payload->pk_data && ske->callbacks->verify_key) {
- 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;
- return status;
- }
- if (recv_payload->pk_data && ske->callbacks->verify_key) {
- SILC_LOG_DEBUG(("Verifying public key"));
-
- ske->users++;
- (*ske->callbacks->verify_key)(ske, recv_payload->pk_data,
- recv_payload->pk_len,
- recv_payload->pk_type,
- ske->callbacks->context,
- silc_ske_responder_phase2_final, NULL);
-
- /* We will continue to the final state after the public key has
- been verified by the caller. */
- return SILC_SKE_STATUS_PENDING;
- }
- }
+ SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
- /* Continue to final state */
- ske->users++;
- silc_ske_responder_phase2_final(ske, SILC_SKE_STATUS_OK, NULL);
+ /* Compute the shared secret key */
+ KEY = silc_calloc(1, sizeof(*KEY));
+ silc_mp_init(KEY);
+ silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x,
+ &ske->prop->group->group);
+ ske->KEY = KEY;
- return SILC_SKE_STATUS_OK;
+ /** Send KE2 payload */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase5);
+ return SILC_FSM_CONTINUE;
}
-/* This functions generates the secret key KEY = e ^ x mod p, and, a hash
- value to be signed and sent to the other end. This then encodes Key
- Exchange Payload and sends it to the other end. */
+/* Phase-5. Send KE2 payload */
-SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
- SilcPublicKey public_key,
- SilcPrivateKey private_key,
- SilcSKEPKType pk_type)
+SILC_FSM_STATE(silc_ske_st_responder_phase5)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
SilcBuffer payload_buf;
- SilcMPInt *KEY;
- unsigned char hash[32], sign[2048], *pk;
+ unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1], *pk;
SilcUInt32 hash_len, sign_len, pk_len;
SILC_LOG_DEBUG(("Start"));
- SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
-
- /* Compute the shared secret key */
- KEY = silc_calloc(1, sizeof(*KEY));
- silc_mp_init(KEY);
- silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x,
- &ske->prop->group->group);
- ske->KEY = KEY;
-
- if (public_key && private_key) {
+ if (ske->public_key && ske->private_key) {
SILC_LOG_DEBUG(("Getting public key"));
-
+
/* Get the public key */
- pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+ pk = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
if (!pk) {
+ /** Error encoding public key */
status = SILC_SKE_STATUS_OUT_OF_MEMORY;
- goto err;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
ske->ke2_payload->pk_data = pk;
ske->ke2_payload->pk_len = pk_len;
-
- SILC_LOG_DEBUG(("Computing HASH value"));
-
- /* Compute the hash value */
- memset(hash, 0, sizeof(hash));
- status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
- if (status != SILC_SKE_STATUS_OK)
- goto err;
+ }
+
+ SILC_LOG_DEBUG(("Computing HASH value"));
+
+ /* Compute the hash value */
+ memset(hash, 0, sizeof(hash));
+ status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Error computing hash */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+ ske->hash = silc_memdup(hash, hash_len);
+ ske->hash_len = hash_len;
- ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
- memcpy(ske->hash, hash, hash_len);
- ske->hash_len = hash_len;
-
+ if (ske->public_key && ske->private_key) {
SILC_LOG_DEBUG(("Signing HASH value"));
-
+
/* Sign the hash value */
- silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv,
- private_key->prv_len);
- if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
- !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
+ if (!silc_pkcs_sign(ske->private_key, hash, hash_len, sign,
+ sizeof(sign) - 1, &sign_len, FALSE, ske->prop->hash)) {
+ /** Error computing signature */
status = SILC_SKE_STATUS_SIGNATURE_ERROR;
- goto err;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
- ske->ke2_payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
- memcpy(ske->ke2_payload->sign_data, sign, sign_len);
- memset(sign, 0, sizeof(sign));
+ ske->ke2_payload->sign_data = silc_memdup(sign, sign_len);
ske->ke2_payload->sign_len = sign_len;
+ memset(sign, 0, sizeof(sign));
}
- ske->ke2_payload->pk_type = 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,
&payload_buf);
- if (status != SILC_SKE_STATUS_OK)
- goto err;
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Error encoding KE payload */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
/* Send the packet. */
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, payload_buf,
- SILC_PACKET_KEY_EXCHANGE_2,
- ske->callbacks->context);
+ 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;
+ }
silc_buffer_free(payload_buf);
- return status;
-
- err:
- silc_mp_uninit(ske->KEY);
- silc_free(ske->KEY);
- ske->KEY = NULL;
- silc_ske_payload_ke_free(ske->ke2_payload);
-
- if (status == SILC_SKE_STATUS_OK)
- return SILC_SKE_STATUS_ERROR;
-
- ske->status = status;
- return status;
-}
-
-/* The Key Exchange protocol is ended by calling this function. This
- must not be called until the keys are processed like the protocol
- defines. This function is for both initiator and responder. */
-
-SilcSKEStatus silc_ske_end(SilcSKE ske)
-{
- SilcBuffer packet;
-
- SILC_LOG_DEBUG(("Start"));
-
- packet = silc_buffer_alloc_size(4);
- if (!packet)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
- silc_buffer_format(packet,
- SILC_STR_UI_INT((SilcUInt32)SILC_SKE_STATUS_OK),
- SILC_STR_END);
-
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_SUCCESS,
- ske->callbacks->context);
-
- silc_buffer_free(packet);
-
- return SILC_SKE_STATUS_OK;
-}
-
-/* Aborts the Key Exchange protocol. This is called if error occurs
- while performing the protocol. The status argument is the error
- status and it is sent to the remote end. */
-
-SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status)
-{
- SilcBuffer packet;
-
- SILC_LOG_DEBUG(("Start"));
-
- if (status > SILC_SKE_STATUS_INVALID_COOKIE)
- status = SILC_SKE_STATUS_BAD_PAYLOAD;
-
- packet = silc_buffer_alloc_size(4);
- if (!packet)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
- silc_buffer_format(packet,
- SILC_STR_UI_INT((SilcUInt32)status),
- SILC_STR_END);
-
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_FAILURE,
- ske->callbacks->context);
-
- silc_buffer_free(packet);
+ /* In case we are doing rekey move to finish it. */
+ if (ske->rekey) {
+ /** Finish rekey */
+ silc_fsm_next(fsm, silc_ske_st_rekey_responder_done);
+ return SILC_FSM_CONTINUE;
+ }
- return SILC_SKE_STATUS_OK;
+ /** Waiting completion */
+ silc_fsm_next(fsm, silc_ske_st_responder_end);
+ return SILC_FSM_WAIT;
}
-/* Assembles security properties to Key Exchange Start Payload to be
- sent to the remote end. This checks system wide (SILC system, that is)
- settings and chooses from those. However, if other properties
- should be used this function is easy to replace by another function,
- as, this function is called by the caller of the protocol and not
- by the protocol itself. */
+/* Protocol completed */
-SilcSKEStatus
-silc_ske_assemble_security_properties(SilcSKE ske,
- SilcSKESecurityPropertyFlag flags,
- const char *version,
- SilcSKEStartPayload **return_payload)
+SILC_FSM_STATE(silc_ske_st_responder_end)
{
- 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(ske->rng);
- rp->cookie_len = SILC_SKE_COOKIE_LEN;
+ SilcSKE ske = fsm_context;
+ unsigned char tmp[4];
+ SilcUInt32 hash_len, key_len, block_len;
+
+ 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_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,
+ &ske->rekey);
+ if (!ske->keymat) {
+ /** Error processing key material */
+ ske->status = SILC_SKE_STATUS_ERROR;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
- /* Put version */
- rp->version = strdup(version);
- rp->version_len = strlen(version);
+ /* Send SUCCESS packet */
+ SILC_PUT32_MSB(SILC_SKE_STATUS_OK, tmp);
+ silc_ske_packet_send(ske, SILC_PACKET_SUCCESS, 0, tmp, 4);
- /* Get supported Key Exhange groups */
- rp->ke_grp_list = silc_ske_get_supported_groups();
- rp->ke_grp_len = strlen(rp->ke_grp_list);
+ silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+ silc_schedule_task_del_by_context(ske->schedule, ske);
- /* Get supported PKCS algorithms */
- rp->pkcs_alg_list = silc_pkcs_get_supported();
- rp->pkcs_alg_len = strlen(rp->pkcs_alg_list);
+ /* Call completion */
+ silc_ske_completion(ske);
- /* Get supported encryption algorithms */
- rp->enc_alg_list = silc_cipher_get_supported();
- rp->enc_alg_len = strlen(rp->enc_alg_list);
+ return SILC_FSM_FINISH;
+}
- /* Get supported hash algorithms */
- rp->hash_alg_list = silc_hash_get_supported();
- rp->hash_alg_len = strlen(rp->hash_alg_list);
+/* Aborted by application */
- /* Get supported HMACs */
- rp->hmac_alg_list = silc_hmac_get_supported();
- rp->hmac_alg_len = strlen(rp->hmac_alg_list);
+SILC_FSM_STATE(silc_ske_st_responder_aborted)
+{
+ SilcSKE ske = fsm_context;
+ unsigned char tmp[4];
- /* XXX */
- /* Get supported compression algorithms */
- rp->comp_alg_list = strdup("");
- rp->comp_alg_len = 0;
+ SILC_LOG_DEBUG(("Key exchange protocol aborted"));
- 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;
+ /* Send FAILURE packet */
+ SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, tmp);
+ silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, tmp, 4);
- *return_payload = rp;
+ silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+ silc_schedule_task_del_by_context(ske->schedule, ske);
- return SILC_SKE_STATUS_OK;
+ /* Call completion */
+ silc_ske_completion(ske);
+
+ return SILC_FSM_FINISH;
}
-/* Selects the supported security properties from the remote end's Key
- Exchange Start Payload. */
+/* Failure received from remote */
-SilcSKEStatus
-silc_ske_select_security_properties(SilcSKE ske,
- const char *version,
- SilcSKEStartPayload *payload,
- SilcSKEStartPayload *remote_payload)
+SILC_FSM_STATE(silc_ske_st_responder_failure)
{
- SilcSKEStatus status;
- SilcSKEStartPayload *rp;
- char *cp;
- int len;
-
- SILC_LOG_DEBUG(("Parsing KE Start Payload"));
+ SilcSKE ske = fsm_context;
+ SilcUInt32 error = SILC_SKE_STATUS_ERROR;
- rp = remote_payload;
+ SILC_LOG_DEBUG(("Key exchange protocol failed"));
- /* Check version string */
- if (ske->callbacks->check_version) {
- status = ske->callbacks->check_version(ske, rp->version,
- rp->version_len,
- ske->callbacks->context);
- if (status != SILC_SKE_STATUS_OK) {
- ske->status = status;
- return 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;
}
- ske->remote_version = silc_memdup(rp->version, rp->version_len);
+ silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+ silc_schedule_task_del_by_context(ske->schedule, ske);
- /* Flags are returned unchanged. */
- payload->flags = rp->flags;
+ /* Call completion */
+ silc_ske_completion(ske);
- /* 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);
+ return SILC_FSM_FINISH;
+}
- /* Put our version to our reply */
- payload->version = strdup(version);
- payload->version_len = strlen(version);
+/* Error occurred */
- /* Get supported Key Exchange groups */
- cp = rp->ke_grp_list;
- if (cp && strchr(cp, ',')) {
- while(cp) {
- char *item;
+SILC_FSM_STATE(silc_ske_st_responder_error)
+{
+ SilcSKE ske = fsm_context;
+ unsigned char tmp[4];
- len = strcspn(cp, ",");
- item = silc_calloc(len + 1, sizeof(char));
- memcpy(item, cp, len);
+ SILC_LOG_DEBUG(("Error %d (%s) during key exchange protocol",
+ ske->status, silc_ske_map_status(ske->status)));
- SILC_LOG_DEBUG(("Proposed KE group `%s'", item));
+ /* Send FAILURE packet */
+ if (ske->status > SILC_SKE_STATUS_INVALID_COOKIE)
+ ske->status = SILC_SKE_STATUS_BAD_PAYLOAD;
+ SILC_PUT32_MSB(ske->status, tmp);
+ silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, tmp, 4);
- if (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
- SILC_LOG_DEBUG(("Found KE group `%s'", item));
+ silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+ silc_schedule_task_del_by_context(ske->schedule, ske);
- payload->ke_grp_len = len;
- payload->ke_grp_list = item;
- break;
- }
+ /* Call completion */
+ silc_ske_completion(ske);
- cp += len;
- if (strlen(cp) == 0)
- cp = NULL;
- else
- cp++;
+ return SILC_FSM_FINISH;
+}
- if (item)
- silc_free(item);
- }
+/* Starts the protocol as responder. */
- if (!payload->ke_grp_len && !payload->ke_grp_list) {
- SILC_LOG_DEBUG(("Could not find supported KE group"));
- silc_free(payload);
- return SILC_SKE_STATUS_UNKNOWN_GROUP;
- }
- } else {
+SilcAsyncOperation silc_ske_responder(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcSKEParams params)
+{
+ SILC_LOG_DEBUG(("Start SKE as responder"));
- if (!rp->ke_grp_len) {
- SILC_LOG_DEBUG(("KE group not defined in payload"));
- silc_free(payload);
- return SILC_SKE_STATUS_BAD_PAYLOAD;
- }
+ if (!ske || !stream || !params || !params->version)
+ return NULL;
- SILC_LOG_DEBUG(("Proposed KE group `%s'", rp->ke_grp_list));
- SILC_LOG_DEBUG(("Found KE group `%s'", rp->ke_grp_list));
+ if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+ return NULL;
- payload->ke_grp_len = rp->ke_grp_len;
- payload->ke_grp_list = strdup(rp->ke_grp_list);
- }
+ if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule))
+ return NULL;
- /* Get supported PKCS algorithms */
- cp = rp->pkcs_alg_list;
- if (cp && strchr(cp, ',')) {
- while(cp) {
- char *item;
+ 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;
- len = strcspn(cp, ",");
- item = silc_calloc(len + 1, sizeof(char));
- memcpy(item, cp, len);
+ /* 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_KEY_EXCHANGE,
+ SILC_PACKET_KEY_EXCHANGE_1,
+ SILC_PACKET_SUCCESS,
+ SILC_PACKET_FAILURE, -1);
- SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", item));
+ /* Start SKE as responder */
+ silc_fsm_start(&ske->fsm, silc_ske_st_responder_start);
- if (silc_pkcs_is_supported(item) == TRUE) {
- SILC_LOG_DEBUG(("Found PKCS alg `%s'", item));
+ return &ske->op;
+}
- payload->pkcs_alg_len = len;
- payload->pkcs_alg_list = item;
- break;
- }
+/***************************** Initiator Rekey ******************************/
- cp += len;
- if (strlen(cp) == 0)
- cp = NULL;
- else
- cp++;
+/* Start rekey */
- if (item)
- silc_free(item);
- }
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start)
+{
+ SilcSKE ske = fsm_context;
+ SilcStatus status;
- if (!payload->pkcs_alg_len && !payload->pkcs_alg_list) {
- SILC_LOG_DEBUG(("Could not find supported PKCS alg"));
- silc_free(payload->ke_grp_list);
- silc_free(payload);
- return SILC_SKE_STATUS_UNKNOWN_PKCS;
- }
- } else {
+ SILC_LOG_DEBUG(("Start rekey (%s)", ske->rekey->pfs ? "PFS" : "No PFS"));
- 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;
- }
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+ return SILC_FSM_CONTINUE;
+ }
- SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list));
- SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list));
+ /* Add rekey exchange timeout */
+ silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout,
+ ske, 30, 0);
- payload->pkcs_alg_len = rp->pkcs_alg_len;
- payload->pkcs_alg_list = strdup(rp->pkcs_alg_list);
+ 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;
}
- /* Get supported encryption algorithms */
- cp = rp->enc_alg_list;
- if (cp && strchr(cp, ',')) {
- while(cp) {
- char *item;
+ if (!silc_hash_alloc(ske->rekey->hash, &ske->prop->hash)) {
+ /** Cannot allocate hash */
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
- len = strcspn(cp, ",");
- item = silc_calloc(len + 1, sizeof(char));
- memcpy(item, cp, len);
+ /* 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;
+ }
- SILC_LOG_DEBUG(("Proposed encryption alg `%s'", item));
+ /* 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;
+ }
- if (silc_cipher_is_supported(item) == TRUE) {
- SILC_LOG_DEBUG(("Found encryption alg `%s'", item));
+ 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;
+ }
- payload->enc_alg_len = len;
- payload->enc_alg_list = item;
- break;
- }
+ /** Rekey with PFS */
+ silc_fsm_next(fsm, silc_ske_st_initiator_phase2);
+ return SILC_FSM_CONTINUE;
+}
- cp += len;
- if (strlen(cp) == 0)
- cp = NULL;
- else
- cp++;
+/* Sends REKEY_DONE packet to finish the protocol. */
- if (item)
- silc_free(item);
- }
+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;
- if (!payload->enc_alg_len && !payload->enc_alg_list) {
- SILC_LOG_DEBUG(("Could not find supported encryption alg"));
- silc_free(payload->ke_grp_list);
- silc_free(payload->pkcs_alg_list);
- silc_free(payload);
- return SILC_SKE_STATUS_UNKNOWN_CIPHER;
+ SILC_LOG_DEBUG(("Start"));
+
+ silc_packet_get_keys(ske->stream, &send_key, NULL, &hmac_send, NULL);
+ key_len = silc_cipher_get_key_len(send_key);
+ block_len = silc_cipher_get_block_len(send_key);
+ hash = ske->prop->hash;
+ hash_len = silc_hash_len(hash);
+
+ /* Process key material */
+ if (ske->rekey->pfs) {
+ /* PFS */
+ pfsbuf = silc_mp_mp2bin(ske->KEY, 0, &x_len);
+ if (pfsbuf) {
+ ske->keymat = silc_ske_process_key_material_data(pfsbuf, x_len,
+ block_len, key_len,
+ hash_len, hash);
+ memset(pfsbuf, 0, x_len);
+ silc_free(pfsbuf);
}
} else {
+ /* No PFS */
+ ske->keymat =
+ silc_ske_process_key_material_data(ske->rekey->send_enc_key,
+ ske->rekey->enc_key_len / 8,
+ block_len, key_len,
+ hash_len, hash);
+ }
- if (!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;
- }
+ if (!ske->keymat) {
+ SILC_LOG_ERROR(("Error processing key material"));
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
- SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
- rp->enc_alg_list));
+ ske->prop->cipher = send_key;
+ ske->prop->hmac = hmac_send;
- payload->enc_alg_len = rp->enc_alg_len;
- payload->enc_alg_list = strdup(rp->enc_alg_list);
+ /* Get sending keys */
+ if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, &send_key, NULL,
+ &hmac_send, NULL, NULL)) {
+ /** Cannot get keys */
+ ske->status = SILC_SKE_STATUS_ERROR;
+ ske->prop->cipher = NULL;
+ ske->prop->hmac = NULL;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
- /* Get supported hash algorithms */
- cp = rp->hash_alg_list;
- if (cp && strchr(cp, ',')) {
- while(cp) {
- char *item;
+ ske->prop->cipher = NULL;
+ ske->prop->hmac = NULL;
- len = strcspn(cp, ",");
- item = silc_calloc(len + 1, sizeof(char));
- memcpy(item, cp, len);
+ /* Set the new keys into use. This will also send REKEY_DONE packet. Any
+ packet sent after this call will be protected with the new keys. */
+ if (!silc_packet_set_keys(ske->stream, send_key, NULL, hmac_send, NULL,
+ TRUE)) {
+ /** Cannot set keys */
+ SILC_LOG_DEBUG(("Cannot set new keys, error sending REKEY_DONE"));
+ ske->status = SILC_SKE_STATUS_ERROR;
+ silc_cipher_free(send_key);
+ silc_hmac_free(hmac_send);
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
- SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
+ /** Wait for REKEY_DONE */
+ silc_fsm_next(fsm, silc_ske_st_rekey_initiator_end);
+ return SILC_FSM_WAIT;
+}
- if (silc_hash_is_supported(item) == TRUE) {
- SILC_LOG_DEBUG(("Found hash alg `%s'", item));
+/* Rekey protocol end */
- payload->hash_alg_len = len;
- payload->hash_alg_list = item;
- break;
- }
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_end)
+{
+ SilcSKE ske = fsm_context;
+ SilcCipher receive_key;
+ SilcHmac hmac_receive;
+ SilcSKERekeyMaterial rekey;
- cp += len;
- if (strlen(cp) == 0)
- cp = NULL;
- else
- cp++;
+ SILC_LOG_DEBUG(("Start"));
- if (item)
- silc_free(item);
- }
+ 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;
+ }
- if (!payload->hash_alg_len && !payload->hash_alg_list) {
- SILC_LOG_DEBUG(("Could not find supported hash alg"));
- 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;
- }
- } else {
+ silc_packet_get_keys(ske->stream, NULL, &receive_key, NULL, &hmac_receive);
+ ske->prop->cipher = receive_key;
+ ske->prop->hmac = hmac_receive;
- 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;
- }
+ /* Get receiving keys */
+ if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, NULL, &receive_key,
+ NULL, &hmac_receive, NULL)) {
+ /** Cannot get keys */
+ ske->status = SILC_SKE_STATUS_ERROR;
+ ske->prop->cipher = NULL;
+ ske->prop->hmac = NULL;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
- SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it",
- rp->hash_alg_list));
+ /* Set new receiving keys into use. All packets received after this will
+ be decrypted with the new keys. */
+ if (!silc_packet_set_keys(ske->stream, NULL, receive_key, NULL,
+ hmac_receive, FALSE)) {
+ /** Cannot set keys */
+ SILC_LOG_DEBUG(("Cannot set new keys"));
+ ske->status = SILC_SKE_STATUS_ERROR;
+ silc_cipher_free(receive_key);
+ silc_hmac_free(hmac_receive);
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
- payload->hash_alg_len = rp->hash_alg_len;
- payload->hash_alg_list = strdup(rp->hash_alg_list);
+ SILC_LOG_DEBUG(("Rekey completed successfully"));
+
+ /* Generate new rekey material */
+ rekey = silc_ske_make_rekey_material(ske, ske->keymat);
+ if (!rekey) {
+ /** No memory */
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ ske->prop->cipher = NULL;
+ ske->prop->hmac = NULL;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
+ rekey->pfs = ske->rekey->pfs;
+ ske->rekey = rekey;
- /* Get supported HMACs */
- cp = rp->hmac_alg_list;
- if (cp && strchr(cp, ',')) {
- while(cp) {
- char *item;
+ 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);
- len = strcspn(cp, ",");
- item = silc_calloc(len + 1, sizeof(char));
- memcpy(item, cp, len);
+ /* Call completion */
+ silc_ske_completion(ske);
- SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
+ return SILC_FSM_FINISH;
+}
- if (silc_hmac_is_supported(item) == TRUE) {
- SILC_LOG_DEBUG(("Found HMAC `%s'", item));
+/* Starts rekey protocol as initiator */
- payload->hmac_alg_len = len;
- payload->hmac_alg_list = item;
- break;
- }
+SilcAsyncOperation
+silc_ske_rekey_initiator(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcSKERekeyMaterial rekey)
+{
+ SILC_LOG_DEBUG(("Start SKE rekey as initator"));
+
+ if (!ske || !stream || !rekey) {
+ SILC_LOG_ERROR(("Missing arguments to silc_ske_rekey_initiator"));
+ SILC_ASSERT(rekey);
+ return NULL;
+ }
+
+ if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+ return NULL;
+
+ if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule))
+ return NULL;
- cp += len;
- if (strlen(cp) == 0)
- cp = NULL;
- else
- cp++;
+ ske->rekey = rekey;
+ ske->responder = FALSE;
+ ske->running = TRUE;
+ ske->rekeying = TRUE;
- if (item)
- silc_free(item);
- }
+ /* 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);
- if (!payload->hmac_alg_len && !payload->hmac_alg_list) {
- SILC_LOG_DEBUG(("Could not find supported HMAC"));
- 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;
- }
- } else {
+ /* Start SKE rekey as initiator */
+ silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start);
- 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;
- }
+ return &ske->op;
+}
- SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
- rp->hmac_alg_list));
+/***************************** Responder Rekey ******************************/
- payload->hmac_alg_len = rp->hmac_alg_len;
- payload->hmac_alg_list = strdup(rp->hmac_alg_list);
+/* Wait for initiator's packet */
+
+SILC_FSM_STATE(silc_ske_st_rekey_responder_wait)
+{
+ SilcSKE ske = fsm_context;
+
+ SILC_LOG_DEBUG(("Start rekey (%s)", ske->rekey->pfs ? "PFS" : "No PFS"));
+
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+ return SILC_FSM_CONTINUE;
}
-#if 0
- /* Get supported compression algorithms */
- cp = rp->hash_alg_list;
- if (cp && strchr(cp, ',')) {
- while(cp) {
- char *item;
+ /* Add rekey exchange timeout */
+ silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout,
+ ske, 30, 0);
- len = strcspn(cp, ",");
- item = silc_calloc(len + 1, sizeof(char));
- memcpy(item, cp, len);
+ silc_fsm_next(fsm, silc_ske_st_rekey_responder_start);
- SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
+ /* If REKEY packet already received process it directly */
+ if (ske->packet && ske->packet->type == SILC_PACKET_REKEY)
+ return SILC_FSM_CONTINUE;
- if (silc_hash_is_supported(item) == TRUE) {
- SILC_LOG_DEBUG(("Found hash alg `%s'", item));
+ /* Wait for REKEY */
+ return SILC_FSM_WAIT;
+}
- payload->hash_alg_len = len;
- payload->hash_alg_list = item;
- break;
- }
+/* Process initiator's REKEY packet */
- cp += len;
- if (strlen(cp) == 0)
- cp = NULL;
- else
- cp++;
+SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
+{
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
- if (item)
- silc_free(item);
- }
+ SILC_LOG_DEBUG(("Start"));
- if (!payload->hash_alg_len && !payload->hash_alg_list) {
- SILC_LOG_DEBUG(("Could not find supported hash alg"));
- silc_ske_abort(ske, SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION);
- silc_free(payload->ke_grp_list);
- silc_free(payload->pkcs_alg_list);
- silc_free(payload->enc_alg_list);
- silc_free(payload);
- return;
- }
- } else {
+ if (ske->packet->type != SILC_PACKET_REKEY) {
+ ske->status = SILC_SKE_STATUS_ERROR;
+ silc_packet_free(ske->packet);
+ ske->packet = NULL;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ ske->prop = silc_calloc(1, sizeof(*ske->prop));
+ if (!ske->prop) {
+ /** No memory */
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+ if (!silc_hash_alloc(ske->rekey->hash, &ske->prop->hash)) {
+ /** Cannot allocate hash */
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
-#endif
- payload->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
- 2 + payload->version_len +
- 2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len +
- 2 + payload->enc_alg_len + 2 + payload->hash_alg_len +
- 2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
+ /* If doing rekey without PFS, move directly to the end of the protocol. */
+ if (!ske->rekey->pfs) {
+ /** Rekey without PFS */
+ silc_fsm_next(fsm, silc_ske_st_rekey_responder_done);
+ return SILC_FSM_CONTINUE;
+ }
- return SILC_SKE_STATUS_OK;
+ status = silc_ske_group_get_by_number(ske->rekey->ske_group,
+ &ske->prop->group);
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Unknown group */
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /** Rekey with PFS */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase2);
+ return SILC_FSM_WAIT;
}
-/* Creates random number such that 1 < rnd < n and at most length
- of len bits. The rnd sent as argument must be initialized. */
+/* Sends REKEY_DONE packet to finish the protocol. */
-static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
- SilcUInt32 len,
- SilcMPInt *rnd)
+SILC_FSM_STATE(silc_ske_st_rekey_responder_done)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- unsigned char *string;
+ 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(("Creating random number"));
+ SILC_LOG_DEBUG(("Start"));
- /* Get the random number as string */
- string = silc_rng_get_rn_data(ske->rng, (len / 8));
- if (!string)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ silc_packet_get_keys(ske->stream, &send_key, NULL, &hmac_send, NULL);
+ key_len = silc_cipher_get_key_len(send_key);
+ block_len = silc_cipher_get_block_len(send_key);
+ hash = ske->prop->hash;
+ hash_len = silc_hash_len(hash);
+
+ /* Process key material */
+ if (ske->rekey->pfs) {
+ /* PFS */
+ pfsbuf = silc_mp_mp2bin(ske->KEY, 0, &x_len);
+ if (pfsbuf) {
+ ske->keymat = silc_ske_process_key_material_data(pfsbuf, x_len,
+ block_len, key_len,
+ hash_len, hash);
+ memset(pfsbuf, 0, x_len);
+ silc_free(pfsbuf);
+ }
+ } else {
+ /* No PFS */
+ ske->keymat =
+ silc_ske_process_key_material_data(ske->rekey->send_enc_key,
+ ske->rekey->enc_key_len / 8,
+ block_len, key_len,
+ hash_len, hash);
+ }
- /* Decode the string into a MP integer */
- silc_mp_bin2mp(string, (len / 8), rnd);
- silc_mp_mod_2exp(rnd, rnd, len);
+ if (!ske->keymat) {
+ SILC_LOG_ERROR(("Error processing key material"));
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
- /* Checks */
- if (silc_mp_cmp_ui(rnd, 1) < 0)
- status = SILC_SKE_STATUS_ERROR;
+ ske->prop->cipher = send_key;
+ ske->prop->hmac = hmac_send;
- if (silc_mp_cmp(rnd, n) >= 0)
- status = SILC_SKE_STATUS_ERROR;
+ /* Get sending keys */
+ if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, &send_key, NULL,
+ &hmac_send, NULL, NULL)) {
+ /** Cannot get keys */
+ ske->status = SILC_SKE_STATUS_ERROR;
+ ske->prop->cipher = NULL;
+ ske->prop->hmac = NULL;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
- memset(string, 'F', (len / 8));
- silc_free(string);
+ ske->prop->cipher = NULL;
+ ske->prop->hmac = NULL;
- return status;
+ /* Set the new keys into use. This will also send REKEY_DONE packet. Any
+ packet sent after this call will be protected with the new keys. */
+ if (!silc_packet_set_keys(ske->stream, send_key, NULL, hmac_send, NULL,
+ TRUE)) {
+ /** Cannot set keys */
+ SILC_LOG_DEBUG(("Cannot set new keys, error sending REKEY_DONE"));
+ ske->status = SILC_SKE_STATUS_ERROR;
+ silc_cipher_free(send_key);
+ silc_hmac_free(hmac_send);
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /** Wait for REKEY_DONE */
+ silc_fsm_next(fsm, silc_ske_st_rekey_responder_end);
+ return SILC_FSM_WAIT;
}
-/* Creates a hash value HASH as defined in the SKE protocol. If the
- `initiator' is TRUE then this function is used to create the HASH_i
- hash value defined in the protocol. If it is FALSE then this is used
- to create the HASH value defined by the protocol. */
+/* Rekey protocol end */
-static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
- unsigned char *return_hash,
- SilcUInt32 *return_hash_len,
- int initiator)
+SILC_FSM_STATE(silc_ske_st_rekey_responder_end)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcBuffer buf;
- unsigned char *e, *f, *KEY;
- SilcUInt32 e_len, f_len, KEY_len;
- int ret;
+ SilcSKE ske = fsm_context;
+ SilcCipher receive_key;
+ SilcHmac hmac_receive;
+ SilcSKERekeyMaterial rekey;
SILC_LOG_DEBUG(("Start"));
- if (initiator == FALSE) {
- e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
- f = silc_mp_mp2bin(&ske->ke2_payload->x, 0, &f_len);
- KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
-
- /* Format the buffer used to compute the hash value */
- buf = silc_buffer_alloc_size(ske->start_payload_copy->len +
- ske->ke2_payload->pk_len +
- ske->ke1_payload->pk_len +
- e_len + f_len + KEY_len);
- if (!buf)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
-
- /* Initiator is not required to send its public key */
- if (!ske->ke1_payload->pk_data) {
- ret =
- silc_buffer_format(buf,
- SILC_STR_UI_XNSTRING(ske->start_payload_copy->
- data,
- ske->start_payload_copy->
- len),
- SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
- ske->ke2_payload->pk_len),
- SILC_STR_UI_XNSTRING(e, e_len),
- SILC_STR_UI_XNSTRING(f, f_len),
- SILC_STR_UI_XNSTRING(KEY, KEY_len),
- SILC_STR_END);
- } else {
- ret =
- silc_buffer_format(buf,
- SILC_STR_UI_XNSTRING(ske->start_payload_copy->
- data,
- ske->start_payload_copy->
- len),
- SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
- ske->ke2_payload->pk_len),
- SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
- ske->ke1_payload->pk_len),
- SILC_STR_UI_XNSTRING(e, e_len),
- SILC_STR_UI_XNSTRING(f, f_len),
- SILC_STR_UI_XNSTRING(KEY, KEY_len),
- SILC_STR_END);
- }
- if (ret == -1) {
- silc_buffer_free(buf);
- memset(e, 0, e_len);
- memset(f, 0, f_len);
- memset(KEY, 0, KEY_len);
- silc_free(e);
- silc_free(f);
- silc_free(KEY);
- return SILC_SKE_STATUS_ERROR;
- }
+ 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;
+ }
- memset(e, 0, e_len);
- memset(f, 0, f_len);
- memset(KEY, 0, KEY_len);
- silc_free(e);
- silc_free(f);
- silc_free(KEY);
- } else {
- e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
+ silc_packet_get_keys(ske->stream, NULL, &receive_key, NULL, &hmac_receive);
+ ske->prop->cipher = receive_key;
+ ske->prop->hmac = hmac_receive;
- buf = silc_buffer_alloc_size(ske->start_payload_copy->len +
- ske->ke1_payload->pk_len + e_len);
- if (!buf)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
-
- /* Format the buffer used to compute the hash value */
- ret =
- silc_buffer_format(buf,
- SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
- ske->start_payload_copy->len),
- SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
- ske->ke1_payload->pk_len),
- SILC_STR_UI_XNSTRING(e, e_len),
- SILC_STR_END);
- if (ret == -1) {
- silc_buffer_free(buf);
- memset(e, 0, e_len);
- silc_free(e);
- return SILC_SKE_STATUS_ERROR;
- }
+ /* Get receiving keys */
+ if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, NULL, &receive_key,
+ NULL, &hmac_receive, NULL)) {
+ /** Cannot get keys */
+ ske->status = SILC_SKE_STATUS_ERROR;
+ ske->prop->cipher = NULL;
+ ske->prop->hmac = NULL;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
- memset(e, 0, e_len);
- silc_free(e);
+ /* Set new receiving keys into use. All packets received after this will
+ be decrypted with the new keys. */
+ if (!silc_packet_set_keys(ske->stream, NULL, receive_key, NULL,
+ hmac_receive, FALSE)) {
+ /** Cannot set keys */
+ SILC_LOG_DEBUG(("Cannot set new keys"));
+ ske->status = SILC_SKE_STATUS_ERROR;
+ ske->prop->cipher = NULL;
+ ske->prop->hmac = NULL;
+ silc_cipher_free(receive_key);
+ silc_hmac_free(hmac_receive);
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
- /* Make the hash */
- silc_hash_make(ske->prop->hash, buf->data, buf->len, return_hash);
- *return_hash_len = silc_hash_len(ske->prop->hash);
+ SILC_LOG_DEBUG(("Rekey completed successfully"));
- if (initiator == FALSE) {
- SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len);
- } else {
- SILC_LOG_HEXDUMP(("HASH_i"), return_hash, *return_hash_len);
+ /* Generate new rekey material */
+ rekey = silc_ske_make_rekey_material(ske, ske->keymat);
+ if (!rekey) {
+ /** No memory */
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ ske->prop->cipher = NULL;
+ ske->prop->hmac = NULL;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
+ rekey->pfs = ske->rekey->pfs;
+ ske->rekey = rekey;
- silc_buffer_free(buf);
+ 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);
- return status;
+ /* Call completion */
+ silc_ske_completion(ske);
+
+ return SILC_FSM_FINISH;
+}
+
+/* Starts rekey protocol as responder */
+
+SilcAsyncOperation
+silc_ske_rekey_responder(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcSKERekeyMaterial rekey,
+ SilcPacket packet)
+{
+ SILC_LOG_DEBUG(("Start SKE rekey as responder"));
+
+ if (!ske || !stream || !rekey)
+ return NULL;
+
+ if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+ return NULL;
+
+ if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule))
+ return NULL;
+
+ ske->rekey = rekey;
+ ske->responder = TRUE;
+ ske->running = TRUE;
+ ske->rekeying = TRUE;
+ ske->packet = packet;
+
+ /* Link to packet stream to get key exchange packets */
+ ske->stream = stream;
+ silc_packet_stream_link(ske->stream, &silc_ske_stream_cbs, ske, 1000000,
+ SILC_PACKET_REKEY,
+ SILC_PACKET_REKEY_DONE,
+ SILC_PACKET_KEY_EXCHANGE_1,
+ SILC_PACKET_SUCCESS,
+ SILC_PACKET_FAILURE, -1);
+
+ /* Start SKE rekey as responder */
+ silc_fsm_start_sync(&ske->fsm, silc_ske_st_rekey_responder_wait);
+
+ return &ske->op;
}
-/* Processes the provided key material `data' as the SILC protocol
+/* Processes the provided key material `data' as the SILC protocol
specification defines. */
-SilcSKEStatus
+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,
- SilcSKEKeyMaterial *key)
+ SilcHash hash)
{
SilcBuffer buf;
- unsigned char hashd[32];
+ unsigned char hashd[SILC_HASH_MAXLEN];
SilcUInt32 hash_len = req_hmac_key_len;
SilcUInt32 enc_key_len = req_enc_key_len / 8;
+ SilcSKEKeyMaterial key;
SILC_LOG_DEBUG(("Start"));
if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
- return SILC_SKE_STATUS_ERROR;
+ return NULL;
+
+ key = silc_calloc(1, sizeof(*key));
+ if (!key)
+ return NULL;
buf = silc_buffer_alloc_size(1 + data_len);
if (!buf)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return NULL;
silc_buffer_format(buf,
SILC_STR_UI_CHAR(0),
- SILC_STR_UI_XNSTRING(data, data_len),
+ SILC_STR_DATA(data, data_len),
SILC_STR_END);
/* Take IVs */
memset(hashd, 0, sizeof(hashd));
buf->data[0] = 0;
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->send_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
memcpy(key->send_iv, hashd, req_iv_len);
memset(hashd, 0, sizeof(hashd));
buf->data[0] = 1;
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->receive_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
memcpy(key->receive_iv, hashd, req_iv_len);
key->iv_len = req_iv_len;
buf->data[0] = 2;
if (enc_key_len > hash_len) {
SilcBuffer dist;
- unsigned char k1[32], k2[32], k3[32];
+ unsigned char k1[SILC_HASH_MAXLEN], k2[SILC_HASH_MAXLEN],
+ k3[SILC_HASH_MAXLEN];
unsigned char *dtmp;
-
+
/* XXX */
if (enc_key_len > (3 * hash_len))
- return SILC_SKE_STATUS_ERROR;
-
+ return NULL;
+
/* Take first round */
memset(k1, 0, sizeof(k1));
- silc_hash_make(hash, buf->data, buf->len, k1);
-
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), k1);
+
/* Take second round */
dist = silc_buffer_alloc_size(data_len + hash_len);
if (!dist)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return NULL;
silc_buffer_format(dist,
- SILC_STR_UI_XNSTRING(data, data_len),
- SILC_STR_UI_XNSTRING(k1, hash_len),
+ SILC_STR_DATA(data, data_len),
+ SILC_STR_DATA(k1, hash_len),
SILC_STR_END);
memset(k2, 0, sizeof(k2));
- silc_hash_make(hash, dist->data, dist->len, k2);
+ silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
/* Take third round */
dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
silc_buffer_pull_tail(dist, hash_len);
silc_buffer_pull(dist, data_len + hash_len);
silc_buffer_format(dist,
- SILC_STR_UI_XNSTRING(k2, hash_len),
+ SILC_STR_DATA(k2, hash_len),
SILC_STR_END);
silc_buffer_push(dist, data_len + hash_len);
memset(k3, 0, sizeof(k3));
- silc_hash_make(hash, dist->data, dist->len, k3);
+ silc_hash_make(hash, dist->data, silc_buffer_len(dist), k3);
/* Then, save the keys */
dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
} else {
/* Take normal hash as key */
memset(hashd, 0, sizeof(hashd));
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
memcpy(key->send_enc_key, hashd, enc_key_len);
key->enc_key_len = req_enc_key_len;
buf->data[0] = 3;
if (enc_key_len > hash_len) {
SilcBuffer dist;
- unsigned char k1[32], k2[32], k3[32];
+ unsigned char k1[SILC_HASH_MAXLEN], k2[SILC_HASH_MAXLEN],
+ k3[SILC_HASH_MAXLEN];
unsigned char *dtmp;
-
+
/* XXX */
if (enc_key_len > (3 * hash_len))
- return SILC_SKE_STATUS_ERROR;
-
+ return NULL;
+
/* Take first round */
memset(k1, 0, sizeof(k1));
- silc_hash_make(hash, buf->data, buf->len, k1);
-
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), k1);
+
/* Take second round */
dist = silc_buffer_alloc_size(data_len + hash_len);
if (!dist)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return NULL;
silc_buffer_format(dist,
- SILC_STR_UI_XNSTRING(data, data_len),
- SILC_STR_UI_XNSTRING(k1, hash_len),
+ SILC_STR_DATA(data, data_len),
+ SILC_STR_DATA(k1, hash_len),
SILC_STR_END);
memset(k2, 0, sizeof(k2));
- silc_hash_make(hash, dist->data, dist->len, k2);
-
+ silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
+
/* Take third round */
dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
silc_buffer_pull_tail(dist, hash_len);
silc_buffer_pull(dist, data_len + hash_len);
silc_buffer_format(dist,
- SILC_STR_UI_XNSTRING(k2, hash_len),
+ SILC_STR_DATA(k2, hash_len),
SILC_STR_END);
silc_buffer_push(dist, data_len + hash_len);
memset(k3, 0, sizeof(k3));
- silc_hash_make(hash, dist->data, dist->len, k3);
+ silc_hash_make(hash, dist->data, silc_buffer_len(dist), k3);
/* Then, save the keys */
dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
} else {
/* Take normal hash as key */
memset(hashd, 0, sizeof(hashd));
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
memcpy(key->receive_enc_key, hashd, enc_key_len);
key->enc_key_len = req_enc_key_len;
/* Take HMAC keys */
memset(hashd, 0, sizeof(hashd));
buf->data[0] = 4;
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->send_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
memcpy(key->send_hmac_key, hashd, req_hmac_key_len);
memset(hashd, 0, sizeof(hashd));
buf->data[0] = 5;
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->receive_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
memcpy(key->receive_hmac_key, hashd, req_hmac_key_len);
key->hmac_key_len = req_hmac_key_len;
silc_buffer_clear(buf);
silc_buffer_free(buf);
- return SILC_SKE_STATUS_OK;
+ SILC_LOG_HEXDUMP(("enc"), key->send_enc_key, key->enc_key_len / 8);
+
+ return key;
}
/* Processes negotiated key material as protocol specifies. This returns
the actual keys to be used in the SILC. */
-SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
- SilcUInt32 req_iv_len,
- SilcUInt32 req_enc_key_len,
- SilcUInt32 req_hmac_key_len,
- SilcSKEKeyMaterial *key)
+SilcSKEKeyMaterial
+silc_ske_process_key_material(SilcSKE ske,
+ SilcUInt32 req_iv_len,
+ SilcUInt32 req_enc_key_len,
+ SilcUInt32 req_hmac_key_len,
+ SilcSKERekeyMaterial *rekey)
{
- SilcSKEStatus status;
SilcBuffer buf;
unsigned char *tmpbuf;
SilcUInt32 klen;
+ SilcSKEKeyMaterial key;
/* Encode KEY to binary data */
tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
buf = silc_buffer_alloc_size(klen + ske->hash_len);
if (!buf)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return NULL;
silc_buffer_format(buf,
- SILC_STR_UI_XNSTRING(tmpbuf, klen),
- SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
+ SILC_STR_DATA(tmpbuf, klen),
+ SILC_STR_DATA(ske->hash, ske->hash_len),
SILC_STR_END);
/* Process the key material */
- status = silc_ske_process_key_material_data(buf->data, buf->len,
- req_iv_len, req_enc_key_len,
- req_hmac_key_len,
- ske->prop->hash, key);
+ key = silc_ske_process_key_material_data(buf->data, silc_buffer_len(buf),
+ req_iv_len, req_enc_key_len,
+ req_hmac_key_len,
+ ske->prop->hash);
memset(tmpbuf, 0, klen);
silc_free(tmpbuf);
silc_buffer_clear(buf);
silc_buffer_free(buf);
- return status;
+ if (rekey) {
+ *rekey = silc_ske_make_rekey_material(ske, key);
+ if (!(*rekey))
+ return NULL;
+ }
+
+ return key;
}
/* Free key material structure */
-void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
+void silc_ske_free_key_material(SilcSKEKeyMaterial key)
{
if (!key)
return;
silc_free(key);
}
-const char *silc_ske_status_string[] =
+/* 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,
+ SilcSKEKeyMaterial keymat,
+ SilcSKESecurityProperties prop,
+ SilcCipher *ret_send_key,
+ SilcCipher *ret_receive_key,
+ SilcHmac *ret_hmac_send,
+ SilcHmac *ret_hmac_receive,
+ SilcHash *ret_hash)
+{
+ unsigned char iv[SILC_HASH_MAXLEN];
+ SilcBool iv_included = (prop->flags & SILC_SKE_SP_FLAG_IV_INCLUDED);
+
+ /* Allocate ciphers to be used in the communication */
+ if (ret_send_key) {
+ if (!silc_cipher_alloc((char *)silc_cipher_get_name(prop->cipher),
+ ret_send_key))
+ return FALSE;
+ }
+ if (ret_receive_key) {
+ if (!silc_cipher_alloc((char *)silc_cipher_get_name(prop->cipher),
+ ret_receive_key))
+ return FALSE;
+ }
+
+ /* Allocate HMACs */
+ if (ret_hmac_send) {
+ if (!silc_hmac_alloc((char *)silc_hmac_get_name(prop->hmac), NULL,
+ ret_hmac_send))
+ return FALSE;
+ }
+ if (ret_hmac_receive) {
+ if (!silc_hmac_alloc((char *)silc_hmac_get_name(prop->hmac), NULL,
+ ret_hmac_receive))
+ return FALSE;
+ }
+
+ /* Allocate hash */
+ if (ret_hash) {
+ if (!silc_hash_alloc(silc_hash_get_name(prop->hash), ret_hash))
+ return FALSE;
+ }
+
+ /* Set key material */
+ memset(iv, 0, sizeof(iv));
+ if (ske->responder) {
+ if (ret_send_key) {
+ silc_cipher_set_key(*ret_send_key, keymat->receive_enc_key,
+ keymat->enc_key_len, TRUE);
+
+ if (silc_cipher_get_mode(*ret_send_key) == SILC_CIPHER_MODE_CTR) {
+ /* Counter mode */
+ if (!ske->rekeying) {
+ /* Set IV. */
+ memcpy(iv, ske->hash, 4);
+ if (!iv_included)
+ memcpy(iv + 4, keymat->receive_iv, 8);
+ } else {
+ /* Rekey, recompute the truncated hash value. */
+ silc_hash_make(prop->hash, keymat->receive_iv, 8, iv);
+ if (!iv_included)
+ memcpy(iv + 4, keymat->receive_iv, 8);
+ else
+ memset(iv + 4, 0, 12);
+ }
+
+ silc_cipher_set_iv(*ret_send_key, iv);
+ } else {
+ /* Other modes */
+ silc_cipher_set_iv(*ret_send_key, keymat->receive_iv);
+ }
+ }
+ if (ret_receive_key) {
+ silc_cipher_set_key(*ret_receive_key, keymat->send_enc_key,
+ keymat->enc_key_len, FALSE);
+
+ if (silc_cipher_get_mode(*ret_receive_key) == SILC_CIPHER_MODE_CTR) {
+ /* Counter mode */
+ if (!ske->rekeying) {
+ /* Set IV. */
+ memcpy(iv, ske->hash, 4);
+ if (!iv_included)
+ memcpy(iv + 4, keymat->send_iv, 8);
+ } else {
+ /* Rekey, recompute the truncated hash value. */
+ silc_hash_make(prop->hash, keymat->send_iv, 8, iv);
+ if (!iv_included)
+ memcpy(iv + 4, keymat->send_iv, 8);
+ else
+ memset(iv + 4, 0, 12);
+ }
+
+ silc_cipher_set_iv(*ret_receive_key, iv);
+ } else {
+ /* Other modes */
+ silc_cipher_set_iv(*ret_receive_key, keymat->send_iv);
+ }
+ }
+ if (ret_hmac_send)
+ silc_hmac_set_key(*ret_hmac_send, keymat->receive_hmac_key,
+ keymat->hmac_key_len);
+ if (ret_hmac_receive)
+ silc_hmac_set_key(*ret_hmac_receive, keymat->send_hmac_key,
+ keymat->hmac_key_len);
+ } else {
+ if (ret_send_key) {
+ silc_cipher_set_key(*ret_send_key, keymat->send_enc_key,
+ keymat->enc_key_len, TRUE);
+
+ if (silc_cipher_get_mode(*ret_send_key) == SILC_CIPHER_MODE_CTR) {
+ /* Counter mode */
+ if (!ske->rekeying) {
+ /* Set IV. */
+ memcpy(iv, ske->hash, 4);
+ if (!iv_included)
+ memcpy(iv + 4, keymat->send_iv, 8);
+ } else {
+ /* Rekey, recompute the truncated hash value. */
+ silc_hash_make(prop->hash, keymat->send_iv, 8, iv);
+ if (!iv_included)
+ memcpy(iv + 4, keymat->send_iv, 8);
+ else
+ memset(iv + 4, 0, 12);
+ }
+
+ silc_cipher_set_iv(*ret_send_key, iv);
+ } else {
+ /* Other modes */
+ silc_cipher_set_iv(*ret_send_key, keymat->send_iv);
+ }
+ }
+ if (ret_receive_key) {
+ silc_cipher_set_key(*ret_receive_key, keymat->receive_enc_key,
+ keymat->enc_key_len, FALSE);
+
+ if (silc_cipher_get_mode(*ret_receive_key) == SILC_CIPHER_MODE_CTR) {
+ /* Counter mode */
+ if (!ske->rekeying) {
+ /* Set IV. If IV Included flag was negotiated we only set the
+ truncated hash value. */
+ memcpy(iv, ske->hash, 4);
+ if (!iv_included)
+ memcpy(iv + 4, keymat->receive_iv, 8);
+ } else {
+ /* Rekey, recompute the truncated hash value. */
+ silc_hash_make(prop->hash, keymat->receive_iv, 8, iv);
+ if (!iv_included)
+ memcpy(iv + 4, keymat->receive_iv, 8);
+ else
+ memset(iv + 4, 0, 12);
+ }
+
+ silc_cipher_set_iv(*ret_receive_key, iv);
+ } else {
+ /* Other modes */
+ silc_cipher_set_iv(*ret_receive_key, keymat->receive_iv);
+ }
+ }
+ if (ret_hmac_send)
+ silc_hmac_set_key(*ret_hmac_send, keymat->send_hmac_key,
+ keymat->hmac_key_len);
+ if (ret_hmac_receive)
+ silc_hmac_set_key(*ret_hmac_receive, keymat->receive_hmac_key,
+ keymat->hmac_key_len);
+ }
+
+ return TRUE;
+}
+
+const char *silc_ske_status_string[] =
{
/* Official */
"Ok",
- "Unkown error occurred",
+ "Unexpected error occurred",
"Bad payload in packet",
"Unsupported group",
"Unsupported cipher",
"Invalid cookie",
/* Other errors */
- "Pending",
"Remote did not provide public key",
- "Key exchange protocol is not active",
"Bad reserved field in packet",
"Bad payload length in packet",
"Error computing signature",
"System out of memory",
+ "Key exchange timeout",
NULL
};
/* Parses remote host's version string. */
-bool silc_ske_parse_version(SilcSKE ske,
- SilcUInt32 *protocol_version,
- char **protocol_version_string,
- SilcUInt32 *software_version,
- char **software_version_string,
- char **vendor_version)
+SilcBool silc_ske_parse_version(SilcSKE ske,
+ SilcUInt32 *protocol_version,
+ char **protocol_version_string,
+ SilcUInt32 *software_version,
+ char **software_version_string,
+ char **vendor_version)
{
return silc_parse_version_string(ske->remote_version,
- protocol_version,
- protocol_version_string,
+ protocol_version,
+ protocol_version_string,
software_version,
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;
+}