More client library rewrites (key agreement, plus other).
authorPekka Riikonen <priikone@silcnet.org>
Thu, 7 Dec 2006 17:01:49 +0000 (17:01 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 7 Dec 2006 17:01:49 +0000 (17:01 +0000)
15 files changed:
lib/silcclient/Makefile.ad
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/client_connect.c
lib/silcclient/client_entry.c
lib/silcclient/client_internal.h
lib/silcclient/client_keyagr.c
lib/silcclient/client_keyagr.h [new file with mode: 0644]
lib/silcclient/client_notify.c
lib/silcclient/client_prvmsg.c
lib/silcclient/client_register.c
lib/silcclient/command.c
lib/silcclient/command_reply.c
lib/silcclient/silcclient.h
lib/silcclient/tests/test_silcclient.c

index 1263c327aa77787c62fceb1e1a251b692d52d70b..58912a132221333f26b591b03a76d8514692fc87 100644 (file)
@@ -28,6 +28,7 @@ libsilcclient_la_SOURCES =            \
        client_register.c               \
        client_notify.c                 \
        client_attrs.c                  \
+       client_keyagr.c                 \
        command.c                       \
        command_reply.c
 
index bd354495316759bc9ce72cc3d5b615e6eef483cf..9354d28d6c6e3b641a94dc0fe396cb2dd2477e4c 100644 (file)
@@ -281,8 +281,8 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
     break;
 
   case SILC_PACKET_KEY_AGREEMENT:
-    /* Key agreement */
-    //    silc_client_key_agreement(client, conn, packet);
+    /** Key agreement */
+    silc_fsm_next(fsm, silc_client_key_agreement);
     break;
 
   case SILC_PACKET_COMMAND:
@@ -388,8 +388,10 @@ SILC_FSM_STATE(silc_client_disconnect)
   silc_packet_free(packet);
 
   /* Signal to close connection */
-  conn->internal->disconnected = TRUE;
-  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  }
 
   return SILC_FSM_FINISH;
 }
@@ -682,8 +684,10 @@ void silc_client_close_connection(SilcClient client,
   SILC_LOG_DEBUG(("Closing connection %p", conn));
 
   /* Signal to close connection */
-  conn->internal->disconnected = TRUE;
-  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  }
 }
 
 #if 0
index d60b740a9ab8586440dd9f04002185ab4d7b9f97..53a5550aaea5b22cfb7aa21109ce54aceaa63331 100644 (file)
@@ -57,8 +57,8 @@ typedef struct SilcClientEntryInternalStruct {
   unsigned int valid       : 1;        /* FALSE if this entry is not valid */
   unsigned int resolving   : 1; /* TRUE when entry is being resolved */
   unsigned int generated   : 1; /* TRUE if library generated `key' */
-  unsigned int prv_resp    : 1; /* TRUE if private message key indicator
-                                  has been received (responder). */
+  unsigned int prv_resp    : 1; /* TRUE if we are responder when using
+                                  private message keys. */
   SilcUInt16 resolve_cmd_ident;        /* Command identifier when resolving */
   SilcAtomic8 refcnt;          /* Reference counter */
 } SilcClientEntryInternal;
index 4aff0a6b77c0906c9a0aa58e70ad70f2352ed744..7c50d2b9e199b549364c65f9b6ef336e1fa16b2d 100644 (file)
 
 /************************** Types and definitions ***************************/
 
-/* Public key verification context */
-typedef struct {
-  SilcSKE ske;
-  SilcSKEVerifyCbCompletion completion;
-  void *completion_context;
-} *VerifyKeyContext;
-
 /************************ Static utility functions **************************/
 
 /* Callback called after connected to remote host */
