+ 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 {
+
+ if (!rp->enc_alg_len) {
+ SILC_LOG_DEBUG(("Encryption alg not defined in payload"));
+ silc_free(payload->ke_grp_list);
+ silc_free(payload->pkcs_alg_list);
+ silc_free(payload);
+ return SILC_SKE_STATUS_BAD_PAYLOAD;
+ }
+
+ SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
+ rp->enc_alg_list));
+
+ payload->enc_alg_len = rp->enc_alg_len;
+ payload->enc_alg_list = strdup(rp->enc_alg_list);
+ }
+
+ /* 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 {
+
+ if (!rp->hash_alg_len) {
+ SILC_LOG_DEBUG(("Hash alg not defined in payload"));
+ silc_free(payload->ke_grp_list);
+ silc_free(payload->pkcs_alg_list);
+ silc_free(payload->enc_alg_list);
+ silc_free(payload);
+ return SILC_SKE_STATUS_BAD_PAYLOAD;
+ }
+
+ SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it",
+ rp->hash_alg_list));
+
+ payload->hash_alg_len = rp->hash_alg_len;
+ payload->hash_alg_list = strdup(rp->hash_alg_list);
+ }
+
+ /* 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 {
+
+ if (!rp->hmac_alg_len) {
+ SILC_LOG_DEBUG(("HMAC not defined in payload"));
+ silc_free(payload->ke_grp_list);
+ silc_free(payload->pkcs_alg_list);
+ silc_free(payload->enc_alg_list);
+ silc_free(payload->hash_alg_list);
+ silc_free(payload);
+ return SILC_SKE_STATUS_BAD_PAYLOAD;
+ }
+
+ SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
+ rp->hmac_alg_list));
+
+ payload->hmac_alg_len = rp->hmac_alg_len;
+ payload->hmac_alg_list = strdup(rp->hmac_alg_list);
+ }
+
+ /* 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;
+
+ 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)
+{
+ SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ SilcBuffer buf;
+ unsigned char *e, *f, *KEY;
+ SilcUInt32 e_len, f_len, KEY_len;
+ int ret;
+
+ 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(silc_buffer_len(ske->start_payload_copy) +
+ 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,
+ silc_buffer_len(ske->start_payload_copy)),
+ SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
+ ske->ke2_payload->pk_len),
+ SILC_STR_UI_XNSTRING(e, e_len),
+ SILC_STR_UI_XNSTRING(f, f_len),
+ SILC_STR_UI_XNSTRING(KEY, KEY_len),
+ SILC_STR_END);
+ } else {
+ ret =
+ silc_buffer_format(buf,
+ SILC_STR_UI_XNSTRING(
+ ske->start_payload_copy->data,
+ silc_buffer_len(ske->start_payload_copy)),
+ SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
+ ske->ke2_payload->pk_len),
+ SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
+ ske->ke1_payload->pk_len),
+ SILC_STR_UI_XNSTRING(e, e_len),
+ SILC_STR_UI_XNSTRING(f, f_len),
+ SILC_STR_UI_XNSTRING(KEY, KEY_len),
+ SILC_STR_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 {
+ e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
+
+ buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
+ ske->ke1_payload->pk_len + e_len);
+ if (!buf)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
+
+ /* Format the buffer used to compute the hash value */
+ ret =
+ silc_buffer_format(buf,
+ SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
+ silc_buffer_len(ske->start_payload_copy)),
+ SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
+ ske->ke1_payload->pk_len),
+ SILC_STR_UI_XNSTRING(e, e_len),
+ SILC_STR_END);
+ if (ret == -1) {
+ silc_buffer_free(buf);
+ memset(e, 0, e_len);
+ silc_free(e);
+ return SILC_SKE_STATUS_ERROR;
+ }
+
+ 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;
+}
+
+
+/******************************* Protocol API *******************************/
+
+/* Allocates new SKE object. */
+
+SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
+ SilcPublicKey public_key, SilcPrivateKey private_key,
+ void *context)
+{
+ SilcSKE ske;
+
+ SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
+
+ if (!rng || !schedule)
+ return NULL;
+
+ ske = silc_calloc(1, sizeof(*ske));
+ if (!ske)
+ return NULL;
+ ske->status = SILC_SKE_STATUS_OK;
+ ske->rng = rng;
+ ske->user_data = context;
+ ske->schedule = schedule;
+ ske->public_key = public_key;
+ ske->private_key = private_key;
+ ske->pk_type = SILC_SKE_PK_TYPE_SILC;
+
+ return ske;
+}
+
+/* Free's SKE object. */
+
+void silc_ske_free(SilcSKE ske)
+{
+ SILC_LOG_DEBUG(("Freeing Key Exchange object"));
+
+ if (ske) {
+ /* Free start payload */
+ if (ske->start_payload)
+ silc_ske_payload_start_free(ske->start_payload);
+
+ /* Free KE payload */
+ if (ske->ke1_payload)
+ silc_ske_payload_ke_free(ske->ke1_payload);
+ if (ske->ke2_payload)
+ silc_ske_payload_ke_free(ske->ke2_payload);
+ silc_free(ske->remote_version);
+
+ /* Free rest */
+ if (ske->prop) {
+ if (ske->prop->group)
+ silc_ske_group_free(ske->prop->group);
+ if (ske->prop->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);
+ }
+ 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 protocol callbacks */
+
+void silc_ske_set_callbacks(SilcSKE ske,
+ SilcSKEVerifyCb verify_key,
+ SilcSKECompletionCb completed,
+ void *context)
+{
+ if (ske->callbacks)
+ silc_free(ske->callbacks);
+ ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks));
+ if (!ske->callbacks)
+ return;
+ ske->callbacks->verify_key = verify_key;
+ ske->callbacks->completed = completed;
+ ske->callbacks->context = context;
+}
+
+
+/******************************** Initiator *********************************/
+
+/* Initiator state machine */
+SILC_FSM_STATE(silc_ske_st_initiator_start);
+SILC_FSM_STATE(silc_ske_st_initiator_phase1);
+SILC_FSM_STATE(silc_ske_st_initiator_phase2);
+SILC_FSM_STATE(silc_ske_st_initiator_phase3);
+SILC_FSM_STATE(silc_ske_st_initiator_phase4);
+SILC_FSM_STATE(silc_ske_st_initiator_end);
+SILC_FSM_STATE(silc_ske_st_initiator_aborted);
+SILC_FSM_STATE(silc_ske_st_initiator_error);
+
+/* Start protocol. Send our proposal */
+
+SILC_FSM_STATE(silc_ske_st_initiator_start)
+{
+ SilcSKE ske = fsm_context;
+ SilcBuffer payload_buf;
+ SilcStatus status;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ 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, 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;
+ }
+
+ /* Save the the payload buffer for future use. It is later used to
+ compute the HASH value. */
+ ske->start_payload_copy = payload_buf;
+
+ /* Send the packet */
+ /* XXX */
+
+ /** Wait for responder proposal */
+ SILC_LOG_DEBUG(("Waiting for reponder proposal"));
+ silc_fsm_next(ske, silc_ske_st_initiator_phase1);
+ return SILC_FSM_WAIT;
+}
+
+/* Phase-1. Receives responder's proposal */
+
+SILC_FSM_STATE(silc_ske_st_initiator_phase1)
+{
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEStartPayload payload;
+ SilcSKESecurityProperties prop;
+ SilcSKEDiffieHellmanGroup group;
+ SilcBuffer packet_buf = &ske->packet->buffer;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /* Decode the payload */
+ status = silc_ske_payload_start_decode(ske, packet_buf, &payload);
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Error decoding Start Payload */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /* Check that the cookie is returned unmodified */
+ if (memcmp(ske->start_payload->cookie, payload->cookie,
+ ske->start_payload->cookie_len)) {
+ /** Invalid cookie */
+ SILC_LOG_ERROR(("Responder modified our cookie and it must not do it"));
+ ske->status = SILC_SKE_STATUS_INVALID_COOKIE;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /* Check version string */
+ 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. */
+ ske->prop = prop = silc_calloc(1, sizeof(*prop));
+ if (!ske->prop)
+ goto err;
+ prop->flags = payload->flags;
+ status = silc_ske_group_get_by_name(payload->ke_grp_list, &group);
+ if (status != SILC_SKE_STATUS_OK)
+ goto err;
+
+ prop->group = group;
+
+ if (silc_pkcs_alloc(payload->pkcs_alg_list, ske->pk_type,
+ &prop->pkcs) == FALSE) {
+ 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;
+
+ /** 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 (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 (status == SILC_SKE_STATUS_OK)
+ status = SILC_SKE_STATUS_ERROR;
+
+ /** Error */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+}
+
+/* Phase-2. Send KE payload */
+
+SILC_FSM_STATE(silc_ske_st_initiator_phase2)
+{
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcBuffer payload_buf;
+ SilcMPInt *x;
+ 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;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+ silc_mp_init(x);
+ 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;
+ 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;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+ ske->ke1_payload = payload;
+
+ SILC_LOG_DEBUG(("Computing e = g ^ x mod p"));
+
+ /* 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,
+ &ske->prop->group->group);
+
+ /* Get public key */
+ if (ske->public_key) {
+ payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
+ if (!payload->pk_data) {
+ /** Error encoding public key */
+ silc_mp_uninit(x);
+ silc_free(x);
+ silc_mp_uninit(&payload->x);
+ silc_free(payload);
+ ske->ke1_payload = NULL;
+ ske->status = SILC_SKE_STATUS_ERROR;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+ payload->pk_len = pk_len;
+ }
+ payload->pk_type = ske->pk_type;
+
+ /* Compute signature data if we are doing mutual authentication */
+ if (ske->private_key &&
+ ske->start_payload->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_LOG_DEBUG(("Computing HASH_i value"));
+
+ /* Compute the hash value */
+ memset(hash, 0, sizeof(hash));
+ 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, ske->private_key->prv,
+ ske->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)) {
+ /** Error computing signature */
+ silc_mp_uninit(x);
+ silc_free(x);
+ silc_mp_uninit(&payload->x);
+ silc_free(payload->pk_data);
+ silc_free(payload);
+ ske->ke1_payload = NULL;
+ ske->status = SILC_SKE_STATUS_SIGNATURE_ERROR;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+ payload->sign_data = silc_memdup(sign, sign_len);
+ payload->sign_len = sign_len;
+ memset(sign, 0, sizeof(sign));
+ }
+
+ 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);