More client library rewrites (key agreement, plus other).
[silc.git] / lib / silcclient / client_prvmsg.c
index ec0e3b8de39a9b8dac782d6d47b42995ba225da1..25ee56503e512de1383d4f2f3358000ec962c9b8 100644 (file)
@@ -30,6 +30,7 @@ SilcBool silc_client_send_private_message(SilcClient client,
                                          SilcClientConnection conn,
                                          SilcClientEntry client_entry,
                                          SilcMessageFlags flags,
+                                         SilcHash hash,
                                          unsigned char *data,
                                          SilcUInt32 data_len)
 {
@@ -40,6 +41,8 @@ SilcBool silc_client_send_private_message(SilcClient client,
 
   if (!client || !conn || !client_entry)
     return FALSE;
+  if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
+    return FALSE;
 
   /* Encode private message payload */
   buffer =
@@ -49,7 +52,7 @@ SilcBool silc_client_send_private_message(SilcClient client,
                                TRUE, client_entry->internal.send_key,
                                client_entry->internal.hmac_send,
                                client->rng, NULL, conn->private_key,
-                               client->sha1hash, NULL);
+                               hash, NULL);
   if (!buffer) {
     SILC_LOG_ERROR(("Error encoding private message"));
     return FALSE;
@@ -76,10 +79,7 @@ typedef struct {
   unsigned int stopped      : 1;
 } *SilcClientPrivateMessageWait;
 
-/* Client resolving callback.  This continues the private message packet
-   processing in the packet processor thread, which is in waiting state
-   (for incoming packets) when we get here.  We can safely continue in
-   the thread and then return back to waiting when we do it synchronously. */
+/* Client resolving callback.  Continues with the private message processing */
 
 static void silc_client_private_message_resolved(SilcClient client,
                                                 SilcClientConnection conn,
@@ -87,15 +87,12 @@ static void silc_client_private_message_resolved(SilcClient client,
                                                 SilcDList clients,
                                                 void *context)
 {
-  if (!clients) {
-    silc_packet_free(context);
-    return;
-  }
+  /* If no client found, ignore the private message, a silent error */
+  if (!clients)
+    silc_fsm_next(context, silc_client_private_message_error);
 
   /* Continue processing the private message packet */
-  silc_fsm_set_state_context(&conn->internal->packet_thread, context);
-  silc_fsm_next(&conn->internal->packet_thread, silc_client_private_message);
-  silc_fsm_continue_sync(&conn->internal->packet_thread);
+  SILC_FSM_CALL_CONTINUE(context);
 }
 
 /* Private message received. */
@@ -113,24 +110,31 @@ SILC_FSM_STATE(silc_client_private_message)
   SilcUInt32 message_len;
   SilcClientPrivateMessageWait pmw;
 
-  if (packet->src_id_type != SILC_ID_CLIENT)
-    goto out;
+  SILC_LOG_DEBUG(("Received private message"));
+
+  if (packet->src_id_type != SILC_ID_CLIENT) {
+    /** Invalid packet */
+    silc_fsm_next(fsm, silc_client_private_message_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
-                     &remote_id, sizeof(remote_id)))
-    goto out;
+                     &remote_id, sizeof(remote_id))) {
+    /** Invalid source ID */
+    silc_fsm_next(fsm, silc_client_private_message_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   /* Check whether we know this client already */
   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
   if (!remote_client || !remote_client->nickname[0]) {
-    /* Resolve the client info.  We return back to packet thread to receive
-       other packets while we wait for the resolving to finish. */
+    /** Resolve client info */
     silc_client_unref_client(client, conn, remote_client);
-    silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &remote_id, NULL,
                                         silc_client_private_message_resolved,
-                                        packet);
-    silc_fsm_next(fsm, silc_client_connection_st_packet);
-    return SILC_FSM_CONTINUE;
+                                        fsm));
+    /* NOT REACHED */
   }
 
   if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
@@ -189,19 +193,27 @@ SILC_FSM_STATE(silc_client_private_message)
     /* Send the away message */
     silc_client_send_private_message(client, conn, remote_client,
                                     SILC_MESSAGE_FLAG_AUTOREPLY |
-                                    SILC_MESSAGE_FLAG_NOREPLY,
+                                    SILC_MESSAGE_FLAG_NOREPLY, NULL,
                                     conn->internal->away->away,
                                     strlen(conn->internal->away->away));
   }
 
  out:
   /** Packet processed */
+  silc_packet_free(packet);
   silc_client_unref_client(client, conn, remote_client);
   if (payload)
     silc_message_payload_free(payload);