@@ -41,6 +34,7 @@ static void silc_client_connect_callback(SilcNetStatus status,
   SilcClientConnection conn = silc_fsm_get_context(fsm);
   SilcClient client = conn->client;
 
+  conn->internal->op = NULL;
   if (conn->internal->verbose) {
     switch (status) {
     case SILC_NET_OK:
@@ -172,6 +166,7 @@ static void silc_client_ke_completion(SilcSKE ske,
   SilcCipher send_key, receive_key;
   SilcHmac hmac_send, hmac_receive;
 
+  conn->internal->op = NULL;
   if (status != SILC_SKE_STATUS_OK) {
     /* Key exchange failed */
     SILC_LOG_DEBUG(("Error during key exchange with %s: %s (%d)",
@@ -266,6 +261,22 @@ static void silc_client_connect_auth_completion(SilcConnAuth connauth,
   SILC_FSM_CALL_CONTINUE(fsm);
 }
 
+/* Connection timeout callback */
+
+SILC_TASK_CALLBACK(silc_client_connect_timeout)
+{
+  SilcClientConnection conn = context;
+  SilcClient client = conn->client;
+
+  SILC_LOG_DEBUG(("Connection timeout"));
+
+  conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_TIMEOUT, 0, NULL,
+                conn->callback_context);
+
+  silc_fsm_next(&conn->internal->event_thread, silc_client_st_connect_error);
+  silc_fsm_continue_sync(&conn->internal->event_thread);
+}
+
 /*************************** Connect remote host ****************************/
 
 /* Creates a connection to remote host */
@@ -281,6 +292,12 @@ SILC_FSM_STATE(silc_client_st_connect)
   /** Connect */
   silc_fsm_next(fsm, silc_client_st_connect_set_stream);
 
+  /* Add connection timeout */
+  if (conn->internal->params.timeout_secs)
+    silc_schedule_task_add_timeout(conn->internal->schedule,
+                                  silc_client_connect_timeout, conn,
+                                  conn->internal->params.timeout_secs, 0);
+
   if (conn->internal->params.udp) {
     SilcStream stream;
 
@@ -294,7 +311,9 @@ SILC_FSM_STATE(silc_client_st_connect)
     }
 
     /* Connect (UDP) */
-    stream = silc_net_udp_connect(conn->internal->params.local_ip,
+    stream = silc_net_udp_connect(conn->internal->params.bind_ip ?
+                                 conn->internal->params.bind_ip :
+                                 conn->internal->params.local_ip,
                                  conn->internal->params.local_port,
                                  conn->remote_host, conn->remote_port,
                                  conn->internal->schedule);
@@ -517,6 +536,8 @@ SILC_FSM_STATE(silc_client_st_connected)
     return SILC_FSM_CONTINUE;
   }
 
+  silc_schedule_task_del_by_context(conn->internal->schedule, conn);
+
   /* Call connection callback */
   conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
                 conn->callback_context);
@@ -531,8 +552,12 @@ SILC_FSM_STATE(silc_client_st_connect_error)
   SilcClientConnection conn = fsm_context;
 
   /* Signal to close connection */
-  conn->internal->disconnected = TRUE;
-  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  }
+
+  silc_schedule_task_del_by_context(conn->internal->schedule, conn);
 
   return SILC_FSM_FINISH;
 }
index 43687fb079dc590ce5b148e5aab5fad21e44f58b..c68dc4754508a7301bc6d200d90bd7ff28bbf175 100644 (file)
@@ -750,6 +750,8 @@ SilcClientEntry silc_client_add_client(SilcClient client,
   silc_mutex_unlock(conn->internal->lock);
   silc_client_ref_client(client, conn, client_entry);
 
+  SILC_LOG_DEBUG(("Added"));
+
   return client_entry;
 }
 
@@ -1326,6 +1328,8 @@ SilcChannelEntry silc_client_add_channel(SilcClient client,
   silc_mutex_unlock(conn->internal->lock);
   silc_client_ref_channel(client, conn, channel);
 
+  SILC_LOG_DEBUG(("Added"));
+
   return channel;
 }
 
@@ -1694,6 +1698,8 @@ SilcServerEntry silc_client_add_server(SilcClient client,
   silc_mutex_unlock(conn->internal->lock);
   silc_client_ref_server(client, conn, server_entry);
 
+  SILC_LOG_DEBUG(("Added"));
+
   return server_entry;
 }
 
index 3dbcd6729439b7193f6bf30bcfeea313d327e759..8f56f3571897708f628def8915cc39feddbb5025 100644 (file)
@@ -28,6 +28,7 @@
 #include "client_prvmsg.h"
 #include "client_channel.h"
 #include "client_notify.h"
+#include "client_keyagr.h"
 
 /****************************** Definitions *********************************/
 
 
 /********************************** Types ***********************************/
 
+/* Public key verification context */
+typedef struct {
+  SilcSKE ske;
+  SilcSKEVerifyCbCompletion completion;
+  void *completion_context;
+} *VerifyKeyContext;
+
 /* Context to hold the connection authentication request callbacks that
    will be called when the server has replied back to our request about
    current authentication method in the session. */
@@ -214,9 +222,6 @@ void silc_client_command_free(SilcClientCommandContext cmd);
 
 void silc_client_ftp(SilcClient client, SilcClientConnection conn,
                     SilcPacket packet);
-void silc_client_key_agreement(SilcClient client,
-                              SilcClientConnection conn,
-                              SilcPacket packet);
 void silc_client_connection_auth_request(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcPacket packet);
index a68d94a0600f364569cbd84d7d7e14b8ad4e602d..52dca47c040be11f66ac6e3782fbc0fd95c6b742 100644 (file)
 
 /************************** Types and definitions ***************************/
 
-/* Key agreement context */
+/* Key agreement context, used by responder */
 struct SilcClientKeyAgreementStruct {
   SilcClient client;                     /* Client */
   SilcClientConnection conn;             /* Server connection */
   SilcKeyAgreementCallback completion;   /* Key agreement completion */
   void *context;                         /* User context */
-
-  /* Responder */
-  SilcNetListener listener;              /* TCP listener */
-  SilcStream stream;                     /* Remote connection (TCP or UDP) */
-
-  /* Initiator */
-  SilcClientConnection client_conn;      /* Connection to remote client */
+  SilcClientConnectionParams params;      /* Connection parameters */
+  SilcPublicKey public_key;              /* Responder public key */
+  SilcPrivateKey private_key;            /* Responder private key */
+  SilcNetListener tcp_listener;                  /* TCP listener */
+  SilcPacketStream udp_listener;         /* UDP listener */
+  SilcPacketStream stream;               /* Remote connection (TCP or UDP) */
+  SilcAsyncOperation op;                 /* SKE operation */
+  SilcSKE ske;                           /* SKE */
 };
 
 /************************ Static utility functions **************************/
 
-/* TCP network listener callback.  Accepts new key agreement connection */
+/* Destroyes key agreement session */
 
-static void silc_client_tcp_accept(SilcNetStatus status,
-                                  SilcStream stream,
-                                  void *context)
+static void silc_client_keyagr_free(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcClientEntry client_entry)
 {
-  SilcClientEntry client_entry = context;
-  SilcClientKeyAgreement ke = client_entry->ke;
+  SilcClientKeyAgreement ke = client_entry->internal.ke;
+
+  silc_schedule_task_del_by_context(conn->internal->schedule, client_entry);
+
+  if (ke->op)
+    silc_async_abort(ke->op, NULL, NULL);
+  if (ke->ske)
+    silc_ske_free(ke->ske);
+  if (ke->tcp_listener)
+    silc_net_close_listener(ke->tcp_listener);
+  silc_packet_stream_destroy(ke->stream);
+  silc_packet_stream_destroy(ke->udp_listener);
 
-  ke->stream = stream;
-  silc_client_process_key_agreement(ke->client, ke->conn, ke);
+  client_entry->internal.ke = NULL;
+  client_entry->internal.prv_resp = FALSE;
+  silc_client_unref_client(client, conn, client_entry);
+
+  silc_free(ke);
 }
 
-/* UDP network callback.  All UDP packets are read from here. */
+/* Key agreement timeout callback */
 
-static void silc_client_udp_accept(SilcStream stream,
-                                  SilcStreamStatus status,
-                                  void *context)
+SILC_TASK_CALLBACK(silc_client_keyagr_timeout)
 {
+  SilcClientEntry client_entry = context;
+  SilcClientKeyAgreement ke = client_entry->internal.ke;
 
-}
+  SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
 
+  ke->completion(ke->client, ke->conn, client_entry,
+                SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
+
+  silc_client_keyagr_free(ke->client, ke->conn, client_entry);
+}
 
-/* Packet sending function used by the SKE in the key agreement process. */
+/* Client resolving callback.  Continues with the key agreement processing */
 
-static void silc_client_key_agreement_send_packet(SilcSKE ske,
-                                                 SilcBuffer packet,
-                                                 SilcPacketType type,
-                                                 void *context)
+static void silc_client_keyagr_resolved(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcStatus status,
+                                       SilcDList clients,
+                                       void *context)
 {
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx =
-    (SilcClientKEInternalContext *)protocol->context;
-  void *tmp;
-
-  /* Send the packet immediately. We will assure that the packet is not
-     encrypted by setting the socket's user_data pointer to NULL. The
-     silc_client_packet_send would take the keys (wrong keys that is,
-     because user_data is the current SilcClientConnection) from it and
-     we cannot allow that. The packets are never encrypted when doing SKE
-     with another client. */
-  tmp = ske->sock->user_data;
-  ske->sock->user_data = NULL;
-  silc_client_packet_send(ctx->client, ske->sock, type, NULL, 0, NULL, NULL,
-                         packet->data, packet->len, TRUE);
-  ske->sock->user_data = tmp;
+  /* If no client found, ignore the packet, a silent error */
+  if (!clients)
+    silc_fsm_next(context, silc_client_key_agreement_error);
+
+  /* Continue processing the packet */
+  SILC_FSM_CALL_CONTINUE(context);
 }
 
-/* Timeout callback that is called to close the connection and free the
-   socket connection data. */
+/* Called after application has verified remote host's public key.  Responder
+   function. */
 
-SILC_TASK_CALLBACK(silc_client_key_agreement_close)
+static void silc_client_keyagr_verify_key_cb(SilcBool success, void *context)
 {
-  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+  VerifyKeyContext verify = context;
 
-  silc_schedule_unset_listen_fd(ke->client->schedule, ke->sock->sock);
-  silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
-  silc_net_close_connection(ke->sock->sock);
-  silc_net_close_connection(ke->fd);
-  silc_socket_free(ke->sock);
-  silc_free(ke);
+  /* Call the completion callback back to the SKE */
+  verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
+                    SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+                    verify->completion_context);
+
+  silc_free(verify);
 }
 
-/* This callback is called after the key agreement protocol has been
-   performed. This calls the final completion callback for the application. */
+/* Verify remote host's public key.  Responder function. */
 
-SILC_TASK_CALLBACK(silc_client_key_agreement_final)
+static void silc_client_keyagr_verify_key(SilcSKE ske,
+                                         SilcPublicKey public_key,
+                                         void *context,
+                                         SilcSKEVerifyCbCompletion completion,
+                                         void *completion_context)
 {
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx =
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
-      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
-    /* Error occured during protocol */
-    ke->client_entry->ke = NULL;
-    ke->completion(ke->client, ke->conn, ke->client_entry,
-                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
-    silc_ske_free_key_material(ctx->keymat);
-    goto out;
+  SilcClientEntry client_entry = context;
+  SilcClientKeyAgreement ke = client_entry->internal.ke;
+  SilcClientConnection conn = ke->conn;
+  SilcClient client = conn->client;
+  VerifyKeyContext verify;
+
+  /* If we provided repository for SKE and we got here the key was not
+     found from the repository. */
+  if (ke->params.repository && !ke->params.verify_notfound) {
+    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+              completion_context);
+    return;
   }
 
-  /* Pass the negotiated key material to the application. The application
-     is responsible of freeing the key material. */
-  ke->client_entry->ke = NULL;
-  ke->completion(ke->client, ke->conn, ke->client_entry,
-                SILC_KEY_AGREEMENT_OK, ctx->keymat, ke->context);
-
- out:
-  silc_protocol_free(protocol);
-  if (ctx->ske)
-    silc_ske_free(ctx->ske);
-  if (ctx->dest_id)
-    silc_free(ctx->dest_id);
-  silc_schedule_task_del_by_fd(client->schedule, ke->fd);
-  silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
-  silc_net_close_connection(ke->fd);
-  if (ke->timeout)
-    silc_schedule_task_del(client->schedule, ke->timeout);
-  silc_client_del_socket(ke->client, ke->sock);
-
-  silc_schedule_task_add(client->schedule, 0,
-                    silc_client_key_agreement_close,
-                    (void *)ke, 0, 1,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-  silc_free(ctx);
+  SILC_LOG_DEBUG(("Verify remote public key"));
+
+  verify = silc_calloc(1, sizeof(*verify));
+  if (!verify) {
+    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+              completion_context);
+    return;
+  }
+  verify->ske = ske;
+  verify->completion = completion;
+  verify->completion_context = completion_context;
+
+  /* Verify public key in application */
+  client->internal->ops->verify_public_key(client, conn,
+                                          SILC_CONN_CLIENT, public_key,
+                                          silc_client_keyagr_verify_key_cb,
+                                          verify);
 }
 
-/* Key agreement callback that is called when remote end has initiated
-   the key agreement protocol. This accepts the incoming TCP/IP connection
-   for the key agreement protocol. */
+/* Key exchange protocol completion callback.  Responder function. */
 
-SILC_TASK_CALLBACK(silc_client_process_key_agreement)
+static void silc_client_keyagr_completion(SilcSKE ske,
+                                         SilcSKEStatus status,
+                                         SilcSKESecurityProperties prop,
+                                         SilcSKEKeyMaterial keymat,
+                                         SilcSKERekeyMaterial rekey,
+                                         void *context)
 {
-  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
-  SilcClient client = ke->client;
+  SilcClientEntry client_entry = context;
+  SilcClientKeyAgreement ke = client_entry->internal.ke;
   SilcClientConnection conn = ke->conn;
-  SilcSocketConnection newsocket;
-  SilcClientKEInternalContext *proto_ctx;
-  int sock;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  sock = silc_net_accept_connection(ke->fd);
-  if (sock < 0) {
-    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                              "Could not accept key agreement connection: ",
-                              strerror(errno));
-    ke->client_entry->ke = NULL;
-    ke->completion(ke->client, ke->conn, ke->client_entry,
-                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
-    silc_schedule_task_del_by_fd(client->schedule, ke->fd);
-    silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
-    silc_net_close_connection(ke->fd);
-    if (ke->timeout)
-      silc_schedule_task_del(client->schedule, ke->timeout);
-    silc_free(ke);
+  SilcClient client = conn->client;
+
+  if (status != SILC_SKE_STATUS_OK) {
+    /* Key exchange failed */
+    ke->completion(client, conn, client_entry,
+                  status == SILC_SKE_STATUS_TIMEOUT ?
+                  SILC_KEY_AGREEMENT_TIMEOUT :
+                  SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
+    silc_client_keyagr_free(client, conn, client_entry);
+    return;
+  }
+
+  /* Returns the negotiated key material to application.  Key agreement
+     was successful. */
+  ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_OK,
+                keymat, ke->context);
+
+  silc_client_keyagr_free(client, conn, client_entry);
+}
+
+/* Starts key agreement as responder.  */
+
+static void silc_client_process_key_agreement(SilcClient client,
+                                             SilcClientConnection conn,
+                                             SilcClientEntry client_entry)
+{
+  SilcClientKeyAgreement ke = client_entry->internal.ke;
+  SilcSKEParamsStruct params;
+
+  SILC_LOG_DEBUG(("Processing key agrement %p session", ke));
+
+  /* Allocate SKE */
+  ke->ske = silc_ske_alloc(client->rng, conn->internal->schedule,
+                          ke->params.repository, ke->public_key,
+                          ke->private_key, client_entry);
+  if (!ke->ske) {
+    ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
+                  NULL, ke->context);
+    silc_client_keyagr_free(client, conn, client_entry);
     return;
   }
 
-  /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-  /* Create socket for this connection (it is of type UNKNOWN since this
-     really is not a real SILC connection. It is only for the key
-     agreement protocol). */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &newsocket);
-  ke->sock = newsocket;
-
-  /* Perform name and address lookups for the remote host. */
-  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
-  if (!newsocket->hostname && !newsocket->ip) {
-    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                              "Could not resolve the remote IP or hostname");
-    ke->client_entry->ke = NULL;
-    ke->completion(ke->client, ke->conn, ke->client_entry,
-                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
-    silc_schedule_task_del_by_fd(client->schedule, ke->fd);
-    silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
-    silc_net_close_connection(ke->fd);
-    if (ke->timeout)
-      silc_schedule_task_del(client->schedule, ke->timeout);
-    silc_free(ke);
+  /* Set SKE parameters */
+  params.version = client->internal->silc_client_version;
+  params.flags = SILC_SKE_SP_FLAG_MUTUAL;
+  if (ke->params.udp) {
+    params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
+    params.session_port = ke->params.local_port;
+  }
+
+  silc_ske_set_callbacks(ke->ske, silc_client_keyagr_verify_key,
+                        silc_client_keyagr_completion, client_entry);
+
+  /* Start key exchange as responder */
+  ke->op = silc_ske_responder(ke->ske, ke->stream, &params);
+  if (!ke->op) {
+    ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
+                  NULL, ke->context);
+    silc_client_keyagr_free(client, conn, client_entry);
+  }
+}
+
+/* TCP network listener callback.  Accepts new key agreement connection.
+   Responder function. */
+
+static void silc_client_tcp_accept(SilcNetStatus status,
+                                  SilcStream stream,
+                                  void *context)
+{
+  SilcClientEntry client_entry = context;
+  SilcClientKeyAgreement ke = client_entry->internal.ke;
+
+  /* Create packet stream */
+  ke->stream = silc_packet_stream_create(ke->client->internal->packet_engine,
+                                        ke->conn->internal->schedule, stream);
+  if (!ke->stream) {
+    silc_stream_destroy(stream);
     return;
   }
-  if (!newsocket->hostname)
-    newsocket->hostname = strdup(newsocket->ip);
-  newsocket->port = silc_net_get_remote_port(sock);
-  silc_client_add_socket(client, newsocket);
-
-  /* Allocate internal context for key exchange protocol. This is
-     sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = client;
-  proto_ctx->sock = silc_socket_dup(newsocket);
-  proto_ctx->rng = client->rng;
-  proto_ctx->responder = TRUE;
-  proto_ctx->context = context;
-  proto_ctx->send_packet = silc_client_key_agreement_send_packet;
-  proto_ctx->verify = silc_client_protocol_ke_verify_key;
-  ke->proto_ctx = proto_ctx;
-
-  /* Prepare the connection for key exchange protocol. We allocate the
-     protocol but will not start it yet. The connector will be the
-     initiator of the protocol thus we will wait for initiation from
-     there before we start the protocol. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
-                     &newsocket->protocol, proto_ctx,
-                     silc_client_key_agreement_final);
-
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection
-     and sets that outgoing packets may be sent to this connection as well.
-     However, this doesn't set the scheduler for outgoing traffic, it
-     will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  context = (void *)client;
-  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
+
+  /* Process session */
+  silc_client_process_key_agreement(ke->client, ke->conn, client_entry);
 }
 
-/* Timeout occured during key agreement. This means that the key agreement
-   protocol was not completed in the specified timeout. We will call the
-   completion callback. */
+/* UDP network listener callback.  Accepts new key agreement session.
+   Responder function. */
 
-SILC_TASK_CALLBACK(silc_client_key_agreement_timeout)
+static SilcBool silc_client_udp_accept(SilcPacketEngine engine,
+                                       SilcPacketStream stream,
+                                       SilcPacket packet,
+                                       void *callback_context,
+                                       void *stream_context)
 {
-  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+  SilcClientEntry client_entry = callback_context;
+  SilcClientKeyAgreement ke = client_entry->internal.ke;
+  SilcUInt16 port;
+  const char *ip;
+
+  /* We want only key exchange packet.  Eat other packets so that default
+     packet callback doesn't get them. */
+  if (packet->type != SILC_PACKET_KEY_EXCHANGE) {
+    silc_packet_free(packet);
+    return TRUE;
+  }
 
-  ke->client_entry->ke = NULL;
-  ke->completion(ke->client, ke->conn, ke->client_entry,
-                SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
+  /* Create packet stream for this remote UDP session */
+  if (!silc_packet_get_sender(packet, &ip, &port)) {
+    silc_packet_free(packet);
+    return TRUE;
+  }
+  ke->stream = silc_packet_stream_add_remote(stream, ip, port, packet);
+  if (!ke->stream) {
+    silc_packet_free(packet);
+    return TRUE;
+  }
+
+  /* Process session */
+  silc_client_process_key_agreement(ke->client, ke->conn, client_entry);
+  return TRUE;
+}
 
-  if (ke->sock) {
-    silc_client_del_socket(ke->client, ke->sock);
-    silc_socket_free(ke->sock);
+/* Client connect completion callback.  Initiator function. */
+
+static void silc_client_keyagr_perform_cb(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientConnectionStatus status,
+                                         SilcStatus error,
+                                         const char *message,
+                                         void *context)
+{
+  SilcClientEntry client_entry = context;
+  SilcClientKeyAgreement ke = client_entry->internal.ke;
+  SilcSKEKeyMaterial keymat;
+
+  ke->op = NULL;
+
+  switch (status) {
+  case SILC_CLIENT_CONN_SUCCESS:
+    SILC_LOG_DEBUG(("Key agreement %p successful", ke));
+
+    keymat = silc_ske_get_key_material(conn->internal->ske);
+    ke->completion(ke->client, ke->conn, client_entry, SILC_KEY_AGREEMENT_OK,
+                  keymat, ke->context);
+    break;
+
+  case SILC_CLIENT_CONN_ERROR_TIMEOUT:
+    SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
+    ke->completion(ke->client, ke->conn, client_entry,
+                  SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
+    break;
+
+  default:
+    SILC_LOG_DEBUG(("Key agreement %p error %d", ke, status));
+    ke->completion(ke->client, ke->conn, client_entry,
+                  SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
+    break;
   }
-  if (ke->proto_ctx && ke->proto_ctx->ske)
-    silc_ske_free(ke->proto_ctx->ske);
-  ke->client_entry->ke = NULL;
-  if (ke->fd)
-    silc_schedule_task_del_by_fd(ke->client->schedule, ke->fd);
-  silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
-  silc_net_close_connection(ke->fd);
-  silc_free(ke);
+
+  /* Close the created connection */
+  if (conn)
+    silc_client_close_connection(ke->client, conn);
+
+  silc_client_keyagr_free(ke->client, ke->conn, client_entry);
 }
 
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_client_keyagr_stream_cb =
+{
+  silc_client_udp_accept, NULL, NULL
+};
+
 /*************************** Key Agreement API ******************************/
 
 /* Sends key agreement packet to remote client.  If IP addresses are provided
@@ -281,23 +346,24 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_timeout)
 void silc_client_send_key_agreement(SilcClient client,
                                    SilcClientConnection conn,
                                    SilcClientEntry client_entry,
-                                   const char *local_ip,
-                                   const char *bind_ip,
-                                   int port,
-                                   SilcUInt32 timeout_secs,
-                                   SilcBool udp,
+                                   SilcClientConnectionParams *params,
+                                   SilcPublicKey public_key,
+                                   SilcPrivateKey private_key,
                                    SilcKeyAgreementCallback completion,
                                    void *context)
 {
   SilcClientKeyAgreement ke = NULL;
-  SilcAsyncOperation op;
   SilcBuffer buffer;
-  SilcUInt16 ports = NULL;
+  SilcUInt16 port = 0, protocol = 0;
+  char *local_ip = NULL;
+  SilcStream stream;
+
+  SILC_LOG_DEBUG(("Sending key agreement"));
 
   if (!client_entry)
     return;
 
-  if (client_entry->internal->ke) {
+  if (client_entry->internal.ke) {
     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ALREADY_STARTED,
               NULL, context);
     return;
@@ -310,7 +376,7 @@ void silc_client_send_key_agreement(SilcClient client,
   }
 
   /* If local IP is provided, create listener */
-  if (local_ip || bind_ip) {
+  if (params && (params->local_ip || params->bind_ip)) {
     ke = silc_calloc(1, sizeof(*ke));
     if (!ke) {
       completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
@@ -319,187 +385,133 @@ void silc_client_send_key_agreement(SilcClient client,
     }
 
     /* Create network listener */
-    if (udp) {
+    if (params->udp) {
       /* UDP listener */
-      ke->stream =
-       silc_net_udp_connect(bind_ip ? bind_ip : local_ip, port, NULL, 0,
-                            client_entry);
-      if (!ke->stream) {
+      stream = silc_net_udp_connect(params->bind_ip ? params->bind_ip :
+                                   params->local_ip, params->local_port,
+                                   NULL, 0, conn->internal->schedule);
+      ke->udp_listener =
+       silc_packet_stream_create(client->internal->packet_engine,
+                                 conn->internal->schedule, stream);
+      if (!ke->udp_listener) {
        client->internal->ops->say(
                     client, conn, SILC_CLIENT_MESSAGE_ERROR,
                     "Cannot create UDP listener on %s on port %d: %s",
-                    bind_ip ? bind_ip : local_ip, port, strerror(errno));
+                    params->bind_ip ? params->bind_ip :
+                    params->local_ip, params->local_port, strerror(errno));
        completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
                   NULL, context);
+       if (stream)
+         silc_stream_destroy(stream);
        silc_free(ke);
        return;
       }
-      silc_stream_set_notifier(ke->stream, conn->schedule,
-                              silc_client_udp_accept, client_entry);
+      silc_packet_stream_link(ke->udp_listener,
+                             &silc_client_keyagr_stream_cb,
+                             client_entry, 1000000,
+                             SILC_PACKET_ANY, -1);
+
+      port = params->local_port;
+      if (!port) {
+       /* Get listener port */
+       int sock;
+       silc_socket_stream_get_info(stream, &sock, NULL, NULL, NULL);
+       port = silc_net_get_local_port(sock);
+      }
     } else {
       /* TCP listener */
-      ke->listener =
-       silc_net_tcp_create_listener(bind_ip ? &bind_ip :
-                                    &local_ip, 1, port, FALSE,
-                                    FALSE, conn->internal->schedule,
+      ke->tcp_listener =
+       silc_net_tcp_create_listener(params->bind_ip ?
+                                    (const char **)&params->bind_ip :
+                                    (const char **)&params->local_ip,
+                                    1, params->local_port, FALSE, FALSE,
+                                    conn->internal->schedule,
                                     silc_client_tcp_accept,
                                     client_entry);
-      if (!ke->listener) {
+      if (!ke->tcp_listener) {
        client->internal->ops->say(
                     client, conn, SILC_CLIENT_MESSAGE_ERROR,
                     "Cannot create listener on %s on port %d: %s",
-                    bind_ip ? bind_ip : local_ip, port, strerror(errno));
+                    params->bind_ip ? params->bind_ip :
+                    params->local_ip, params->local_port, strerror(errno));
        completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
                   NULL, context);
        silc_free(ke);
        return;
       }
+
+      port = params->local_port;
+      if (!port) {
+       /* Get listener port */
+       SilcUInt16 *ports;
+       ports = silc_net_listener_get_port(ke->tcp_listener, NULL);
+       port = ports[0];
+       silc_free(ports);
+      }
     }
 
+    local_ip = params->local_ip;
+    protocol = params->udp;
+
     ke->client = client;
     ke->conn = conn;
     ke->completion = completion;
     ke->context = context;
+    ke->params = *params;
+    ke->public_key = public_key;
+    ke->private_key = private_key;
+    silc_client_ref_client(client, conn, client_entry);
+    client_entry->internal.ke = ke;
+    client_entry->internal.prv_resp = TRUE;
   }
 
-  /* Add key agreement timeout task */
-  silc_schedule_task_add_timeout(conn->internal->schedule,
-                                silc_client_key_agreement_timeout,
-                                client_entry, timeout_secs, 0);
-
   /* Encode the key agreement payload */
-  if (ke && ke->listener)
-    ports = silc_net_listener_get_port(ke->listener, NULL);
-  buffer = silc_key_agreement_payload_encode(local_ip, (port ? port :
-                                                       ports ? ports[0] : 0));
+  buffer = silc_key_agreement_payload_encode(local_ip, protocol, port);
   if (!buffer) {
-    if (ke) {
-      if (ke->listener)
-       silc_net_close_listener(ke->listener);
-      silc_free(ke);
-    }
     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
               NULL, context);
+    silc_client_keyagr_free(client, conn, client_entry);
     return;
   }
-  silc_free(ports);
-
-  if (ke) {
-    silc_client_ref_client(client, conn, client_entry);
-    client_entry->internal.ke = ke;
-  }
 
   /* Send the key agreement packet to the client */
-  silc_packet_send(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0,
-                  silc_buffer_data(buffer), silc_buffer_len(data));
-
-  silc_buffer_free(buffer);
-}
-
-/* Callback that is called after connection has been created. This actually
-   starts the key agreement protocol. This is initiator function. */
-
-SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
-{
-  SilcClientInternalConnectContext *ctx =
-    (SilcClientInternalConnectContext *)context;
-  SilcClient client = ctx->client;
-  SilcClientConnection conn = ctx->conn;
-  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context;
-  int opt, opt_len = sizeof(opt);
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Check the socket status as it might be in error */
-  silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
-  if (opt != 0) {
-    if (ctx->tries < 2) {
-      /* Connection failed but lets try again */
-      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                                "Could not connect to client %s: %s",
-                                ctx->host, strerror(opt));
-      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                                "Connecting to port %d of client %s resumed",
-                                ctx->port, ctx->host);
-
-      /* Unregister old connection try */
-      silc_schedule_unset_listen_fd(client->schedule, fd);
-      silc_net_close_connection(fd);
-      silc_schedule_task_del(client->schedule, ctx->task);
-
-      /* Try again */
-      silc_client_connect_to_client_internal(ctx);
-      ctx->tries++;
-    } else {
-      /* Connection failed and we won't try anymore */
-      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                                "Could not connect to client %s: %s",
-                                ctx->host, strerror(opt));
-      silc_schedule_unset_listen_fd(client->schedule, fd);
-      silc_net_close_connection(fd);
-      silc_schedule_task_del(client->schedule, ctx->task);
-      silc_free(ctx->host);
-      silc_free(ctx);
-
-      /* Call the completion callback */
-      ke->completion(ke->client, ke->conn, ke->client_entry,
-                    SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
-      silc_free(ke);
-    }
+  if (!silc_packet_send_ext(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0,
+                           0, NULL, SILC_ID_CLIENT, &client_entry->id,
+                           silc_buffer_datalen(buffer), NULL, NULL)) {
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
+              NULL, context);
+    silc_client_keyagr_free(client, conn, client_entry);
+    silc_buffer_free(buffer);
     return;
   }
 
-  silc_schedule_unset_listen_fd(client->schedule, fd);
-  silc_schedule_task_del(client->schedule, ctx->task);
-
-  ke->fd = fd;
+  /* Add key agreement timeout task */
+  if (params && params->timeout_secs)
+    silc_schedule_task_add_timeout(conn->internal->schedule,
+                                  silc_client_keyagr_timeout,
+                                  client_entry, params->timeout_secs, 0);
 
-  /* Now actually perform the key agreement protocol */
-  silc_client_perform_key_agreement_fd(ke->client, ke->conn,
-                                      ke->client_entry, ke->fd, ctx->host,
-                                      ke->completion, ke->context);
-  silc_free(ke);
-  silc_free(ctx->host);
-  silc_free(ctx);
+  silc_buffer_free(buffer);
 }
 
-/* Performs the actual key agreement protocol. Application may use this
-   to initiate the key agreement protocol. This can be called for example
-   after the application has received the `key_agreement' client operation,
-   and did not return TRUE from it.
-
-   The `hostname' is the remote hostname (or IP address) and the `port'
-   is the remote port. The `completion' callback with the `context' will
-   be called after the key agreement protocol.
-
-   NOTE: If the application returns TRUE in the `key_agreement' client
-   operation the library will automatically start the key agreement. In this
-   case the application must not call this function. However, application
-   may choose to just ignore the `key_agreement' client operation (and
-   merely just print information about it on the screen) and call this
-   function when the user whishes to do so (by, for example, giving some
-   specific command). Thus, the API provides both, automatic and manual
-   initiation of the key agreement. Calling this function is the manual
-   initiation and returning TRUE in the `key_agreement' client operation
-   is the automatic initiation. */
+/* Perform key agreement protocol as initiator.  Conneects to remote host. */
 
 void silc_client_perform_key_agreement(SilcClient client,
                                       SilcClientConnection conn,
                                       SilcClientEntry client_entry,
-                                      char *hostname,
-                                      int port,
+                                      SilcClientConnectionParams *params,
+                                      SilcPublicKey public_key,
+                                      SilcPrivateKey private_key,
+                                      char *hostname, int port,
                                       SilcKeyAgreementCallback completion,
                                       void *context)
 {
   SilcClientKeyAgreement ke;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!client_entry)
-    return;
+  SILC_LOG_DEBUG(("Performing key agreement"));
 
-  if (!hostname || !port) {
-    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+  if (!client_entry || !hostname || !port) {
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
               NULL, context);
     return;
   }
@@ -511,43 +523,55 @@ void silc_client_perform_key_agreement(SilcClient client,
   }
 
   ke = silc_calloc(1, sizeof(*ke));
+  if (!ke) {
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
+              NULL, context);
+    return;
+  }
   ke->client = client;
   ke->conn = conn;
-  ke->client_entry = client_entry;
   ke->completion = completion;
   ke->context = context;
-
-  /* Connect to the remote client */
-  ke->fd = silc_client_connect_to_client(client, conn, port, hostname, ke);
-  if (ke->fd < 0) {
-    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+  silc_client_ref_client(client, conn, client_entry);
+  client_entry->internal.ke = ke;
+
+  if (params)
+    params->no_authentication = TRUE;
+
+  /* Connect to the remote client.  Performs key exchange automatically. */
+  if (!silc_client_connect_to_client(client, params, public_key,
+                                    private_key, hostname, port,
+                                    silc_client_keyagr_perform_cb,
+                                    client_entry)) {
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
               NULL, context);
-    silc_free(ke);
+    silc_client_keyagr_free(client, conn, client_entry);
     return;
   }
 }
 
-/* Same as above but application has created already the connection to
-   the remote host. The `sock' is the socket to the remote connection.
-   Application can use this function if it does not want the client library
-   to create the connection. */
-
-void silc_client_perform_key_agreement_fd(SilcClient client,
-                                         SilcClientConnection conn,
-                                         SilcClientEntry client_entry,
-                                         int sock,
-                                         char *hostname,
-                                         SilcKeyAgreementCallback completion,
-                                         void *context)
+/* Same as above but caller has created connection. */
+
+void
+silc_client_perform_key_agreement_stream(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        SilcClientConnectionParams *params,
+                                        SilcPublicKey public_key,
+                                        SilcPrivateKey private_key,
+                                        SilcStream stream,
+                                        SilcKeyAgreementCallback completion,
+                                        void *context)
 {
   SilcClientKeyAgreement ke;
-  SilcClientKEInternalContext *proto_ctx;
-  SilcProtocol protocol;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Performing key agreement"));
 
-  if (!client_entry)
+  if (!client_entry || !stream) {
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
+              NULL, context);
     return;
+  }
 
   if (client_entry == conn->local_entry) {
     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
@@ -556,48 +580,31 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
   }
 
   ke = silc_calloc(1, sizeof(*ke));
+  if (!ke) {
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
+              NULL, context);
+    return;
+  }
   ke->client = client;
   ke->conn = conn;
