updates.
authorPekka Riikonen <priikone@silcnet.org>
Tue, 27 Feb 2001 21:37:44 +0000 (21:37 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Tue, 27 Feb 2001 21:37:44 +0000 (21:37 +0000)
14 files changed:
CHANGES
apps/silcd/packet_send.c
apps/silcd/protocol.c
doc/draft-riikonen-silc-pp-01.nroff
doc/draft-riikonen-silc-spec-01.nroff
lib/silcclient/client.c
lib/silcclient/idlist.c
lib/silcclient/idlist.h
lib/silcclient/protocol.c
lib/silcclient/silcapi.h
lib/silccrypt/silccipher.c
lib/silccrypt/silccipher.h
lib/silcske/silcske.c
lib/silcske/silcske.h

diff --git a/CHANGES b/CHANGES
index 3936b9d13b21d72d2af7bf102dfa92a2c76378dc..c97304a55f76848950fe85510fb64f7bdfca8e3b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,35 @@
+Tue Feb 27 20:24:25 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_ske_process_key_material_data as generic routine
+         to process any key material as the SILC protocol dictates.  The
+         function is used by the actual SKE library but can be used by
+         applications as well.  This relates to the private message keys
+         and the channel private keys since they must be processed the
+         same way the normal SILC session keys.  The protocol dictates
+         this.  Affected files: lib/silcske/silcske.[ch].
+
+         Added also silc_ske_free_key_material to free the
+         SilcSKEKeyMaterial structure.
+
+       * Defined silc_cipher_set_key function to set the key for
+         cipher without using the object's method function.  The affected
+         files: lib/silccrypt/silccipher.[ch].
+
+       * Implemented silc silc_client_add_private_message_key,
+         silc_client_add_private_message_key_ske, 
+         silc_client_del_private_message_key,
+         silc_client_list_private_message_keys and
+         silc_client_free_private_message_keys functions in the
+         client library.
+
+         Added functions silc_client_send_private_message_key to send
+         the Private Message Key payload and silc_client_private_message_key
+         to handle incoming Private Message Key payload.
+
+       * Added Cipher field to the Private Message Key payload to set
+         the cipher to be used.  If ignored, the default cipher defined
+         in the SILC protocol (aes-256-cbc) is used.
+
 Tue Feb 27 13:30:52 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
        * Removed lib/silcclient/ops.h file.
@@ -11,8 +43,8 @@ Tue Feb 27 13:30:52 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
          SILC Client Library.  Other files need not be included by
          the application anymore.
 
-       * Added new key_agreement client callback and also defined the
-         Key Agreement library API for the application.
+       * Added new key_agreement client operation callback and also
+         defined the Key Agreement library API for the application.
 
 Tue Feb 27 11:28:31 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
index b8c720c61b429ec5bd8b04aec8bdb6813ad128de..840fd68b3aa2d8dc3ed863c5896107a708782a14 100644 (file)
@@ -628,7 +628,7 @@ void silc_server_send_private_message(SilcServer server,
 {
   SilcBuffer buffer = packet->buffer;
 
-  /* Send and re-encrypt if private messge key does not exist */
+  /* Re-encrypt and send if private messge key does not exist */
   if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
 
     silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
index ecca5e688776ec5ea88428671ba5056370218227..ad86e001293603e345f9931b3bab9928a28dc100 100644 (file)
@@ -367,9 +367,12 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
                                            ctx->responder)) {
        protocol->state = SILC_PROTOCOL_STATE_ERROR;
        protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
+       silc_ske_free_key_material(keymat);
        return;
       }
 
+      silc_ske_free_key_material(keymat);
+
       /* Unregister the timeout task since the protocol has ended. 
         This was the timeout task to be executed if the protocol is
         not completed fast enough. */
index 0fa63ff2ed3928e013a014f874a6a8a874121b8e..3edb41106d7206e615f74865cb849145aff9ada1 100644 (file)
@@ -1543,6 +1543,12 @@ diagram represents the Private Message Key Payload.
 ~                      Private Message Key                      ~
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Cipher Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 .in 3
 
 .ce
@@ -1557,8 +1563,16 @@ o Private Message Key Length (2 bytes) - Indicates the length
   any other field.
 
 o Private Message Key (variable length) - The actual private
-  message key material.  This key is used as such as key material 
-  for encryption function.
+  message key material.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher Name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher to use
+  in the private message encryption.  If this field does not
+  exist then the default cipher of the SILC protocol is used.
+  See the [SILC1] for defined ciphers.
 .in 3
 
 
@@ -1972,10 +1986,9 @@ After the key material has been received from the SKE protocol it is
 processed as the [SILC3] describes.  If the key material is used as
 channel private key then the Sending Encryption Key, as defined in
 [SILC3] is used as the channel private key.  Other key material must
-be discarded.  If the key material is used as private message key then
-the keys and the IV's are used as defined in the [SILC3].  The HMAC
-key, however, must be discarded.  Any other use for the key material
-is undefined.
+be discarded.  The [SILC1] defines the way to use the key material if
+it is intended to be used as private message keys.  Any other use for
+the key material is undefined.
 
 
 .ti 0
index d31621949614bdc5a5566c35784b04c6e2e6fb05..473c8fe458c89e47087ac5af3bc87d95b32cfcd9 100644 (file)
@@ -1650,10 +1650,14 @@ The key material used as private message key is implementation issue.
 However, SILC_PACKET_KEY_AGREEMENT packet may be used to negotiate
 the key material.  If the key is normal pre-shared-key or randomly
 generated key, and the SILC_PACKET_KEY_AGREEMENT was not used, then
-the key material should be processed as defined in the [SILC3].  After
-processing the key material it is employed as defined in [SILC3],
-however, the HMAC key material must be discarded.
-
+the key material should be processed as defined in the [SILC3].  In
+the processing, however, the HASH, as defined in [SILC3] must be 
+ignored.  After processing the key material it is employed as defined
+in [SILC3], however, the HMAC key material must be discarded.
+
+If the key is pre-shared-key or randomly generated the implementations
+should use the SILC protocol's mandatory cipher as the cipher.  If the
+SKE was used to negotiate key material the cipher was negotiated as well.
 
 .ti 0
 4.7 Channel Message Sending and Reception
index 5e567801f60775d801ff09c6d381e00263a2630c..fe16923271e3e66a414e5e658cb6f6945b39bd97 100644 (file)
@@ -1119,15 +1119,12 @@ void silc_client_send_private_message(SilcClient client,
   hmac = conn->hmac;
 
   /* Set the packet context pointers. */
-  packetdata.flags = 0;
+  packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
   packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
   packetdata.src_id = conn->local_id_data;
   packetdata.src_id_len = SILC_ID_CLIENT_LEN;
   packetdata.src_id_type = SILC_ID_CLIENT;
-  if (client_entry)
-    packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
-  else
-    packetdata.dst_id = conn->local_id_data;
+  packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
   packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
   packetdata.dst_id_type = SILC_ID_CLIENT;
   packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
@@ -1147,7 +1144,7 @@ void silc_client_send_private_message(SilcClient client,
   packetdata.buffer = sock->outbuf;
 
   /* Encrypt payload of the packet. Encrypt with private message specific
-     key if it exist, otherwise with session key. */
+     key */
   cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data,
                          buffer->len, cipher->iv);
       
@@ -1158,6 +1155,7 @@ void silc_client_send_private_message(SilcClient client,
   silc_packet_assemble(&packetdata);
 
   /* Encrypt the header and padding of the packet. */
+  cipher = conn->send_key;
   silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
                      packetdata.src_id_len + packetdata.dst_id_len +
                      packetdata.padlen);
@@ -2373,3 +2371,503 @@ char *silc_client_chumode_char(unsigned int mode)
 
   return strdup(string);
 }
+
+/* Function that actually employes the received private message key */
+
+static void silc_client_private_message_key_cb(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcClientEntry *clients,
+                                              unsigned int clients_count,
+                                              void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+  unsigned char *key;
+  unsigned short key_len;
+  unsigned char *cipher;
+  int ret;
+
+  if (!clients)
+    goto out;
+
+  /* Parse the private message key payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI16_NSTRING(&key, &key_len),
+                            SILC_STR_UI16_STRING(&cipher),
+                            SILC_STR_END);
+  if (!ret)
+    goto out;
+
+  if (key_len > packet->buffer->len)
+    goto out;
+
+  /* Now take the key in use */
+  if (!silc_client_add_private_message_key(client, conn, clients[0],
+                                          cipher, key, key_len, FALSE))
+    goto out;
+
+  /* Print some info for application */
+  client->ops->say(client, conn, 
+                  "Received private message key from %s%s%s %s%s%s", 
+                  clients[0]->nickname,
+                  clients[0]->server ? "@" : "",
+                  clients[0]->server ? clients[0]->server : "",
+                  clients[0]->username ? "(" : "",
+                  clients[0]->username ? clients[0]->username : "",
+                  clients[0]->username ? ")" : "");
+
+ out:
+  silc_packet_context_free(packet);
+}
+
+/* Processes incoming Private Message Key payload. The libary always
+   accepts the key and takes it into use. */
+
+void silc_client_private_message_key(SilcClient client,
+                                    SilcSocketConnection sock,
+                                    SilcPacketContext *packet)
+{
+  SilcClientID *remote_id;
+
+  if (packet->src_id_type != SILC_ID_CLIENT)
+    return;
+
+  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                            SILC_ID_CLIENT);
+  if (!remote_id)
+    return;
+
+  silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+                                      silc_client_private_message_key_cb,
+                                      silc_packet_context_dup(packet));
+  silc_free(remote_id);
+}
+
+/* Adds private message key to the client library. The key will be used to
+   encrypt all private message between the client and the remote client
+   indicated by the `client_entry'. If the `key' is NULL and the boolean
+   value `generate_key' is TRUE the library will generate random key.
+   The `key' maybe for example pre-shared-key, passphrase or similar.
+   The `cipher' MAY be provided but SHOULD be NULL to assure that the
+   requirements of the SILC protocol are met. The API, however, allows
+   to allocate any cipher.
+
+   It is not necessary to set key for normal private message usage. If the
+   key is not set then the private messages are encrypted using normal
+   session keys. Setting the private key, however, increases the security. 
+
+   Returns FALSE if the key is already set for the `client_entry', TRUE
+   otherwise. */
+
+int silc_client_add_private_message_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcClientEntry client_entry,
+                                       char *cipher,
+                                       unsigned char *key,
+                                       unsigned int key_len,
+                                       int generate_key)
+{
+  unsigned char private_key[32];
+  unsigned int len;
+  int i;
+  SilcSKEKeyMaterial *keymat;
+
+  assert(client_entry);
+
+  /* Return FALSE if key already set */
+  if (client_entry->send_key && client_entry->receive_key)
+    return FALSE;
+
+  if (!cipher)
+    cipher = "aes-256-cbc";
+
+  /* Check the requested cipher */
+  if (!silc_cipher_is_supported(cipher))
+    return FALSE;
+
+  /* Generate key if not provided */
+  if (!key && generate_key == TRUE) {
+    len = 32;
+    for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
+    key = private_key;
+    key_len = len;
+    client_entry->generated = TRUE;
+  }
+
+  /* Save the key */
+  client_entry->key = silc_calloc(key_len, sizeof(*client_entry->key));
+  memcpy(client_entry->key, key, key_len);
+  client_entry->key_len = key_len;
+
+  /* Produce the key material as the protocol defines */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
+                                        client->md5hash, keymat) 
+      != SILC_SKE_STATUS_OK)
+    return FALSE;
+
+  /* Allocate the ciphers */
+  silc_cipher_alloc(cipher, &client_entry->send_key);
+  silc_cipher_alloc(cipher, &client_entry->receive_key);
+
+  /* Set the keys */
+  silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
+                     keymat->enc_key_len);
+  silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
+  silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
+                     keymat->enc_key_len);
+  silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
+
+  /* Free the key material */
+  silc_ske_free_key_material(keymat);
+
+  return TRUE;
+}
+
+/* Same as above but takes the key material from the SKE key material
+   structure. This structure is received if the application uses the
+   silc_client_send_key_agreement to negotiate the key material. The
+   `cipher' SHOULD be provided as it is negotiated also in the SKE
+   protocol. */
+
+int silc_client_add_private_message_key_ske(SilcClient client,
+                                           SilcClientConnection conn,
+                                           SilcClientEntry client_entry,
+                                           char *cipher,
+                                           SilcSKEKeyMaterial *key)
+{
+  assert(client_entry);
+
+  /* Return FALSE if key already set */
+  if (client_entry->send_key && client_entry->receive_key)
+    return FALSE;
+
+  if (!cipher)
+    cipher = "aes-256-cbc";
+
+  /* Check the requested cipher */
+  if (!silc_cipher_is_supported(cipher))
+    return FALSE;
+
+  /* Allocate the ciphers */
+  silc_cipher_alloc(cipher, &client_entry->send_key);
+  silc_cipher_alloc(cipher, &client_entry->receive_key);
+
+  /* Set the keys */
+  silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
+                     key->enc_key_len);
+  silc_cipher_set_iv(client_entry->send_key, key->send_iv);
+  silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
+                     key->enc_key_len);
+  silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
+
+  return TRUE;
+}
+
+/* Sends private message key payload to the remote client indicated by
+   the `client_entry'. If the `force_send' is TRUE the packet is sent
+   immediately. Returns FALSE if error occurs, TRUE otherwise. The
+   application should call this function after setting the key to the
+   client.
+
+   Note that the key sent using this function is sent to the remote client
+   through the SILC network. The packet is protected using normal session
+   keys. */
+
+int silc_client_send_private_message_key(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        int force_send)
+{
+  SilcSocketConnection sock = conn->sock;
+  SilcBuffer buffer;
+  int cipher_len;
+
+  if (!client_entry->send_key || !client_entry->key)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Sending private message key"));
+
+  cipher_len = strlen(client_entry->send_key->cipher->name);
+
+  /* Create private message key payload */
+  buffer = silc_buffer_alloc(2 + client_entry->key_len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(client_entry->key_len),
+                    SILC_STR_UI_XNSTRING(client_entry->key, 
+                                         client_entry->key_len),
+                    SILC_STR_UI_SHORT(cipher_len),
+                    SILC_STR_UI_XNSTRING(client_entry->send_key->cipher->name,
+                                         cipher_len),
+                    SILC_STR_END);
+
+  /* Send the packet */
+  silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
+                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+                         buffer->data, buffer->len, force_send);
+  silc_free(buffer);
+
+  return TRUE;
+}
+
+/* Removes the private message from the library. The key won't be used
+   after this to protect the private messages with the remote `client_entry'
+   client. Returns FALSE on error, TRUE otherwise. */
+
+int silc_client_del_private_message_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcClientEntry client_entry)
+{
+  assert(client_entry);
+
+  if (!client_entry->send_key && !client_entry->receive_key)
+    return FALSE;
+
+  silc_cipher_free(client_entry->send_key);
+  silc_cipher_free(client_entry->receive_key);
+
+  if (client_entry->key) {
+    memset(client_entry->key, 0, client_entry->key_len);
+    silc_free(client_entry->key);
+  }
+
+  client_entry->send_key = NULL;
+  client_entry->receive_key = NULL;
+  client_entry->key = NULL;
+
+  return TRUE;
+}
+
+/* Returns array of set private message keys associated to the connection
+   `conn'. Returns allocated SilcPrivateMessageKeys array and the array
+   count to the `key_count' argument. The array must be freed by the caller
+   by calling the silc_client_free_private_message_keys function. Note: 
+   the keys returned in the array is in raw format. It might not be desired
+   to show the keys as is. The application might choose not to show the keys
+   at all or to show the fingerprints of the keys. */
+
+SilcPrivateMessageKeys
+silc_client_list_private_message_keys(SilcClient client,
+                                     SilcClientConnection conn,
+                                     unsigned int *key_count)
+{
+  SilcPrivateMessageKeys keys;
+  unsigned int count = 0;
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list;
+  SilcClientEntry entry;
+
+  if (!silc_idcache_find_by_id(conn->client_cache, SILC_ID_CACHE_ANY, 
+                              SILC_ID_CLIENT, &list))
+    return NULL;
+
+  if (!silc_idcache_list_count(list)) {
+    silc_idcache_list_free(list);
+    return NULL;
+  }
+
+  keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
+
+  silc_idcache_list_first(list, &id_cache);
+  while (id_cache) {
+    entry = (SilcClientEntry)id_cache->context;
+
+    if (entry->send_key) {
+      keys[count].client_entry = entry;
+      keys[count].cipher = entry->send_key->cipher->name;
+      keys[count].key = entry->generated == FALSE ? entry->key : NULL;
+      keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
+      count++;
+    }
+
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+  }
+
+  if (key_count)
+    *key_count = count;
+
+  return keys;
+}
+
+/* Frees the SilcPrivateMessageKeys array returned by the function
+   silc_client_list_private_message_keys. */
+
+void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
+                                          unsigned int key_count)
+{
+  silc_free(keys);
+}
+
+/* Adds private key for channel. This may be set only if the channel's mode
+   mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
+   mode is not set. When channel has private key then the messages are
+   encrypted using that key. All clients on the channel must also know the
+   key in order to decrypt the messages. However, it is possible to have
+   several private keys per one channel. In this case only some of the
+   clients on the channel may now the one key and only some the other key.
+
+   The private key for channel is optional. If it is not set then the
+   channel messages are encrypted using the channel key generated by the
+   server. However, setting the private key (or keys) for the channel 
+   significantly adds security. If more than one key is set the library
+   will automatically try all keys at the message decryption phase. Note:
+   setting many keys slows down the decryption phase as all keys has to
+   be tried in order to find the correct decryption key. However, setting
+   a few keys does not have big impact to the decryption performace. 
+
+   NOTE: that this is entirely local setting. The key set using this function
+   is not sent to the network at any phase.
+
+   NOTE: If the key material was originated by the SKE protocol (using
+   silc_client_send_key_agreement) then the `key' MUST be the
+   key->send_enc_key as this is dictated by the SILC protocol. However,
+   currently it is not expected that the SKE key material would be used
+   as channel private key. However, this API allows it. */
+
+int silc_client_add_channel_private_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       char *cipher,
+                                       unsigned char *key,
+                                       unsigned int key_len)
+{
+
+  return TRUE;
+}
+
+/* Removes all private keys from the `channel'. The old channel key is used
+   after calling this to protect the channel messages. Returns FALSE on
+   on error, TRUE otherwise. */
+
+int silc_client_del_channel_private_keys(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcChannelEntry channel)
+{
+
+  return TRUE;
+}
+
+/* Removes and frees private key `key' from the channel `channel'. The `key'
+   is retrieved by calling the function silc_client_list_channel_private_keys.
+   The key is not used after this. If the key was last private key then the
+   old channel key is used hereafter to protect the channel messages. This
+   returns FALSE on error, TRUE otherwise. */
+
+int silc_client_del_channel_private_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       SilcChannelPrivateKey key)
+{
+
+  return TRUE;
+}
+
+/* Returns array (pointers) of private keys associated to the `channel'.
+   The caller must free the array by calling the function
+   silc_client_free_channel_private_keys. The pointers in the array may be
+   used to delete the specific key by giving the pointer as argument to the
+   function silc_client_del_channel_private_key. */
+
+SilcChannelPrivateKey *
+silc_client_list_channel_private_keys(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcChannelEntry channel,
+                                     unsigned int key_count)
+{
+
+  return NULL;
+}
+
+/* Frees the SilcChannelPrivateKey array. */
+
+void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
+                                          unsigned int key_count)
+{
+
+}
+
+/* Sends key agreement request to the remote client indicated by the
+   `client_entry'. If the caller provides the `hostname' and the `port'
+   arguments then the library will bind the client to that hostname and
+   that port for the key agreement protocol. It also sends the `hostname'
+   and the `port' in the key agreement packet to the remote client. This
+   would indicate that the remote client may initiate the key agreement
+   protocol to the `hostname' on the `port'.
+
+   If the `hostname' and `port' is not provided then empty key agreement
+   packet is sent to the remote client. The remote client may reply with
+   the same packet including its hostname and port. If the library receives
+   the reply from the remote client the `key_agreement' client operation
+   callback will be called to verify whether the user wants to perform the
+   key agreement or not. 
+
+   NOTE: If the application provided the `hostname' and the `port' and the 
+   remote side initiates the key agreement protocol it is not verified
+   from the user anymore whether the protocol should be executed or not.
+   By setting the `hostname' and `port' the user gives permission to
+   perform the protocol (we are responder in this case).
+
+   NOTE: If the remote side decides not to initiate the key agreement
+   or decides not to reply with the key agreement packet then we cannot
+   perform the key agreement at all. If the key agreement protocol is
+   performed the `completion' callback with the `context' will be called.
+   If remote side decides to ignore the request the `completion' will never
+   be called and the caller is responsible of freeing the `context' memory. 
+   The application can do this by setting, for example, timeout. */
+
+void silc_client_send_key_agreement(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcClientEntry client_entry,
+                                   char *hostname,
+                                   int port,
+                                   SilcKeyAgreementCallback completion,
+                                   void *context)
+{
+
+}
+
+/* Performs the actual key agreement protocol. Application may use this
+   to initiate the key agreement protocol. This can be called for example
+   after the application has received the `key_agreement' client operation,
+   and did not return TRUE from it.
+
+   The `hostname' is the remote hostname (or IP address) and the `port'
+   is the remote port. The `completion' callblack with the `context' will
+   be called after the key agreement protocol.
+   
+   NOTE: If the application returns TRUE in the `key_agreement' client
+   operation the library will automatically start the key agreement. In this
+   case the application must not call this function. However, application
+   may choose to just ignore the `key_agreement' client operation (and
+   merely just print information about it on the screen) and call this
+   function when the user whishes to do so (by, for example, giving some
+   specific command). Thus, the API provides both, automatic and manual
+   initiation of the key agreement. Calling this function is the manual
+   initiation and returning TRUE in the `key_agreement' client operation
+   is the automatic initiation. */
+
+void silc_client_perform_key_agreement(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientEntry client_entry,
+                                      char *hostname,
+                                      int port,
+                                      SilcKeyAgreementCallback completion,
+                                      void *context)
+{
+
+}
+
+/* This function can be called to unbind the hostname and the port for
+   the key agreement protocol. However, this function has effect only 
+   before the key agreement protocol has been performed. After it has
+   been performed the library will automatically unbind the port. The 
+   `client_entry' is the client to which we sent the key agreement 
+   request. */
+
+void silc_client_abort_key_agreement(SilcClient client,
+                                    SilcClientConnection conn,
+                                    SilcClientEntry client_entry)
+{
+
+}
index c50b8a5f8d5420e1a6d2f3fd0d5ac279cd33f9e7..1197a90bf47e636f4290a53f19efde326292279a 100644 (file)
@@ -41,7 +41,8 @@ SILC_CLIENT_CMD_FUNC(get_client_callback)
                                          i->nickname, i->server,
                                          &clients_count);
   if (clients) {
-    i->completion(i->cmd->client, i->cmd->conn, NULL, 0, i->context);
+    i->completion(i->cmd->client, i->cmd->conn, clients, 
+                 clients_count, i->context);
     i->found = TRUE;
     silc_free(clients);
   }
