updates.
authorPekka Riikonen <priikone@silcnet.org>
Thu, 5 Apr 2001 14:53:19 +0000 (14:53 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 5 Apr 2001 14:53:19 +0000 (14:53 +0000)
19 files changed:
CHANGES
TODO
apps/silcd/idlist.h
apps/silcd/packet_receive.c
apps/silcd/protocol.c
apps/silcd/protocol.h
apps/silcd/server.c
doc/draft-riikonen-silc-ke-auth-02.nroff
doc/draft-riikonen-silc-spec-02.nroff
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/protocol.c
lib/silcclient/protocol.h
lib/silccore/silcprotocol.c
lib/silccore/silcprotocol.h
lib/silcske/groups.c
lib/silcske/groups.h
lib/silcske/payload.c
lib/silcske/silcske.c

diff --git a/CHANGES b/CHANGES
index 12b4ed672ee107f763d255c430a79fdab3db56d5..737be54b1884f19ea507ac6e60572dce5789bc1e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,7 @@
 Thu Apr  5 17:42:30 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
+       * Added SilcServerRekey context into silcd/idlist.h.
+
        * Added the PFS support as defined in the specification to the
          SKE protocol.  Affected files lib/silcske/*.c.
 
@@ -21,6 +23,8 @@ Thu Apr  5 17:42:30 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
          SilcClientConnection structure; not needed.  Affected file is
          lib/silcclient/client.h.
 
+       * Updated TODO.
+
 Wed Apr  4 16:32:31 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
        * Do not ask whether user wants to use the negotiated private key
diff --git a/TODO b/TODO
index f69b4ef64a406cbe44f2aec8e80fc9f576a8b384..8de20df22b4e933786416e22c1e93e8e944a7617 100644 (file)
--- a/TODO
+++ b/TODO
@@ -56,12 +56,6 @@ TODO/bugs In SILC Server
 TODO/bugs In SILC Libraries
 ===========================
 
- o Implement PFS (Perfect Forward Secrecy) flag in SKE (and in client and
-   server, actually).  If PFS is set, re-key must cause new key exchange.
-   This is required by the SILC protocol.
-
- o Re-key in general is actually missing (from everywhere) and must be done.
-
  o Compression routines are missing.  The protocol supports packet
    compression thus it must be implemented.  SILC Comp API must be
    defined.  zlib package is already included into the lib dir (in CVS,
index 730352412b856473a87bc64fead3b9d6ac0fa2e1..b55820a150e544d75370c16cc1e877a68e53e194 100644 (file)
@@ -40,6 +40,18 @@ typedef struct {
   uint32 key_len;
 } *SilcServerChannelRekey;
 
+/* Generic rekey context for connections */
+typedef struct {
+  /* Current sending encryption key, provided for re-key. The `pfs'
+     is TRUE if the Perfect Forward Secrecy is performed in re-key. */
+  unsigned char *send_enc_key;
+  uint32 enc_key_len;
+  int ske_group;
+  bool pfs;
+  uint32 timeout;
+  void *context;
+} *SilcServerRekey;
+
 /*
    Generic ID list data structure.
 
@@ -58,11 +70,8 @@ typedef struct {
   SilcCipher send_key;
   SilcCipher receive_key;
 
-  /* Current sending encryption key, provided for re-key. The `pfs'
-     is TRUE if the Perfect Forward Secrecy is performed in re-key. */
-  unsigned char *send_enc_key;
-  uint32 enc_key_len;
-  bool pfs;
+  /* Re-key context */
+  SilcServerRekey rekey;
 
   /* Hash selected in the SKE protocol, NULL if not needed at all */
   SilcHash hash;
@@ -106,7 +115,7 @@ typedef struct {
        Logical name of the server. There is no limit of the length of the
        server name. This is usually the same name as defined in DNS.
 
-   int server_type
+   uint8 server_type
 
        Type of the server. SILC_SERVER or SILC_ROUTER are the possible
        choices for this.
@@ -147,7 +156,7 @@ struct SilcServerEntryStruct {
   SilcIDListDataStruct data;
 
   char *server_name;
-  int server_type;
+  uint8 server_type;
   SilcServerID *id;
   char *server_info;
   char *motd;
@@ -257,7 +266,7 @@ typedef struct SilcChannelClientEntryStruct {
        nickname. Nickname is not relevant information that would need to be 
        saved as plain.
 
-   int mode
+   uint32 mode
 
        Client's mode.  Client maybe for example server operator or
        router operator (SILC operator).
@@ -300,7 +309,7 @@ struct SilcClientEntryStruct {
   char *username;
   char *userinfo;
   SilcClientID *id;
-  int mode;
+  uint32 mode;
 
   long last_command;
   uint8 fast_command;
index 2d254cfd65a878bf23379f357a1ca561890e719c..46b0aac16c8e3da3184c30bb2a3ec7e12f157f74 100644 (file)
@@ -2036,6 +2036,7 @@ void silc_server_rekey(SilcServer server,
 {
   SilcProtocol protocol;
   SilcServerRekeyInternalContext *proto_ctx;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -2045,6 +2046,7 @@ void silc_server_rekey(SilcServer server,
   proto_ctx->server = (void *)server;
   proto_ctx->sock = sock;
   proto_ctx->responder = TRUE;
+  proto_ctx->pfs = idata->rekey->pfs;
       
   /* Perform rekey protocol. Will call the final callback after the
      protocol is over. */
@@ -2052,6 +2054,7 @@ void silc_server_rekey(SilcServer server,
                      &protocol, proto_ctx, silc_server_rekey_final);
   sock->protocol = protocol;
 
-  /* Run the protocol */
-  protocol->execute(server->timeout_queue, 0, protocol, sock->sock, 0, 0);
+  if (proto_ctx->pfs == FALSE)
+    /* Run the protocol */
+    protocol->execute(server->timeout_queue, 0, protocol, sock->sock, 0, 0);
 }
index d31955fe56c2c01b247b467cbe93c5ffe521bf23..53521be26aabfe61f3f53360256c88a7d9df22b2 100644 (file)
@@ -27,6 +27,7 @@
 
 SILC_TASK_CALLBACK(silc_server_protocol_connection_auth);
 SILC_TASK_CALLBACK(silc_server_protocol_key_exchange);
+SILC_TASK_CALLBACK(silc_server_protocol_rekey);
 
 extern char *silc_version_string;
 
@@ -61,6 +62,7 @@ int silc_server_protocol_ke_set_keys(SilcSKE ske,
                                     SilcPKCS pkcs,
                                     SilcHash hash,
                                     SilcHmac hmac,
+                                    SilcSKEDiffieHellmanGroup group,
                                     bool is_responder)
 {
   SilcUnknownEntry conn_data;
@@ -71,9 +73,6 @@ int silc_server_protocol_ke_set_keys(SilcSKE ske,
   conn_data = silc_calloc(1, sizeof(*conn_data));
   idata = (SilcIDListData)conn_data;
 
-  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
-    idata->pfs = TRUE;
-
   /* Allocate cipher to be used in the communication */
   if (!silc_cipher_alloc(cipher->cipher->name, &idata->send_key)) {
     silc_free(conn_data);
@@ -100,11 +99,17 @@ int silc_server_protocol_ke_set_keys(SilcSKE ske,
     silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
   }
 
-  /* Note that for responder the initiator's sending key is receiving key */
-  idata->send_enc_key = silc_calloc(keymat->enc_key_len / 8,
-                                   sizeof(*idata->send_enc_key));
-  memcpy(idata->send_enc_key, keymat->send_enc_key, keymat->enc_key_len / 8);
-  idata->enc_key_len = keymat->enc_key_len / 8;
+  idata->rekey = silc_calloc(1, sizeof(*idata->rekey));
+  idata->rekey->send_enc_key = 
+    silc_calloc(keymat->enc_key_len / 8,
+               sizeof(*idata->rekey->send_enc_key));
+  memcpy(idata->rekey->send_enc_key, 
+        keymat->send_enc_key, keymat->enc_key_len / 8);
+  idata->rekey->enc_key_len = keymat->enc_key_len / 8;
+
+  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
+    idata->rekey->pfs = TRUE;
+  idata->rekey->ske_group = silc_ske_group_get_number(group);
 
   /* Save the remote host's public key */
   silc_pkcs_public_key_decode(ske->ke1_payload->pk_data, 
@@ -994,6 +999,43 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth)
  * Re-key protocol routines
  */
 
+/* Actually takes the new keys into use. */
+
+static void 
+silc_server_protocol_rekey_validate(SilcServer server,
+                                   SilcServerRekeyInternalContext *ctx,
+                                   SilcIDListData idata,
+                                   SilcSKEKeyMaterial *keymat)
+{
+  if (ctx->responder == TRUE) {
+    silc_cipher_set_key(idata->send_key, keymat->receive_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(idata->send_key, keymat->receive_iv);
+    silc_cipher_set_key(idata->receive_key, keymat->send_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(idata->receive_key, keymat->send_iv);
+  } else {
+    silc_cipher_set_key(idata->send_key, keymat->send_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(idata->send_key, keymat->send_iv);
+    silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
+  }
+
+  silc_hmac_set_key(idata->hmac, keymat->hmac_key, keymat->hmac_key_len);
+
+  /* Save the current sending encryption key */
+  memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
+  silc_free(idata->rekey->send_enc_key);
+  idata->rekey->send_enc_key = 
+    silc_calloc(keymat->enc_key_len / 8,
+               sizeof(*idata->rekey->send_enc_key));
+  memcpy(idata->rekey->send_enc_key, keymat->send_enc_key, 
+        keymat->enc_key_len / 8);
+  idata->rekey->enc_key_len = keymat->enc_key_len / 8;
+}
+
 /* This function actually re-generates (when not using PFS) the keys and
    takes them into use. */
 
@@ -1009,43 +1051,69 @@ void silc_server_protocol_rekey_generate(SilcServer server,
 
   /* Generate the new key */
   keymat = silc_calloc(1, sizeof(*keymat));
-  silc_ske_process_key_material_data(idata->send_enc_key,
-                                    idata->enc_key_len,
+  silc_ske_process_key_material_data(idata->rekey->send_enc_key,
+                                    idata->rekey->enc_key_len,
                                     16, key_len, hash_len, 
                                     idata->hash, keymat);
 
   /* Set the keys into use */
+  silc_server_protocol_rekey_validate(server, ctx, idata, keymat);
 
-  if (ctx->responder == TRUE) {
-    silc_cipher_set_key(idata->send_key, keymat->receive_enc_key, 
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(idata->send_key, keymat->receive_iv);
-    silc_cipher_set_key(idata->receive_key, keymat->send_enc_key, 
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(idata->receive_key, keymat->send_iv);
-  } else {
-    silc_cipher_set_key(idata->send_key, keymat->send_enc_key, 
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(idata->send_key, keymat->send_iv);
-    silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key, 
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
-  }
+  silc_ske_free_key_material(keymat);
+}
 
-  silc_hmac_set_key(idata->hmac, keymat->hmac_key, keymat->hmac_key_len);
+/* This function actually re-generates (with PFS) the keys and
+   takes them into use. */
 
-  /* Save the current sending encryption key */
-  memset(idata->send_enc_key, 0, idata->enc_key_len);
-  silc_free(idata->send_enc_key);
-  idata->send_enc_key = silc_calloc(keymat->enc_key_len / 8,
-                                   sizeof(*idata->send_enc_key));
-  memcpy(idata->send_enc_key, keymat->send_enc_key, keymat->enc_key_len / 8);
-  idata->enc_key_len = keymat->enc_key_len / 8;
+void 
+silc_server_protocol_rekey_generate_pfs(SilcServer server,
+                                       SilcServerRekeyInternalContext *ctx)
+{
+  SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
+  SilcSKEKeyMaterial *keymat;
+  uint32 key_len = silc_cipher_get_key_len(idata->send_key);
+  uint32 hash_len = idata->hash->hash->hash_len;
+  unsigned char *tmpbuf;
+  uint32 klen;
+
+  SILC_LOG_DEBUG(("Generating new session keys (with PFS)"));
+
+  /* Encode KEY to binary data */
+  tmpbuf = silc_mp_mp2bin(ctx->ske->KEY, 0, &klen);
 
+  /* Generate the new key */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len, 
+                                    idata->hash, keymat);
+
+  /* Set the keys into use */
+  silc_server_protocol_rekey_validate(server, ctx, idata, keymat);
+
+  memset(tmpbuf, 0, klen);
+  silc_free(tmpbuf);
   silc_ske_free_key_material(keymat);
 }
 
-/* Performs re-key as defined the SILC protocol specification. */
+/* Packet sending callback. This function is provided as packet sending
+   routine to the Key Exchange functions. */
+
+static void 
+silc_server_protocol_rekey_send_packet(SilcSKE ske,
+                                      SilcBuffer packet,
+                                      SilcPacketType type,
+                                      void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerRekeyInternalContext *ctx = 
+    (SilcServerRekeyInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+
+  /* Send the packet immediately */
+  silc_server_packet_send(server, ctx->sock,
+                         type, 0, packet->data, packet->len, TRUE);
+}
+
+/* Performs re-key as defined in the SILC protocol specification. */
 
 SILC_TASK_CALLBACK(silc_server_protocol_rekey)
 {
@@ -1053,6 +1121,8 @@ SILC_TASK_CALLBACK(silc_server_protocol_rekey)
   SilcServerRekeyInternalContext *ctx = 
     (SilcServerRekeyInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
+  SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
+  SilcSKEStatus status;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1079,6 +1149,34 @@ SILC_TASK_CALLBACK(silc_server_protocol_rekey)
           * using the SKE protocol.
           */
 
+         if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
+           /* Error in protocol */
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           protocol->execute(server->timeout_queue, 0, protocol, fd, 
+                             0, 300000);
+         }
+
+         ctx->ske = silc_ske_alloc();
+         ctx->ske->rng = server->rng;
+         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+         silc_ske_get_group_by_number(idata->rekey->ske_group,
+                                      &ctx->ske->prop->group);
+
+         status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer,
+                                             NULL, NULL, NULL, NULL);
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                             status));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           protocol->execute(server->timeout_queue, 0, 
+                             protocol, fd, 0, 300000);
+           return;
+         }
+
+         /* Advance the protocol state */
+         protocol->state++;
+         protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
        } else {
          /*
           * Do normal and simple re-key.
@@ -1097,71 +1195,126 @@ SILC_TASK_CALLBACK(silc_server_protocol_rekey)
         * We are the initiator of this protocol
         */
 
+       /* Start the re-key by sending the REKEY packet */
+       silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY,
+                               0, NULL, 0, TRUE);
+
        if (ctx->pfs == TRUE) {
          /* 
           * Use Perfect Forward Secrecy, ie. negotiate the key material
           * using the SKE protocol.
           */
+         ctx->ske = silc_ske_alloc();
+         ctx->ske->rng = server->rng;
+         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+         silc_ske_get_group_by_number(idata->rekey->ske_group,
+                                      &ctx->ske->prop->group);
+
+         status = 
+           silc_ske_initiator_phase_2(ctx->ske, NULL, NULL,
+                                      silc_server_protocol_rekey_send_packet,
+                                      context);
+
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                             status));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           protocol->execute(server->timeout_queue, 0, 
+                             protocol, fd, 0, 300000);
+           return;
+         }
 
+         /* Advance the protocol state */
+         protocol->state++;
        } else {
          /*
           * Do normal and simple re-key.
           */
 
-         /* Start the re-key by sending the REKEY packet */
-         silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY,
-                                 0, NULL, 0, TRUE);
-
-         /* The protocol ends in next stage. */
+         /* The protocol ends in next stage. We have sent the REKEY packet
+            and now we just wait that the responder send REKEY_DONE and
+            the we'll generate the new key, simple. */
          protocol->state = SILC_PROTOCOL_STATE_END;
        }
       }
-
     }
     break;
 
-  case SILC_PROTOCOL_STATE_END:
-    /* 
-     * End protocol
+  case 2:
+    /*
+     * Second state, used only when oding re-key with PFS.
      */
-
     if (ctx->responder == TRUE) {
-
       if (ctx->pfs == TRUE) {
        /*
-        *
-        */
-       
-      } else {
-       /*
-        * We must have received the REKEY_DONE from the initiator.
+        * Send our KE packe to the initiator now that we've processed
+        * the initiator's KE packet.
         */
+       status = 
+         silc_ske_responder_finish(ctx->ske, NULL, NULL, 
+                                   SILC_SKE_PK_TYPE_SILC,
+                                   silc_server_protocol_rekey_send_packet,
+                                   context);
 
-       if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
-         /* Error in protocol */
-         protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
-       }
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                             status));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           protocol->execute(server->timeout_queue, 0, 
+                             protocol, fd, 0, 300000);
+           return;
+         }
       }
 
     } else {
-
       if (ctx->pfs == TRUE) {
        /*
-        *
+        * The packet type must be KE packet
         */
-       
-      } else {
-       /*
-        * We must have received the REKEY_DONE from the responder.
-        */
-
-       if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
+       if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
          /* Error in protocol */
          protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
+         protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
        }
+       
+       status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer,
+                                          NULL, NULL, NULL, NULL);
+       if (status != SILC_SKE_STATUS_OK) {
+         SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                           status));
+         
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         protocol->execute(server->timeout_queue, 0, 
+                           protocol, fd, 0, 300000);
+         return;
+       }
+      }
+    }
+
+    /* Send the REKEY_DONE to indicate we will take new keys into use 
+       now. */ 
+    silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
+                           0, NULL, 0, TRUE);
+    
+    /* The protocol ends in next stage. */
+    protocol->state = SILC_PROTOCOL_STATE_END;
+    break;
+
+  case SILC_PROTOCOL_STATE_END:
+    /* 
+     * End protocol
+     */
+
+    if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
+      /* Error in protocol */
+      protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
+    }
 
