FSM based SKE protocol. Simpler interface.
authorPekka Riikonen <priikone@silcnet.org>
Sat, 26 Nov 2005 13:26:41 +0000 (13:26 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 26 Nov 2005 13:26:41 +0000 (13:26 +0000)
lib/silcske/payload.c
lib/silcske/silcske.c
lib/silcske/silcske.h
lib/silcske/silcske_payload.h

index d7fb301d5e514acfb0ff31626a026d87ee2e7313..be670e467323713af42b9b0cfb1d73a947ca66dc 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  payload.c 
+  payload.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 - 2002 Pekka Riikonen
+  Copyright (C) 2000 - 2005 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
@@ -24,7 +24,7 @@
    to the other end. */
 
 SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
-                                           SilcSKEStartPayload *payload,
+                                           SilcSKEStartPayload payload,
                                            SilcBuffer *return_buffer)
 {
   SilcBuffer buf;
@@ -44,10 +44,10 @@ SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
                           SILC_STR_UI_CHAR(0),        /* RESERVED field */
                           SILC_STR_UI_CHAR(payload->flags),
                           SILC_STR_UI_SHORT(payload->len),
-                          SILC_STR_UI_XNSTRING(payload->cookie, 
+                          SILC_STR_UI_XNSTRING(payload->cookie,
                                                payload->cookie_len),
                           SILC_STR_UI_SHORT(payload->version_len),
-                          SILC_STR_UI_XNSTRING(payload->version, 
+                          SILC_STR_UI_XNSTRING(payload->version,
                                                payload->version_len),
                           SILC_STR_UI_SHORT(payload->ke_grp_len),
                           SILC_STR_UI_XNSTRING(payload->ke_grp_list,
@@ -76,7 +76,7 @@ SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
   /* Return the encoded buffer */
   *return_buffer = buf;
 
-  SILC_LOG_HEXDUMP(("KE Start Payload"), buf->data, buf->len);
+  SILC_LOG_HEXDUMP(("KE Start Payload"), buf->data, silc_buffer_len(buf));
 
   return SILC_SKE_STATUS_OK;
 }
@@ -84,19 +84,20 @@ SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
 /* Parses the Key Exchange Start Payload. Parsed data is returned
    to allocated payload structure. */
 
-SilcSKEStatus 
+SilcSKEStatus
 silc_ske_payload_start_decode(SilcSKE ske,
                              SilcBuffer buffer,
-                             SilcSKEStartPayload **return_payload)
+                             SilcSKEStartPayload *return_payload)
 {
-  SilcSKEStartPayload *payload;
+  SilcSKEStartPayload payload;
   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
   unsigned char tmp;
   int ret;
 
   SILC_LOG_DEBUG(("Decoding Key Exchange Start Payload"));
 
-  SILC_LOG_HEXDUMP(("KE Start Payload"), buffer->data, buffer->len);
+  SILC_LOG_HEXDUMP(("KE Start Payload"), buffer->data,
+                  silc_buffer_len(buffer));
 
   payload = silc_calloc(1, sizeof(*payload));
   if (!payload)
@@ -104,12 +105,12 @@ silc_ske_payload_start_decode(SilcSKE ske,
   payload->cookie_len = SILC_SKE_COOKIE_LEN;
 
   /* Parse start of the payload */
-  ret = 
+  ret =
     silc_buffer_unformat(buffer,
                         SILC_STR_UI_CHAR(&tmp),     /* RESERVED Field */
                         SILC_STR_UI_CHAR(&payload->flags),
                         SILC_STR_UI_SHORT(&payload->len),
-                        SILC_STR_UI_XNSTRING_ALLOC(&payload->cookie, 
+                        SILC_STR_UI_XNSTRING_ALLOC(&payload->cookie,
                                                    payload->cookie_len),
                         SILC_STR_UI16_NSTRING_ALLOC(&payload->version,
                                                     &payload->version_len),
@@ -138,7 +139,7 @@ silc_ske_payload_start_decode(SilcSKE ske,
     goto err;
   }
 
-  if (payload->len != buffer->len) {
+  if (payload->len != silc_buffer_len(buffer)) {
     SILC_LOG_ERROR(("Garbage after KE Start Payload"));
     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
     goto err;
@@ -168,7 +169,7 @@ silc_ske_payload_start_decode(SilcSKE ske,
 
 /* Free's Start Payload */
 
-void silc_ske_payload_start_free(SilcSKEStartPayload *payload)
+void silc_ske_payload_start_free(SilcSKEStartPayload payload)
 {
   if (payload) {
     silc_free(payload->cookie);
@@ -187,7 +188,7 @@ void silc_ske_payload_start_free(SilcSKEStartPayload *payload)
    end. */
 
 SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
-                                        SilcSKEKEPayload *payload,
+                                        SilcSKEKEPayload payload,
                                         SilcBuffer *return_buffer)
 {
   SilcBuffer buf;
@@ -200,7 +201,7 @@ SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
   if (!payload)
     return SILC_SKE_STATUS_ERROR;
 
-  if (ske->start_payload && 
+  if (ske->start_payload &&
       ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL &&
       !payload->sign_data) {
     SILC_LOG_DEBUG(("Signature data is missing"));
@@ -212,21 +213,21 @@ SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
 
   /* Allocate channel payload buffer. The length of the buffer
      is 4 + public key + 2 + x + 2 + signature. */
-  buf = silc_buffer_alloc_size(4 + payload->pk_len + 2 + x_len + 
+  buf = silc_buffer_alloc_size(4 + payload->pk_len + 2 + x_len +
                               2 + payload->sign_len);
   if (!buf)
     return SILC_SKE_STATUS_OUT_OF_MEMORY;
 
   /* Encode the payload */
-  ret = silc_buffer_format(buf, 
+  ret = silc_buffer_format(buf,
                           SILC_STR_UI_SHORT(payload->pk_len),
                           SILC_STR_UI_SHORT(payload->pk_type),
-                          SILC_STR_UI_XNSTRING(payload->pk_data, 
+                          SILC_STR_UI_XNSTRING(payload->pk_data,
                                                payload->pk_len),
                           SILC_STR_UI_SHORT(x_len),
                           SILC_STR_UI_XNSTRING(x_str, x_len),
                           SILC_STR_UI_SHORT(payload->sign_len),
-                          SILC_STR_UI_XNSTRING(payload->sign_data, 
+                          SILC_STR_UI_XNSTRING(payload->sign_data,
                                                payload->sign_len),
                           SILC_STR_END);
   if (ret == -1) {
@@ -239,7 +240,7 @@ SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
   /* Return encoded buffer */
   *return_buffer = buf;
 
-  SILC_LOG_HEXDUMP(("KE Payload"), buf->data, buf->len);
+  SILC_LOG_HEXDUMP(("KE Payload"), buf->data, silc_buffer_len(buf));
 
   memset(x_str, 'F', x_len);
   silc_free(x_str);
@@ -252,10 +253,10 @@ SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
 
 SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
                                         SilcBuffer buffer,
-                                        SilcSKEKEPayload **return_payload)
+                                        SilcSKEKEPayload *return_payload)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
-  SilcSKEKEPayload *payload;
+  SilcSKEKEPayload payload;
   unsigned char *x = NULL;
   SilcUInt16 x_len;
   SilcUInt32 tot_len = 0, len2;
@@ -263,13 +264,13 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
 
   SILC_LOG_DEBUG(("Decoding Key Exchange Payload"));
 
-  SILC_LOG_HEXDUMP(("KE Payload"), buffer->data, buffer->len);
+  SILC_LOG_HEXDUMP(("KE Payload"), buffer->data, silc_buffer_len(buffer));
 
   payload = silc_calloc(1, sizeof(*payload));
   if (!payload)
     return SILC_SKE_STATUS_OUT_OF_MEMORY;
 
-  len2 = buffer->len;
+  len2 = silc_buffer_len(buffer);
 
   /* Parse start of the payload */
   ret = silc_buffer_unformat(buffer,
@@ -283,7 +284,7 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
   }
 
   if (ske->start_payload &&
-      ((payload->pk_type < SILC_SKE_PK_TYPE_SILC || 
+      ((payload->pk_type < SILC_SKE_PK_TYPE_SILC ||
        payload->pk_type > SILC_SKE_PK_TYPE_SPKI) || !payload->pk_len)) {
     SILC_LOG_ERROR(("Malformed public key in KE payload"));
     status = SILC_SKE_STATUS_BAD_PAYLOAD;
@@ -298,7 +299,7 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
                             SILC_STR_UI_XNSTRING_ALLOC(&payload->pk_data,
                                                        payload->pk_len),
                             SILC_STR_UI16_NSTRING_ALLOC(&x, &x_len),
-                            SILC_STR_UI16_NSTRING_ALLOC(&payload->sign_data, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&payload->sign_data,
                                                         &payload->sign_len),
                             SILC_STR_END);
   if (ret == -1) {
@@ -316,7 +317,7 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
     goto err;
   }
 
-  if (ske->start_payload && 
+  if (ske->start_payload &&
       (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) &&
       (payload->sign_len < 3 || !payload->sign_data)) {
     SILC_LOG_ERROR(("The signature data is missing - both parties are "
@@ -330,7 +331,7 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
     goto err;
   }
-  
+
   /* Decode the binary data to integer */
   silc_mp_init(&payload->x);
   silc_mp_bin2mp(x, x_len, &payload->x);
@@ -353,7 +354,7 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
 
 /* Free's KE Payload */
 
-void silc_ske_payload_ke_free(SilcSKEKEPayload *payload)
+void silc_ske_payload_ke_free(SilcSKEKEPayload payload)
 {
   if (payload) {
     silc_free(payload->pk_data);
index 26690f7a86d8727c7f106598fe49c748b83bddce..7c04aa1fc56ab3d7bc4bf4a0a8794af855d4654e 100644 (file)
@@ -30,31 +30,72 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
                                        unsigned char *return_hash,
                                        SilcUInt32 *return_hash_len,
                                        int initiator);
+static SilcSKEStatus
+silc_ske_select_security_properties(SilcSKE ske,
+                                   const char *version,
+                                   SilcSKEStartPayload payload,
+                                   SilcSKEStartPayload remote_payload);
+SilcSKEKeyMaterial
+silc_ske_process_key_material_data(unsigned char *data,
+                                  SilcUInt32 data_len,
+                                  SilcUInt32 req_iv_len,
+                                  SilcUInt32 req_enc_key_len,
+                                  SilcUInt32 req_hmac_key_len,
+                                  SilcHash hash);
+SilcSKEKeyMaterial
+silc_ske_process_key_material(SilcSKE ske,
+                             SilcUInt32 req_iv_len,
+                             SilcUInt32 req_enc_key_len,
+                             SilcUInt32 req_hmac_key_len);
+static void silc_ske_packet_receive(SilcPacketEngine engine,
+                                   SilcPacketStream stream,
+                                   SilcPacket packet,
+                                   void *callback_context,
+                                   void *app_context);
+static void silc_ske_packet_eos(SilcPacketEngine engine,
+                               SilcPacketStream stream,
+                               void *callback_context,
+                               void *app_context);
+static void silc_ske_packet_error(SilcPacketEngine engine,
+                                 SilcPacketStream stream,
+                                 SilcPacketError error,
+                                 void *callback_context,
+                                 void *app_context);
 
 /* Structure to hold all SKE callbacks. */
 struct SilcSKECallbacksStruct {
-  SilcSKESendPacketCb send_packet;
-  SilcSKECb payload_receive;
   SilcSKEVerifyCb verify_key;
-  SilcSKECb proto_continue;
-  SilcSKECheckVersion check_version;
+  SilcSKECheckVersionCb check_version;
+  SilcSKECompletionCb completed;
   void *context;
 };
 
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_ske_stream_cbs =
+{
+  silc_ske_packet_receive,
+  silc_ske_packet_eos,
+  silc_ske_packet_error
+};
+
 /* Allocates new SKE object. */
 
-SilcSKE silc_ske_alloc(SilcRng rng, void *context)
+SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, void *context)
 {
   SilcSKE ske;
 
   SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
 
+  if (!rng || !schedule)
+    return NULL;
+
   ske = silc_calloc(1, sizeof(*ske));
   if (!ske)
     return NULL;
   ske->status = SILC_SKE_STATUS_OK;
   ske->rng = rng;
   ske->user_data = context;
+  ske->schedule = schedule;
   ske->users = 1;
 
   return ske;
@@ -117,45 +158,19 @@ void silc_ske_free(SilcSKE ske)
   }
 }
 
-/* Sets the callback functions for the SKE session.
-
-   The `send_packet' callback is a function that sends the packet to
-   network. The SKE library will call it at any time packet needs to
-   be sent to the remote host.
-
-   The `payload_receive' callback is called when the remote host's Key
-   Exchange Start Payload has been processed.  The payload is saved
-   to ske->start_payload if the application would need it.  The application
-   must also provide the payload to the next state of the SKE.
+/* Return user context */
 
-   The `verify_key' callback is called to verify the received public key
-   or certificate.  The verification process is most likely asynchronous.
-   That is why the application must call the completion callback when the
-   verification process has been completed. The library then calls the user
-   callback (`proto_continue'), if it is provided to indicate that the SKE
-   protocol may continue.
-
-   The `proto_continue' callback is called to indicate that it is
-   safe to continue the execution of the SKE protocol after executing
-   an asynchronous operation, such as calling the `verify_key' callback
-   function, which is asynchronous. The application should check the
-   ske->status in this function to check whether it is Ok to continue
-   the execution of the protocol.
-
-   The `check_version' callback is called to verify the remote host's
-   version. The application may check its own version against the remote
-   host's version and determine whether supporting the remote host
-   is possible.
+void *silc_ske_get_context(SilcSKE ske)
+{
+  return ske->user_data;
+}
 
-   The `context' is passed as argument to all of the above callback
-   functions. */
+/* Sets protocol callbacks */
 
 void silc_ske_set_callbacks(SilcSKE ske,
-                           SilcSKESendPacketCb send_packet,
-                           SilcSKECb payload_receive,
                            SilcSKEVerifyCb verify_key,
-                           SilcSKECb proto_continue,
-                           SilcSKECheckVersion check_version,
+                           SilcSKECheckVersionCb check_version,
+                           SilcSKECompletionCb completed,
                            void *context)
 {
   if (ske->callbacks)
@@ -163,87 +178,112 @@ void silc_ske_set_callbacks(SilcSKE ske,
   ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks));
   if (!ske->callbacks)
     return;
-  ske->callbacks->send_packet = send_packet;
-  ske->callbacks->payload_receive = payload_receive;
   ske->callbacks->verify_key = verify_key;
-  ske->callbacks->proto_continue = proto_continue;
   ske->callbacks->check_version = check_version;
+  ske->callbacks->completed = completed;
   ske->callbacks->context = context;
 }
 
-/* Starts the SILC Key Exchange protocol for initiator. The connection
-   to the remote end must be established before calling this function
-   and the connecting socket must be sent as argument. This function
-   creates the Key Exchange Start Payload which includes all our
-   configured security properties. This payload is then sent to the
-   remote end for further processing. This payload must be sent as
-   argument to the function, however, it must not be encoded
-   already, it is done by this function. The caller must not free
-   the `start_payload' since the SKE library will save it.
-
-   The packet sending is done by calling a callback function. Caller
-   must provide a routine to send the packet. */
-
-SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
-                                      SilcSocketConnection sock,
-                                      SilcSKEStartPayload *start_payload)
+/* Aborts SKE protocol */
+
+static void silc_ske_abort(SilcAsyncOperation op, void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKE ske = context;
+  ske->aborted = TRUE;
+}
+
+/* Public key verification completion callback */
+
+static void silc_ske_pk_verified(SilcSKE ske, SilcSKEStatus status,
+                                void *completion_context)
+{
+  SILC_FSM_CALL_CONTINUE(&ske->fsm);
+}
+
+/* Initiator state machine */
+SILC_FSM_STATE(silc_ske_st_initiator_start);
+SILC_FSM_STATE(silc_ske_st_initiator_phase1);
+SILC_FSM_STATE(silc_ske_st_initiator_phase2);
+SILC_FSM_STATE(silc_ske_st_initiator_phase3);
+SILC_FSM_STATE(silc_ske_st_initiator_phase4);
+SILC_FSM_STATE(silc_ske_st_initiator_end);
+SILC_FSM_STATE(silc_ske_st_initiator_aborted);
+SILC_FSM_STATE(silc_ske_st_initiator_error);
+
+/* Start protocol.  Send our proposal */
+
+SILC_FSM_STATE(silc_ske_st_initiator_start)
+{
+  SilcSKE ske = fsm_context;
   SilcBuffer payload_buf;
+  SilcStatus status;
 
   SILC_LOG_DEBUG(("Start"));
 
-  ske->sock = sock;
-  ske->rng = rng;
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
   /* Encode the payload */
-  status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
-  if (status != SILC_SKE_STATUS_OK)
-    return status;
-
-  /* Send the packet. */
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE,
-                                  ske->callbacks->context);
+  status = silc_ske_payload_start_encode(ske, ske->start_payload,
+                                        &payload_buf);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error encoding Start Payload */
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   /* Save the the payload buffer for future use. It is later used to
      compute the HASH value. */
   ske->start_payload_copy = payload_buf;
-  ske->start_payload = start_payload;
 
-  return status;
+  /* Send the packet */
+  /* XXX */
+
+  /** Wait for responder proposal */
+  SILC_LOG_DEBUG(("Waiting for reponder proposal"));
+  silc_fsm_next(ske, silc_ske_st_initiator_phase1);
+  return SILC_FSM_WAIT;
 }
 
-/* Function called after ske_initiator_start fuction. This receives
-   the remote ends Key Exchange Start payload which includes the
-   security properties selected by the responder from our payload
-   sent in the silc_ske_initiator_start function. */
+/* Phase-1.  Receives responder's proposal */
 
-SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
-                                        SilcBuffer start_payload)
+SILC_FSM_STATE(silc_ske_st_initiator_phase1)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEStartPayload *payload;
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEStartPayload payload;
   SilcSKESecurityProperties prop;
   SilcSKEDiffieHellmanGroup group;
 
   SILC_LOG_DEBUG(("Start"));
 
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
   /* Decode the payload */
-  status = silc_ske_payload_start_decode(ske, start_payload, &payload);
+  status = silc_ske_payload_start_decode(ske, ske->packet_buf, &payload);
   if (status != SILC_SKE_STATUS_OK) {
+    /** Error decoding Start Payload */
     ske->status = status;
-    silc_ske_payload_start_free(ske->start_payload);
-    return status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
 
   /* Check that the cookie is returned unmodified */
   if (memcmp(ske->start_payload->cookie, payload->cookie,
             ske->start_payload->cookie_len)) {
+    /** Invalid cookie */
     SILC_LOG_ERROR(("Responder modified our cookie and it must not do it"));
     ske->status = SILC_SKE_STATUS_INVALID_COOKIE;
-    silc_ske_payload_start_free(ske->start_payload);
-    return status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
 
   /* Check version string */
@@ -252,19 +292,20 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
                                           payload->version_len,
                                           ske->callbacks->context);
     if (status != SILC_SKE_STATUS_OK) {
+      /** Version mismatch */
       ske->status = status;
-      silc_ske_payload_start_free(ske->start_payload);
-      return status;
+      silc_fsm_next(fsm, silc_ske_st_initiator_error);
+      return SILC_FSM_CONTINUE;
     }
   }
 
   /* Free our KE Start Payload context, we don't need it anymore. */
   silc_ske_payload_start_free(ske->start_payload);
+  ske->start_payload = NULL;
 
   /* Take the selected security properties into use while doing
-     the key exchange. This is used only while doing the key
-     exchange. The same data is returned to upper levels by calling
-     the callback function. */
+     the key exchange.  This is used only while doing the key
+     exchange. */
   ske->prop = prop = silc_calloc(1, sizeof(*prop));
   if (!ske->prop)
     goto err;
@@ -279,17 +320,14 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
     status = SILC_SKE_STATUS_UNKNOWN_PKCS;
     goto err;
   }
-
   if (silc_cipher_alloc(payload->enc_alg_list, &prop->cipher) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
     goto err;
   }
-
   if (silc_hash_alloc(payload->hash_alg_list, &prop->hash) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
     goto err;
   }
-
   if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_HMAC;
     goto err;
@@ -298,11 +336,9 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
   /* Save remote's KE Start Payload */
   ske->start_payload = payload;
 
-  /* Return the received payload by calling the callback function. */
-  if (ske->callbacks->payload_receive)
-    (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
-
-  return status;
+  /** Send KE Payload */
+  silc_fsm_next(fsm, silc_ske_st_initiator_phase2);
+  return SILC_FSM_CONTINUE;
 
  err:
   if (payload)
@@ -322,25 +358,23 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
   ske->prop = NULL;
 
   if (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+    status = SILC_SKE_STATUS_ERROR;
 
+  /** Error */
   ske->status = status;
-  return status;
+  silc_fsm_next(fsm, silc_ske_st_initiator_error);
+  return SILC_FSM_CONTINUE;
 }
 
-/* This function creates random number x, such that 1 < x < q and
-   computes e = g ^ x mod p and sends the result to the remote end in
-   Key Exchange Payload. */
+/* Phase-2.  Send KE payload */
 
-SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
-                                        SilcPublicKey public_key,
-                                        SilcPrivateKey private_key,
-                                        SilcSKEPKType pk_type)
+SILC_FSM_STATE(silc_ske_st_initiator_phase2)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
   SilcBuffer payload_buf;
   SilcMPInt *x;
-  SilcSKEKEPayload *payload;
+  SilcSKEKEPayload payload;
   SilcUInt32 pk_len;
 
   SILC_LOG_DEBUG(("Start"));
@@ -348,8 +382,10 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
   /* Create the random number x, 1 < x < q. */
   x = silc_calloc(1, sizeof(*x));
   if (!x){
+    /** Out of memory */
     ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-    return ske->status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
   silc_mp_init(x);
   status =
@@ -357,20 +393,24 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
                        silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
                        x);
   if (status != SILC_SKE_STATUS_OK) {
+    /** Error generating random number */
     silc_mp_uninit(x);
     silc_free(x);
     ske->status = status;
-    return status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
 
   /* Encode the result to Key Exchange Payload. */
 
   payload = silc_calloc(1, sizeof(*payload));
   if (!payload) {
+    /** Out of memory */
     silc_mp_uninit(x);
     silc_free(x);
     ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-    return ske->status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
   ske->ke1_payload = payload;
 
@@ -382,23 +422,26 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
                  &ske->prop->group->group);
 
   /* Get public key */
-  if (public_key) {
-    payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
+  if (ske->public_key) {
+    payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
     if (!payload->pk_data) {
+      /** Error encoding public key */
       silc_mp_uninit(x);
       silc_free(x);
       silc_mp_uninit(&payload->x);
       silc_free(payload);
       ske->ke1_payload = NULL;
-      ske->status = SILC_SKE_STATUS_OK;
-      return ske->status;
+      ske->status = SILC_SKE_STATUS_ERROR;
+      silc_fsm_next(fsm, silc_ske_st_initiator_error);
+      return SILC_FSM_CONTINUE;
     }
     payload->pk_len = pk_len;
   }
-  payload->pk_type = pk_type;
+  payload->pk_type = ske->pk_type;
 
   /* Compute signature data if we are doing mutual authentication */
-  if (private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+  if (ske->private_key &&
+      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
     unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1];
     SilcUInt32 hash_len, sign_len;
 
@@ -412,10 +455,11 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
     SILC_LOG_DEBUG(("Signing HASH_i value"));
 
     /* Sign the hash value */
-    silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv,
-                                  private_key->prv_len);
+    silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv,
+                                  ske->private_key->prv_len);
     if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
        !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
+      /** Error computing signature */
       silc_mp_uninit(x);
       silc_free(x);
       silc_mp_uninit(&payload->x);
@@ -423,7 +467,8 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
       silc_free(payload);
       ske->ke1_payload = NULL;
       ske->status = SILC_SKE_STATUS_SIGNATURE_ERROR;
-      return ske->status;
+      silc_fsm_next(fsm, silc_ske_st_initiator_error);
+      return SILC_FSM_CONTINUE;
     }
     payload->sign_data = silc_memdup(sign, sign_len);
     payload->sign_len = sign_len;
@@ -432,6 +477,7 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 
   status = silc_ske_payload_ke_encode(ske, payload, &payload_buf);
   if (status != SILC_SKE_STATUS_OK) {
+    /** Error encoding KE payload */
     silc_mp_uninit(x);
     silc_free(x);
     silc_mp_uninit(&payload->x);
@@ -440,68 +486,133 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
     silc_free(payload);
     ske->ke1_payload = NULL;
     ske->status = status;
-    return status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
 
   ske->x = x;
 
   /* Send the packet. */
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, payload_buf,
-                                  SILC_PACKET_KEY_EXCHANGE_1,
-                                  ske->callbacks->context);
+  /* XXX */
 
   silc_buffer_free(payload_buf);
 
-  return status;
+  /** Waiting responder's KE payload */
+  silc_fsm_next(fsm, silc_ske_st_initiator_phase3);
+  return SILC_FSM_WAIT;
 }
 
-/* An initiator finish final callback that is called to indicate that
-   the SKE protocol may continue. */
+/* Phase-3.  Process responder's KE payload */
 
-static void silc_ske_initiator_finish_final(SilcSKE ske,
-                                           SilcSKEStatus status,
-                                           void *context)
+SILC_FSM_STATE(silc_ske_st_initiator_phase3)
 {
-  SilcSKEKEPayload *payload;
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEKEPayload payload;
+  SilcMPInt *KEY;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Decode the payload */
+  status = silc_ske_payload_ke_decode(ske, ske->packet_buf, &payload);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error decoding KE payload */
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+  ske->ke2_payload = payload;
+
+  if (!payload->pk_data && ske->callbacks->verify_key) {
+    SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), "
+                   "even though we require it"));
+    ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+    goto err;
+  }
+
+  SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+
+  /* Compute the shared secret key */
+  KEY = silc_calloc(1, sizeof(*KEY));
+  silc_mp_init(KEY);
+  silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group);
+  ske->KEY = KEY;
+
+  if (payload->pk_data && ske->callbacks->verify_key) {
+    SILC_LOG_DEBUG(("Verifying public key"));
+
+    /** Waiting public key verification */
+    silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
+    SILC_FSM_CALL(ske->callbacks->verify_key(ske, payload->pk_data,
+                                            payload->pk_len,
+                                            payload->pk_type,
+                                            ske->callbacks->context,
+                                            silc_ske_pk_verified, NULL));
+    /* NOT REACHED */
+  }
+
+  /** Process key material */
+  silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
+  return SILC_FSM_CONTINUE;
+
+ err:
+  silc_ske_payload_ke_free(payload);
+  ske->ke2_payload = NULL;
+
+  silc_mp_uninit(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
+
+  if (status == SILC_SKE_STATUS_OK)
+    return SILC_SKE_STATUS_ERROR;
+
+  /** Error */
+  ske->status = status;
+  silc_fsm_next(fsm, silc_ske_st_initiator_error);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Process key material */
+
+SILC_FSM_STATE(silc_ske_st_initiator_phase4)
+{
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEKEPayload payload;
   unsigned char hash[SILC_HASH_MAXLEN];
   SilcUInt32 hash_len;
   SilcPublicKey public_key = NULL;
+  int key_len, block_len;
 
-  /* If the SKE was freed during the async call then free it really now,
-     otherwise just decrement the reference counter. */
-  if (ske->status == SILC_SKE_STATUS_FREED) {
-    silc_ske_free(ske);
-    return;
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
   }
 
-  /* If the caller returns PENDING status SKE library will assume that
-     the caller will re-call this callback when it is not anymore in
-     PENDING status. */
-  if (status == SILC_SKE_STATUS_PENDING)
-    return;
+  /* Check result of public key verification */
+  if (ske->status != SILC_SKE_STATUS_OK) {
+    /** Public key not verified */
+    SILC_LOG_DEBUG(("Public key verification failed"));
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
 
-  ske->users--;
   payload = ske->ke2_payload;
 
-  /* If the status is an error then the public key that was verified
-     by the caller is not authentic. */
-  if (status != SILC_SKE_STATUS_OK) {
-    ske->status = status;
-    if (ske->callbacks->proto_continue)
-      ske->callbacks->proto_continue(ske, ske->callbacks->context);
-    return;
-  }
-
   if (payload->pk_data) {
     /* Decode the public key */
     if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len,
                                     &public_key)) {
-      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
       SILC_LOG_ERROR(("Unsupported/malformed public key received"));
-      if (ske->callbacks->proto_continue)
-       ske->callbacks->proto_continue(ske, ske->callbacks->context);
-      return;
+      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      goto err;
     }
 
     SILC_LOG_DEBUG(("Public key is authentic"));
@@ -533,11 +644,24 @@ static void silc_ske_initiator_finish_final(SilcSKE ske,
 
   ske->status = SILC_SKE_STATUS_OK;
 
-  /* Call the callback. The caller may now continue the SKE protocol. */
-  if (ske->callbacks->proto_continue)
-    ske->callbacks->proto_continue(ske, ske->callbacks->context);
+  /* Process key material */
+  key_len = silc_cipher_get_key_len(ske->prop->cipher);
+  block_len = silc_cipher_get_key_len(ske->prop->cipher);
+  hash_len = silc_hash_len(ske->prop->hash);
+  ske->keymat = silc_ske_process_key_material(ske, block_len,
+                                             key_len, hash_len);
+  if (!ske->keymat) {
+    SILC_LOG_ERROR(("Error processing key material"));
+    status = SILC_SKE_STATUS_ERROR;
+    goto err;
+  }
 
-  return;
+  /* Send SUCCESS packet */
+  /* XXX */
+
+  /** Waiting completion */
+  silc_fsm_next(fsm, silc_ske_st_initiator_end);
+  return SILC_FSM_WAIT;
 
  err:
   memset(hash, 'F', sizeof(hash));
@@ -558,185 +682,176 @@ static void silc_ske_initiator_finish_final(SilcSKE ske,
   }
 
   if (status == SILC_SKE_STATUS_OK)
-    ske->status = SILC_SKE_STATUS_ERROR;
+    status = SILC_SKE_STATUS_ERROR;
 
+  /** Error */
   ske->status = status;
+  silc_fsm_next(fsm, silc_ske_st_initiator_error);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Protocol completed */
+
+SILC_FSM_STATE(silc_ske_st_initiator_end)
+{
+  SilcSKE ske = fsm_context;
+
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Call the completion callback */
+  if (ske->callbacks->completed)
+    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
 
-  /* Call the callback. */
-  if (ske->callbacks->proto_continue)
-    ske->callbacks->proto_continue(ske, ske->callbacks->context);
+  return SILC_FSM_FINISH;
 }
 
-/* Receives Key Exchange Payload from responder consisting responders
-   public key, f, and signature. This function verifies the public key,
-   computes the secret shared key and verifies the signature.
-
-   The `proto_continue' will be called to indicate that the caller may
-   continue with the SKE protocol.  The caller must not continue
-   before the SKE libary has called that callback.  If this function
-   returns an error the callback will not be called.  It is called
-   if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
-   However, note that when the library calls the callback the ske->status
-   may be error.
-
-   This calls the `verify_key' callback to verify the received public
-   key or certificate. If the `verify_key' is provided then the remote
-   must send public key and it is considered to be an error if remote
-   does not send its public key. If caller is performing a re-key with
-   SKE then the `verify_key' is usually not provided when it is not also
-   required for the remote to send its public key. */
-
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
-                                       SilcBuffer ke_payload)
+/* Aborted by application */
+
+SILC_FSM_STATE(silc_ske_st_initiator_aborted)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEKEPayload *payload;
-  SilcMPInt *KEY;
 
-  SILC_LOG_DEBUG(("Start"));
+  return SILC_FSM_FINISH;
+}
 
-  /* Decode the payload */
-  status = silc_ske_payload_ke_decode(ske, ke_payload, &payload);
-  if (status != SILC_SKE_STATUS_OK) {
-    ske->status = status;
-    return status;
-  }
-  ske->ke2_payload = payload;
+/* Error occurred */
 
-  if (!payload->pk_data && ske->callbacks->verify_key) {
-    SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), "
-                   "even though we require it"));
-    ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
-    goto err;
-  }
+SILC_FSM_STATE(silc_ske_st_initiator_error)
+{
 
-  SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+  return SILC_FSM_FINISH;
+}
 
-  /* Compute the shared secret key */
-  KEY = silc_calloc(1, sizeof(*KEY));
-  silc_mp_init(KEY);
-  silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group);
-  ske->KEY = KEY;
 
-  if (payload->pk_data && ske->callbacks->verify_key) {
-    SILC_LOG_DEBUG(("Verifying public key"));
+static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context,
+                                       void *destructor_context)
+{
 
-    ske->users++;
-    (*ske->callbacks->verify_key)(ske, payload->pk_data, payload->pk_len,
-                                 payload->pk_type, ske->callbacks->context,
-                                 silc_ske_initiator_finish_final, NULL);
+}
 
-    /* We will continue to the final state after the public key has
-       been verified by the caller. */
-    return SILC_SKE_STATUS_PENDING;
-  }
+/* Starts the protocol as initiator */
 
-  /* Continue to final state */
-  ske->users++;
-  silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, NULL);
+SilcAsyncOperation
+silc_ske_initiator_start(SilcSKE ske,
+                        SilcPacketStream stream,
+                        SilcSKEStartPayload start_payload)
+{
+  SILC_LOG_DEBUG(("Start SKE as initiator"));
 
-  return SILC_SKE_STATUS_OK;
+  if (!ske || !stream || !start_payload)
+    return NULL;
 
- err:
-  silc_ske_payload_ke_free(payload);
-  ske->ke2_payload = NULL;
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
 
-  silc_mp_uninit(ske->KEY);
-  silc_free(ske->KEY);
-  ske->KEY = NULL;
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_initiator_finished, ske,
+                    ske->schedule))
+    return NULL;
 
-  if (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+  ske->start_payload = start_payload;
 
-  ske->status = status;
-  return status;
+  /* Link to packet stream to get key exchange packets */
+  ske->stream = stream;
+  silc_packet_stream_ref(ske->stream);
+  silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+
+  /* Start SKE as initiator */
+  silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start);
+
+  return &ske->op;
 }
 
-/* Starts Key Exchange protocol for responder. Responder receives
-   Key Exchange Start Payload from initiator consisting of all the
-   security properties the initiator supports. This function decodes
-   the payload and parses the payload further and selects the right
-   security properties. */
-
-SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
-                                      SilcSocketConnection sock,
-                                      const char *version,
-                                      SilcBuffer start_payload,
-                                      SilcSKESecurityPropertyFlag flags)
+/* Responder state machine */
+SILC_FSM_STATE(silc_ske_st_responder_start);
+SILC_FSM_STATE(silc_ske_st_responder_phase1);
+SILC_FSM_STATE(silc_ske_st_responder_phase2);
+SILC_FSM_STATE(silc_ske_st_responder_phase3);
+SILC_FSM_STATE(silc_ske_st_responder_phase4);
+SILC_FSM_STATE(silc_ske_st_responder_end);
+SILC_FSM_STATE(silc_ske_st_responder_aborted);
+SILC_FSM_STATE(silc_ske_st_responder_error);
+
+/* Start protocol as responder.  Decode initiator's start payload */
+
+SILC_FSM_STATE(silc_ske_st_responder_start)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEStartPayload *remote_payload = NULL, *payload = NULL;
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEStartPayload remote_payload = NULL, payload = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  ske->sock = sock;
-  ske->rng = rng;
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
   /* Decode the payload */
-  status = silc_ske_payload_start_decode(ske, start_payload, &remote_payload);
+  status = silc_ske_payload_start_decode(ske, ske->packet_buf,
+                                        &remote_payload);
   if (status != SILC_SKE_STATUS_OK) {
+    /** Error decoding Start Payload */
     ske->status = status;
-    return status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
   }
 
   /* Take a copy of the payload buffer for future use. It is used to
      compute the HASH value. */
-  ske->start_payload_copy = silc_buffer_copy(start_payload);
+  ske->start_payload_copy = silc_buffer_copy(ske->packet_buf);
 
   /* Force the mutual authentication flag if we want to do it. */
-  if (flags & SILC_SKE_SP_FLAG_MUTUAL) {
+  if (ske->flags & SILC_SKE_SP_FLAG_MUTUAL) {
     SILC_LOG_DEBUG(("Force mutual authentication"));
     remote_payload->flags |= SILC_SKE_SP_FLAG_MUTUAL;
   }
 
   /* Force PFS flag if we require it */
-  if (flags & SILC_SKE_SP_FLAG_PFS) {
+  if (ske->flags & SILC_SKE_SP_FLAG_PFS) {
     SILC_LOG_DEBUG(("Force PFS"));
     remote_payload->flags |= SILC_SKE_SP_FLAG_PFS;
   }
 
   /* Disable IV Included flag if requested */
   if (remote_payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED &&
-      !(flags & SILC_SKE_SP_FLAG_IV_INCLUDED)) {
+      !(ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)) {
     SILC_LOG_DEBUG(("We do not support IV Included flag"));
     remote_payload->flags &= ~SILC_SKE_SP_FLAG_IV_INCLUDED;
   }
 
   /* Parse and select the security properties from the payload */
   payload = silc_calloc(1, sizeof(*payload));
-  status = silc_ske_select_security_properties(ske, version,
+  status = silc_ske_select_security_properties(ske, ske->version,
                                               payload, remote_payload);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error selecting proposal */
+    if (remote_payload)
+      silc_ske_payload_start_free(remote_payload);
+    silc_free(payload);
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   ske->start_payload = payload;
 
-  /* Call the callback function. */
-  if (ske->callbacks->payload_receive)
-    (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
-
   silc_ske_payload_start_free(remote_payload);
 
-  return status;
-
- err:
-  if (remote_payload)
-    silc_ske_payload_start_free(remote_payload);
-  silc_free(payload);
-
-  if (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
-
-  ske->status = status;
-  return status;
+  /** Send proposal to initiator */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase1);
+  return SILC_FSM_CONTINUE;
 }
 
-/* The selected security properties from the initiator payload is now
-   encoded into Key Exchange Start Payload and sent to the initiator. */
+/* Phase-1.  Send Start Payload */
 
-SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske)
+SILC_FSM_STATE(silc_ske_st_responder_phase1)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
   SilcBuffer payload_buf;
   SilcSKESecurityProperties prop;
   SilcSKEDiffieHellmanGroup group = NULL;
@@ -758,19 +873,16 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske)
     status = SILC_SKE_STATUS_UNKNOWN_PKCS;
     goto err;
   }
-
   if (silc_cipher_alloc(ske->start_payload->enc_alg_list,
                        &prop->cipher) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
     goto err;
   }
-
   if (silc_hash_alloc(ske->start_payload->hash_alg_list,
                      &prop->hash) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
     goto err;
   }
-
   if (silc_hmac_alloc(ske->start_payload->hmac_alg_list, NULL,
                      &prop->hmac) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_HMAC;
@@ -784,13 +896,13 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske)
     goto err;
 
   /* Send the packet. */
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE,
-                                  ske->callbacks->context);
+  /* XXX */
 
   silc_buffer_free(payload_buf);
 
-  return status;
+  /** Waiting initiator's KE payload */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase2);
+  return SILC_FSM_WAIT;
 
  err:
   if (group)
@@ -808,47 +920,101 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske)
   ske->prop = NULL;
 
   if (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+    status = SILC_SKE_STATUS_ERROR;
 
+  /** Error */
   ske->status = status;
-  return status;
+  silc_fsm_next(fsm, silc_ske_st_responder_error);
+  return SILC_FSM_CONTINUE;
 }
 