-  ke->client_entry = client_entry;
-  ke->fd = sock;
   ke->completion = completion;
   ke->context = context;
-
-  /* Allocate new socket connection object */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &ke->sock);
-  silc_client_add_socket(client, ke->sock);
-  ke->sock->hostname = strdup(hostname);
-  ke->sock->port = silc_net_get_remote_port(sock);
-
-  /* Allocate internal context for key exchange protocol. This is
-     sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = client;
-  proto_ctx->sock = silc_socket_dup(ke->sock);
-  proto_ctx->rng = client->rng;
-  proto_ctx->responder = FALSE;
-  proto_ctx->context = ke;
-  proto_ctx->send_packet = silc_client_key_agreement_send_packet;
-  proto_ctx->verify = silc_client_protocol_ke_verify_key;
-  ke->proto_ctx = proto_ctx;
-
-  /* Perform key exchange protocol. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
-                     &protocol, (void *)proto_ctx,
-                     silc_client_key_agreement_final);
-  ke->sock->protocol = protocol;
-
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection
-     and sets that outgoing packets may be sent to this connection as well.
-     However, this doesn't set the scheduler for outgoing traffic, it will
-     be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  context = (void *)client;
-  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
-
-  /* Execute the protocol */
-  silc_protocol_execute(protocol, client->schedule, 0, 0);
+  silc_client_ref_client(client, conn, client_entry);
+  client_entry->internal.ke = ke;
+
+  if (params)
+    params->no_authentication = TRUE;
+
+  /* Perform key exchange protocol */
+  if (!silc_client_key_exchange(client, params, public_key,
+                               private_key, stream, SILC_CONN_CLIENT,
+                               silc_client_keyagr_perform_cb,
+                               client_entry)) {
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
+              NULL, context);
+    silc_client_keyagr_free(client, conn, client_entry);
+    return;
+  }
 }
 
 /* This function can be called to unbind the hostname and the port for
@@ -611,98 +618,91 @@ void silc_client_abort_key_agreement(SilcClient client,
                                     SilcClientConnection conn,
                                     SilcClientEntry client_entry)
 {
-  if (!client_entry)
+  SilcClientKeyAgreement ke;
+
+  if (!client_entry || !client_entry->internal.ke)
     return;
 
-  if (client_entry->ke) {
-    SilcClientKeyAgreement ke;
+  ke = client_entry->internal.ke;
 
-    if (client_entry->ke->sock) {
-      silc_client_del_socket(client_entry->ke->client, client_entry->ke->sock);
-      silc_socket_free(client_entry->ke->sock);
-    }
-    silc_schedule_task_del_by_fd(client->schedule, client_entry->ke->fd);
-    if (client_entry->ke->timeout)
-      silc_schedule_task_del(client->schedule,
-                            client_entry->ke->timeout);
-    ke = client_entry->ke;
-    client_entry->ke = NULL;
-    ke->completion(client, conn, client_entry,
-                  SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context);
-    silc_free(ke);
-  }
+  SILC_LOG_DEBUG(("Abort key agreement %p"));
+
+  ke->completion(client, conn, client_entry,
+                SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context);
+
+  silc_client_keyagr_free(client, conn, client_entry);
 }
 
-/* Callback function that is called after we've resolved the client
-   information who sent us the key agreement packet from the server.
-   We actually call the key_agreement client operation now. */
+/* Key agreement packet received */
 