+    if (ctx->responder == FALSE) {
+      if (ctx->pfs == FALSE) {
        /* Send the REKEY_DONE to indicate we will take new keys into use 
           now. */ 
        silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
index d0c2bba34398e33a9022cdeb4d519169103166bc..c309aabc401437d1a215976307f5baeb67c3dc0a 100644 (file)
@@ -89,7 +89,6 @@ typedef struct {
   bool responder;                  /* TRUE if we are receiving party */
   bool pfs;                        /* TRUE if PFS is to be used */
   SilcSKE ske;                     /* Defined if PFS is used */
-  SilcSKEKeyMaterial *keymat;      /* Defined if PFS is used */
   SilcPacketContext *packet;
 } SilcServerRekeyInternalContext;
 
index c2dd90f453139ed9dc1cac2b3ac3621441781b40..12769c445fc26f1e08fe82deb7e8dfb45bb51654 100644 (file)
@@ -3688,8 +3688,6 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     silc_protocol_cancel(server->timeout_queue, protocol);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
-    if (ctx->keymat)
-      silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
@@ -3707,8 +3705,6 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;
-  if (ctx->keymat)
-    silc_ske_free_key_material(ctx->keymat);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
index 0a63644baaccef54a6b2a35b1d572b2d1622bb6c..56f3d0bdd2193017b1dde412b1ab5adc8bf43d22 100644 (file)
@@ -402,6 +402,15 @@ two SILC clients.  In normal case, where client is connecting to the
 server or server is connecting to the router the Mutual Authentication
 flag is not necessary.
 
+When performing re-key with PFS selected this is the only payload that
+is sent in the SKE protocol.  The Key Exchange Start Payload is not sent
+at all.  However, this payload does not have all the fields present.
+In re-key with PFS the public key and a possible signature data should
+not be present.  If they are present they must be ignored.  The only
+field that is present is the public data that is used to create the
+new key material.  In the re-key the Mutual Authentication flag must
+also be ignored.
+
 This payload is sent inside SILC_PACKET_KEY_EXCHANGE_1 and inside
 SILC_PACKET_KEY_EXCHANGE_2 packet types.  The initiator uses the 
 SILC_PACKET_KEY_EXCHANGE_1 and the responder the latter.
index 734a32c30fc9fec94068e162df608a4fd86a8e33..d945f2aa1270eba1e1183c5f72d930aea5da5910 100644 (file)
@@ -1787,6 +1787,13 @@ and the protocol results to new key material.  See [SILC3] for more
 information.  After the SILC_PACKET_REKEY packet is sent the sender
 will perform the SKE protocol.
 
+If PFS flag was set the resulted key material is processed as described
+in the section Processing the Key Material in [SILC3].  The difference
+with re-key in the processing is that the initial data for the hash 
+function is just the resulted key material and not the HASH as it
+is not computed at all with re-key.  Other than that, the key processing
+it equivalent to normal SKE negotiation.
+
 If PFS flag was not set, which is the default case, then re-key is done
 without executing SKE protocol.  In this case, the new key is created by
 providing the current sending encryption key to the SKE protocol's key
index 58053e9d30b3e1769272b7dc6a983280c7d56800..a43baa1146c4afc3cfde1514f0a9133f6d163780 100644 (file)
@@ -1612,8 +1612,6 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
     silc_protocol_cancel(client->timeout_queue, protocol);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
-    if (ctx->keymat)
-      silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
@@ -1631,8 +1629,6 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;
-  if (ctx->keymat)
-    silc_ske_free_key_material(ctx->keymat);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
index 660aae65b2c59baeb4e6f58b7c1815becb2bcdbc..b16e308f415185621751a090df47d88cf2f2c901 100644 (file)
@@ -32,6 +32,18 @@ typedef struct SilcClientKeyAgreementStruct *SilcClientKeyAgreement;
 #include "command.h"
 #include "silcapi.h"
 
+/* Generic rekey context for connections */
+typedef struct {
+  /* Current sending encryption key, provided for re-key. The `pfs'
+     is TRUE if the Perfect Forward Secrecy is performed in re-key. */
+  unsigned char *send_enc_key;
+  uint32 enc_key_len;
+  int ske_group;
+  bool pfs;
+  uint32 timeout;
+  void *context;
+} *SilcClientRekey;
+
 /* Connection structure used in client to associate all the important
    connection specific data to this structure. */
 struct SilcClientConnectionStruct {
@@ -74,8 +86,6 @@ struct SilcClientConnectionStruct {
   SilcCipher send_key;
   SilcCipher receive_key;
   SilcHmac hmac;
-  unsigned char *hmac_key;
-  uint32 hmac_key_len;
   SilcHash hash;
 
   /* Client ID and Channel ID cache. Messages transmitted in SILC network
@@ -107,6 +117,9 @@ struct SilcClientConnectionStruct {
   /* Set away message */
   SilcClientAway *away;
 
+  /* Re-key context */
+  SilcClientRekey rekey;
+
   /* Pointer back to the SilcClient. This object is passed to the application
      and the actual client object is accesible through this pointer. */
   SilcClient client;
index 5255b67abf6d04d9906a84c31803600ffd86b97a..a21ea9c45975a53b436e88bbe7174bfe854521fa 100644 (file)
@@ -27,6 +27,7 @@
 
 SILC_TASK_CALLBACK(silc_client_protocol_connection_auth);
 SILC_TASK_CALLBACK(silc_client_protocol_key_exchange);
+SILC_TASK_CALLBACK(silc_client_protocol_rekey);
 
 extern char *silc_version_string;
 
@@ -49,7 +50,6 @@ void silc_client_protocol_ke_send_packet(SilcSKE ske,
   /* Send the packet immediately */
   silc_client_packet_send(client, ske->sock, type, NULL, 0, NULL, NULL,
                          packet->data, packet->len, TRUE);
-
 }
 
 /* Callback that is called when we have received KE2 payload from
@@ -85,7 +85,8 @@ void silc_client_protocol_ke_set_keys(SilcSKE ske,
                                      SilcCipher cipher,
                                      SilcPKCS pkcs,
                                      SilcHash hash,
-                                     SilcHmac hmac)
+                                     SilcHmac hmac,
+                                     SilcSKEDiffieHellmanGroup group)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 
@@ -114,6 +115,18 @@ void silc_client_protocol_ke_set_keys(SilcSKE ske,
                           ske->ke2_payload->pk_len);
 #endif
 
+  conn->rekey = silc_calloc(1, sizeof(*conn->rekey));
+  conn->rekey->send_enc_key = 
+    silc_calloc(keymat->enc_key_len / 8,
+               sizeof(*conn->rekey->send_enc_key));
+  memcpy(conn->rekey->send_enc_key, 
+        keymat->send_enc_key, keymat->enc_key_len / 8);
+  conn->rekey->enc_key_len = keymat->enc_key_len / 8;
+
+  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
+    conn->rekey->pfs = TRUE;
+  conn->rekey->ske_group = silc_ske_group_get_number(group);
+
   /* Save HMAC key to be used in the communication. */
   silc_hmac_alloc(hmac->hmac->name, NULL, &conn->hmac);
   silc_hmac_set_key(conn->hmac, keymat->hmac_key, keymat->hmac_key_len);
@@ -612,6 +625,381 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
   }
 }
 
+/*
+ * Re-key protocol routines
+ */
+
+/* Actually takes the new keys into use. */
+
+static void 
+silc_client_protocol_rekey_validate(SilcClient client,
+                                   SilcClientRekeyInternalContext *ctx,
+                                   SilcSocketConnection sock,
+                                   SilcSKEKeyMaterial *keymat)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+
+  if (ctx->responder == TRUE) {
+    silc_cipher_set_key(conn->send_key, keymat->receive_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(conn->send_key, keymat->receive_iv);
+    silc_cipher_set_key(conn->receive_key, keymat->send_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(conn->receive_key, keymat->send_iv);
+  } else {
+    silc_cipher_set_key(conn->send_key, keymat->send_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(conn->send_key, keymat->send_iv);
+    silc_cipher_set_key(conn->receive_key, keymat->receive_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(conn->receive_key, keymat->receive_iv);
+  }
+
+  silc_hmac_set_key(conn->hmac, keymat->hmac_key, keymat->hmac_key_len);
+
+  /* Save the current sending encryption key */
+  memset(conn->rekey->send_enc_key, 0, conn->rekey->enc_key_len);
+  silc_free(conn->rekey->send_enc_key);
+  conn->rekey->send_enc_key = 
+    silc_calloc(keymat->enc_key_len / 8,
+               sizeof(*conn->rekey->send_enc_key));
+  memcpy(conn->rekey->send_enc_key, keymat->send_enc_key, 
+        keymat->enc_key_len / 8);
+  conn->rekey->enc_key_len = keymat->enc_key_len / 8;
+}
+
+/* This function actually re-generates (when not using PFS) the keys and
+   takes them into use. */
+
+void silc_client_protocol_rekey_generate(SilcClient client,
+                                        SilcClientRekeyInternalContext *ctx)
+{
+  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+  SilcSKEKeyMaterial *keymat;
+  uint32 key_len = silc_cipher_get_key_len(conn->send_key);
+  uint32 hash_len = conn->hash->hash->hash_len;
+
+  SILC_LOG_DEBUG(("Generating new session keys (no PFS)"));
+
+  /* Generate the new key */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  silc_ske_process_key_material_data(conn->rekey->send_enc_key,
+                                    conn->rekey->enc_key_len,
+                                    16, key_len, hash_len, 
+                                    conn->hash, keymat);
+
+  /* Set the keys into use */
+  silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat);
+
+  silc_ske_free_key_material(keymat);
+}
+
+/* This function actually re-generates (with PFS) the keys and
+   takes them into use. */
+
+void 
+silc_client_protocol_rekey_generate_pfs(SilcClient client,
+                                       SilcClientRekeyInternalContext *ctx)
+{
+  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+  SilcSKEKeyMaterial *keymat;
+  uint32 key_len = silc_cipher_get_key_len(conn->send_key);
+  uint32 hash_len = conn->hash->hash->hash_len;
+  unsigned char *tmpbuf;
+  uint32 klen;
+
+  SILC_LOG_DEBUG(("Generating new session keys (with PFS)"));
+
+  /* Encode KEY to binary data */
+  tmpbuf = silc_mp_mp2bin(ctx->ske->KEY, 0, &klen);
+
+  /* Generate the new key */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len, 
+                                    conn->hash, keymat);
+
+  /* Set the keys into use */
+  silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat);
+
+  memset(tmpbuf, 0, klen);
+  silc_free(tmpbuf);
+  silc_ske_free_key_material(keymat);
+}
+
+/* Packet sending callback. This function is provided as packet sending
+   routine to the Key Exchange functions. */
+
+static void 
+silc_client_protocol_rekey_send_packet(SilcSKE ske,
+                                      SilcBuffer packet,
+                                      SilcPacketType type,
+                                      void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientRekeyInternalContext *ctx = 
+    (SilcClientRekeyInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+
+  /* Send the packet immediately */
+  silc_client_packet_send(client, ctx->sock, type, NULL, 0, NULL, NULL,
+                         packet->data, packet->len, TRUE);
+}
+
+/* Performs re-key as defined in the SILC protocol specification. */
+
+SILC_TASK_CALLBACK(silc_client_protocol_rekey)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientRekeyInternalContext *ctx = 
+    (SilcClientRekeyInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+  SilcSKEStatus status;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
+    protocol->state = SILC_PROTOCOL_STATE_START;
+
+  SILC_LOG_DEBUG(("State=%d", protocol->state));
+
+  switch(protocol->state) {
+  case SILC_PROTOCOL_STATE_START:
+    {
+      /* 
+       * Start protocol.
+       */
+
+      if (ctx->responder == TRUE) {
+       /*
+        * We are receiving party
+        */
+
+       if (ctx->pfs == TRUE) {
+         /* 
+          * Use Perfect Forward Secrecy, ie. negotiate the key material
+          * using the SKE protocol.
+          */
+
+         if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
+           /* Error in protocol */
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           protocol->execute(client->timeout_queue, 0, protocol, fd, 
+                             0, 300000);
+         }
+
+         ctx->ske = silc_ske_alloc();
+         ctx->ske->rng = client->rng;
+         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+         silc_ske_get_group_by_number(conn->rekey->ske_group,
+                                      &ctx->ske->prop->group);
+
+         status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer,
+                                             NULL, NULL, NULL, NULL);
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                             status));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           protocol->execute(client->timeout_queue, 0, 
+                             protocol, fd, 0, 300000);
+           return;
+         }
+
+         /* Advance the protocol state */
+         protocol->state++;
+         protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+       } else {
+         /*
+          * Do normal and simple re-key.
+          */
+
+         /* Send the REKEY_DONE to indicate we will take new keys into use */
+         silc_client_packet_send(client, ctx->sock, 
+                                 SILC_PACKET_REKEY_DONE, 
+                                 NULL, 0, NULL, NULL, NULL, 0, TRUE);
+
+         /* The protocol ends in next stage. */
+         protocol->state = SILC_PROTOCOL_STATE_END;
+       }
+      
+      } else {
+       /*
+        * We are the initiator of this protocol
+        */
+
+       /* Start the re-key by sending the REKEY packet */
+       silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY, 
+                               NULL, 0, NULL, NULL, NULL, 0, TRUE);
+
+       if (ctx->pfs == TRUE) {
+         /* 
+          * Use Perfect Forward Secrecy, ie. negotiate the key material
+          * using the SKE protocol.
+          */
+         ctx->ske = silc_ske_alloc();
+         ctx->ske->rng = client->rng;
+         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+         silc_ske_get_group_by_number(conn->rekey->ske_group,
+                                      &ctx->ske->prop->group);
+
+         status = 
+           silc_ske_initiator_phase_2(ctx->ske, NULL, NULL,
+                                      silc_client_protocol_rekey_send_packet,
+                                      context);
+
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                             status));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           protocol->execute(client->timeout_queue, 0, 
+                             protocol, fd, 0, 300000);
+           return;
+         }
+
+         /* Advance the protocol state */
+         protocol->state++;
+       } else {
+         /*
+          * Do normal and simple re-key.
+          */
+
+         /* The protocol ends in next stage. We have sent the REKEY packet
+            and now we just wait that the responder send REKEY_DONE and
+            the we'll generate the new key, simple. */
+         protocol->state = SILC_PROTOCOL_STATE_END;
+       }
+      }
+    }
+    break;
+
+  case 2:
+    /*
+     * Second state, used only when oding re-key with PFS.
+     */
+    if (ctx->responder == TRUE) {
+      if (ctx->pfs == TRUE) {
+       /*
+        * Send our KE packe to the initiator now that we've processed
+        * the initiator's KE packet.
+        */
+       status = 
+         silc_ske_responder_finish(ctx->ske, NULL, NULL, 
+                                   SILC_SKE_PK_TYPE_SILC,
+                                   silc_client_protocol_rekey_send_packet,
+                                   context);
+
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                             status));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           protocol->execute(client->timeout_queue, 0, 
+                             protocol, fd, 0, 300000);
+           return;
+         }
+      }
+
+    } else {
+      if (ctx->pfs == TRUE) {
+       /*
+        * The packet type must be KE packet
+        */
+       if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
+         /* Error in protocol */
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 300000);
+       }
+       
+       status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer,
+                                          NULL, NULL, NULL, NULL);
+       if (status != SILC_SKE_STATUS_OK) {
+         SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                           status));
+         
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         protocol->execute(client->timeout_queue, 0, 
+                           protocol, fd, 0, 300000);
+         return;
+       }
+      }
+    }
+
+    /* Send the REKEY_DONE to indicate we will take new keys into use 
+       now. */ 
+    silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY_DONE, 
+                           NULL, 0, NULL, NULL, NULL, 0, TRUE);
+    
+    /* The protocol ends in next stage. */
+    protocol->state = SILC_PROTOCOL_STATE_END;
+    break;
+
+  case SILC_PROTOCOL_STATE_END:
+    /* 
+     * End protocol
+     */
+
+    if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
+      /* Error in protocol */
+      protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+    }
+
+    if (ctx->responder == FALSE) {
+      if (ctx->pfs == FALSE) {
+       /* Send the REKEY_DONE to indicate we will take new keys into use 
+          now. */ 
+       silc_client_packet_send(client, ctx->sock, 
+                               SILC_PACKET_REKEY_DONE, 
+                               NULL, 0, NULL, NULL, NULL, 0, TRUE);
+      }
+    }
+
+    /* Protocol has ended, call the final callback */
+    if (protocol->final_callback)
+      protocol->execute_final(client->timeout_queue, 0, protocol, fd);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_ERROR:
+    /*
+     * Error occured
+     */
+
+    if (ctx->pfs == TRUE) {
+      /* Send abort notification */
+      silc_ske_abort(ctx->ske, ctx->ske->status, 
+                    silc_client_protocol_ke_send_packet,
+                    context);
+    }
+
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      protocol->execute_final(client->timeout_queue, 0, protocol, fd);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /*
+     * We have received failure from remote
+     */
+
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      protocol->execute_final(client->timeout_queue, 0, protocol, fd);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_UNKNOWN:
+    break;
+  }
+
+}
+
 /* Registers protocols used in client */
 
 void silc_client_protocols_register(void)