-/* An responder phase 2 final callback that is called to indicate that
-   the SKE protocol may continue. */
+/* Phase-2.  Decode initiator's KE payload */
 
-static void silc_ske_responder_phase2_final(SilcSKE ske,
-                                           SilcSKEStatus status,
-                                           void *context)
+SILC_FSM_STATE(silc_ske_st_responder_phase2)
 {
-  SilcSKEKEPayload *recv_payload, *send_payload;
-  SilcMPInt *x;
-
-  /* If the SKE was freed during the async call then free it really now,
-     otherwise just decrement the reference counter. */
-  if (ske->status == SILC_SKE_STATUS_FREED) {
-    silc_ske_free(ske);
-    return;
-  }
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEKEPayload recv_payload;
 
-  /* If the caller returns PENDING status SKE library will assume that
-     the caller will re-call this callback when it is not anymore in
-     PENDING status. */
-  if (status == SILC_SKE_STATUS_PENDING)
-    return;
+  SILC_LOG_DEBUG(("Start"));
 
-  ske->users--;
-  recv_payload = ske->ke1_payload;
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
-  /* If the status is an error then the public key that was verified
-     by the caller is not authentic. */
+  /* Decode Key Exchange Payload */
+  status = silc_ske_payload_ke_decode(ske, ske->packet_buf, &recv_payload);
   if (status != SILC_SKE_STATUS_OK) {
+    /** Error decoding KE payload */
     ske->status = status;
-    if (ske->callbacks->proto_continue)
-      ske->callbacks->proto_continue(ske, ske->callbacks->context);
-    return;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
   }
 
+  ske->ke1_payload = recv_payload;
+
+  /* Verify the received public key and verify the signature if we are
+     doing mutual authentication. */
+  if (ske->start_payload &&
+      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+
+    SILC_LOG_DEBUG(("We are doing mutual authentication"));
+
+    if (!recv_payload->pk_data && ske->callbacks->verify_key) {
+      /** Public key not provided */
+      SILC_LOG_ERROR(("Remote end did not send its public key (or "
+                     "certificate), even though we require it"));
+      ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
+    }
+
+    if (recv_payload->pk_data && ske->callbacks->verify_key) {
+      SILC_LOG_DEBUG(("Verifying public key"));
+
+      /** Waiting public key verification */
+      silc_fsm_next(fsm, silc_ske_st_responder_phase3);
+      SILC_FSM_CALL(ske->callbacks->verify_key(ske, recv_payload->pk_data,
+                                              recv_payload->pk_len,
+                                              recv_payload->pk_type,
+                                              ske->callbacks->context,
+                                              silc_ske_pk_verified, NULL));
+      /* NOT REACHED */
+    }
+  }
+
+  /** Generate KE2 payload */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase3);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Phase-3. Generate KE2 payload */
+
+SILC_FSM_STATE(silc_ske_st_responder_phase3)
+{
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEKEPayload recv_payload, send_payload;
+  SilcMPInt *x, *KEY;
+
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Check result of public key verification */
+  if (ske->status != SILC_SKE_STATUS_OK) {
+    /** Public key not verified */
+    SILC_LOG_DEBUG(("Public key verification failed"));
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  recv_payload = ske->ke1_payload;
+
   /* The public key verification was performed only if the Mutual
      Authentication flag is set. */
   if (ske->start_payload &&
@@ -861,11 +1027,11 @@ static void silc_ske_responder_phase2_final(SilcSKE ske,
     if (!silc_pkcs_public_key_decode(recv_payload->pk_data,
                                     recv_payload->pk_len,
                                     &public_key)) {
-      ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      /** Error decoding public key */
       SILC_LOG_ERROR(("Unsupported/malformed public key received"));
-      if (ske->callbacks->proto_continue)
-       ske->callbacks->proto_continue(ske, ske->callbacks->context);
-      return;
+      ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
     }
 
     SILC_LOG_DEBUG(("Public key is authentic"));
@@ -873,10 +1039,10 @@ static void silc_ske_responder_phase2_final(SilcSKE ske,
     /* Compute the hash value */
     status = silc_ske_make_hash(ske, hash, &hash_len, TRUE);
     if (status != SILC_SKE_STATUS_OK) {
+      /** Error computing hash */
       ske->status = status;
-      if (ske->callbacks->proto_continue)
-       ske->callbacks->proto_continue(ske, ske->callbacks->context);
-      return;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
     }
 
     SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
@@ -885,11 +1051,11 @@ static void silc_ske_responder_phase2_final(SilcSKE ske,
     silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
     if (silc_pkcs_verify(ske->prop->pkcs, recv_payload->sign_data,
                         recv_payload->sign_len, hash, hash_len) == FALSE) {
+      /** Incorrect signature */
       SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
       ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
-      if (ske->callbacks->proto_continue)
-       ske->callbacks->proto_continue(ske, ske->callbacks->context);
-      return;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
     }
 
     SILC_LOG_DEBUG(("Signature is Ok"));
@@ -906,12 +1072,12 @@ static void silc_ske_responder_phase2_final(SilcSKE ske,
                        silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
                        x);
   if (status != SILC_SKE_STATUS_OK) {
+    /** Error generating random number */
     silc_mp_uninit(x);
     silc_free(x);
     ske->status = status;
-    if (ske->callbacks->proto_continue)
-      ske->callbacks->proto_continue(ske, ske->callbacks->context);
-    return;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
   }
 
   /* Save the results for later processing */
@@ -926,119 +1092,42 @@ static void silc_ske_responder_phase2_final(SilcSKE ske,
   silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x,
                  &ske->prop->group->group);
 
-  /* Call the callback. The caller may now continue with the SKE protocol. */
-  ske->status = SILC_SKE_STATUS_OK;
-  if (ske->callbacks->proto_continue)
-    ske->callbacks->proto_continue(ske, ske->callbacks->context);
-}
-
-/* This function receives the Key Exchange Payload from the initiator.
-   This also performs the mutual authentication if required. Then, this
-   function first generated a random number x, such that 1 < x < q
-   and computes f = g ^ x mod p. This then puts the result f to a Key
-   Exchange Payload.
-
-   The `proto_continue' will be called to indicate that the caller may
-   continue with the SKE protocol.  The caller must not continue
-   before the SKE libary has called that callback.  If this function
-   returns an error the callback will not be called.  It is called
-   if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
-   However, note that when the library calls the callback the ske->status
-   may be error.
-
-   This calls the `verify_key' callback to verify the received public
-   key or certificate if the Mutual Authentication flag is set. If the
-   `verify_key' is provided then the remote must send public key and it
-   is considered to be an error if remote does not send its public key. */
-
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
-                                        SilcBuffer ke_payload)
-{
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEKEPayload *recv_payload;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Decode Key Exchange Payload */
-  status = silc_ske_payload_ke_decode(ske, ke_payload, &recv_payload);
-  if (status != SILC_SKE_STATUS_OK) {
-    ske->status = status;
-    return status;
-  }
-
-  ske->ke1_payload = recv_payload;
-
-  /* Verify the received public key and verify the signature if we are
-     doing mutual authentication. */
-  if (ske->start_payload &&
-      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
-
-    SILC_LOG_DEBUG(("We are doing mutual authentication"));
-
-    if (!recv_payload->pk_data && ske->callbacks->verify_key) {
-      SILC_LOG_ERROR(("Remote end did not send its public key (or "
-                     "certificate), even though we require it"));
-      ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
-      return status;
-    }
-
-    if (recv_payload->pk_data && ske->callbacks->verify_key) {
-      SILC_LOG_DEBUG(("Verifying public key"));
-
-      ske->users++;
-      (*ske->callbacks->verify_key)(ske, recv_payload->pk_data,
-                                   recv_payload->pk_len,
-                                   recv_payload->pk_type,
-                                   ske->callbacks->context,
-                                   silc_ske_responder_phase2_final, NULL);
-
-      /* We will continue to the final state after the public key has
-        been verified by the caller. */
-      return SILC_SKE_STATUS_PENDING;
-    }
-  }
+  SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
 
-  /* Continue to final state */
-  ske->users++;
-  silc_ske_responder_phase2_final(ske, SILC_SKE_STATUS_OK, NULL);
+  /* Compute the shared secret key */
+  KEY = silc_calloc(1, sizeof(*KEY));
+  silc_mp_init(KEY);
+  silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x,
+                 &ske->prop->group->group);
+  ske->KEY = KEY;
 
-  return SILC_SKE_STATUS_OK;
+  /** Send KE2 payload */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase4);
+  return SILC_FSM_CONTINUE;
 }
 
-/* This functions generates the secret key KEY = e ^ x mod p, and, a hash
-   value to be signed and sent to the other end. This then encodes Key
-   Exchange Payload and sends it to the other end. */
+/* Phase-4.  Send KE2 payload */
 
-SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
-                                       SilcPublicKey public_key,
-                                       SilcPrivateKey private_key,
-                                       SilcSKEPKType pk_type)
+SILC_FSM_STATE(silc_ske_st_responder_phase4)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
   SilcBuffer payload_buf;
-  SilcMPInt *KEY;
   unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1], *pk;
   SilcUInt32 hash_len, sign_len, pk_len;
 
   SILC_LOG_DEBUG(("Start"));
 