+  return SILC_FSM_FINISH;
+}
+
+/* Private message error. */
+
+SILC_FSM_STATE(silc_client_private_message_error)
+{
+  SilcPacket packet = state_context;
   silc_packet_free(packet);
-  silc_fsm_next(fsm, silc_client_connection_st_packet);
-  return SILC_FSM_CONTINUE;
+  return SILC_FSM_FINISH;
 }
 
 #if 0 /* XXX we need to rethink this */
@@ -333,6 +345,33 @@ SilcBool silc_client_private_message_wait(SilcClientConnection conn,
 
 /*************************** Private Message Key ****************************/
 
+/* Sends private message key request.  Sender of this packet is initiator
+   when setting the private message key. */
+
+static SilcBool
+silc_client_send_private_message_key_request(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry)
+{
+  const char *cipher, *hmac;
+
+  SILC_LOG_DEBUG(("Sending private message key request"));
+
+  cipher = silc_cipher_get_name(client_entry->internal.send_key);
+  hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
+
+  /* Send the packet */
+  return silc_packet_send_va_ext(conn->stream,
+                                SILC_PACKET_PRIVATE_MESSAGE_KEY,
+                                0, 0, NULL, SILC_ID_CLIENT,
+                                &client_entry->id, NULL, NULL,
+                                SILC_STR_UI_SHORT(strlen(cipher)),
+                                SILC_STR_DATA(cipher, strlen(cipher)),
+                                SILC_STR_UI_SHORT(strlen(hmac)),
+                                SILC_STR_DATA(hmac, strlen(hmac)),
+                                SILC_STR_END);
+}
+
 /* Client resolving callback.  Here we simply mark that we are the responder
    side of this private message key request.  */
 
@@ -365,7 +404,9 @@ static void silc_client_private_message_key_cb(SilcClient client,
   client_entry->internal.prv_resp = TRUE;
 
   /* XXX we should notify application that remote wants to set up the
-     static key */
+     static key.  And we should tell if we already have key with remote.
+     Application should return status telling whether to delete the key
+     or not. */
 
  out:
   silc_free(cipher);
@@ -385,46 +426,26 @@ SILC_FSM_STATE(silc_client_private_message_key)
 
   if (packet->src_id_type != SILC_ID_CLIENT) {
     silc_packet_free(packet);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
                      &remote_id, sizeof(remote_id))) {
     silc_packet_free(packet);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
   /* Always resolve the remote client.  The actual packet is processed
      in the resolving callback. */
-  silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
+  SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                      client, conn, &remote_id, NULL,
                                       silc_client_private_message_key_cb,
-                                      packet);
-
- out:
-  silc_fsm_next(fsm, silc_client_connection_st_packet);
-  return SILC_FSM_CONTINUE;
+                                      fsm));
 }
 
-/* Adds private message key to the client library. The key will be used to
-   encrypt all private message between the client and the remote client
-   indicated by the `client_entry'. If the `key' is NULL and the boolean
-   value `generate_key' is TRUE the library will generate random key.
-   The `key' maybe for example pre-shared-key, passphrase or similar.
-   The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure
-   that the requirements of the SILC protocol are met. The API, however,
-   allows to allocate any cipher and HMAC.
-
-   If `responder' is TRUE then the sending and receiving keys will be
-   set according the client being the receiver of the private key.  If
-   FALSE the client is being the sender (or negotiator) of the private
-   key.
-
-   It is not necessary to set key for normal private message usage. If the
-   key is not set then the private messages are encrypted using normal
-   session keys. Setting the private key, however, increases the security.
-
-   Returns FALSE if the key is already set for the `client_entry', TRUE
-   otherwise. */
+/* Adds new private message key to `client_entry'.  If we are setting this
+   before receiving request for it from `client_entry' we will send the
+   request to the client.  Otherwise, we are responder side. */
 
 SilcBool silc_client_add_private_message_key(SilcClient client,
                                             SilcClientConnection conn,
@@ -432,15 +453,10 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
                                             const char *cipher,
                                             const char *hmac,
                                             unsigned char *key,
-                                            SilcUInt32 key_len,
-                                            SilcBool generate_key,
-                                            SilcBool responder)
+                                            SilcUInt32 key_len)
 {
-  unsigned char private_key[32];
-  SilcUInt32 len;
   SilcSKEKeyMaterial keymat;
   SilcBool ret;
-  int i;
 
   if (!client || !client_entry)
     return FALSE;
@@ -460,52 +476,41 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
   if (!silc_hmac_is_supported(hmac))
     return FALSE;
 
-  /* Generate key if not provided */
-  if (generate_key == TRUE) {
-    len = 32;
-    for (i = 0; i < len; i++)
-      private_key[i] = silc_rng_get_byte_fast(client->rng);
-    key = private_key;
-    key_len = len;
-  }
-
   /* Save the key */
   client_entry->internal.key = silc_memdup(key, key_len);
   client_entry->internal.key_len = key_len;
 
   /* Produce the key material as the protocol defines */
   keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
-                                             client->sha1hash);
+                                             conn->internal->sha1hash);
   if (!keymat)
     return FALSE;
 
   /* Set the key into use */
   ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