@@ -620,6 +1008,8 @@ void silc_client_protocols_register(void)
                         silc_client_protocol_connection_auth);
   silc_protocol_register(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
                         silc_client_protocol_key_exchange);
+  silc_protocol_register(SILC_PROTOCOL_CLIENT_REKEY,
+                        silc_client_protocol_rekey);
 }
 
 /* Unregisters protocols */
@@ -630,4 +1020,6 @@ void silc_client_protocols_unregister(void)
                           silc_client_protocol_connection_auth);
   silc_protocol_unregister(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
                           silc_client_protocol_key_exchange);
+  silc_protocol_unregister(SILC_PROTOCOL_CLIENT_REKEY,
+                          silc_client_protocol_rekey);
 }
index fc73f6de37e018723cad55286fa19065f1d034fb..71e25069b9d00e8791e4d83e47441c7b590d56f8 100644 (file)
@@ -81,7 +81,6 @@ typedef struct {
   bool responder;                  /* TRUE if we are receiving party */
   bool pfs;                        /* TRUE if PFS is to be used */
   SilcSKE ske;                     /* Defined if PFS is used */
-  SilcSKEKeyMaterial *keymat;      /* Defined if PFS is used */
   SilcPacketContext *packet;
 } SilcClientRekeyInternalContext;
 