-  SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
-
-  /* Compute the shared secret key */
-  KEY = silc_calloc(1, sizeof(*KEY));
-  silc_mp_init(KEY);
-  silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x,
-                 &ske->prop->group->group);
-  ske->KEY = KEY;
-
-  if (public_key && private_key) {
+  if (ske->public_key && ske->private_key) {
     SILC_LOG_DEBUG(("Getting public key"));
 
     /* Get the public key */
-    pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+    pk = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
     if (!pk) {
+      /** Error encoding public key */
       status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-      goto err;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
     }
     ske->ke2_payload->pk_data = pk;
     ske->ke2_payload->pk_len = pk_len;
@@ -1048,8 +1137,12 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
     /* Compute the hash value */
     memset(hash, 0, sizeof(hash));
     status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
-    if (status != SILC_SKE_STATUS_OK)
-      goto err;
+    if (status != SILC_SKE_STATUS_OK) {
+      /** Error computing hash */
+      ske->status = status;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
+    }
 
     ske->hash = silc_memdup(hash, hash_len);
     ske->hash_len = hash_len;
@@ -1057,107 +1150,211 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
     SILC_LOG_DEBUG(("Signing HASH value"));
 
     /* Sign the hash value */
-    silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv,
-                                  private_key->prv_len);
+    silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv,
+                                  ske->private_key->prv_len);
     if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
        !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