-static void
-silc_client_key_agreement_resolve_cb(SilcClient client,
-                                    SilcClientConnection conn,
-                                    SilcClientEntry *clients,
-                                    SilcUInt32 clients_count,
-                                    void *context)
+SILC_FSM_STATE(silc_client_key_agreement)
 {
-  SilcPacketContext *packet = (SilcPacketContext *)context;
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcClientID remote_id;
+  SilcClientEntry remote_client;
   SilcKeyAgreementPayload payload;
-  int ret;
-  SilcKeyAgreementCallback completion;
-  void *completion_context;
 
-  if (!clients)
-    goto out;
+  if (packet->src_id_type != SILC_ID_CLIENT) {
+    /** Invalid packet */
+    silc_fsm_next(fsm, silc_client_key_agreement_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
+                     &remote_id, sizeof(remote_id))) {
+    /** Invalid source ID */
+    silc_fsm_next(fsm, silc_client_key_agreement_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Check whether we know this client already */
+  remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
+  if (!remote_client || !remote_client->nickname[0]) {
+    /** Resolve client info */
+    silc_client_unref_client(client, conn, remote_client);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &remote_id, NULL,
+                                        silc_client_keyagr_resolved, fsm));
+    /* NOT REACHED */
+  }
 
   /* Parse the key agreement payload */
-  payload = silc_key_agreement_payload_parse(packet->buffer->data,
-                                            packet->buffer->len);
-  if (!payload)
-    goto out;
-
-  /* Call the key_agreement client operation */
-  ret = client->internal->ops->key_agreement(
-                                  client, conn, clients[0],
-                                  silc_key_agreement_get_hostname(payload),
-                                  silc_key_agreement_get_port(payload),
-                                  &completion, &completion_context);
+  payload = silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer),
+                                            silc_buffer_len(&packet->buffer));
+  if (!payload) {
+    /** Malformed Payload */
+    SILC_LOG_DEBUG(("Malformed key agreement payload"));
+    silc_fsm_next(fsm, silc_client_key_agreement_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* If remote did not provide connection endpoint, we will assume that we
+     will provide it and will be responder. */
+  if (!silc_key_agreement_get_hostname(payload))
+    remote_client->internal.prv_resp = TRUE;
+  else
+    remote_client->internal.prv_resp = FALSE;
 
-  /* If the user returned TRUE then we'll start the key agreement right
-     here and right now. */
-  if (ret == TRUE)
-    silc_client_perform_key_agreement(client, conn, clients[0],
-                                     silc_key_agreement_get_hostname(payload),
-                                     silc_key_agreement_get_port(payload),
-                                     completion, completion_context);
+  /* Notify application for key agreement request */
+  client->internal->ops->key_agreement(
+                                  client, conn, remote_client,
+                                  silc_key_agreement_get_hostname(payload),
+                                  silc_key_agreement_get_protocol(payload),
+                                  silc_key_agreement_get_port(payload));
 
   silc_key_agreement_payload_free(payload);
 
- out:
-  silc_packet_context_free(packet);
+  silc_packet_free(packet);
+  return SILC_FSM_FINISH;
 }
 
-/* Received Key Agreement packet from remote client. Process the packet
-   and resolve the client information from the server before actually
-   letting the application know that we've received this packet.  Then
-   call the key_agreement client operation and let the user decide
-   whether we perform the key agreement protocol now or not. */
+/* Key agreement packet processing error */
 
-void silc_client_key_agreement(SilcClient client,
-                              SilcSocketConnection sock,
-                              SilcPacketContext *packet)
+SILC_FSM_STATE(silc_client_key_agreement_error)
 {
-  SilcClientID *remote_id;
-
-  if (packet->src_id_type != SILC_ID_CLIENT)
-    return;
-
-  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                            SILC_ID_CLIENT);
-  if (!remote_id)
-    return;
-
-  silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
-                                      NULL,
-                                      silc_client_key_agreement_resolve_cb,
-                                      silc_packet_context_dup(packet));
-  silc_free(remote_id);
+  SilcPacket packet = state_context;
+  silc_packet_free(packet);
+  return SILC_FSM_FINISH;
 }