index 38ca3c9d45176e5ba1e445b5a0e5da8ac3a97b09..9eb50898d1fdf4b1ec6351afa8e3d18b3dd96403 100644 (file)
@@ -144,3 +144,14 @@ void silc_protocol_execute_final(void *qptr, int type,
 
   protocol->final_callback(qptr, 0, context, fd);
 }
+
+/* Cancels the execution of the next state of the protocol. */
+
+void silc_protocol_cancel(void *qptr, void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  silc_task_unregister_by_callback(qptr, protocol->protocol->callback);
+}
index 8336745d6ba98e8975c52e121bb81c80546bdcb1..534aa493d8e88da565e1fb55e3f0d04f9f932a50 100644 (file)
@@ -124,5 +124,6 @@ void silc_protocol_execute(void *qptr, int type,
                           long secs, long usecs);
 void silc_protocol_execute_final(void *qptr, int type, 
                                 void *context, int fd);
+void silc_protocol_cancel(void *qptr, void *context);
 
 #endif
index 2648f165d0bf4310b64ac757f0bc4fc569ae2b08..fd664a06129ba0032099252e26e5fabd99cfaf31 100644 (file)
@@ -160,3 +160,10 @@ char *silc_ske_get_supported_groups()
 
   return list;
 }
+
+/* Returns the number of the `group'. */
+
+int silc_ske_group_get_number(SilcSKEDiffieHellmanGroup group)
+{
+  return group->number;
+}
index 29e33be4d080d74a4141c677355ce26c7c24c1a5..eacd3f257955c04054283d6f6cfbe18619dca2d2 100644 (file)
@@ -36,5 +36,6 @@ SilcSKEStatus silc_ske_get_group_by_number(int number,
 SilcSKEStatus silc_ske_get_group_by_name(const char *name,
                                         SilcSKEDiffieHellmanGroup *ret);
 char *silc_ske_get_supported_groups();
+int silc_ske_group_get_number(SilcSKEDiffieHellmanGroup group);
 
 #endif
index fe03125f78edd4b9236fed0cb015d86a4906c38d..835613ae030e35be58f81522d63eb8b8eff9eb24 100644 (file)
@@ -196,7 +196,8 @@ SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
   if (!payload)
     return SILC_SKE_STATUS_ERROR;
 
-  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL &&
+  if (ske->start_payload && 
+      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL &&
       !payload->sign_data) {
     SILC_LOG_DEBUG(("Signature data is missing"));
     return SILC_SKE_STATUS_ERROR;
@@ -273,7 +274,7 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
     goto err;
   }
 
-  if (payload->pk_len < 5) {
+  if (payload->pk_type == 0) {
     status = SILC_SKE_STATUS_BAD_PAYLOAD;
     goto err;
   }
@@ -302,7 +303,8 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
     goto err;
   }
 
-  if ((ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) &&
+  if (ske->start_payload && 
+      (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) &&
       (payload->sign_len < 3 || !payload->sign_data)) {
     SILC_LOG_DEBUG(("The signature data is missing - both parties are "
                    "required to do authentication"));
index 4d73f2403e20786e240492b559c66306d1defa29..852ad4fd2cd5e9dfbbb19f123f784c409afebe00 100644 (file)
@@ -263,20 +263,22 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
   payload->x = e;
 
   /* Get public key */
-  payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
-  if (!payload->pk_data) {
-    silc_mp_clear(x);
-    silc_free(x);
-    silc_mp_clear(&e);
-    silc_free(payload);
-    ske->status = SILC_SKE_STATUS_OK;
-    return ske->status;
+  if (public_key) {
+    payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
+    if (!payload->pk_data) {
+      silc_mp_clear(x);
+      silc_free(x);
+      silc_mp_clear(&e);
+      silc_free(payload);
+      ske->status = SILC_SKE_STATUS_OK;
+      return ske->status;
+    }
+    payload->pk_len = pk_len;
   }
-  payload->pk_len = pk_len;
   payload->pk_type = SILC_SKE_PK_TYPE_SILC;
 
   /* Compute signature data if we are doing mutual authentication */
-  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+  if (private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
     unsigned char hash[32], sign[1024];
     uint32 hash_len, sign_len;
 
@@ -357,12 +359,14 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
   silc_mp_powm(KEY, &payload->x, ske->x, &ske->prop->group->group);
   ske->KEY = KEY;
 
-  SILC_LOG_DEBUG(("Verifying public key"));
-
-  if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, 
-                                  &public_key)) {
-    status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
-    goto err;
+  if (payload->pk_data) {
+    SILC_LOG_DEBUG(("Verifying public key"));
+    
+    if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, 
+                                    &public_key)) {
+      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      goto err;
+    }
   }
 
   if (verify_key) {
@@ -370,38 +374,40 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
                           payload->pk_type, verify_context);
     if (status != SILC_SKE_STATUS_OK)
       goto err;
-  }  
-
-  SILC_LOG_DEBUG(("Public key is authentic"));
 
-  /* Compute the hash value */
-  status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
+    SILC_LOG_DEBUG(("Public key is authentic"));
+  }
 