+      /** Error computing signature */
       status = SILC_SKE_STATUS_SIGNATURE_ERROR;
-      goto err;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
     }
     ske->ke2_payload->sign_data = silc_memdup(sign, sign_len);
     ske->ke2_payload->sign_len = sign_len;
     memset(sign, 0, sizeof(sign));
   }
-  ske->ke2_payload->pk_type = pk_type;
+  ske->ke2_payload->pk_type = ske->pk_type;
 
   /* Encode the Key Exchange Payload */
   status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
                                      &payload_buf);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error encoding KE payload */
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   /* Send the packet. */
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, payload_buf,
-                                  SILC_PACKET_KEY_EXCHANGE_2,
-                                  ske->callbacks->context);
+  /* XXX */
 
   silc_buffer_free(payload_buf);
 
-  return status;
+  /** Waiting completion */
+  silc_fsm_next(fsm, silc_ske_st_responder_end);
+  return SILC_FSM_WAIT;
+}
 
- err:
-  silc_mp_uninit(ske->KEY);
-  silc_free(ske->KEY);
-  ske->KEY = NULL;
-  silc_ske_payload_ke_free(ske->ke2_payload);
+/* Protocol completed */
 
-  if (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+SILC_FSM_STATE(silc_ske_st_responder_end)
+{
+  SilcSKE ske = fsm_context;
 
-  ske->status = status;
-  return status;
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Call the completion callback */
+  if (ske->callbacks->completed)
+    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
+
+  return SILC_FSM_FINISH;
 }
 
-/* The Key Exchange protocol is ended by calling this function. This
-   must not be called until the keys are processed like the protocol
-   defines. This function is for both initiator and responder. */
+/* Aborted by application */
 
-SilcSKEStatus silc_ske_end(SilcSKE ske)
+SILC_FSM_STATE(silc_ske_st_responder_aborted)
 {
-  SilcBufferStruct packet;
-  unsigned char data[4];
 
-  SILC_LOG_DEBUG(("Start"));
+  /* Send FAILURE */
 
-  SILC_PUT32_MSB((SilcUInt32)SILC_SKE_STATUS_OK, data);
-  silc_buffer_set(&packet, data, 4);
+  return SILC_FSM_FINISH;
+}
 
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, &packet, SILC_PACKET_SUCCESS,
-                                  ske->callbacks->context);
+/* Error occurred */
 
-  return SILC_SKE_STATUS_OK;
+SILC_FSM_STATE(silc_ske_st_responder_error)
+{
+
+  /* Send FAILURE */
+
+  return SILC_FSM_FINISH;
 }
 
-/* Aborts the Key Exchange protocol. This is called if error occurs
-   while performing the protocol. The status argument is the error
-   status and it is sent to the remote end. */
 
-SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status)
+static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context,
+                                       void *destructor_context)
 {
-  SilcBufferStruct packet;
-  unsigned char data[4];
 
-  SILC_LOG_DEBUG(("Start"));
+}
 
-  if (status > SILC_SKE_STATUS_INVALID_COOKIE)
-    status = SILC_SKE_STATUS_BAD_PAYLOAD;
+/* Starts the protocol as responder. */
 
-  SILC_PUT32_MSB((SilcUInt32)status, data);
-  silc_buffer_set(&packet, data, 4);
+SilcAsyncOperation
+silc_ske_responder_start(SilcSKE ske,
+                        SilcPacketStream stream,
+                        const char *version,
+                        SilcBuffer start_payload,
+                        SilcSKESecurityPropertyFlag flags)
+{
+  SILC_LOG_DEBUG(("Start SKE as responder"));
 
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, &packet, SILC_PACKET_FAILURE,
-                                  ske->callbacks->context);
+  if (!ske || !stream || !start_payload)
+    return NULL;
 
-  return SILC_SKE_STATUS_OK;
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
+
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_responder_finished, ske,
+                    ske->schedule))
+    return NULL;
+
+  ske->packet_buf = start_payload;
+  ske->flags = flags;
+  ske->version = strdup(version);
+
+  /* Link to packet stream to get key exchange packets */
+  ske->stream = stream;
+  silc_packet_stream_ref(ske->stream);
+  silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+
+  /* Start SKE as responder */
+  silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start);
+
+  return &ske->op;
+}
+
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start);
+
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start)
+{
+
+}
+
+/* Starts rekey protocol as initiator */
+
+SilcAsyncOperation
+silc_ske_rekey_initiator_start(SilcSKE ske,
+                              SilcPacketStream stream,
+                              SilcSKERekeyMaterial rekey)
+{
+  SILC_LOG_DEBUG(("Start SKE rekey as initator"));
+
+  if (!ske || !stream || !rekey)
+    return NULL;
+
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
+
+  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+    return NULL;
+
+  ske->rekey = rekey;
+
+  /* Link to packet stream to get key exchange packets */
+  ske->stream = stream;
+  silc_packet_stream_ref(ske->stream);
+  silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+
+  /* Start SKE rekey as initiator */
+  silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start);
+
+  return &ske->op;
+}
+
+SILC_FSM_STATE(silc_ske_st_rekey_responder_start);
+
+SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
+{
+
+}
+
+/* Starts rekey protocol as responder */
+
+SilcAsyncOperation
+silc_ske_rekey_responder_start(SilcSKE ske,
+                              SilcPacketStream stream,
+                              SilcBuffer ke_payload,
+                              SilcSKERekeyMaterial rekey)
+{
+  SILC_LOG_DEBUG(("Start SKE rekey as responder"));
+
+  if (!ske || !stream || !rekey)
+    return NULL;
+  if (rekey->pfs && !ke_payload)
+    return NULL;
+
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
+
+  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+    return NULL;
+
+  ske->packet_buf = ke_payload;
+  ske->rekey = rekey;
+
+  /* Link to packet stream to get key exchange packets */
+  ske->stream = stream;
+  silc_packet_stream_ref(ske->stream);
+  silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+
+  /* Start SKE rekey as responder */
+  silc_fsm_start(&ske->fsm, silc_ske_st_rekey_responder_start);
+
+  return &ske->op;
 }
 
-/* Assembles security properties to Key Exchange Start Payload to be
-   sent to the remote end. This checks system wide (SILC system, that is)
-   settings and chooses from those. However, if other properties
-   should be used this function is easy to replace by another function,
-   as, this function is called by the caller of the protocol and not
-   by the protocol itself. */
+/* Assembles security properties */
 