@@ -126,8 +127,10 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client,
   if (!silc_idcache_find_by_data_loose(conn->client_cache, nickname, &list))
     return NULL;
 
-  if (silc_idcache_list_count(list) == 0)
+  if (!silc_idcache_list_count(list)) {
+    silc_idcache_list_free(list);
     return NULL;
+  }
 
   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
   *clients_count = silc_idcache_list_count(list);
@@ -273,6 +276,74 @@ SilcClientEntry silc_client_get_client_by_id(SilcClient client,
   return (SilcClientEntry)id_cache->context;
 }
 
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcClientID *client_id;
+  SilcGetClientCallback completion;
+  void *context;
+  int found;
+} *GetClientByIDInternal;
+
+SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
+{
+  GetClientByIDInternal i = (GetClientByIDInternal)context;
+  SilcClientEntry entry;
+
+  /* Get the client */
+  entry = silc_client_get_client_by_id(i->client, i->conn,
+                                      i->client_id);
+  if (entry) {
+    i->completion(i->client, i->conn, &entry, 1, i->context);
+    i->found = TRUE;
+  }
+}
+
+static void silc_client_get_client_by_id_destructor(void *context)
+{
+  GetClientByIDInternal i = (GetClientByIDInternal)context;
+
+  if (i->found == FALSE)
+    i->completion(i->client, i->conn, NULL, 0, i->context);
+
+  if (i->client_id)
+    silc_free(i->client_id);
+  silc_free(i);
+}
+
+/* Same as above but will always resolve the information from the server.
+   Use this only if you know that you don't have the entry and the only
+   thing you know about the client is its ID. */
+
+void silc_client_get_client_by_id_resolve(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientID *client_id,
+                                         SilcGetClientCallback completion,
+                                         void *context)
+{
+  SilcBuffer idp;
+  GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
+
+  idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+  silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
+                          ++conn->cmd_ident,
+                          1, 3, idp->data, idp->len);
+  silc_buffer_free(idp);
+
+  i->client = client;
+  i->conn = conn;
+  i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
+  i->completion = completion;
+  i->context = context;
+      
+  /* Add pending callback */
+  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                             ++conn->cmd_ident, 
+                             silc_client_get_client_by_id_destructor,
+                             silc_client_command_get_client_by_id_callback, 
+                             (void *)i);
+}
+
 /* Finds entry for channel by the channel name. Returns the entry or NULL
    if the entry was not found. It is found only if the client is joined
    to the channel. */