-  ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
-  memcpy(ske->hash, hash, hash_len);
-  ske->hash_len = hash_len;
+  if (payload->pk_data) {
+    /* Compute the hash value */
+    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+    if (status != SILC_SKE_STATUS_OK)
+      goto err;
 
-  SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
+    ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
+    memcpy(ske->hash, hash, hash_len);
+    ske->hash_len = hash_len;
 
-  /* Verify signature */
-  silc_pkcs_public_key_data_set(ske->prop->pkcs, public_key->pk, 
-                               public_key->pk_len);
-  if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data, 
-                      payload->sign_len, hash, hash_len) == FALSE) {
+    SILC_LOG_DEBUG(("Verifying signature (HASH)"));
 
-    SILC_LOG_DEBUG(("Signature don't match"));
+    /* Verify signature */
+    silc_pkcs_public_key_data_set(ske->prop->pkcs, public_key->pk, 
+                                 public_key->pk_len);
+    if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data, 
+                        payload->sign_len, hash, hash_len) == FALSE) {
+      
+      SILC_LOG_DEBUG(("Signature don't match"));
+      
+      status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
+      goto err;
+    }
 
-    status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
-    goto err;
+    SILC_LOG_DEBUG(("Signature is Ok"));
+    
+    silc_pkcs_public_key_free(public_key);
+    memset(hash, 'F', hash_len);
   }
 