-SilcSKEStatus
+SilcSKEStartPayload
 silc_ske_assemble_security_properties(SilcSKE ske,
                                      SilcSKESecurityPropertyFlag flags,
-                                     const char *version,
-                                     SilcSKEStartPayload **return_payload)
+                                     const char *version)
 {
-  SilcSKEStartPayload *rp;
+  SilcSKEStartPayload rp;
   int i;
 
   SILC_LOG_DEBUG(("Assembling KE Start Payload"));
@@ -1208,22 +1405,20 @@ silc_ske_assemble_security_properties(SilcSKE ske,
     2 + rp->enc_alg_len + 2 + rp->hash_alg_len +
     2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
 
-  *return_payload = rp;
-
-  return SILC_SKE_STATUS_OK;
+  return rp;
 }
 
 /* Selects the supported security properties from the remote end's Key
    Exchange Start Payload. */
 
-SilcSKEStatus
+static SilcSKEStatus
 silc_ske_select_security_properties(SilcSKE ske,
                                    const char *version,
-                                   SilcSKEStartPayload *payload,
-                                   SilcSKEStartPayload *remote_payload)
+                                   SilcSKEStartPayload payload,
+                                   SilcSKEStartPayload remote_payload)
 {
   SilcSKEStatus status;
-  SilcSKEStartPayload *rp;
+  SilcSKEStartPayload rp;
   char *cp;
   int len;
 
@@ -1637,7 +1832,7 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
     KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
 
     /* Format the buffer used to compute the hash value */
-    buf = silc_buffer_alloc_size(ske->start_payload_copy->len +
+    buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
                                 ske->ke2_payload->pk_len +
                                 ske->ke1_payload->pk_len +
                                 e_len + f_len + KEY_len);
@@ -1648,10 +1843,9 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
     if (!ske->ke1_payload->pk_data) {
       ret =
        silc_buffer_format(buf,
-                          SILC_STR_UI_XNSTRING(ske->start_payload_copy->
-                                               data,
-                                               ske->start_payload_copy->
-                                               len),
+                          SILC_STR_UI_XNSTRING(
+                                  ske->start_payload_copy->data,
+                                  silc_buffer_len(ske->start_payload_copy)),
                           SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
                                                ske->ke2_payload->pk_len),
                           SILC_STR_UI_XNSTRING(e, e_len),
@@ -1661,10 +1855,9 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
     } else {
       ret =
        silc_buffer_format(buf,
-                          SILC_STR_UI_XNSTRING(ske->start_payload_copy->
-                                               data,
-                                               ske->start_payload_copy->
-                                               len),
+                          SILC_STR_UI_XNSTRING(
+                                  ske->start_payload_copy->data,
+                                  silc_buffer_len(ske->start_payload_copy)),
                           SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
                                                ske->ke2_payload->pk_len),
                           SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
@@ -1694,8 +1887,8 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
   } else {
     e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
 
-    buf = silc_buffer_alloc_size(ske->start_payload_copy->len +
-                                 ske->ke1_payload->pk_len + e_len);
+    buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
+                                ske->ke1_payload->pk_len + e_len);
     if (!buf)
       return SILC_SKE_STATUS_OUT_OF_MEMORY;
 
@@ -1703,7 +1896,7 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
     ret =
       silc_buffer_format(buf,
                         SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
-                                             ske->start_payload_copy->len),
+                                    silc_buffer_len(ske->start_payload_copy)),
                         SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
                                              ske->ke1_payload->pk_len),
                         SILC_STR_UI_XNSTRING(e, e_len),