diff --git a/lib/silcclient/client_keyagr.h b/lib/silcclient/client_keyagr.h
new file mode 100644 (file)
index 0000000..f386a57
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+
+  client_keyagr.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2006 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_KEYAGR_H
+#define CLIENT_KEYAGR_H
+
+SILC_FSM_STATE(silc_client_key_agreement);
+SILC_FSM_STATE(silc_client_key_agreement_error);
+
+#endif /* CLIENT_KEYAGR_H */
index e498e9c6c4cc82ba1a29e17f97920f092fa105c4..fd8deeb074a571383960dd9470e7d83595ccb663 100644 (file)
@@ -495,17 +495,9 @@ SILC_FSM_STATE(silc_client_notify_signoff)
   /* Notify application */
   NOTIFY(client, conn, type, client_entry, tmp);
 
-  /* Remove from all channels */
-  silc_client_remove_from_channels(client, conn, client_entry);
-
-#if 0
-  /* Remove from cache */
-  silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
-#endif
-
-  /* Free data */
+  /* Delete client */
+  silc_client_del_client(client, conn, client_entry);
   silc_client_unref_client(client, conn, client_entry);
-  silc_client_del_client_entry(client, conn, client_entry);
 
  out:
   /** Notify processed */
index a4addb359edd0ccf8b8d621a8ff5e0133b060a63..25ee56503e512de1383d4f2f3358000ec962c9b8 100644 (file)
@@ -345,6 +345,33 @@ SilcBool silc_client_private_message_wait(SilcClientConnection conn,
 
 /*************************** Private Message Key ****************************/
 
+/* Sends private message key request.  Sender of this packet is initiator
+   when setting the private message key. */
+
+static SilcBool
+silc_client_send_private_message_key_request(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry)
+{
+  const char *cipher, *hmac;
+
+  SILC_LOG_DEBUG(("Sending private message key request"));
+
+  cipher = silc_cipher_get_name(client_entry->internal.send_key);
+  hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
+
+  /* Send the packet */
+  return silc_packet_send_va_ext(conn->stream,
+                                SILC_PACKET_PRIVATE_MESSAGE_KEY,
+                                0, 0, NULL, SILC_ID_CLIENT,
+                                &client_entry->id, NULL, NULL,
+                                SILC_STR_UI_SHORT(strlen(cipher)),
+                                SILC_STR_DATA(cipher, strlen(cipher)),
+                                SILC_STR_UI_SHORT(strlen(hmac)),
+                                SILC_STR_DATA(hmac, strlen(hmac)),
+                                SILC_STR_END);
+}
+
 /* Client resolving callback.  Here we simply mark that we are the responder
    side of this private message key request.  */
 
@@ -377,7 +404,9 @@ static void silc_client_private_message_key_cb(SilcClient client,
   client_entry->internal.prv_resp = TRUE;
 
   /* XXX we should notify application that remote wants to set up the
-     static key */
+     static key.  And we should tell if we already have key with remote.
+     Application should return status telling whether to delete the key
+     or not. */
 
  out:
   silc_free(cipher);
@@ -414,26 +443,9 @@ SILC_FSM_STATE(silc_client_private_message_key)
                                       fsm));
 }
 