-                                               cipher, hmac, keymat,
-                                               responder);
-
-  if (!generate_key)
-    client_entry->internal.generated = FALSE;
+                                               cipher, hmac, keymat);
+  client_entry->internal.generated = FALSE;
 
   /* Free the key material */
   silc_ske_free_key_material(keymat);
 
+  /* If we are setting the key without a request from the remote client,
+     we will send request to remote. */
+  if (!client_entry->internal.prv_resp)
+    silc_client_send_private_message_key_request(client, conn, client_entry);
+
   return ret;
 }
 
 /* Same as above but takes the key material from the SKE key material
-   structure. This structure is received if the application uses the
-   silc_client_send_key_agreement to negotiate the key material. The
-   `cipher' and `hmac' SHOULD be provided as it is negotiated also in
-   the SKE protocol. */
+   structure. */
 
 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
                                                 SilcClientConnection conn,
                                                 SilcClientEntry client_entry,
                                                 const char *cipher,
                                                 const char *hmac,
-                                                SilcSKEKeyMaterial keymat,
-                                                SilcBool responder)
+                                                SilcSKEKeyMaterial keymat)
 {
   if (!client || !client_entry)
     return FALSE;
@@ -538,7 +543,7 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
     return FALSE;
 
   /* Set the keys */
-  if (responder == TRUE) {
+  if (client_entry->internal.prv_resp) {
     silc_cipher_set_key(client_entry->internal.send_key,
                        keymat->receive_enc_key,
                        keymat->enc_key_len);
@@ -575,54 +580,6 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
   return TRUE;
 }
 
-/* Sends private message key indicator.  The sender of this packet is
-   going to be the initiator, if and when, the users set up a static
-   private message key (not Key Agreement). */
-
-SilcBool
-silc_client_send_private_message_key_request(SilcClient client,
-                                            SilcClientConnection conn,
-                                            SilcClientEntry client_entry)
-{
-  SilcBufferStruct buffer;
-  int cipher_len, hmac_len;
-  const char *cipher, *hmac;
-  SilcBool ret;
-
-  if (!client || !conn || !client_entry)
-    return FALSE;
-
-  if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
-    return FALSE;
-
-  SILC_LOG_DEBUG(("Sending private message key indicator"));
-
-  cipher = silc_cipher_get_name(client_entry->internal.send_key);
-  cipher_len = strlen(cipher);
-  hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
-  hmac_len = strlen(hmac);
-
-  /* Create private message key payload */
-  memset(&buffer, 0, sizeof(buffer));
-  if (silc_buffer_format(&buffer,
-                        SILC_STR_UI_SHORT(cipher_len),
-                        SILC_STR_UI_XNSTRING(cipher,
-                                             cipher_len),
-                        SILC_STR_UI_SHORT(hmac_len),
-                        SILC_STR_UI_XNSTRING(hmac,
-                                             hmac_len),
-                        SILC_STR_END) < 0)
-    return FALSE;
-
-  /* Send the packet */
-  ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE_KEY,
-                            0, 0, NULL, SILC_ID_CLIENT, &client_entry->id,
-                            silc_buffer_datalen(&buffer), NULL, NULL);
-  silc_buffer_purge(&buffer);
-
-  return ret;
-}
-
 /* 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. */
@@ -648,6 +605,7 @@ SilcBool silc_client_del_private_message_key(SilcClient client,
   client_entry->internal.send_key = NULL;
   client_entry->internal.receive_key = NULL;
   client_entry->internal.key = NULL;
+  client_entry->internal.prv_resp = FALSE;
 
   return TRUE;
 }
@@ -674,12 +632,17 @@ silc_client_list_private_message_keys(SilcClient client,
   if (!client || !conn)
     return NULL;
 
-  if (!silc_idcache_get_all(conn->internal->client_cache, &list))
+  silc_mutex_lock(conn->internal->lock);
+  if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
+  }
 
   keys = silc_calloc(silc_list_count(list), sizeof(*keys));
-  if (!keys)
+  if (!keys) {
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
+  }
 
   silc_list_start(list);
   while ((id_cache = silc_list_get(list))) {
@@ -696,6 +659,8 @@ silc_client_list_private_message_keys(SilcClient client,
     }
   }
 
+  silc_mutex_unlock(conn->internal->lock);
+
   if (key_count)
     *key_count = count;