@@ -1720,7 +1913,8 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
   }
 
   /* Make the hash */
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, return_hash);
+  silc_hash_make(ske->prop->hash, buf->data, silc_buffer_len(buf),
+                return_hash);
   *return_hash_len = silc_hash_len(ske->prop->hash);
 
   if (initiator == FALSE) {
@@ -1737,28 +1931,28 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
 /* Processes the provided key material `data' as the SILC protocol
    specification defines. */
 
-SilcSKEStatus
+SilcSKEKeyMaterial
 silc_ske_process_key_material_data(unsigned char *data,
                                   SilcUInt32 data_len,
                                   SilcUInt32 req_iv_len,
                                   SilcUInt32 req_enc_key_len,
                                   SilcUInt32 req_hmac_key_len,
-                                  SilcHash hash,
-                                  SilcSKEKeyMaterial *key)
+                                  SilcHash hash)
 {
   SilcBuffer buf;
   unsigned char hashd[SILC_HASH_MAXLEN];
   SilcUInt32 hash_len = req_hmac_key_len;
   SilcUInt32 enc_key_len = req_enc_key_len / 8;
+  SilcSKEKeyMaterial key;
 
   SILC_LOG_DEBUG(("Start"));
 
   if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
-    return SILC_SKE_STATUS_ERROR;
+    return NULL;
 
   buf = silc_buffer_alloc_size(1 + data_len);
   if (!buf)
-    return SILC_SKE_STATUS_OUT_OF_MEMORY;
+    return NULL;
   silc_buffer_format(buf,
                     SILC_STR_UI_CHAR(0),
                     SILC_STR_UI_XNSTRING(data, data_len),
@@ -1767,12 +1961,12 @@ silc_ske_process_key_material_data(unsigned char *data,
   /* Take IVs */
   memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 0;
-  silc_hash_make(hash, buf->data, buf->len, hashd);
+  silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
   key->send_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
   memcpy(key->send_iv, hashd, req_iv_len);
   memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 1;
-  silc_hash_make(hash, buf->data, buf->len, hashd);
+  silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
   key->receive_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
   memcpy(key->receive_iv, hashd, req_iv_len);
   key->iv_len = req_iv_len;
@@ -1789,22 +1983,22 @@ silc_ske_process_key_material_data(unsigned char *data,
 
     /* XXX */
     if (enc_key_len > (3 * hash_len))
-      return SILC_SKE_STATUS_ERROR;
+      return NULL;
 
     /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(hash, buf->data, buf->len, k1);
+    silc_hash_make(hash, buf->data, silc_buffer_len(buf), k1);
 
     /* Take second round */
     dist = silc_buffer_alloc_size(data_len + hash_len);
     if (!dist)
-      return SILC_SKE_STATUS_OUT_OF_MEMORY;
+      return NULL;
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(data, data_len),
                       SILC_STR_UI_XNSTRING(k1, hash_len),
                       SILC_STR_END);
     memset(k2, 0, sizeof(k2));
-    silc_hash_make(hash, dist->data, dist->len, k2);
+    silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
 
     /* Take third round */
     dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
@@ -1815,7 +2009,7 @@ silc_ske_process_key_material_data(unsigned char *data,
                       SILC_STR_END);
     silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(hash, dist->data, dist->len, k3);
+    silc_hash_make(hash, dist->data, silc_buffer_len(dist), k3);
 
     /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
@@ -1837,7 +2031,7 @@ silc_ske_process_key_material_data(unsigned char *data,
   } else {
     /* Take normal hash as key */
     memset(hashd, 0, sizeof(hashd));
-    silc_hash_make(hash, buf->data, buf->len, hashd);
+    silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
     key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
     memcpy(key->send_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
@@ -1852,22 +2046,22 @@ silc_ske_process_key_material_data(unsigned char *data,
 
     /* XXX */
     if (enc_key_len > (3 * hash_len))
-      return SILC_SKE_STATUS_ERROR;
+      return NULL;
 
     /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(hash, buf->data, buf->len, k1);
+    silc_hash_make(hash, buf->data, silc_buffer_len(buf), k1);
 
     /* Take second round */
     dist = silc_buffer_alloc_size(data_len + hash_len);
     if (!dist)
-      return SILC_SKE_STATUS_OUT_OF_MEMORY;
+      return NULL;
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(data, data_len),
                       SILC_STR_UI_XNSTRING(k1, hash_len),
                       SILC_STR_END);
     memset(k2, 0, sizeof(k2));
-    silc_hash_make(hash, dist->data, dist->len, k2);
+    silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
 
     /* Take third round */
     dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
@@ -1878,7 +2072,7 @@ silc_ske_process_key_material_data(unsigned char *data,
                       SILC_STR_END);
     silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(hash, dist->data, dist->len, k3);
+    silc_hash_make(hash, dist->data, silc_buffer_len(dist), k3);
 
     /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
@@ -1900,7 +2094,7 @@ silc_ske_process_key_material_data(unsigned char *data,
   } else {
     /* Take normal hash as key */
     memset(hashd, 0, sizeof(hashd));
-    silc_hash_make(hash, buf->data, buf->len, hashd);
+    silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
     key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
     memcpy(key->receive_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
@@ -1909,12 +2103,12 @@ silc_ske_process_key_material_data(unsigned char *data,
   /* Take HMAC keys */
   memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 4;
-  silc_hash_make(hash, buf->data, buf->len, hashd);
+  silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
   key->send_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
   memcpy(key->send_hmac_key, hashd, req_hmac_key_len);
   memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 5;
-  silc_hash_make(hash, buf->data, buf->len, hashd);
+  silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
   key->receive_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
   memcpy(key->receive_hmac_key, hashd, req_hmac_key_len);
   key->hmac_key_len = req_hmac_key_len;
@@ -1923,51 +2117,52 @@ silc_ske_process_key_material_data(unsigned char *data,
   silc_buffer_clear(buf);
   silc_buffer_free(buf);
 
-  return SILC_SKE_STATUS_OK;
+  return key;
 }
 
 /* Processes negotiated key material as protocol specifies. This returns
    the actual keys to be used in the SILC. */
 
-SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
-                                           SilcUInt32 req_iv_len,
-                                           SilcUInt32 req_enc_key_len,
-                                           SilcUInt32 req_hmac_key_len,
-                                           SilcSKEKeyMaterial *key)
+SilcSKEKeyMaterial
+silc_ske_process_key_material(SilcSKE ske,
+                             SilcUInt32 req_iv_len,
+                             SilcUInt32 req_enc_key_len,
+                             SilcUInt32 req_hmac_key_len)
 {
   SilcSKEStatus status;
   SilcBuffer buf;
   unsigned char *tmpbuf;
   SilcUInt32 klen;
+  SilcSKEKeyMaterial key;
 
   /* Encode KEY to binary data */
   tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
 
   buf = silc_buffer_alloc_size(klen + ske->hash_len);
   if (!buf)
-    return SILC_SKE_STATUS_OUT_OF_MEMORY;
+    return NULL;
   silc_buffer_format(buf,
                     SILC_STR_UI_XNSTRING(tmpbuf, klen),
                     SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
                     SILC_STR_END);
 
   /* Process the key material */
-  status = silc_ske_process_key_material_data(buf->data, buf->len,
-                                             req_iv_len, req_enc_key_len,
-                                             req_hmac_key_len,
-                                             ske->prop->hash, key);
+  key = silc_ske_process_key_material_data(buf->data, silc_buffer_len(buf),
+                                          req_iv_len, req_enc_key_len,
+                                          req_hmac_key_len,
+                                          ske->prop->hash);
 
   memset(tmpbuf, 0, klen);
   silc_free(tmpbuf);
   silc_buffer_clear(buf);
   silc_buffer_free(buf);
 
-  return status;
+  return key;
 }
 
 /* Free key material structure */
 
-void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
+void silc_ske_free_key_material(SilcSKEKeyMaterial key)
 {
   if (!key)
     return;
index 554da9e71bc7cf66cd2dd688ada5a8b779c48cd9..389b5532237705efcc76c354205537692a52b5fc 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcske.h 
+  silcske.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 - 2002 Pekka Riikonen
+  Copyright (C) 2000 - 2005 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
@@ -24,7 +24,7 @@
  *
  * DESCRIPTION
  *
- * Implementation of the SILC Key Exchange Protocol (SKE). The SKE protocol
+ * The SILC Key Exchange (SKE) protocol interface. The SKE protocol
  * is used to negotiate secret key material between two parties, to be used
  * as session key or some other key. For example, when client connects to
  * server SKE is performed to exchange public keys, and to generate the key
  * two create secret key material for securing for example file transfer
  * stream.
  *
- * SKE is based on Diffie-Hellman, and it derives its functionality from
- * SSH2 Key Exchange protocol, OAKLEY Key Determination protocol and
- * Station-To-Station (STS) protocols.
- *
  * This SKE implementation provides easy interface for application
- * that wants to use SKE. In fact, the interface is designed to be 
+ * that wants to use SKE. In fact, the interface is designed to be
  * application independent, and does not expect that the application using
  * SKE would actually relate in any way to SILC. Hence, the interface
  * can be used in any kind of application needing to perform key exchange
  * protocol with two parties. The network connection is also handled
- * outside the SKE interface. For the interface application must provide
- * a packet sending function which SKE library can call when it wants
- * to send packet to the remote host. The actual network connection
- * therefore is handled in the application and not by the SKE library.
+ * outside the SKE interface.
  *
  * The protocol has initiator and responder. The initiator is the one
  * that starts the protocol, and the responder is the one that receives
 
 #include "silcske_status.h"
 
-/****s* silcske/SilcSKEAPI/SilcSKE
+/* Length of cookie in Start Payload */
+#define SILC_SKE_COOKIE_LEN 16
+
+/* Forward declarations */
+typedef struct SilcSKECallbacksStruct *SilcSKECallbacks;
+typedef struct SilcSKEStruct *SilcSKE;
+
+#include "silcske_groups.h"
+#include "silcske_payload.h"
+
+/****d* silcske/SilcSKEAPI/SilcSKESecurityPropertyFlag
  *
  * NAME
  *
- *    typedef struct SilcSKEStruct *SilcSKE;
+ *    typedef enum { ... } SilcSKESecurityPropertyFlag
  *
  * DESCRIPTION
  *
- *    This context is forward declaration for the SilcSKEStruct.
- *    This is allocated by the silc_ske_alloc and freed by the
- *    silc_ske_free function. This context represents the SKE session.
+ *    SKE security property flags as defined by the SK protocol.
  *
- ***/
-typedef struct SilcSKEStruct *SilcSKE;
+ * SOURCE
+ */
+typedef enum {
+  SILC_SKE_SP_FLAG_NONE         = 0x00,         /* No flags */
+  SILC_SKE_SP_FLAG_IV_INCLUDED  = 0x01,         /* IV included in packet */
+  SILC_SKE_SP_FLAG_PFS          = 0x02,         /* Perfect Forward Secrecy */
+  SILC_SKE_SP_FLAG_MUTUAL       = 0x04,         /* Mutual authentication */
+} SilcSKESecurityPropertyFlag;
+/***/
 
 /****s* silcske/SilcSKEAPI/SilcSKESecurityProperties
  *
  * NAME
  *
- *    typedef struct SilcSKESecurityPropertiesStruct
- *                                 *SilcSKESecurityProperties;
+ *    typedef struct { ... } *SilcSKESecurityProperties;
  *
  * DESCRIPTION
  *
- *    This context is forward declaration for the
- *    SilcSKESecurityPropertiesStruct structure. It is allocated by the
- *    library, and it represents the security properties selected during
- *    the SKE negotiation.
+ *    Security Properties negotiated between key exchange parties. This
+ *    structure is filled from the Key Exchange Start Payload which is used
+ *    to negotiate what security properties should be used in the
+ *    communication.
  *
- ***/
-typedef struct SilcSKESecurityPropertiesStruct *SilcSKESecurityProperties;
-
-/* Forward declaration for SKE callbacks structure, which is internal. */
-typedef struct SilcSKECallbacksStruct *SilcSKECallbacks;
+ * SOURCE
+ */
+typedef struct {
+  SilcSKESecurityPropertyFlag flags;    /* Flags */
+  SilcSKEDiffieHellmanGroup group;      /* Selected Diffie Hellman group */
+  SilcPKCS pkcs;                        /* Selected PKCS algorithm */
+  SilcCipher cipher;                    /* Selected cipher */
+  SilcHash hash;                        /* Selected hash algorithm */
+  SilcHmac hmac;                        /* Selected HMAC */
+} *SilcSKESecurityProperties;
+/***/
 
-/****d* silcske/SilcSKEAPI/SilcSKEPKType
+/****s* silcske/SilcSKEAPI/SilcSKEKeyMaterial
  *
  * NAME
  *
- *    typedef enum { ... } SilcSKEPKType;
+ *    typedef struct { ... } *SilcSKEKeyMaterial;
  *
  * DESCRIPTION
  *
- *    Public key and certificate types defined by the SKE protocol.
+ *    This is the key material structure, and is passed as argument by the
+ *    application to silc_ske_process_key_material* functions. It includes
+ *    the processed key material which can be used as SILC session keys.
  *
- * SOURCE
  */
-typedef enum {
-  SILC_SKE_PK_TYPE_SILC    = 1,        /* Mandatory type */
-  /* Optional types. These are not implemented currently */
-  SILC_SKE_PK_TYPE_SSH2    = 2,
-  SILC_SKE_PK_TYPE_X509V3  = 3,
-  SILC_SKE_PK_TYPE_OPENPGP = 4,
-  SILC_SKE_PK_TYPE_SPKI    = 5
-} SilcSKEPKType;
+typedef struct {
+  unsigned char *send_iv;
+  unsigned char *receive_iv;
+  SilcUInt32 iv_len;
+  unsigned char *send_enc_key;
+  unsigned char *receive_enc_key;
+  SilcUInt32 enc_key_len;
+  unsigned char *send_hmac_key;
+  unsigned char *receive_hmac_key;
+  SilcUInt32 hmac_key_len;
+} *SilcSKEKeyMaterial;
 /***/
 
-/****f* silcske/SilcSKEAPI/SilcSKESendPacketCb
+/****s* silcske/SilcSKEAPI/SilcSKERekeyMaterial
  *
- * SYNOPSIS
+ * NAME
  *
- *    typedef void (*SilcSKESendPacketCb)(SilcSKE ske, SilcBuffer packet,
- *                                        SilcPacketType type, void *context);
+ *    typedef struct { ... } *SilcSKERekeyMaterial;
  *
  * DESCRIPTION
  *
- *    Packet sending callback. Caller of the SKE routines must provide
- *    a routine to send packets to negotiation parties. See the
- *    silc_ske_set_callbacks for more information.
+ *    This context is returned after key exchange protocol to application
+ *    in the completion callback.  Application may save it and use it later
+ *    to perform the rekey with silc_ske_rekey_initiator_start and/or
+ *    silc_ske_rekey_responder_start functions.  If application does not
+ *    need the context, it may free it with silc_free function.
  *
- ***/
-typedef void (*SilcSKESendPacketCb)(SilcSKE ske, SilcBuffer packet,
-                                   SilcPacketType type, void *context);
+ *    Application may save application specific data to `user_context'.
+ *
+ */
+typedef struct {
+  void *user_context;                /* Application specific data */
+  unsigned char *send_enc_key;
+  unsigned int enc_key_len  : 23;
+  unsigned int ske_group    : 8;
+  unsigned int pfs          : 1;
+} *SilcSKERekeyMaterial;
+/***/
 
-/****f* silcske/SilcSKEAPI/SilcSKECb
+/****d* silcske/SilcSKEAPI/SilcSKEPKType
  *
- * SYNOPSIS
+ * NAME
  *
- *    typedef void (*SilcSKECb)(SilcSKE ske, void *context);
+ *    typedef enum { ... } SilcSKEPKType;
  *
  * DESCRIPTION
  *
- *    Generic SKE callback function. This is called in various SKE
- *    routines. The SilcSKE object sent as argument provides all the data
- *    callers routine might need (payloads etc). This is usually called
- *    to indicate that the application may continue the execution of the
- *    SKE protocol. The application should check the ske->status in this
- *    callback function. This callback is also called when Start Payload
- *    is processed. See silc_ske_set_callbacks function for more information.
+ *    Public key and certificate types defined by the SKE protocol.
  *
- ***/
-typedef void (*SilcSKECb)(SilcSKE ske, void *context);
+ * SOURCE
+ */
+typedef enum {
+  SILC_SKE_PK_TYPE_SILC    = 1,        /* SILC Public Key, mandatory */
+  SILC_SKE_PK_TYPE_SSH2    = 2,        /* SSH2 Public key, not supported */
+  SILC_SKE_PK_TYPE_X509V3  = 3,        /* X.509v3 certificate, not supported */
+  SILC_SKE_PK_TYPE_OPENPGP = 4,        /* OpenPGP certificate, not supported */
+  SILC_SKE_PK_TYPE_SPKI    = 5 /* SPKI certificate, not supported */
+} SilcSKEPKType;
+/***/
 
 /****f* silcske/SilcSKEAPI/SilcSKEVerifyCbCompletion
  *
@@ -182,7 +210,7 @@ typedef void (*SilcSKEVerifyCbCompletion)(SilcSKE ske,
  * SYNOPSIS
  *
  *    typedef void (*SilcSKEVerifyCb)(SilcSKE ske,
- *                                    unsigned char *pk_data,
+ *                                    const unsigned char *pk_data,
  *                                    SilcUInt32 pk_len,
  *                                    SilcSKEPKType pk_type,
  *                                    void *context,
@@ -192,16 +220,15 @@ typedef void (*SilcSKEVerifyCbCompletion)(SilcSKE ske,
  * DESCRIPTION
  *
  *    Callback function used to verify the received public key or certificate.
- *    The verification process is most likely asynchronous. That's why the
+ *    The verification process is most likely asynchronous.  That's why the
  *    application must call the `completion' callback when the verification
- *    process has been completed. The library then calls the user callback
- *    (SilcSKECb), if it was provided for the function that takes this callback
- *    function as argument, to indicate that the SKE protocol may continue.
- *    See silc_ske_set_callbacks for more information.
+ *    process has been completed.  The `context' is the context given as
+ *    arugment to silc_ske_set_callbacks.  See silc_ske_set_callbacks for
+ *    more information.
  *
  ***/
 typedef void (*SilcSKEVerifyCb)(SilcSKE ske,
-                               unsigned char *pk_data,
+                               const unsigned char *pk_data,
                                SilcUInt32 pk_len,
                                SilcSKEPKType pk_type,
                                void *context,
@@ -212,98 +239,39 @@ typedef void (*SilcSKEVerifyCb)(SilcSKE ske,
  *
  * SYNOPSIS
  *
- *    typedef SilcSKEStatus (*SilcSKECheckVersion)(SilcSKE ske,
- *                                                 unsigned char *version,
- *                                                 SilcUInt32 len, void *context);
+ *    typedef SilcSKEStatus
+ *    (*SilcSKECheckVersionCb)(SilcSKE ske,
+ *                             const unsigned char *version,
+ *                             SilcUInt32 len, void *context);
  *
  * DESCRIPTION
  *
  *    Callback function used to check the version of the remote SKE server.
  *    The SKE library will call this function so that the appliation may
- *    check its version against the remote host's version. This returns
+ *    check its version against the remote host's version.  This returns
  *    SILC_SKE_STATUS_OK if the version string is Ok, and returns
  *    SILC_SKE_STATUS_BAD_VERSION if the version was not acceptable.
  *
  ***/
-typedef SilcSKEStatus (*SilcSKECheckVersion)(SilcSKE ske,
-                                            unsigned char *version,
-                                            SilcUInt32 len, void *context);
+typedef SilcSKEStatus (*SilcSKECheckVersionCb)(SilcSKE ske,
+                                              const unsigned char *version,
+                                              SilcUInt32 len, void *context);
 
-/****s* silcske/SilcSKEAPI/SilcSKEKeyMaterial
+/****f* silcske/SilcSKEAPI/SilcSKECompletionCb
  *
- * NAME
+ * SYNOPSIS
  *
- *    typedef struct { ... } SilcSKEKeyMaterial;
  *
  * DESCRIPTION
  *
- *    This is the key material structure, and is passed as argument by the
- *    application to silc_ske_process_key_material* functions. It includes
- *    the processed key material which can be used as SILC session keys.
  *
  ***/
-typedef struct {
-  unsigned char *send_iv;
-  unsigned char *receive_iv;
-  SilcUInt32 iv_len;
-  unsigned char *send_enc_key;
-  unsigned char *receive_enc_key;
-  SilcUInt32 enc_key_len;
-  unsigned char *send_hmac_key;
-  unsigned char *receive_hmac_key;
-  SilcUInt32 hmac_key_len;
-} SilcSKEKeyMaterial;
-
-/* Length of cookie in Start Payload */
-#define SILC_SKE_COOKIE_LEN 16
-
-#include "silcske_groups.h"
-#include "silcske_payload.h"
-
-/****d* silcske/SilcSKEAPI/SilcSKESecurityPropertyFlag
- *
- * NAME
- *
- *    typedef enum { ... } SilcSKESecurityPropertyFlag
- *
- * DESCRIPTION
- *
- *    SKE security property flags as defined by the SK protocol.
- *
- * SOURCE
- */
-typedef enum {
-  SILC_SKE_SP_FLAG_NONE         = 0x00,         /* No flags */
-  SILC_SKE_SP_FLAG_IV_INCLUDED  = 0x01,         /* IV included in ciphertexts */
-  SILC_SKE_SP_FLAG_PFS          = 0x02,         /* Perfect Forward Secrecy */
-  SILC_SKE_SP_FLAG_MUTUAL       = 0x04,         /* Mutual authentication */
-} SilcSKESecurityPropertyFlag;
-/***/
-
-/****s* silcske/SilcSKEAPI/SilcSKESecurityPropertiesStruct
- *
- * NAME
- *
- *    struct SilcSKESecurityPropertiesStruct { ... };
- *
- * DESCRIPTION
- *
- *    Security Properties negotiated between key exchange parties. This
- *    structure is filled from the Key Exchange Start Payload which is used
- *    to negotiate what security properties should be used in the
- *    communication.
- *
- * SOURCE
- */
-struct SilcSKESecurityPropertiesStruct {
-  SilcSKESecurityPropertyFlag flags;    /* Flags */
-  SilcSKEDiffieHellmanGroup group;      /* Selected Diffie Hellman group */
-  SilcPKCS pkcs;                        /* Selected PKCS algorithm */
-  SilcCipher cipher;                    /* Selected cipher */
-  SilcHash hash;                        /* Selected hash algorithm */
-  SilcHmac hmac;                        /* Selected HMAC */
-};
-/***/
+typedef void (*SilcSKECompletionCb)(SilcSKE ske,
+                                   SilcSKEStatus status,
+                                   SilcSKESecurityProperties prop,
+                                   SilcSKEKeyMaterial keymat,
+                                   SilcSKERekeyMaterial rekey,
+                                   void *context);
 
 /****s* silcske/SilcSKEAPI/SilcSKEStruct
  *
@@ -315,7 +283,7 @@ struct SilcSKESecurityPropertiesStruct {
  *
  *    This structure is the SKE session context, and has a type definition
  *    to SilcSKE. The structure includes the network connection socket,
- *    securit properties collected during the SKE negotiation, payloads
+ *    security properties collected during the SKE negotiation, payloads
  *    sent and received during the negotiation, and the actual raw key
  *    material too. The application usually does not need to reference
  *    to the inside of this structure.  However, checking the current
@@ -324,19 +292,17 @@ struct SilcSKESecurityPropertiesStruct {
  * SOURCE
  */
 struct SilcSKEStruct {
-  /* The connection object. This is initialized by the caller. */
-#if 0
-  SilcSocketConnection sock;
-#endif
+  /* The network socket connection stream.  Set by application. */
+  SilcPacketStream stream;
 
-  /* Security properties negotiated */
+  /* Negotiated Security properties.  May be NULL in case of error. */
   SilcSKESecurityProperties prop;
 
   /* Key Exchange payloads filled during key negotiation with
      remote data. Responder may save local data here as well. */
-  SilcSKEStartPayload *start_payload;
-  SilcSKEKEPayload *ke1_payload;
-  SilcSKEKEPayload *ke2_payload;
+  SilcSKEStartPayload start_payload;
+  SilcSKEKEPayload ke1_payload;
+  SilcSKEKEPayload ke2_payload;
   unsigned char *remote_version;
 
   /* Temporary copy of the KE Start Payload used in the
@@ -346,7 +312,7 @@ struct SilcSKEStruct {
   /* Random number x, 1 < x < q. This is the secret exponent
      used in Diffie Hellman computations. */
   SilcMPInt *x;
-  
+
   /* The secret shared key */
   SilcMPInt *KEY;
 
@@ -374,6 +340,19 @@ struct SilcSKEStruct {
 
   /* Backwards support version indicator */
   SilcUInt32 backward_version;
+
+  char *version;
+  SilcPublicKey public_key;
+  SilcPrivateKey private_key;
+  SilcSKEPKType pk_type;
+  SilcBuffer packet_buf;
+  SilcSKESecurityPropertyFlag flags;
+  SilcSKEKeyMaterial keymat;
+  SilcSKERekeyMaterial rekey;
+  SilcSchedule schedule;
+  SilcFSMStruct fsm;
+  SilcAsyncOperationStruct op;
+  bool aborted;
 };
 /***/
 
@@ -383,21 +362,33 @@ struct SilcSKEStruct {
  *
  * SYNOPSIS
  *
- *    SilcSKE silc_ske_alloc(SilcRng rng, void *context);
+ *    SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
+ *                           void *context);
  *
  * DESCRIPTION
  *
  *    Allocates the SKE session context and returns it.  The `rng' is
  *    the random number generator the SKE is going to use when it needs
  *    random number generation during the SKE session.  The `context' is
- *    user context that the libary will not touch.  The application can
- *    access that context with the ske->user_context if needed.  The
+ *    user context that the libary will not touch.  Application can get the
+ *    context by calling the fuction silc_ske_get_context function.  The
  *    application is responsible of freeing the `context'.  After the
  *    SKE session context is allocated application must call the
  *    silc_ske_set_callbacks.
  *
+ * EXMPALE
+ *
+ *    // Initiator example
+ *    ske = silc_ske_alloc(rng, scheduler, app);
+ *    silc_ske_set_callbacks(ske, verify_public_key, check_version, app);
+ *    start_payload =
+ *      silc_ske_assemble_security_properties(ske, SILC_SKE_SP_FLAG_PFS |
+ *                                            SILC_SKE_SP_FLAG_MUTUAL,
+ *                                            version);
+ *    silc_ske_initiator_start(ske);
+ *
  ***/
-SilcSKE silc_ske_alloc(SilcRng rng, void *context);
+SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, void *context);
 
 /****f* silcske/SilcSKEAPI/silc_ske_free
  *
@@ -412,501 +403,186 @@ SilcSKE silc_ske_alloc(SilcRng rng, void *context);
  ***/
 void silc_ske_free(SilcSKE ske);
 
+/****f* silcske/SilcSKEAPI/silc_ske_get_context
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_ske_get_context(SilcSKE ske);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the context that was given as argument to silc_ske_alloc.
+ *
+ ***/
+void *silc_ske_get_context(SilcSKE ske);
+
 /****f* silcske/SilcSKEAPI/silc_ske_set_callbacks
  *
  * SYNOPSIS
  *
  *    void silc_ske_set_callbacks(SilcSKE ske,
- *                                SilcSKESendPacketCb send_packet,
- *                                SilcSKECb payload_receive,
  *                                SilcSKEVerifyCb verify_key,
- *                                SilcSKECb proto_continue,
  *                                SilcSKECheckVersion check_version,
+ *                                SilcSKECompletion completed,
  *                                void *context);
  *
  * DESCRIPTION
  *
  *    Sets the callback functions for the SKE session.
  *
- *    The `send_packet' callback is a function that sends the packet to
- *    network. The SKE library will call it at any time packet needs to
- *    be sent to the remote host.
- *
- *    The `payload_receive' callback is called when the remote host's Key
- *    Exchange Start Payload has been processed.  The payload is saved
- *    to ske->start_payload if the application would need it.  The application
- *    must also provide the payload to the next state of the SKE.
- *
  *    The `verify_key' callback is called to verify the received public key
  *    or certificate.  The verification process is most likely asynchronous.
  *    That is why the application must call the completion callback when the
- *    verification process has been completed. The library then calls the user
- *    callback (`proto_continue'), if it is provided to indicate that the SKE
- *    protocol may continue. If this SKE session context is used to perform
- *    rekey, this callback usually is not provided as argument since sending
- *    public key in rekey is not mandatory. Setting this callback implies
- *    that remote end MUST send its public key, and this could cause
- *    problems when performing rekey. When doing normal SKE session this
- *    callback should be set.
- *
- *    The `proto_continue' callback is called to indicate that it is
- *    safe to continue the execution of the SKE protocol after executing
- *    an asynchronous operation, such as calling the `verify_key' callback
- *    function, which is asynchronous. The application should check the
- *    ske->status in this function to check whether it is Ok to continue
- *    the execution of the protocol.
+ *    verification process has been completed.  If this SKE session context
+ *    is used to perform  rekey, this callback usually is not provided as
+ *    argument since sending public key in rekey is not mandatory.  Setting
+ *    this callback implies that remote end MUST send its public key.
  *
  *    The `check_version' callback is called to verify the remote host's
- *    version. The application may check its own version against the remote
+ *    version.  The application may check its own version against the remote
  *    host's version and determine whether supporting the remote host
  *    is possible.
  *
+ *    The `completed' callback will be called once the protocol has completed,
+ *    either successfully or with an error.  The status of the protocol is
+ *    delivered to application with the callback.
+ *
  *    The `context' is passed as argument to all of the above callback
  *    functions.
  *
  ***/
 void silc_ske_set_callbacks(SilcSKE ske,
-                           SilcSKESendPacketCb send_packet,
-                           SilcSKECb payload_receive,
                            SilcSKEVerifyCb verify_key,
-                           SilcSKECb proto_continue,
-                           SilcSKECheckVersion check_version,
+                           SilcSKECheckVersionCb check_version,
+                           SilcSKECompletionCb completed,
                            void *context);
 
 /****f* silcske/SilcSKEAPI/silc_ske_initiator_start
  *
  * SYNOPSIS
  *
- *    SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
- *                                           SilcSocketConnection sock,
- *                                           SilcSKEStartPayload
- *                                              *start_payload);
+ *    SilcAsyncOperation
+ *    silc_ske_initiator_start(SilcSKE ske,
+ *                             SilcPacketStream stream,
+ *                             SilcSKEStartPayload start_payload);
  *
  * DESCRIPTION
  *
- *    Starts the SILC Key Exchange protocol for initiator. The connection
- *    to the responder end must be established before calling this function
- *    and the connecting socket must be sent as argument. This function
- *    creates the Key Exchange Start Payload which includes all our
- *    configured security properties. This payload is then sent to the
- *    responder end for further processing. This payload must be sent as
- *    argument to the function, however, it must not be encoded
- *    already, it is done by this function. The caller must not free
- *    the `start_payload' since the SKE library will save it.
+ *    Starts the SILC Key Exchange protocol as initiator.  The completion
+ *    callback that was set in silc_ske_set_callbacks will be called once
+ *    the protocol has completed.
  *
- *    Before calling this function application calls the
- *    silc_ske_assemble_security_properties which returns the `start_payload'
- *    which application must provide for this function.
+ *    The `stream' is the network connection to the remote host.  Note that
+ *    SKE library will take over the packet stream `stream' while the
+ *    protocol is in process.  The application will not receive any packets
+ *    for `stream' after this function is called.  The `stream' is turned
+ *    over to application once the completion callback is called.
  *
- *    After calling this function the application must wait for reply
- *    from the responder.
+ *    The `start_payload' includes all configured security properties that
+ *    will be sent to the responder.  The `start_payload' must be provided.
+ *    It can be created by calling silc_ske_assemble_security_properties
+ *    function.  The caller must not free the payload once it has been
+ *    given as argument to this function.
  *
- ***/
-SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
-#if 0
-                                      SilcSocketConnection sock,
-#endif
-                                      SilcSKEStartPayload *start_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_initiator_phase_1
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
- *                                             SilcBuffer start_payload);
- *
- * DESCRIPTION
- *
- *    Function called after ske_initiator_start fuction. This receives
- *    the responder's Key Exchange Start payload which includes the
- *    security properties selected by the responder from our payload
- *    sent in the silc_ske_initiator_start function. The `start_payload'
- *    is the received payload and the application must send it as argument.
- *
- *    After calling this function the application must call immediately,
- *    or with short timeout, the silc_ske_initiator_phase_2 function.
+ *    This function returns SilcAsyncOperation operation context which can
+ *    be used to control the protocol from the application.  Application may
+ *    for example safely abort the protocol at any point, if needed.  Returns
+ *    NULL on error.
  *
  ***/
-SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
-                                        SilcBuffer start_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_initiator_phase_2
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
- *                                             SilcPublicKey public_key,
- *                                             SilcPrivateKey private_key,
- *                                             SilcSKEPKType pk_type)
- *
- * DESCRIPTION
- *
- *    This function continues the SKE session after the initiator has
- *    called the silc_ske_initiator_phase_1.  After that function returns
- *    the application should call immediately, or with short timeout, this
- *    function which will continue with the session, and sends next phase
- *    packet to the responder.  The caller must provide the caller's
- *    public key and private key as argument, since the public key is
- *    sent to the responder, and the private key is be used to generate
- *    digital signature.
- *
- *    After this function the application must wait for reply from the
- *    responder.
- *
- ***/
-SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
-                                        SilcPublicKey public_key,
-                                        SilcPrivateKey private_key,
-                                        SilcSKEPKType pk_type);
-
-/****f* silcske/SilcSKEAPI/silc_ske_initiator_finish
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
- *                                            SilcBuffer ke_payload);
- *
- * DESCRIPTION
- *
- *    Receives the reply from the responder and processes it.  The
- *    `ke_payload' is the reply and application must provide it as argument.
- *    This function will verify the responder's public key, by calling
- *    the `verify_key' callback that was set with silc_ske_set_callbacks
- *    function.
- *
- *    If this function returns error, no callbacks will be called. If
- *    this function needs to verify remote end's public key, this will
- *    return SILC_SKE_STATUS_PENDING, which indicates application that
- *    SKE is performing asynchronous operation and is in pending status.
- *    When in this status application must not continue with calling
- *    any other SKE routine. The asynchronous operation is the `verify_key'
- *    callback, which application completes by calling its completion
- *    callback. After completion the SKE libary will call the 
- *    `proto_continue' callback, to indicate application that pending
- *    status is over and it is safe to continue the execution of SKE,
- *    which application does by calling the silc_ske_end function.
- *
- *    If this function returns SILC_SKE_STATUS_OK, it will not call the
- *    `verify_key' callback, however, it will or has already called the
- *    `proto_continue' callback.
- *
- *    Application must not continue execution of the SKE before library
- *    has called the `proto_continue' callback.  After it is called
- *    the application finishes SKE session by calling silc_ske_end
- *    function.
- *
- ***/
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
-                                       SilcBuffer ke_payload);
+SilcAsyncOperation
+silc_ske_initiator_start(SilcSKE ske,
+                        SilcPacketStream stream,
+                        SilcSKEStartPayload start_payload);
 
 /****f* silcske/SilcSKEAPI/silc_ske_responder_start
  *
  * SYNOPSIS
  *
- *    SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
- *                                           SilcSocketConnection sock,
- *                                           const char *version,
- *                                           SilcBuffer start_payload,
- *                                           SilcSKESecurityPropertyFlag 
- *                                                               flags);
- *
- * DESCRIPTION
- *
- *    Starts Key Exchange protocol for responder. The application has
- *    received initiator's first packet from network and it must provide
- *    it as `start_payload' argument to this function. The function 
- *    processes the packet and makes security property selection from
- *    the initiator's proposal. The `version' is the responder's version
- *    that will be sent in reply to the initiator. The `flags' indicates
- *    SilcSKESecurityPropertyFlag flags that responder enforces for the
- *    initiator. Responder may, for example, enforce that the PFS
- *    will be performed in rekey. This example can be done by providing
- *    SILC_SKE_SP_FLAG_PFS as `flags'. The `flags' is a bit mask of
- *    enforced flags.
- *
- *    After this function the responder calls immediately, or with short
- *    timeout the silc_ske_responder_phase_1 function.
- *
- ***/
-SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
-#if 0
-                                      SilcSocketConnection sock,
-#endif
-                                      const char *version,
-                                      SilcBuffer start_payload,
-                                      SilcSKESecurityPropertyFlag flags);
-
-/****f* silcske/SilcSKEAPI/silc_ske_responder_phase_1
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske);
- *
- * DESCRIPTION
- *
- *    This function is called after the silc_ske_responder_start, and
- *    is used to send our reply to the initiator.  This function is
- *    called either immediately, or with short timeout, after the
- *    silc_ske_responder_start function returned.
- *
- *    After this function the responder must wait for reply from the
- *    initiator.
- *
- ***/
-SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske);
-
-/****f* silcske/SilcSKEAPI/silc_ske_responder_phase_2
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
- *                                             SilcBuffer ke_payload);
- *
- * DESCRIPTION
- *
- *    Receives the reply from the initiator and procedses it.  The
- *    `ke_payload' is the reply and application must provide it as argument.
- *    This function will verify the remote host's public key, by calling
- *    the `verify_key' callback that was set with silc_ske_set_callbacks
- *    function.
- *
- *    If this function returns error, no callbacks will be called. If
- *    this function needs to verify remote end's public key, this will
- *    return SILC_SKE_STATUS_PENDING, which indicates application that
- *    SKE is performing asynchronous operation and is in pending status.
- *    When in this status application must not continue with calling
- *    any other SKE routine. The asynchronous operation is the `verify_key'
- *    callback, which application completes by calling its completion
- *    callback. After completion the SKE libary will call the
- *    `proto_continue' callback, to indicate application that pending
- *    status is over and it is safe to continue the execution of SKE,
- *    which application does by calling the silc_ske_responder_finish
- *    function.
- *
- *    If this function returns SILC_SKE_STATUS_OK, it will not call the
- *    `verify_key' callback, however, it will or has already called the
- *    `proto_continue' callback.
- *
- *    Application must not continue execution of the SKE before library
- *    has called the `proto_continue' callback.  After it is called
- *    the application calls the silc_ske_responder_finish function.
- *
- ***/
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
-                                        SilcBuffer ke_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_responder_finish
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
- *                                            SilcPublicKey public_key,
- *                                            SilcPrivateKey private_key,
- *                                            SilcSKEPKType pk_type);
- *
- * DESCRIPTION
- *
- *    This function finishes the responder's SKE session, and this function
- *    is called either immediately, or with short timeout, after the
- *    silc_ske_responder_phase_2 returned. This will send our reply to
- *    the initiator.  The caller must provide the caller's public key and
- *    private key as argument, since the public key is sent to the responder,
- *    and the private key is be used to generate digital signature.
- *
- *    After this function the application must wait for the end indication
- *    from the intiator, and when it is received the silc_ske_end is called.
- *
- ***/
-SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
-                                       SilcPublicKey public_key,
-                                       SilcPrivateKey private_key,
-                                       SilcSKEPKType pk_type);
-
-/****f* silcske/SilcSKEAPI/silc_ske_end
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus silc_ske_end(SilcSKE ske);
+ *    SilcAsyncOperation
+ *    silc_ske_responder_start(SilcSKE ske,
+ *                             SilcPacketStream stream,
+ *                             const char *version,
+ *                             SilcBuffer start_payload,
+ *                             SilcSKESecurityPropertyFlag flags);
  *
  * DESCRIPTION
  *
- *    The Key Exchange protocol is ended by calling this function. This
- *    must not be called until the keys are processed by calling the
- *    silc_ske_process_key_material function. The protocol prohibits
- *    calling this function before key material is processed properly.
+ *    Starts SILC Key Exchange protocol as responder.  The completion
+ *    callback that was set in silc_ske_set_callbacks will be called once
+ *    the protocol has completed.
  *
- *    This function is for both initiator and responder. After calling
- *    this function initiator must wait for end indication from the
- *    responder. After that the silc_ske_free may be called. The responder
- *    calls this function after it has received the intiator's end
- *    indication.
+ *    The `stream' is the network connection to the remote host.  Note that
+ *    SKE library will take over the packet stream `stream' while the
+ *    protocol is in process.  The application will not receive any packets
+ *    for `stream' after this function is called.  The `stream' is turned
+ *    over to application once the completion callback is called.
  *
- * NOTES
+ *    The application has received initiator's first packet from network
+ *    and it must provide it as `start_payload' argument to this function.
+ *    The function processes the packet and makes security property selection
+ *    from the initiator's proposal.  The `version' is the responder's version
+ *    that will be sent in reply to the initiator.  The `flags' indicates
+ *    SilcSKESecurityPropertyFlag flags that responder supports and enforces
+ *    for the initiator.  Responder may, for example, enforce that the PFS
+ *    will be performed in rekey.
  *
- *    Initiator must not start using the negotiated key material before
- *    calling this function or before remote end has sent its end
- *    indication. Only after that the key material may be taken in use.
+ *    This function returns SilcAsyncOperation operation context which can
+ *    be used to control the protocol from the application.  Application may
+ *    for example safely abort the protocol at any point, if needed.  Returns
+ *    NULL on error.
  *
  ***/
-SilcSKEStatus silc_ske_end(SilcSKE ske);
-
-/****f* silcske/SilcSKEAPI/silc_ske_abort
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status);
- *
- * DESCRIPTION
- *
- *    Aborts the Key Exchange protocol. This is called if error occurs
- *    while performing the protocol. The status argument is the error
- *    status and it is sent to the remote end.
- *
- ***/
-SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status);
+SilcAsyncOperation
+silc_ske_responder_start(SilcSKE ske,
+                        SilcPacketStream stream,
+                        const char *version,
+                        SilcBuffer start_payload,
+                        SilcSKESecurityPropertyFlag flags);
+
+SilcAsyncOperation
+silc_ske_rekey_initiator_start(SilcSKE ske,
+                              SilcPacketStream stream,
+                              SilcSKERekeyMaterial rekey);
+
+SilcAsyncOperation
+silc_ske_rekey_responder_start(SilcSKE ske,
+                              SilcPacketStream stream,
+                              SilcBuffer ke_payload,
+                              SilcSKERekeyMaterial rekey);
 
 /****f* silcske/SilcSKEAPI/silc_ske_assemble_security_properties
  *
  * SYNOPSIS
  *
- *    SilcSKEStatus
+ *    SilcSKEStartPayload
  *    silc_ske_assemble_security_properties(SilcSKE ske,
  *                                          SilcSKESecurityPropertyFlag flags,
- *                                          const char *version,
- *                                          SilcSKEStartPayload
- *                                            **return_payload);
+ *                                          const char *version);
  *
  * DESCRIPTION
  *
  *    Assembles security properties to Key Exchange Start Payload to be
- *    sent to the remote end. This checks system wide (SILC system, that is)
- *    settings and chooses from those. However, if other properties
+ *    sent to the remote end.  This checks system wide (SILC system, that is)
+ *    settings and chooses from those.  However, if other properties
  *    should be used this function is easy to replace by another function,
  *    as, this function is called by the caller of the library and not
- *    by the SKE library itself. The assembled payload is returned into
- *    the `return_payload' pointer.
+ *    by the SKE library itself.  Returns NULL on error.
  *
  ***/