-/* Adds private message key to the client library. The key will be used to
-   encrypt all private message between the client and the remote client
-   indicated by the `client_entry'. If the `key' is NULL and the boolean
-   value `generate_key' is TRUE the library will generate random key.
-   The `key' maybe for example pre-shared-key, passphrase or similar.
-   The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure
-   that the requirements of the SILC protocol are met. The API, however,
-   allows to allocate any cipher and HMAC.
-
-   If `responder' is TRUE then the sending and receiving keys will be
-   set according the client being the receiver of the private key.  If
-   FALSE the client is being the sender (or negotiator) of the private
-   key.
-
-   It is not necessary to set key for normal private message usage. If the
-   key is not set then the private messages are encrypted using normal
-   session keys. Setting the private key, however, increases the security.
-
-   Returns FALSE if the key is already set for the `client_entry', TRUE
-   otherwise. */
+/* Adds new private message key to `client_entry'.  If we are setting this
+   before receiving request for it from `client_entry' we will send the
+   request to the client.  Otherwise, we are responder side. */
 
 SilcBool silc_client_add_private_message_key(SilcClient client,
                                             SilcClientConnection conn,
@@ -441,15 +453,10 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
                                             const char *cipher,
                                             const char *hmac,
                                             unsigned char *key,
-                                            SilcUInt32 key_len,
-                                            SilcBool generate_key,
-                                            SilcBool responder)
+                                            SilcUInt32 key_len)
 {
-  unsigned char private_key[32];
-  SilcUInt32 len;
   SilcSKEKeyMaterial keymat;
   SilcBool ret;
-  int i;
 
   if (!client || !client_entry)
     return FALSE;
@@ -469,15 +476,6 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
   if (!silc_hmac_is_supported(hmac))
     return FALSE;
 
-  /* Generate key if not provided */
-  if (generate_key == TRUE) {
-    len = 32;
-    for (i = 0; i < len; i++)
-      private_key[i] = silc_rng_get_byte_fast(client->rng);
-    key = private_key;
-    key_len = len;
-  }
-
   /* Save the key */
   client_entry->internal.key = silc_memdup(key, key_len);
   client_entry->internal.key_len = key_len;
@@ -490,31 +488,29 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
 
   /* Set the key into use */
   ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
-                                               cipher, hmac, keymat,
-                                               responder);
-
-  if (!generate_key)
-    client_entry->internal.generated = FALSE;
+                                               cipher, hmac, keymat);
+  client_entry->internal.generated = FALSE;
 
   /* Free the key material */
   silc_ske_free_key_material(keymat);
 
+  /* If we are setting the key without a request from the remote client,
+     we will send request to remote. */
+  if (!client_entry->internal.prv_resp)
+    silc_client_send_private_message_key_request(client, conn, client_entry);
+
   return ret;
 }
 
 /* Same as above but takes the key material from the SKE key material
-   structure. This structure is received if the application uses the
-   silc_client_send_key_agreement to negotiate the key material. The
-   `cipher' and `hmac' SHOULD be provided as it is negotiated also in
-   the SKE protocol. */
+   structure. */
 
 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
                                                 SilcClientConnection conn,
                                                 SilcClientEntry client_entry,
                                                 const char *cipher,
                                                 const char *hmac,
-                                                SilcSKEKeyMaterial keymat,
-                                                SilcBool responder)
+                                                SilcSKEKeyMaterial keymat)
 {
   if (!client || !client_entry)
     return FALSE;
@@ -547,7 +543,7 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
     return FALSE;
 
   /* Set the keys */
-  if (responder == TRUE) {
+  if (client_entry->internal.prv_resp) {
     silc_cipher_set_key(client_entry->internal.send_key,
                        keymat->receive_enc_key,
                        keymat->enc_key_len);
@@ -584,40 +580,6 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
   return TRUE;
 }
 
-/* Sends private message key indicator.  The sender of this packet is
-   going to be the initiator, if and when, the users set up a static
-   private message key (not Key Agreement). */
-
-SilcBool
-silc_client_send_private_message_key_request(SilcClient client,
-                                            SilcClientConnection conn,
-                                            SilcClientEntry client_entry)
-{
-  const char *cipher, *hmac;
-
-  if (!client || !conn || !client_entry)
-    return FALSE;
-
-  if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
-    return FALSE;
-
-  SILC_LOG_DEBUG(("Sending private message key indicator"));
-
-  cipher = silc_cipher_get_name(client_entry->internal.send_key);
-  hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
-
-  /* Send the packet */
-  return silc_packet_send_va_ext(conn->stream,
-                                SILC_PACKET_PRIVATE_MESSAGE_KEY,
-                                0, 0, NULL, SILC_ID_CLIENT,
-                                &client_entry->id, NULL, NULL,
-                                SILC_STR_UI_SHORT(strlen(cipher)),
-                                SILC_STR_DATA(cipher, strlen(cipher)),
-                                SILC_STR_UI_SHORT(strlen(hmac)),
-                                SILC_STR_DATA(hmac, strlen(hmac)),
-                                SILC_STR_END);
-}
-
 /* Removes the private message from the library. The key won't be used
    after this to protect the private messages with the remote `client_entry'
    client. Returns FALSE on error, TRUE otherwise. */
@@ -643,6 +605,7 @@ SilcBool silc_client_del_private_message_key(SilcClient client,
   client_entry->internal.send_key = NULL;
   client_entry->internal.receive_key = NULL;
   client_entry->internal.key = NULL;
+  client_entry->internal.prv_resp = FALSE;
 
   return TRUE;
 }
index 84f35f72cb6c9973311cf4ca56b0d1065732edd9..b0ddc907d61b0eab2ebf1c6a2cfbe1c872dafaea 100644 (file)
@@ -213,6 +213,8 @@ SILC_FSM_STATE(silc_client_st_register_complete)
                 conn->callback_context);
 
   conn->internal->registering = FALSE;
+  silc_schedule_task_del_by_context(conn->internal->schedule, conn);
+
   return SILC_FSM_FINISH;
 }
 
@@ -226,13 +228,17 @@ SILC_FSM_STATE(silc_client_st_register_error)
   SILC_LOG_DEBUG(("Error registering to network"));
 
   /* Signal to close connection */
-  conn->internal->disconnected = TRUE;
-  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  }
 
   /* Call connect callback */
   conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
                 conn->callback_context);
 
+  silc_schedule_task_del_by_context(conn->internal->schedule, conn);
+
   return SILC_FSM_FINISH;
 }
 
index 468ac1bb243e168c552f849ac5980b74bf45c96a..b1d233fc8c1a7f3f0ac6d4ec48104ed7366e8ae1 100644 (file)
@@ -1079,8 +1079,10 @@ SILC_FSM_STATE(silc_client_command_quit_final)
                 0, NULL, conn->callback_context);
 
   /* Signal to close connection */
-  conn->internal->disconnected = TRUE;
-  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  }
 
   return SILC_FSM_FINISH;
 }
index f5adc9c231b95e6ed856c9d0e55a6fc7033ce092..bbb4f2a29961d5c7e8c35267c8e3b5609bdc2605 100644 (file)
 /************************** Types and definitions ***************************/
 
 /* Calls error command reply callback back to command sender. */