-  SILC_LOG_DEBUG(("Signature is Ok"));
-
-  silc_pkcs_public_key_free(public_key);
-  memset(hash, 'F', hash_len);
-
   /* Call the callback. */
   if (callback)
     (*callback)(ske, context);
@@ -585,12 +591,10 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
 }
 
 /* This function receives the Key Exchange Payload from the initiator.
-   After processing the payload this then selects 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 which is later processed
-   in ske_responder_finish function. The callback function should
-   not touch the payload (it should merely call the ske_responder_finish
-   function). */
+   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. */
 
 SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
                                         SilcBuffer ke_payload,
@@ -616,7 +620,8 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
 
   /* Verify the received public key and verify the signature if we are
      doing mutual authentication. */
-  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+  if (ske->start_payload && 
+      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
     SilcPublicKey public_key = NULL;
     unsigned char hash[32];
     uint32 hash_len;
@@ -645,7 +650,7 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
     if (status != SILC_SKE_STATUS_OK)
       return status;
 
-    SILC_LOG_DEBUG(("Verifying signature"));
+    SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
     
     /* Verify signature */
     silc_pkcs_public_key_data_set(ske->prop->pkcs, public_key->pk, 
@@ -698,9 +703,9 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
   return status;
 }
 
-/* This function computes the secret shared 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. */
+/* 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. */
 
 SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
                                        SilcPublicKey public_key,
@@ -717,11 +722,6 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (!public_key || !private_key) {
-    status = SILC_SKE_STATUS_ERROR;
-    goto err;
-  }
-
   SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
 
   /* Compute the shared secret key */
@@ -731,41 +731,43 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
               &ske->prop->group->group);
   ske->KEY = KEY;
 