index d5bfbf98ba4b5f8f19d26d104a71a88a741fd467..ecf8b9b44e8d94ba51d803457c2e5910dd58fd27 100644 (file)
    client entry. This entry also includes the private message keys if
    they are used. */
 typedef struct SilcClientEntryStruct {
-  char *nickname;             /* nickname */
-  char *username;            /* username[@host] */
-  char *server;                      /* SILC server name */
-  char *realname;
+  char *nickname;              /* nickname */
+  char *username;              /* username[@host] */
+  char *server;                        /* SILC server name */
+  char *realname;              /* Realname (userinfo) */
   unsigned int num;
-  SilcClientID *id;
-
-  /* Keys, these are defined if private message key has been defined 
-     with the remote client. */
-  SilcCipher send_key;
-  SilcCipher receive_key;
+  SilcClientID *id;            /* The Client ID */
+  SilcCipher send_key;         /* Private message key for sending */
+  SilcCipher receive_key;      /* Private message key for receiving */
+  unsigned char *key;          /* Set only if appliation provided the
+                                  key material. NULL if the library 
+                                  generated the key. */
+  unsigned int key_len;
+  int generated;               /* TRUE if library generated the key */
 } *SilcClientEntry;
 
 /* Client and its mode on a channel */
@@ -66,10 +68,6 @@ typedef struct SilcChannelEntryStruct {
   unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
 } *SilcChannelEntry;
 