-SilcSKEStatus 
+SilcSKEStartPayload
 silc_ske_assemble_security_properties(SilcSKE ske,
                                      SilcSKESecurityPropertyFlag flags,
-                                     const char *version,
-                                     SilcSKEStartPayload **return_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_select_security_properties
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus 
- *    silc_ske_select_security_properties(SilcSKE ske,
- *                                        const char *version,
- *                                        SilcSKEStartPayload *payload,
- *                                        SilcSKEStartPayload *remote_payload);
- *
- * DESCRIPTION
- *
- *    Parses the Key Exchange Start Payload indicated by `remote_payload',
- *    and selects the security properties properties from it, and puts the
- *    selection into the `payload'. This always attempts to select the
- *    best security properties from the payload, and it always selects
- *    one of each kind of security property, as this is dictated by the
- *    protocol. The `version' is our version, that we will put to the
- *    `payload', since the `payload' is usually sent to the remote end.
- *    the `check_version' callback will be called in this function so
- *    that application can do version check with the remote end.
- *
- ***/
-SilcSKEStatus
-silc_ske_select_security_properties(SilcSKE ske,
-                                   const char *version,
-                                   SilcSKEStartPayload *payload,
-                                   SilcSKEStartPayload *remote_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_process_key_material
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
- *                                                SilcUInt32 req_iv_len,
- *                                                SilcUInt32 req_enc_key_len,
- *                                                SilcUInt32 req_hmac_key_len,
- *                                                SilcSKEKeyMaterial *key);
- *
- * DESCRIPTION
- *
- *    This function is used by the application to process the key material
- *    negotiated with the SKE session, to actually produce the keys that
- *    is to be used in SILC protocol. The key processing is defined by the
- *    protocol. The `req_iv_len', `req_enc_key_len' and `req_hmac_key_len'
- *    are the request IV length, requested encryption/decrypt key length,
- *    and requested HMAC key length, respectively, and  they cannot be
- *    zero (0). They tell the function how long the keys should be, and
- *    it will produce the requested length keys for the application.
- *    The key material is returned in to the `key', which the caller must
- *    free.
- *
- ***/
-SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
-                                           SilcUInt32 req_iv_len,
-                                           SilcUInt32 req_enc_key_len,
-                                           SilcUInt32 req_hmac_key_len,
-                                           SilcSKEKeyMaterial *key);
-
-/****f* silcske/SilcSKEAPI/silc_ske_process_key_material_data
- *
- * SYNOPSIS
- *
- *    SilcSKEStatus
- *    silc_ske_process_key_material_data(unsigned char *data,
- *                                       SilcUInt32 data_len,
- *                                       SilcUInt32 req_iv_len,
- *                                       SilcUInt32 req_enc_key_len,
- *                                       SilcUInt32 req_hmac_key_len,
- *                                       SilcHash hash,
- *                                       SilcSKEKeyMaterial *key);
- *
- * DESCRIPTION
- *
- *    This function is equivalent to silc_ske_process_key_material, except
- *    that the caller provides the raw key material as argument, the `data'
- *    and `data_len'. This is special utility function provided for the
- *    application, if it needs to generate key material as the protocol
- *    defines for some other purpose than strictly SILC session key usage.
- *    Hence, this function can be used outside SKE protocol to just produce
- *    key material from some raw data. The `hash' is a hash algorithm that
- *    is used as part of key processing, and caller must provide it.
- *
- ***/
-SilcSKEStatus
-silc_ske_process_key_material_data(unsigned char *data,
-                                  SilcUInt32 data_len,
-                                  SilcUInt32 req_iv_len,
-                                  SilcUInt32 req_enc_key_len,
-                                  SilcUInt32 req_hmac_key_len,
-                                  SilcHash hash,
-                                  SilcSKEKeyMaterial *key);
-
-/****f* silcske/SilcSKEAPI/silc_ske_free_key_material
- *
- * SYNOPSIS
- *
- *    void silc_ske_free_key_material(SilcSKEKeyMaterial *key);
- *
- * DESCRIPTION
- *
- *    Frees the key material indicated by `key', and all data in it.
- *
- ***/
-void silc_ske_free_key_material(SilcSKEKeyMaterial *key);
+                                     const char *version);
 
 /****f* silcske/SilcSKEAPI/silc_ske_parse_version
  *
  * SYNOPSIS
  *
- *    bool silc_ske_parse_version(SilcSKE ske, 
+ *    bool silc_ske_parse_version(SilcSKE ske,
  *                                SilcUInt32 *protocol_version,
  *                                char **protocol_version_string,
- *                                SilcUInt32 *software_version, 
+ *                                SilcUInt32 *software_version,
  *                                char **software_version_string,
  *                                char **vendor_version);
  *
@@ -920,10 +596,10 @@ void silc_ske_free_key_material(SilcSKEKeyMaterial *key);
  *    string was successfully parsed.
  *
  ***/