-  SILC_LOG_DEBUG(("Getting public key"));
+  if (public_key && private_key) {
+    SILC_LOG_DEBUG(("Getting public key"));
+    
+    /* Get the public key */
+    pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+    if (!pk) {
+      status = SILC_SKE_STATUS_ERROR;
+      goto err;
+    }
+    ske->ke2_payload->pk_data = pk;
+    ske->ke2_payload->pk_len = pk_len;
+    
+    SILC_LOG_DEBUG(("Computing HASH value"));
+    
+    /* Compute the hash value */
+    memset(hash, 0, sizeof(hash));
+    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+    if (status != SILC_SKE_STATUS_OK)
+      goto err;
 
-  /* Get the public key */
-  pk = silc_pkcs_public_key_encode(public_key, &pk_len);
-  if (!pk) {
-    status = SILC_SKE_STATUS_ERROR;
-    goto err;
+    ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
+    memcpy(ske->hash, hash, hash_len);
+    ske->hash_len = hash_len;
+    
+    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_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len);
+    ske->ke2_payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
+    memcpy(ske->ke2_payload->sign_data, sign, sign_len);
+    memset(sign, 0, sizeof(sign));
+    ske->ke2_payload->sign_len = sign_len;
   }
-  ske->ke2_payload->pk_data = pk;
-  ske->ke2_payload->pk_len = pk_len;
   ske->ke2_payload->pk_type = pk_type;
 
-  SILC_LOG_DEBUG(("Computing HASH value"));
-
-  /* Compute the hash value */
-  memset(hash, 0, sizeof(hash));
-  status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
-
-  ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
-  memcpy(ske->hash, hash, hash_len);
-  ske->hash_len = hash_len;
-
-  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_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len);
-  ske->ke2_payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
-  memcpy(ske->ke2_payload->sign_data, sign, sign_len);
-  memset(sign, 0, sizeof(sign));
-  ske->ke2_payload->sign_len = sign_len;
-
   /* Encode the Key Exchange Payload */
   status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
                                      &payload_buf);