-/* Command identifier used by ID list routines when sending WHOIS/IDENTIFY
-   commands to routers. */
-#define SILC_IDLIST_IDENT 3333
-
 /* Prototypes (some functions are defined in the silcapi.h) */
 
 SilcClientEntry silc_idlist_get_client(SilcClient client,
index c50c2318c23e5959aaa76f9b8d776afcc5bd3638..d0734863b377f9bc9df1208c1d5c7016e27fce5b 100644 (file)
@@ -365,6 +365,8 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
                                       ctx->ske->prop->pkcs,
                                       ctx->ske->prop->hash);
 
+      silc_ske_free_key_material(keymat);
+
       /* Protocol has ended, call the final callback */
       if (protocol->final_callback)
        protocol->execute_final(client->timeout_queue, 0, protocol, fd);
index 5f95c7c11843c7d3b7b96eade451ce87b15a5033..658fc5f7adfb8504ddd9874634340ee1654c68cf 100644 (file)
@@ -64,7 +64,6 @@ typedef struct {
   void (*private_message)(SilcClient client, SilcClientConnection conn,
                          SilcClientEntry sender, char *msg);
 
-
   /* Notify message to the client. The notify arguments are sent in the
      same order as servers sends them. The arguments are same as received
      from the server except for ID's.  If ID is received application receives
@@ -76,7 +75,6 @@ typedef struct {
   void (*notify)(SilcClient client, SilcClientConnection conn, 
                 SilcNotifyType type, ...);
 
-
   /* Command handler. This function is called always in the command function.
      If error occurs it will be called as well. `conn' is the associated
      client connection. `cmd_context' is the command context that was
@@ -89,7 +87,6 @@ typedef struct {
                  SilcClientCommandContext cmd_context, int success,
                  SilcCommand command);
 
-
   /* Command reply handler. This function is called always in the command reply
      function. If error occurs it will be called as well. Normal scenario
      is that it will be called after the received command data has been parsed
@@ -156,7 +153,8 @@ typedef struct {
      This is called after we have received an key agreement packet or an
      reply to our key agreement packet. This returns TRUE if the user wants
      the library to perform the key agreement protocol and FALSE if it is not
-     desired. */
+     desired (application may start it later by calling the function
+     silc_client_perform_key_agreement). */
   int (*key_agreement)(SilcClient client, SilcClientConnection conn,
                       SilcClientEntry client_entry, char *hostname,
                       int port);
@@ -313,6 +311,15 @@ SilcClientEntry silc_client_get_client_by_id(SilcClient client,
                                             SilcClientConnection conn,
                                             SilcClientID *client_id);
 
+/* Same as above but will always resolve the information from the server.
+   Use this only if you know that you don't have the entry and the only
+   thing you know about the client is its ID. */
+void silc_client_get_client_by_id_resolve(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientID *client_id,
+                                         SilcGetClientCallback completion,
+                                         void *context);
+
 /* Finds entry for channel by the channel name. Returns the entry or NULL
    if the entry was not found. It is found only if the client is joined
    to the channel. */
@@ -374,32 +381,49 @@ void silc_client_command_pending(SilcClientConnection conn,
    indicated by the `client_entry'. If the `key' is NULL and the boolean
    value `generate_key' is TRUE the library will generate random key.
    The `key' maybe for example pre-shared-key, passphrase or similar.
+   The `cipher' MAY be provided but SHOULD be NULL to assure that the
+   requirements of the SILC protocol are met. The API, however, allows
+   to allocate any cipher.
 
    It is not necessary to set key for normal private message usage. If the
    key is not set then the private messages are encrypted using normal
    session keys. Setting the private key, however, increases the security. 
 
-   Note that the key set using this function is sent to the remote client
-   through the SILC network. The packet is protected using normal session
-   keys. 
-
    Returns FALSE if the key is already set for the `client_entry', TRUE
    otherwise. */
 int silc_client_add_private_message_key(SilcClient client,
                                        SilcClientConnection conn,
-                                       SilcClientConnection client_entry,
+                                       SilcClientEntry client_entry,
+                                       char *cipher,
                                        unsigned char *key,
                                        unsigned int key_len,
                                        int generate_key);
 
 /* Same as above but takes the key material from the SKE key material
    structure. This structure is received if the application uses the
-   silc_client_send_key_agreement to negotiate the key material. */
+   silc_client_send_key_agreement to negotiate the key material. The
+   `cipher' SHOULD be provided as it is negotiated also in the SKE
+   protocol. */
 int silc_client_add_private_message_key_ske(SilcClient client,
                                            SilcClientConnection conn,
-                                           SilcClientConnection client_entry,
+                                           SilcClientEntry client_entry,
+                                           char *cipher,
                                            SilcSKEKeyMaterial *key);
 
+/* Sends private message key payload to the remote client indicated by
+   the `client_entry'. If the `force_send' is TRUE the packet is sent
+   immediately. Returns FALSE if error occurs, TRUE otherwise. The
+   application should call this function after setting the key to the
+   client.
+
+   Note that the key sent using this function is sent to the remote client
+   through the SILC network. The packet is protected using normal session
+   keys. */
+int silc_client_send_private_message_key(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        int force_send);
+
 /* Removes the private message from the library. The key won't be used
    after this to protect the private messages with the remote `client_entry'
    client. Returns FALSE on error, TRUE otherwise. */
@@ -412,6 +436,7 @@ int silc_client_del_private_message_key(SilcClient client,
    function. */
 typedef struct {
   SilcClientEntry client_entry;       /* The remote client entry */
+  char *cipher;                              /* The cipher name */
   unsigned char *key;                /* The original key, If the appliation
                                         provided it. This is NULL if the
                                         library generated the key or if
@@ -467,6 +492,7 @@ void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
 int silc_client_add_channel_private_key(SilcClient client,
                                        SilcClientConnection conn,
                                        SilcChannelEntry channel,
+                                       char *cipher,
                                        unsigned char *key,
                                        unsigned int key_len);
 
@@ -479,6 +505,7 @@ int silc_client_del_channel_private_keys(SilcClient client,
 
 /* Structure to hold one channel private key. */
 typedef struct {
+  char *cipher;                              /* The cipher name */
   unsigned char *key;                /* The key */
   unsigned int key_len;                      /* The key length */
 } *SilcChannelPrivateKey;
@@ -537,12 +564,18 @@ typedef void (*SilcKeyAgreementCallback)(SilcClient client,
    the same packet including its hostname and port. If the library receives
    the reply from the remote client the `key_agreement' client operation
    callback will be called to verify whether the user wants to perform the
-   key agreement or not.
+   key agreement or not. 
+
+   NOTE: If the application provided the `hostname' and the `port' and the 
+   remote side initiates the key agreement protocol it is not verified
+   from the user anymore whether the protocol should be executed or not.
+   By setting the `hostname' and `port' the user gives permission to
+   perform the protocol (we are responder in this case).
 
-   Note, that if the remote side decides not to initiate the key agreement
+   NOTE: If the remote side decides not to initiate the key agreement
    or decides not to reply with the key agreement packet then we cannot
    perform the key agreement at all. If the key agreement protocol is
-   performed the `completion' callback with `context' will be called.
+   performed the `completion' callback with the `context' will be called.
    If remote side decides to ignore the request the `completion' will never
    be called and the caller is responsible of freeing the `context' memory. 
    The application can do this by setting, for example, timeout. */
@@ -554,6 +587,33 @@ void silc_client_send_key_agreement(SilcClient client,
                                    SilcKeyAgreementCallback completion,
                                    void *context);
 
+/* Performs the actual key agreement protocol. Application may use this
+   to initiate the key agreement protocol. This can be called for example
+   after the application has received the `key_agreement' client operation,
+   and did not return TRUE from it.
+
+   The `hostname' is the remote hostname (or IP address) and the `port'
+   is the remote port. The `completion' callblack with the `context' will
+   be called after the key agreement protocol.
+   
+   NOTE: If the application returns TRUE in the `key_agreement' client
+   operation the library will automatically start the key agreement. In this
+   case the application must not call this function. However, application
+   may choose to just ignore the `key_agreement' client operation (and
+   merely just print information about it on the screen) and call this
+   function when the user whishes to do so (by, for example, giving some
+   specific command). Thus, the API provides both, automatic and manual
+   initiation of the key agreement. Calling this function is the manual
+   initiation and returning TRUE in the `key_agreement' client operation
+   is the automatic initiation. */
+void silc_client_perform_key_agreement(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientEntry client_entry,
+                                      char *hostname,
+                                      int port,
+                                      SilcKeyAgreementCallback completion,
+                                      void *context);
+
 /* This function can be called to unbind the hostname and the port for
    the key agreement protocol. However, this function has effect only 
    before the key agreement protocol has been performed. After it has
index 8162abd9f046be88a90bf7bac1182a8b57434fc9..74b8bb98a3d5167f3fa55842a861bad5b98215b9 100644 (file)
@@ -200,7 +200,7 @@ int silc_cipher_alloc(const unsigned char *name, SilcCipher *new_cipher)
       c = c->next;
     }
 
-    if (!c)
+    if (!c || !c->cipher->context_len)
       goto check_builtin;
 
     /* Set the pointers */
@@ -222,6 +222,7 @@ int silc_cipher_alloc(const unsigned char *name, SilcCipher *new_cipher)
 
   if (silc_cipher_builtin_list[i].name == NULL) {
     silc_free(*new_cipher);
+    *new_cipher = NULL;
     return FALSE;
   }
 
@@ -313,33 +314,41 @@ char *silc_cipher_get_supported()
   return list;
 }
 
+/* Sets the key for the cipher */
+
+int silc_cipher_set_key(SilcCipher cipher, const unsigned char *key,
+                       unsigned int keylen)
+{
+  return cipher->cipher->set_key(cipher->context, key, keylen);
+}
+
 /* Sets the IV (initial vector) for the cipher. */
 
-void silc_cipher_set_iv(SilcCipher itself, const unsigned char *iv)
+void silc_cipher_set_iv(SilcCipher cipher, const unsigned char *iv)
 {
-  memset(&itself->iv, 0, sizeof(itself->iv));
-  memcpy(&itself->iv, iv, itself->cipher->block_len);
+  memset(&cipher->iv, 0, sizeof(cipher->iv));
+  memcpy(&cipher->iv, iv, cipher->cipher->block_len);
 }
 
 /* Returns the IV (initial vector) of the cipher. The IV is returned 
    to 'iv' argument. */
 
-void silc_cipher_get_iv(SilcCipher itself, unsigned char *iv)
+void silc_cipher_get_iv(SilcCipher cipher, unsigned char *iv)
 {
-  memcpy(iv, &itself->iv, itself->cipher->block_len);
+  memcpy(iv, &cipher->iv, cipher->cipher->block_len);
 }
 
 /* Returns the key length of the cipher. */
 
-unsigned int silc_cipher_get_key_len(SilcCipher itself
+unsigned int silc_cipher_get_key_len(SilcCipher cipher
                                     const unsigned char *name)
 {
-  return itself->cipher->key_len;
+  return cipher->cipher->key_len;
 }
 
 /* Returns the block size of the cipher. */
 
-unsigned int silc_cipher_get_block_len(SilcCipher itself)
+unsigned int silc_cipher_get_block_len(SilcCipher cipher)
 {
-  return itself->cipher->block_len;
+  return cipher->cipher->block_len;
 }
index f408b1ffa70ed115e2aa8dfcb921e740fa229524..6eea2c9c0a647ba9eb4fcf7a040d05c437c98256 100644 (file)
@@ -122,10 +122,12 @@ int silc_cipher_alloc(const unsigned char *name, SilcCipher *new_cipher);
 void silc_cipher_free(SilcCipher cipher);
 int silc_cipher_is_supported(const unsigned char *name);
 char *silc_cipher_get_supported();
-void silc_cipher_set_iv(SilcCipher itself, const unsigned char *iv);
-void silc_cipher_get_iv(SilcCipher itself, unsigned char *iv);
-unsigned int silc_cipher_get_key_len(SilcCipher itself, 
+int silc_cipher_set_key(SilcCipher cipher, const unsigned char *key,
+                       unsigned int keylen);
+void silc_cipher_set_iv(SilcCipher cipher, const unsigned char *iv);
+void silc_cipher_get_iv(SilcCipher cipher, unsigned char *iv);
+unsigned int silc_cipher_get_key_len(SilcCipher cipher, 
                                     const unsigned char *name);
-unsigned int silc_cipher_get_block_len(SilcCipher itself);
+unsigned int silc_cipher_get_block_len(SilcCipher cipher);
 
 #endif
index e06dab55e17eb029167663ce39d112383a8ddfdb..e82b41a218af2bc32b7e0ddb2229ac7266b86a87 100644 (file)
@@ -1203,53 +1203,43 @@ SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
   return status;
 }
 
-/* Processes negotiated key material as protocol specifies. This returns
-   the actual keys to be used in the SILC. */
+/* Processes the provided key material `data' as the SILC protocol 
+   specification specifies. */
 
-SilcSKEStatus silc_ske_process_key_material(SilcSKE ske, 
-                                           unsigned int req_iv_len,
-                                           unsigned int req_enc_key_len,
-                                           unsigned int req_hmac_key_len,
-                                           SilcSKEKeyMaterial *key)
+SilcSKEStatus 
+silc_ske_process_key_material_data(unsigned char *data,
+                                  unsigned int data_len,
+                                  unsigned int req_iv_len,
+                                  unsigned int req_enc_key_len,
+                                  unsigned int req_hmac_key_len,
+                                  SilcHash hash,
+                                  SilcSKEKeyMaterial *key)
 {
-  int klen;
   SilcBuffer buf;
-  unsigned char *tmpbuf;
-  unsigned char hash[32];
-  unsigned int hash_len = ske->prop->hash->hash->hash_len;
+  unsigned char hashd[32];
+  unsigned int hash_len = req_hmac_key_len;
   unsigned int enc_key_len = req_enc_key_len / 8;
-  int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* Encode KEY to binary data */
-  tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
-
-  buf = silc_buffer_alloc(1 + klen + hash_len);
+  buf = silc_buffer_alloc(1 + data_len);
   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
-  ret = silc_buffer_format(buf,
-                          SILC_STR_UI_CHAR(0),
-                          SILC_STR_UI_XNSTRING(tmpbuf, klen),
-                          SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
-                          SILC_STR_END);
-  if (ret == -1) {
-    memset(tmpbuf, 0, klen);
-    silc_free(tmpbuf);
-    silc_buffer_free(buf);
-    return SILC_SKE_STATUS_ERROR;
-  }
+  silc_buffer_format(buf,
+                    SILC_STR_UI_CHAR(0),
+                    SILC_STR_UI_XNSTRING(data, data_len),
+                    SILC_STR_END);
 
   /* Take IVs */
-  memset(hash, 0, sizeof(hash));
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 0;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+  silc_hash_make(hash, buf->data, buf->len, hashd);
   key->send_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
-  memcpy(key->send_iv, hash, req_iv_len);
-  memset(hash, 0, sizeof(hash));
+  memcpy(key->send_iv, hashd, req_iv_len);
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 1;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+  silc_hash_make(hash, buf->data, buf->len, hashd);
   key->receive_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
-  memcpy(key->receive_iv, hash, req_iv_len);
+  memcpy(key->receive_iv, hashd, req_iv_len);
   key->iv_len = req_iv_len;
 
   /* Take the encryption keys. If requested key size is more than
@@ -1267,27 +1257,27 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     
     /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, k1);
+    silc_hash_make(hash, buf->data, buf->len, k1);
     
     /* Take second round */
-    dist = silc_buffer_alloc(klen + hash_len);
+    dist = silc_buffer_alloc(data_len + hash_len);
     silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(tmpbuf, klen),
+                      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(ske->prop->hash, dist->data, dist->len, k2);
+    silc_hash_make(hash, dist->data, dist->len, k2);
     
     /* Take third round */
-    dist = silc_buffer_realloc(dist, klen + hash_len + hash_len);
-    silc_buffer_pull(dist, klen + hash_len);
+    dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+    silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(k2, hash_len),
                       SILC_STR_END);
-    silc_buffer_push(dist, klen + hash_len);
+    silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(ske->prop->hash, dist->data, dist->len, k3);
+    silc_hash_make(hash, dist->data, dist->len, k3);
 
     /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
@@ -1307,10 +1297,10 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     silc_buffer_free(dist);
   } else {
     /* Take normal hash as key */
-    memset(hash, 0, sizeof(hash));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+    memset(hashd, 0, sizeof(hashd));
+    silc_hash_make(hash, buf->data, buf->len, hashd);
     key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
-    memcpy(key->send_enc_key, hash, enc_key_len);
+    memcpy(key->send_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
   }
 
@@ -1326,27 +1316,27 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     
     /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, k1);
+    silc_hash_make(hash, buf->data, buf->len, k1);
     
     /* Take second round */
-    dist = silc_buffer_alloc(klen + hash_len);
+    dist = silc_buffer_alloc(data_len + hash_len);
     silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(tmpbuf, klen),
+                      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(ske->prop->hash, dist->data, dist->len, k2);
+    silc_hash_make(hash, dist->data, dist->len, k2);
     
     /* Take third round */
-    dist = silc_buffer_realloc(dist, klen + hash_len + hash_len);
-    silc_buffer_pull(dist, klen + hash_len);
+    dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+    silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(k2, hash_len),
                       SILC_STR_END);
-    silc_buffer_push(dist, klen + hash_len);
+    silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(ske->prop->hash, dist->data, dist->len, k3);
+    silc_hash_make(hash, dist->data, dist->len, k3);
 
     /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
@@ -1366,23 +1356,81 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     silc_buffer_free(dist);
   } else {
     /* Take normal hash as key */
-    memset(hash, 0, sizeof(hash));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+    memset(hashd, 0, sizeof(hashd));
+    silc_hash_make(hash, buf->data, buf->len, hashd);
     key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
-    memcpy(key->receive_enc_key, hash, enc_key_len);
+    memcpy(key->receive_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
   }
 
   /* Take HMAC key */
-  memset(hash, 0, sizeof(hash));
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 4;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+  silc_hash_make(hash, buf->data, buf->len, hashd);
   key->hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
-  memcpy(key->hmac_key, hash, req_hmac_key_len);
+  memcpy(key->hmac_key, hashd, req_hmac_key_len);
   key->hmac_key_len = req_hmac_key_len;
 
+  silc_buffer_free(buf);
+
+  return SILC_SKE_STATUS_OK;
+}
+
+/* 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, 
+                                           unsigned int req_iv_len,
+                                           unsigned int req_enc_key_len,
+                                           unsigned int req_hmac_key_len,
+                                           SilcSKEKeyMaterial *key)
+{
+  SilcSKEStatus status;
+  SilcBuffer buf;
+  unsigned char *tmpbuf;
+  int klen;
+
+  /* Encode KEY to binary data */
+  tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
+
+  buf = silc_buffer_alloc(klen + ske->hash_len);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+  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);
+
   memset(tmpbuf, 0, klen);
   silc_free(tmpbuf);
+  silc_buffer_free(buf);
 
-  return SILC_SKE_STATUS_OK;
+  return status;
+}
+
+/* Free key material structure */
+
+void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
+{
+  if (key->send_iv)
+    silc_free(key->send_iv);
+  if (key->receive_iv)
+    silc_free(key->receive_iv);
+  if (key->send_enc_key) {
+    memset(key->send_enc_key, 0, key->enc_key_len);
+    silc_free(key->send_enc_key);
+  }
+  if (key->receive_enc_key) {
+    memset(key->receive_enc_key, 0, key->enc_key_len);
+    silc_free(key->receive_enc_key);
+  }
+  if (key->hmac_key) {
+    memset(key->hmac_key, 0, key->hmac_key_len);
+    silc_free(key->hmac_key);
+  }
 }
index 02d94dced6f77fcae772221bafc22d41e438376b..1a2bdc28cc1dbedc64f61f01ed300368bb182695 100644 (file)
@@ -204,6 +204,14 @@ SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n,
 SilcSKEStatus silc_ske_make_hash(SilcSKE ske, 
                                 unsigned char *return_hash,
                                 unsigned int *return_hash_len);
+SilcSKEStatus 
+silc_ske_process_key_material_data(unsigned char *data,
+                                  unsigned int data_len,
+                                  unsigned int req_iv_len,
+                                  unsigned int req_enc_key_len,
+                                  unsigned int req_hmac_key_len,
+                                  SilcHash hash,
+                                  SilcSKEKeyMaterial *key);
 SilcSKEStatus silc_ske_process_key_material(SilcSKE ske, 
                                            unsigned int req_iv_len,
                                            unsigned int req_enc_key_len,
@@ -212,4 +220,6 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
 SilcSKEStatus silc_ske_check_version(SilcSKE ske,
                                     unsigned char *version,
                                     unsigned int version_len);
+void silc_ske_free_key_material(SilcSKEKeyMaterial *key);
+
 #endif