-bool silc_ske_parse_version(SilcSKE ske, 
+bool silc_ske_parse_version(SilcSKE ske,
                            SilcUInt32 *protocol_version,
                            char **protocol_version_string,
-                           SilcUInt32 *software_version, 
+                           SilcUInt32 *software_version,
                            char **software_version_string,
                            char **vendor_version);
 
index 22755524470848d3ec545b03a4eb8601f9ec9ef2..849e12f69f34b2eeddd3de1fe2d7a7c3a72965b3 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcske_payload.h 
+  silcske_payload.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 - 2002 Pekka Riikonen
+  Copyright (C) 2000 - 2005 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
@@ -35,7 +35,7 @@
 /****s* silcske/SilcSKEPayloads/SilcSKEStartPayload
  *
  * NAME
- * 
+ *
  *    typedef struct SilcSKEStartPayloadStruct SilcSKEStartPayload;
  *
  * DESCRIPTION
  *    silc_ske_payload_start_free function.
  *
  ***/
-typedef struct SilcSKEStartPayloadStruct SilcSKEStartPayload;
+typedef struct SilcSKEStartPayloadStruct *SilcSKEStartPayload;
 
 /****s* silcske/SilcSKEPayloads/SilcSKEKEPayload
  *
  * NAME
- * 
+ *
  *    typedef struct SilcSKEKEPayloadStruct SilcSKEKEPayload;
  *
  * DESCRIPTION
  *
  *    This context is the actual Key Exchange Payload and is allocated
  *    by silc_ske_payload_ke_decode. It is freed by calling the
- *    silc_ske_payload_ke_free function. 
+ *    silc_ske_payload_ke_free function.
  *
  ***/
-typedef struct SilcSKEKEPayloadStruct SilcSKEKEPayload;
+typedef struct SilcSKEKEPayloadStruct *SilcSKEKEPayload;
 
 /* SILC Key Exchange Start Payload */
 struct SilcSKEStartPayloadStruct {
@@ -81,7 +81,7 @@ struct SilcSKEStartPayloadStruct {
 
   SilcUInt16 enc_alg_len;
   unsigned char *enc_alg_list;
-  
+
   SilcUInt16 hash_alg_len;
   unsigned char *hash_alg_list;
 
@@ -111,7 +111,7 @@ struct SilcSKEKEPayloadStruct {
  * SYNOPSIS
  *
  *    SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
- *                                                SilcSKEStartPayload *payload,
+ *                                                SilcSKEStartPayload payload,
  *                                                SilcBuffer *return_buffer);
  *
  * DESCRIPTION
@@ -122,17 +122,17 @@ struct SilcSKEKEPayloadStruct {
  *
  ***/
 SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
-                                           SilcSKEStartPayload *payload,
+                                           SilcSKEStartPayload payload,
                                            SilcBuffer *return_buffer);
 
 /****f* silcske/SilcSKEPayloads/silc_ske_payload_start_decode
  *
  * SYNOPSIS
  *
- *    SilcSKEStatus 
+ *    SilcSKEStatus
  *    silc_ske_payload_start_decode(SilcSKE ske,
  *                                  SilcBuffer buffer,
- *                                  SilcSKEStartPayload **return_payload);
+ *                                  SilcSKEStartPayload *return_payload);
  *
  * DESCRIPTION
  *
@@ -141,10 +141,10 @@ SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
  *    `return_payload' and the caller must free it.
  *
  ***/
-SilcSKEStatus 
+SilcSKEStatus
 silc_ske_payload_start_decode(SilcSKE ske,
                              SilcBuffer buffer,
-                             SilcSKEStartPayload **return_payload);
+                             SilcSKEStartPayload *return_payload);
 
 /****f* silcske/SilcSKEPayloads/silc_ske_payload_start_free
  *
@@ -157,14 +157,14 @@ silc_ske_payload_start_decode(SilcSKE ske,
  *    Frees the Key Exchange Start Payload indicated by `payload'.
  *
  ***/
-void silc_ske_payload_start_free(SilcSKEStartPayload *payload);
+void silc_ske_payload_start_free(SilcSKEStartPayload payload);
 
 /****f* silcske/SilcSKEPayloads/silc_ske_payload_ke_encode
  *
  * SYNOPSIS
  *
  *    SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
- *                                             SilcSKEKEPayload *payload,
+ *                                             SilcSKEKEPayload payload,
  *                                             SilcBuffer *return_buffer);
  *
  * DESCRIPTION
@@ -175,7 +175,7 @@ void silc_ske_payload_start_free(SilcSKEStartPayload *payload);
  *
  ***/
 SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
-                                        SilcSKEKEPayload *payload,
+                                        SilcSKEKEPayload payload,
                                         SilcBuffer *return_buffer);
 
 /****f* silcske/SilcSKEPayloads/silc_ske_payload_ke_decode
@@ -184,7 +184,7 @@ SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
  *
  *    SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
  *                                             SilcBuffer buffer,
- *                                             SilcSKEKEPayload 
+ *                                             SilcSKEKEPayload
  *                                               **return_payload);
  *
  * DESCRIPTION
@@ -196,7 +196,7 @@ SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
  ***/
 SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
                                         SilcBuffer buffer,
-                                        SilcSKEKEPayload **return_payload);
+                                        SilcSKEKEPayload *return_payload);
 
 /****f* silcske/SilcSKEPayloads/silc_ske_payload_ke_free
  *
@@ -209,6 +209,6 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
  *    Frees the Key Exchange Payload indicated by `payload'.
  *
  ***/
-void silc_ske_payload_ke_free(SilcSKEKEPayload *payload);
+void silc_ske_payload_ke_free(SilcSKEKEPayload payload);
 
 #endif /* SILCSKE_PAYLOAD_H */