-#define ERROR_CALLBACK(error)                                  \
+#define ERROR_CALLBACK(err)                                    \
 do {                                                           \
   void *arg1 = NULL, *arg2 = NULL;                             \
   if (cmd->status != SILC_STATUS_OK)                           \
     silc_status_get_args(cmd->status, args, &arg1, &arg2);     \
   else                                                         \
-    cmd->status = error;                                       \
+    cmd->status = cmd->error = err;                            \
   SILC_LOG_DEBUG(("Error in command reply: %s",                        \
                 silc_get_status_message(cmd->status)));        \
   silc_client_command_callback(cmd, arg1, arg2);               \
@@ -424,7 +424,7 @@ SILC_FSM_STATE(silc_client_command_reply_whois)
       silc_client_add_client(client, conn, nickname, username, realname,
                             &id.u.client_id, mode);
     if (!client_entry) {
-      ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
       goto out;
     }
     silc_client_ref_client(client, conn, client_entry);
@@ -560,7 +560,7 @@ SILC_FSM_STATE(silc_client_command_reply_identify)
        silc_client_add_client(client, conn, name, info, NULL,
                               &id.u.client_id, 0);
       if (!client_entry) {
-       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
        goto out;
       }
       silc_client_ref_client(client, conn, client_entry);
index b44b09ff4a471a103af3858bb191582ae16a374d..2426cb57e5da8798e3c90f9c97c9d2bdeb296143 100644 (file)
@@ -299,7 +299,7 @@ typedef void (*SilcKeyAgreementCallback)(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcClientEntry client_entry,
                                         SilcKeyAgreementStatus status,
-                                        SilcSKEKeyMaterial *key,
+                                        SilcSKEKeyMaterial key,
                                         void *context);
 
 /****s* silcclient/SilcClientAPI/SilcPrivateMessageKeys
@@ -320,8 +320,7 @@ typedef struct {
   SilcClientEntry client_entry;       /* The remote client entry */
   char *cipher;                              /* The cipher name */
   unsigned char *key;                /* The original key, If the appliation
-                                        provided it. This is NULL if the
-                                        library generated the key or if
+                                        provided it. This is NULL if
                                         the SKE key material was used. */
   SilcUInt32 key_len;                /* The key length */
 } *SilcPrivateMessageKeys;
@@ -554,18 +553,17 @@ typedef struct {
   void (*ask_passphrase)(SilcClient client, SilcClientConnection conn,
                         SilcAskPassphrase completion, void *context);
 
-  /* Asks whether the user would like to perform the key agreement protocol.
-     This is called after we have received an key agreement packet or an
-     reply to our key agreement packet. This returns TRUE if the user wants
-     the library to perform the key agreement protocol and FALSE if it is not
-     desired (application may start it later by calling the function
-     silc_client_perform_key_agreement). If TRUE is returned also the
-     `completion' and `context' arguments must be set by the application. */
-  SilcBool (*key_agreement)(SilcClient client, SilcClientConnection conn,
-                           SilcClientEntry client_entry,
-                           const char *hostname, SilcUInt16 port,
-                           SilcKeyAgreementCallback *completion,
-                           void **context);
+  /* Called to indicate that incoming key agreement request has been
+     received.  If the application wants to perform key agreement it may
+     call silc_client_perform_key_agreement to initiate key agreementn or
+     silc_client_send_key_agreement to provide connection point to the
+     remote client in case the `hostname' is NULL.  If key agreement is
+     not desired this request can be ignored.  The `protocol' is either
+     value 0 for TCP or value 1 for UDP. */
+  void (*key_agreement)(SilcClient client, SilcClientConnection conn,
+                       SilcClientEntry client_entry,
+                       const char *hostname, SilcUInt16 protocol,
+                       SilcUInt16 port);
 
   /* Notifies application that file transfer protocol session is being
      requested by the remote client indicated by the `client_entry' from
@@ -600,6 +598,7 @@ typedef struct {
   /* Called when the client library is up and running.  After this callback
      is called the application may start using the client library APIs. */
   void (*running)(SilcClient client, void *application);
+
 } SilcClientOperations;
 /***/
 
@@ -847,7 +846,8 @@ void silc_client_stop(SilcClient client);
  *
  *    Client connection parameters.  This can be filled by the application
  *    and given as argument to silc_client_connect_to_server,
- *    silc_client_connect_to_client or silc_client_key_exchange.
+ *    silc_client_connect_to_client, silc_client_key_exchange or
+ *    silc_client_send_key_agreement.
  *
  * SOURCE
  */
@@ -884,14 +884,19 @@ typedef struct {
   void *auth;
   SilcUInt32 auth_len;
 
-  /* If this boolean is set to TRUE then the client's connection to the
-     remote host will use UDP instead of TCP.  The `local_ip' specifies
-     the local IP address used with the UDP connection, and it must be
-     non-NULL.  If the `local_port' is non-zero it will be used as local
-     port with the UDP connection.  The remote host will also send packets
-     to the specified address and port. */
+  /* If this boolean is set to TRUE then the connection will use UDP instead
+     of TCP.  If UDP is set the also the next `local_ip' and `local_port'
+     must be set. */
   SilcBool udp;
+
+  /* The `local_ip' specifies the local IP address used with the connection.
+     It must be non-NULL if `udp' boolean is TRUE.  If the `local_port' is
+     non-zero it will be used as local port with UDP connection.  The remote
+     host will also send packets to the specified address and port.  If the
+     `bind_ip' is non-NULL a listener is bound to that address instead of
+     `local_ip'. */
   char *local_ip;
+  char *bind_ip;
   int local_port;
 
   /* If this boolean is set to TRUE then the key exchange is done with
@@ -918,6 +923,9 @@ typedef struct {
   unsigned char *detach_data;
   SilcUInt32 detach_data_len;
 
+  /* Connection timeout.  If non-zero, the connection will timeout unless
+     the SILC connection is completed in the specified amount of time. */
+  SilcUInt32 timeout_secs;
 } SilcClientConnectionParams;
 /***/
 
@@ -1387,29 +1395,26 @@ SilcBool silc_client_command_pending(SilcClientConnection conn,
  *                                        const char *cipher,
  *                                        const char *hmac,
  *                                        unsigned char *key,
- *                                        SilcUInt32 key_len,
- *                                        SilcBool generate_key,
- *                                        SilcBool responder);
+ *                                        SilcUInt32 key_len);
  *
  * DESCRIPTION
  *
- *    Adds private message key to the client library. The key will be used to
- *    encrypt all private message between the client and the remote client
- *    indicated by the `client_entry'. If the `key' is NULL and the boolean
- *    value `generate_key' is TRUE the library will generate random key.
- *    The `key' maybe for example pre-shared-key, passphrase or similar.
- *    The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure
- *    that the requirements of the SILC protocol are met. The API, however,
- *    allows to allocate any cipher and HMAC.
+ *    Adds a static private message key to the client library.  The key
+ *    will be used to encrypt all private message between the client and
+ *    the remote client indicated by the `client_entry'.  The `key' can
+ *    be for example a pre-shared-key, passphrase or similar shared secret
+ *    string.  The `cipher' and `hmac' MAY be provided but SHOULD be NULL
+ *    to assure that the requirements of the SILC protocol are met. The
+ *    API, however, allows to allocate any cipher and HMAC.
  *
- *    If `responder' is TRUE then the sending and receiving keys will be
- *    set according the client being the receiver of the private key.  If
- *    FALSE the client is being the sender (or negotiator) of the private
- *    key.
+ *    If the private message key is added to client without first receiving
+ *    a request for it from the remote `client_entry' this function will
+ *    send the request to `client_entry'.  Note that, the actual key is
+ *    not sent to the network.
  *
  *    It is not necessary to set key for normal private message usage. If the
  *    key is not set then the private messages are encrypted using normal
- *    session keys. Setting the private key, however, increases the security.
+ *    session keys.  Setting the private key, however, increases the security.
  *
  *    Returns FALSE if the key is already set for the `client_entry', TRUE
  *    otherwise.
@@ -1421,9 +1426,7 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
                                             const char *cipher,
                                             const char *hmac,
                                             unsigned char *key,
-                                            SilcUInt32 key_len,
-                                            SilcBool generate_key,
-                                            SilcBool responder);
+                                            SilcUInt32 key_len);
 
 /****f* silcclient/SilcClientAPI/silc_client_add_private_message_key_ske
  *
@@ -1440,9 +1443,9 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
  * DESCRIPTION
  *
  *    Same as silc_client_add_private_message_key but takes the key material
- *    from the SKE key material structure. This structure is received if
+ *    from the SKE key material structure.  This structure is received if
  *    the application uses the silc_client_send_key_agreement to negotiate
- *    the key material. The `cipher' and `hmac' SHOULD be provided as it is
+ *    the key material.  The `cipher' and `hmac' SHOULD be provided as it is
  *    negotiated also in the SKE protocol.
  *
  ***/
@@ -1451,8 +1454,7 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
                                                 SilcClientEntry client_entry,
                                                 const char *cipher,
                                                 const char *hmac,
-                                                SilcSKEKeyMaterial key,
-                                                SilcBool responder);
+                                                SilcSKEKeyMaterial key);
 
 /****f* silcclient/SilcClientAPI/silc_client_del_private_message_key
  *
@@ -1498,40 +1500,6 @@ silc_client_list_private_message_keys(SilcClient client,
                                      SilcClientConnection conn,
                                      SilcUInt32 *key_count);
 
-/****f* silcclient/SilcClientAPI/silc_client_send_private_message_key_request
- *
- * SYNOPSIS
- *
- *    SilcBool
- *    silc_client_send_private_message_key_request(SilcClient client,
- *                                               SilcClientConnection conn,
- *                                               SilcClientEntry client_entry);
- *
- * DESCRIPTION
- *
- *    This function can be used to send an private message key indicator
- *    request to the remote client indicated by 'client_entry'.  This can
- *    be used when setting a static or pre-shared private message key.
- *    The sender of this packet is the initiator and must set the 'responder'
- *    argument in silc_client_add_private_message_key function to FALSE.
- *    The receiver of this indicator request must set it to TRUE, if the
- *    receiver decides to set a private message key.  By using this
- *    function applications may automate initiator/responder setting in
- *    private message key functions, without asking from user which one is
- *    the initiator and which one is responder.
- *
- * NOTES
- *
- *    The sender of this packet must set the private message key for
- *    'client_entry' before calling this function.  The 'responder'
- *    argument MUST be set to FALSE when setting the key.
- *
- ***/
-SilcBool
-silc_client_send_private_message_key_request(SilcClient client,
-                                            SilcClientConnection conn,
-                                            SilcClientEntry client_entry);
-
 /****f* silcclient/SilcClientAPI/silc_client_free_private_message_keys
  *
  * SYNOPSIS
@@ -1698,7 +1666,7 @@ void silc_client_current_channel_private_key(SilcClient client,
                                             SilcChannelPrivateKey key);
 
 
-/* Key Agreement routines (client_keyagr.c) */
+/* Key Agreement routines */
 
 /****f* silcclient/SilcClientAPI/silc_client_send_key_agreement
  *
@@ -1707,62 +1675,46 @@ void silc_client_current_channel_private_key(SilcClient client,
  *    void silc_client_send_key_agreement(SilcClient client,
  *                                        SilcClientConnection conn,
  *                                        SilcClientEntry client_entry,
- *                                        char *hostname,
- *                                        int port,
- *                                        SilcUInt32 timeout_secs,
+ *                                        SilcClientConnectionParams *params,
+ *                                        SilcPublicKey public_key,
+ *                                        SilcPrivateKey private_key,
  *                                        SilcKeyAgreementCallback completion,
  *                                        void *context);
  *
  * DESCRIPTION
  *
  *    Sends key agreement request to the remote client indicated by the
- *    `client_entry'. If the caller provides the `hostname' and the `port'
- *    arguments then the library will bind the client to that hostname and
- *    that port for the key agreement protocol. It also sends the `hostname'
- *    and the `port' in the key agreement packet to the remote client. This
- *    would indicate that the remote client may initiate the key agreement
- *    protocol to the `hostname' on the `port'.  If port is zero then the
- *    bound port is undefined (the operating system defines it).
- *
- *    If the `hostname' and `port' is not provided then empty key agreement
- *    packet is sent to the remote client. The remote client may reply with
- *    the same packet including its hostname and port. If the library receives
- *    the reply from the remote client the `key_agreement' client operation
- *    callback will be called to verify whether the user wants to perform the
- *    key agreement or not.
- *
- * NOTES
- *
- *    NOTE: If the application provided the `hostname' and the `port' and the
- *    remote side initiates the key agreement protocol it is not verified
- *    from the user anymore whether the protocol should be executed or not.
- *    By setting the `hostname' and `port' the user gives permission to
- *    perform the protocol (we are responder in this case).
- *
- *    NOTE: If the remote side decides not to initiate the key agreement
- *    or decides not to reply with the key agreement packet then we cannot
- *    perform the key agreement at all. If the key agreement protocol is
- *    performed the `completion' callback with the `context' will be called.
- *    If remote side decides to ignore the request the `completion' will be
- *    called after the specified timeout, `timeout_secs'.
- *
- *    NOTE: If the `hostname' and the `port' was not provided the `completion'
- *    will not be called at all since this does nothing more than sending
- *    a packet to the remote host.
- *
- *    NOTE: There can be only one active key agreement for one client entry.
- *    Before setting new one, the old one must be finished (it is finished
- *    after calling the completion callback) or the function
- *    silc_client_abort_key_agreement must be called.
+ *    `client_entry'.
+ *
+ *    If `params' is non-NULL and it has the `local_ip' and `local_port' set
+ *    the caller will provide the connection endpoint for the key agreement
+ *    connection.  The `bind_ip' can be used to bind to that IP instead of
+ *    `local_ip'.  If the `udp' is set to TRUE the connection will be UDP
+ *    instead of TCP.  Caller may also set the `repository', `verify_notfound'
+ *    and `timeout_secs' fields in `params'.  Other fields are ignored.
+ *    If `params' is NULL, then the `client_entry' is expected to provide
+ *    the connection endpoint for us.  It is recommended the `timeout_secs'
+ *    is specified in case the remote client does not reply anything to
+ *    the request.
+ *
+ *    The `public_key' and `private_key' is our identity in the key agreement.
+ *
+ *    In case we do not provide the connection endpoint, we will receive
+ *    the `key_agreement' client operation when the remote send its own
+ *    key agreement request packet.  We may then there start the key
+ *    agreement with silc_client_perform_key_agreement.  If we provided the
+ *    the connection endpoint, the client operation will not be called.
+ *
+ *    There can be only one active key agreement for `client_entry'.  Old
+ *    key agreement may be aborted by calling silc_client_abort_key_agreement.
  *
  ***/
 void silc_client_send_key_agreement(SilcClient client,
                                    SilcClientConnection conn,
                                    SilcClientEntry client_entry,
-                                   const char *hostname,
-                                   const char *bindhost,
-                                   int port,
-                                   SilcUInt32 timeout_secs,
+                                   SilcClientConnectionParams *params,
+                                   SilcPublicKey public_key,
+                                   SilcPrivateKey private_key,
                                    SilcKeyAgreementCallback completion,
                                    void *context);
 
@@ -1774,72 +1726,69 @@ void silc_client_send_key_agreement(SilcClient client,
  *    silc_client_perform_key_agreement(SilcClient client,
  *                                      SilcClientConnection conn,
  *                                      SilcClientEntry client_entry,
- *                                      char *hostname,
- *                                      int port,
+ *                                      SilcClientConnectionParams *params,
+ *                                      SilcPublicKey public_key,
+ *                                      SilcPrivateKey private_key,
+ *                                      char *hostname, int port,
  *                                      SilcKeyAgreementCallback completion,
  *                                      void *context);
  *
  * DESCRIPTION
  *
- *    Performs the actual key agreement protocol. Application may use this
- *    to initiate the key agreement protocol. This can be called for example
- *    after the application has received the `key_agreement' client operation,
- *    and did not return TRUE from it.
+ *    Performs the key agreement protocol.  Application may use this to
+ *    initiate the key agreement protocol.  Usually this is called after
+ *    receiving the `key_agreement' client operation.
  *
  *    The `hostname' is the remote hostname (or IP address) and the `port'
- *    is the remote port. The `completion' callback with the `context' will
+ *    is the remote port.  The `completion' callback with the `context' will
  *    be called after the key agreement protocol.
  *
- * NOTES
- *
- *    NOTE: If the application returns TRUE in the `key_agreement' client
- *    operation the library will automatically start the key agreement. In this
- *    case the application must not call this function. However, application
- *    may choose to just ignore the `key_agreement' client operation (and
- *    merely just print information about it on the screen) and call this
- *    function when the user whishes to do so (by, for example, giving some
- *    specific command). Thus, the API provides both, automatic and manual
- *    initiation of the key agreement. Calling this function is the manual
- *    initiation and returning TRUE in the `key_agreement' client operation
- *    is the automatic initiation.
+ *    The `params' is connection parameters and it may be used to define
+ *    the key agreement connection related parameters.  It may be NULL.
  *
  ***/
 void silc_client_perform_key_agreement(SilcClient client,
                                       SilcClientConnection conn,
                                       SilcClientEntry client_entry,
-                                      char *hostname,
-                                      int port,
+                                      SilcClientConnectionParams *params,
+                                      SilcPublicKey public_key,
+                                      SilcPrivateKey private_key,
+                                      char *hostname, int port,
                                       SilcKeyAgreementCallback completion,
                                       void *context);
 
-/****f* silcclient/SilcClientAPI/silc_client_perform_key_agreement_fd
+/****f* silcclient/SilcClientAPI/silc_client_perform_key_agreement_stream
  *
  * SYNOPSIS
  *
  *    void
- *    silc_client_perform_key_agreement_fd(SilcClient client,
- *                                         SilcClientConnection conn,
- *                                         SilcClientEntry client_entry,
- *                                         int sock,
- *                                         char *hostname,
- *                                         SilcKeyAgreementCallback completion,
- *                                         void *context);
+ *    silc_client_perform_key_agreement_stream(
+ *                                      SilcClient client,
+ *                                      SilcClientConnection conn,
+ *                                      SilcClientEntry client_entry,
+ *                                      SilcClientConnectionParams *params,
+ *                                      SilcPublicKey public_key,
+ *                                      SilcPrivateKey private_key,
+ *                                      SilcStream stream,
+ *                                      SilcKeyAgreementCallback completion,
+ *                                      void *context);
  *
  * DESCRIPTION
  *
- *    Same as above but application has created already the connection to
- *    the remote host. The `sock' is the socket to the remote connection.
- *    Application can use this function if it does not want the client library
- *    to create the connection.
+ *    Same as silc_client_perform_key_agreement but the caller has created
+ *    the connection.  The `stream' is the created connection.
  *
  ***/
-void silc_client_perform_key_agreement_fd(SilcClient client,
-                                         SilcClientConnection conn,
-                                         SilcClientEntry client_entry,
-                                         int sock,
-                                         char *hostname,
-                                         SilcKeyAgreementCallback completion,
-                                         void *context);
+void
+silc_client_perform_key_agreement_stream(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        SilcClientConnectionParams *params,
+                                        SilcPublicKey public_key,
+                                        SilcPrivateKey private_key,
+                                        SilcStream stream,
+                                        SilcKeyAgreementCallback completion,
+                                        void *context);
 
 /****f* silcclient/SilcClientAPI/silc_client_abort_key_agreement
  *
index 839b084b5c12da6879f47b9eb4817192bbc739da..ef5529a050a6baa3bcae125471c32a3793bd8a69 100644 (file)
@@ -343,11 +343,10 @@ silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
    silc_client_perform_key_agreement). If TRUE is returned also the
    `completion' and `context' arguments must be set by the application. */
 
-static bool
+static void
 silc_key_agreement(SilcClient client, SilcClientConnection conn,
                   SilcClientEntry client_entry, const char *hostname,
-                  SilcUInt16 port, SilcKeyAgreementCallback *completion,
-                  void **context)
+                  SilcUInt16 port)
 {
   /* MyBot does not support incoming key agreement protocols, it's too
      simple for that. */