Added SSH public key support to SKR
[crypto.git] / lib / silcskr / silcskr.c
index 2a495f4362945475dda58cff5fa4d0ff32c5c83d..f720d34584d7b7157f7c6d8f876311f5a05ee25b 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 - 2007 Pekka Riikonen
+  Copyright (C) 2005 - 2008 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 */
 
-#include "silc.h"
+#include "silccrypto.h"
 #include "silcskr.h"
 
-/* XXX Locking, when removing keys */
-
 /************************** Types and definitions ***************************/
 
 /* Search constraints */
@@ -53,16 +51,16 @@ typedef struct {
 
 #if defined(SILC_DEBUG)
 static const char *find_name[] = {
-  "PKCS TYPE",
-  "USERNAME",
-  "HOST",
-  "REALNAME",
-  "EMAIL",
-  "ORG",
-  "COUNTRY",
+  "PKCS TYPE ",
+  "USERNAME  ",
+  "HOST      ",
+  "REALNAME  ",
+  "EMAIL     ",
+  "ORG       ",
+  "COUNTRY   ",
   "PUBLIC KEY",
-  "CONTEXT",
-  "USAGE",
+  "CONTEXT   ",
+  "USAGE     ",
   NULL
 };
 #endif /* SILC_DEBUG */
@@ -80,19 +78,19 @@ static void silc_skr_type_string(SilcSKRFindType type, void *data,
   case SILC_SKR_FIND_PKCS_TYPE:
   case SILC_SKR_FIND_USAGE:
     silc_snprintf(retbuf, retbuf_size, "[%s] [%d]", find_name[type],
-            (int)SILC_PTR_TO_32(data));
+                 (int)SILC_PTR_TO_32(data));
     break;
 
   case SILC_SKR_FIND_PUBLIC_KEY:
+  case SILC_SKR_FIND_CONTEXT:
     silc_snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
     break;
 
   default:
     silc_snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
-            (char *)data);
+                 (char *)data);
   }
 }
-
 #endif /* SILC_DEBUG */
 
 /* Hash table destructor for search constraints */
@@ -101,6 +99,7 @@ static void silc_skr_find_destructor(void *key, void *context,
                                     void *user_context)
 {
   SilcSKRFindType type = SILC_PTR_TO_32(key);
+  SilcPKCSType pkcs_type = SILC_PTR_TO_32(user_context);
 
   switch (type) {
   case SILC_SKR_FIND_PKCS_TYPE:
@@ -113,6 +112,12 @@ static void silc_skr_find_destructor(void *key, void *context,
     break;
 
   default:
+    /* In SILC and SSH2 keys all entries are referenced from the public key
+       so don't free them.  This test is valid only when removing key
+       from the repository. */
+    if (pkcs_type == SILC_PKCS_SILC || pkcs_type == SILC_PKCS_SSH2)
+      break;
+
     silc_free(context);
   }
 }
@@ -123,11 +128,13 @@ static void silc_skr_destructor(void *key, void *context, void *user_context)
 {
   SilcSKREntry type = key;
   SilcSKRKeyInternal entry = context;
+  SilcPKCSType pkcs_type = silc_pkcs_get_type(entry->key.key);
 
   /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
      shares same context with the key entry. */
   if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
-    silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data, NULL);
+    silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data,
+                            SILC_32_TO_PTR(pkcs_type));
   silc_free(type);
 
   /* Destroy key */
@@ -135,6 +142,8 @@ static void silc_skr_destructor(void *key, void *context, void *user_context)
   if (entry->refcnt > 0)
     return;
 
+  SILC_LOG_DEBUG(("Freeing public key %p", entry->key.key));
+
   silc_pkcs_public_key_free(entry->key.key);
   silc_free(entry);
 }
@@ -159,7 +168,7 @@ static SilcUInt32 silc_skr_hash(void *key, void *user_context)
     break;
   }
 
-  return type->type + silc_hash_string(type->data, user_context);
+  return type->type + silc_hash_string_case(type->data, user_context);
 }
 
 /* Hash table comparison function for key entries */
@@ -217,7 +226,7 @@ static void silc_skr_find_foreach(void *key, void *context,
 /* Finds entry from repository by search constraint type and data */
 
 static SilcBool silc_skr_find_entry(SilcSKR skr,
-                                   SilcSKRStatus *status,
+                                   SilcResult *status,
                                    SilcSKRFindType type,
                                    void *type_data,
                                    SilcDList *results,
@@ -262,6 +271,13 @@ static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
 {
   SilcSKREntry entry;
 
+#if defined(SILC_DEBUG)
+  char tmp[256];
+  memset(tmp, 0, sizeof(tmp));
+  silc_skr_type_string(type, type_data, tmp, sizeof(tmp) - 1);
+  SILC_LOG_DEBUG(("Search constraint %s", tmp));
+#endif /* SILC_DEBUG */
+
   entry = silc_calloc(1, sizeof(*entry));
   if (!entry)
     return FALSE;
@@ -272,23 +288,98 @@ static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
   return silc_hash_table_add(skr->keys, entry, key);
 }
 
+/* Delete a key by search constraint type from repository */
+
+static SilcBool silc_skr_del_entry(SilcSKR skr, SilcSKRFindType type,
+                                  void *type_data, SilcSKRKeyInternal key)
+{
+  SilcSKREntryStruct entry;
+
+  if (!type_data)
+    return FALSE;
+
+  entry.type = type;
+  entry.data = type_data;
+
+  return silc_hash_table_del_by_context(skr->keys, &entry, key);
+}
+
+/* This performs AND operation.  Any entry already in `results' that is not
+   in `list' will be removed from `results'. */
+
+static SilcBool silc_skr_results_and(SilcDList list, SilcResult *status,
+                                    SilcDList *results)
+{
+  SilcSKRKeyInternal entry, r;
+
+  if (*results == NULL) {
+    *results = silc_dlist_init();
+    if (*results == NULL) {
+      *status |= SILC_SKR_NO_MEMORY;
+      return FALSE;
+    }
+  }
+
+  /* If results is empty, just add all entries from list to results */
+  if (!silc_dlist_count(*results)) {
+    silc_dlist_start(list);
+    while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
+      silc_dlist_add(*results, entry);
+
+    return TRUE;
+  }
+
+  silc_dlist_start(*results);
+  while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
+
+    /* Check if this entry is in list  */
+    silc_dlist_start(list);
+    while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
+      if (r == entry)
+       break;
+    }
+    if (r != SILC_LIST_END)
+      continue;
+
+    /* Remove from results */
+    silc_dlist_del(*results, entry);
+  }
+
+  /* If results became empty, we did not find any key */
+  if (!silc_dlist_count(*results)) {
+    SILC_LOG_DEBUG(("Not all search constraints found"));
+    *status |= SILC_SKR_NOT_FOUND;
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+
+/***************************** SILC Public Key ******************************/
+
 /* Add SILC style public key to repository */
 
-static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
-                                      SilcPublicKey public_key,
-                                      SilcSKRKeyUsage usage,
-                                      void *key_context)
+static SilcResult silc_skr_add_silc(SilcSKR skr,
+                                   SilcPublicKey public_key,
+                                   SilcSKRKeyUsage usage,
+                                   void *key_context,
+                                   SilcSKRKey *return_key)
 {
   SilcSKRKeyInternal key;
-  SilcSKRStatus status = SILC_SKR_ERROR;
+  SilcResult status = SILC_ERR;
   SilcPublicKeyIdentifier ident;
   SilcSILCPublicKey silc_pubkey;
+#if defined(SILC_DEBUG)
+  char tmp[256];
+#endif /* SILC_DEBUG */
 
   /* Get the SILC public key */
-  silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
+  silc_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SILC, public_key);
   ident = &silc_pubkey->identifier;
 
-  SILC_LOG_DEBUG(("Adding SILC public key [%s]", ident->username));
+  SILC_LOG_DEBUG(("Adding SILC public key %p [%s], context %p",
+                 public_key, ident->username, key_context));
 
   silc_mutex_lock(skr->lock);
 
@@ -297,20 +388,26 @@ static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
                          public_key, NULL, key_context, 0)) {
     silc_mutex_unlock(skr->lock);
     SILC_LOG_DEBUG(("Key already added"));
-    return status | SILC_SKR_ALREADY_EXIST;
+    return SILC_ERR_ALREADY_EXISTS;
   }
 
   /* Allocate key entry */
   key = silc_calloc(1, sizeof(*key));
   if (!key) {
     silc_mutex_unlock(skr->lock);
-    return status | SILC_SKR_NO_MEMORY;
+    return silc_errno;
   }
 
   key->key.usage = usage;
   key->key.key = public_key;
   key->key.key_context = key_context;
 
+#if defined(SILC_DEBUG)
+  silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
+                      tmp, sizeof(tmp) - 1);
+  SILC_LOG_DEBUG((" Search constraint %s", tmp));
+#endif /* SILC_DEBUG */
+
   /* Add key specifics */
 
   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
@@ -374,7 +471,10 @@ static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
 
   silc_mutex_unlock(skr->lock);
 
-  return SILC_SKR_OK;
+  if (return_key)
+    *return_key = (SilcSKRKey)key;
+
+  return SILC_OK;
 
  err:
   silc_mutex_unlock(skr->lock);
@@ -384,13 +484,17 @@ static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
 /* Add SILC style public key to repository, and only the public key, not
    other details from the key. */
 
-static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
-                                             SilcPublicKey public_key,
-                                             SilcSKRKeyUsage usage,
-                                             void *key_context)
+static SilcResult silc_skr_add_silc_simple(SilcSKR skr,
+                                          SilcPublicKey public_key,
+                                          SilcSKRKeyUsage usage,
+                                          void *key_context,
+                                          SilcSKRKey *return_key)
 {
   SilcSKRKeyInternal key;
-  SilcSKRStatus status = SILC_SKR_ERROR;
+  SilcResult status = SILC_ERR;
+#if defined(SILC_DEBUG)
+  char tmp[256];
+#endif /* SILC_DEBUG */
 
   SILC_LOG_DEBUG(("Adding SILC public key"));
 
@@ -401,20 +505,26 @@ static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
                          public_key, NULL, key_context, 0)) {
     silc_mutex_unlock(skr->lock);
     SILC_LOG_DEBUG(("Key already added"));
-    return status | SILC_SKR_ALREADY_EXIST;
+    return SILC_ERR_ALREADY_EXISTS;
   }
 
   /* Allocate key entry */
   key = silc_calloc(1, sizeof(*key));
   if (!key) {
     silc_mutex_unlock(skr->lock);
-    return status | SILC_SKR_NO_MEMORY;
+    return silc_errno;
   }
 
   key->key.usage = usage;
   key->key.key = public_key;
   key->key.key_context = key_context;
 
+#if defined(SILC_DEBUG)
+  silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
+                      tmp, sizeof(tmp) - 1);
+  SILC_LOG_DEBUG(("Search cons %s", tmp));
+#endif /* SILC_DEBUG */
+
   /* Add key specifics */
 
   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
@@ -431,62 +541,276 @@ static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
 
   silc_mutex_unlock(skr->lock);
 
-  return SILC_SKR_OK;
+  if (return_key)
+    *return_key = (SilcSKRKey)key;
+
+  return SILC_OK;
 
  err:
   silc_mutex_unlock(skr->lock);
   return status;
 }
 
-/* This performs AND operation.  Any entry already in `results' that is not
-   in `list' will be removed from `results'. */
+/* Deletes SILC public key from repository */
 
-static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
-                                    SilcDList *results)
+static SilcResult silc_skr_del_silc_public_key(SilcSKR skr,
+                                              SilcPublicKey public_key,
+                                              void *key_context)
 {
-  SilcSKRKeyInternal entry, r;
+  SilcResult status = SILC_ERR;
+  SilcPublicKeyIdentifier ident;
+  SilcSILCPublicKey silc_pubkey;
+  SilcSKRKeyInternal key;
+  SilcDList entry;
 
-  if (*results == NULL) {
-    *results = silc_dlist_init();
-    if (*results == NULL) {
-      *status |= SILC_SKR_NO_MEMORY;
-      return FALSE;
-    }
+  /* Get the SILC public key */
+  silc_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SILC, public_key);
+  ident = &silc_pubkey->identifier;
+
+  SILC_LOG_DEBUG(("Deleting SILC public key [%s]", ident->username));
+
+  silc_mutex_lock(skr->lock);
+
+  /* Check that this key exists */
+  if (!silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
+                          public_key, &entry, key_context, 0)) {
+    silc_mutex_unlock(skr->lock);
+    SILC_LOG_DEBUG(("Key does not exist"));
+    return SILC_ERR_NOT_FOUND;
   }
 
-  /* If results is empty, just add all entries from list to results */
-  if (!silc_dlist_count(*results)) {
-    silc_dlist_start(list);
-    while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
-      silc_dlist_add(*results, entry);
+  silc_dlist_start(entry);
+  key = silc_dlist_get(entry);
+  silc_dlist_uninit(entry);
+
+  silc_skr_del_entry(skr, SILC_SKR_FIND_PUBLIC_KEY, public_key, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
+                    SILC_32_TO_PTR(SILC_PKCS_SILC), key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_USERNAME, ident->username, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_HOST, ident->host, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_REALNAME, ident->realname, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_EMAIL, ident->email, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_ORG, ident->org, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_COUNTRY, ident->country, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_CONTEXT, key_context, key);
 
-    return TRUE;
+  silc_mutex_unlock(skr->lock);
+
+  return SILC_OK;
+}
+
+
+/***************************** SSH2 Public Key ******************************/
+
+/* Add SSH2 style public key to repository */
+
+static SilcResult silc_skr_add_ssh(SilcSKR skr,
+                                  SilcPublicKey public_key,
+                                  SilcSKRKeyUsage usage,
+                                  void *key_context,
+                                  SilcSKRKey *return_key)
+{
+  SilcSKRKeyInternal key;
+  SilcResult status = SILC_ERR;
+  SilcSshPublicKey ssh_pubkey;
+  const char *subject;
+#if defined(SILC_DEBUG)
+  char tmp[256];
+#endif /* SILC_DEBUG */
+
+  /* Get the SSH public key */
+  ssh_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SSH2, public_key);
+
+  /* Get subject */
+  subject = silc_ssh_public_key_get_field(ssh_pubkey, "Subject");
+
+  SILC_LOG_DEBUG(("Adding SSH public key %p [%s], context %p", public_key,
+                 subject ? subject : "none", key_context));
+
+  silc_mutex_lock(skr->lock);
+
+  /* Check that this key hasn't been added already */
+  if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
+                         public_key, NULL, key_context, 0)) {
+    silc_mutex_unlock(skr->lock);
+    SILC_LOG_DEBUG(("Key already added"));
+    return SILC_ERR_ALREADY_EXISTS;
   }
 
-  silc_dlist_start(*results);
-  while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
+  /* Allocate key entry */
+  key = silc_calloc(1, sizeof(*key));
+  if (!key) {
+    silc_mutex_unlock(skr->lock);
+    return silc_errno;
+  }
 
-    /* Check if this entry is in list  */
-    silc_dlist_start(list);
-    while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
-      if (r == entry)
-       break;
-    }
-    if (r != SILC_LIST_END)
-      continue;
+  key->key.usage = usage;
+  key->key.key = public_key;
+  key->key.key_context = key_context;
 
-    /* Remove from results */
-    silc_dlist_del(*results, entry);
+#if defined(SILC_DEBUG)
+  silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
+                      tmp, sizeof(tmp) - 1);
+  SILC_LOG_DEBUG((" Search constraint %s", tmp));
+#endif /* SILC_DEBUG */
+
+  /* Add key specifics */
+
+  if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
+                         public_key, key))
+    goto err;
+  key->refcnt++;
+
+  if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
+                         SILC_32_TO_PTR(SILC_PKCS_SSH2), key))
+    goto err;
+  key->refcnt++;
+
+  if (subject) {
+    if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
+                           (void *)subject, key))
+      goto err;
+    key->refcnt++;
   }
 
-  /* If results became empty, we did not find any key */
-  if (!silc_dlist_count(*results)) {
-    SILC_LOG_DEBUG(("Not all search constraints found"));
-    *status |= SILC_SKR_NOT_FOUND;
-    return FALSE;
+  if (key_context) {
+    if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
+                           key_context, key))
+      goto err;
+    key->refcnt++;
   }
 
-  return TRUE;
+  silc_mutex_unlock(skr->lock);
+
+  if (return_key)
+    *return_key = (SilcSKRKey)key;
+
+  return SILC_OK;
+
+ err:
+  silc_mutex_unlock(skr->lock);
+  return status;
+}
+
+/* Add SSH2 style public key to repository.  Only the public key is added,
+   not other information from the key. */
+
+static SilcResult silc_skr_add_ssh_simple(SilcSKR skr,
+                                         SilcPublicKey public_key,
+                                         SilcSKRKeyUsage usage,
+                                         void *key_context,
+                                         SilcSKRKey *return_key)
+{
+  SilcSKRKeyInternal key;
+  SilcResult status = SILC_ERR;
+  SilcSshPublicKey ssh_pubkey;
+#if defined(SILC_DEBUG)
+  char tmp[256];
+#endif /* SILC_DEBUG */
+
+  /* Get the SSH public key */
+  ssh_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SSH2, public_key);
+
+  SILC_LOG_DEBUG(("Adding SSH public key %p, context %p", public_key,
+                 key_context));
+
+  silc_mutex_lock(skr->lock);
+
+  /* Check that this key hasn't been added already */
+  if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
+                         public_key, NULL, key_context, 0)) {
+    silc_mutex_unlock(skr->lock);
+    SILC_LOG_DEBUG(("Key already added"));
+    return SILC_ERR_ALREADY_EXISTS;
+  }
+
+  /* Allocate key entry */
+  key = silc_calloc(1, sizeof(*key));
+  if (!key) {
+    silc_mutex_unlock(skr->lock);
+    return silc_errno;
+  }
+
+  key->key.usage = usage;
+  key->key.key = public_key;
+  key->key.key_context = key_context;
+
+#if defined(SILC_DEBUG)
+  silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
+                      tmp, sizeof(tmp) - 1);
+  SILC_LOG_DEBUG((" Search constraint %s", tmp));
+#endif /* SILC_DEBUG */
+
+  /* Add key specifics */
+
+  if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
+                         public_key, key))
+    goto err;
+  key->refcnt++;
+
+  if (key_context) {
+    if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
+                           key_context, key))
+      goto err;
+    key->refcnt++;
+  }
+
+  silc_mutex_unlock(skr->lock);
+
+  if (return_key)
+    *return_key = (SilcSKRKey)key;
+
+  return SILC_OK;
+
+ err:
+  silc_mutex_unlock(skr->lock);
+  return status;
+}
+
+/* Deletes SSH public key from repository */
+
+static SilcResult silc_skr_del_ssh_public_key(SilcSKR skr,
+                                             SilcPublicKey public_key,
+                                             void *key_context)
+{
+  SilcResult status = SILC_ERR;
+  SilcSshPublicKey ssh_pubkey;
+  SilcSKRKeyInternal key;
+  SilcDList entry;
+  const char *subject;
+
+  /* Get the SSH public key */
+  ssh_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SSH2, public_key);
+
+  /* Get subject */
+  subject = silc_ssh_public_key_get_field(ssh_pubkey, "Subject");
+
+  SILC_LOG_DEBUG(("Deleting SSH public key %p [%s]", public_key,
+                 subject ? subject : "none"));
+
+  silc_mutex_lock(skr->lock);
+
+  /* Check that this key exists */
+  if (!silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
+                          public_key, &entry, key_context, 0)) {
+    silc_mutex_unlock(skr->lock);
+    SILC_LOG_DEBUG(("Key does not exist"));
+    return SILC_ERR_NOT_FOUND;
+  }
+
+  silc_dlist_start(entry);
+  key = silc_dlist_get(entry);
+  silc_dlist_uninit(entry);
+
+  silc_skr_del_entry(skr, SILC_SKR_FIND_PUBLIC_KEY, public_key, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
+                    SILC_32_TO_PTR(SILC_PKCS_SSH2), key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_USERNAME, (void *)subject, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_CONTEXT, key_context, key);
+
+  silc_mutex_unlock(skr->lock);
+
+  return SILC_OK;
 }
 
 
@@ -525,7 +849,7 @@ SilcBool silc_skr_init(SilcSKR skr)
   if (!silc_mutex_alloc(&skr->lock))
     return FALSE;
 
-  skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
+  skr->keys = silc_hash_table_alloc(NULL, 0, silc_skr_hash, NULL,
                                    silc_skr_compare, NULL,
                                    silc_skr_destructor, NULL, TRUE);
   if (!skr->keys)
@@ -545,60 +869,137 @@ void silc_skr_uninit(SilcSKR skr)
 
 /* Adds public key to key repository */
 
-SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
-                                     SilcPublicKey public_key,
-                                     SilcSKRKeyUsage usage,
-                                     void *key_context)
+SilcResult silc_skr_add_public_key(SilcSKR skr,
+                                  SilcPublicKey public_key,
+                                  SilcSKRKeyUsage usage,
+                                  void *key_context,
+                                  SilcSKRKey *return_key)
 {
   SilcPKCSType type;
 
   if (!public_key)
-    return SILC_SKR_ERROR;
+    return SILC_ERR_INVALID_ARGUMENT;
 
   type = silc_pkcs_get_type(public_key);
 
-  SILC_LOG_DEBUG(("Adding public key to repository"));
+  SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
 
   switch (type) {
 
   case SILC_PKCS_SILC:
-    return silc_skr_add_silc(skr, public_key, usage, key_context);
+    return silc_skr_add_silc(skr, public_key, usage, key_context, return_key);
+    break;
+
+  case SILC_PKCS_SSH2:
+    return silc_skr_add_ssh(skr, public_key, usage, key_context, return_key);
     break;
 
   default:
     break;
   }
 
-  return SILC_SKR_ERROR;
+  return SILC_ERR_NOT_SUPPORTED;
 }
 
 /* Adds public key to repository. */
 
-SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
-                                            SilcPublicKey public_key,
-                                            SilcSKRKeyUsage usage,
-                                            void *key_context)
+SilcResult silc_skr_add_public_key_simple(SilcSKR skr,
+                                         SilcPublicKey public_key,
+                                         SilcSKRKeyUsage usage,
+                                         void *key_context,
+                                         SilcSKRKey *return_key)
 {
   SilcPKCSType type;
 
   if (!public_key)
-    return SILC_SKR_ERROR;
+    return SILC_ERR_INVALID_ARGUMENT;
 
   type = silc_pkcs_get_type(public_key);
 
-  SILC_LOG_DEBUG(("Adding public key to repository"));
+  SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
 
   switch (type) {
 
   case SILC_PKCS_SILC:
-    return silc_skr_add_silc_simple(skr, public_key, usage, key_context);
+    return silc_skr_add_silc_simple(skr, public_key, usage, key_context,
+                                   return_key);
+    break;
+
+  case SILC_PKCS_SSH2:
+    return silc_skr_add_ssh_simple(skr, public_key, usage, key_context,
+                                  return_key);
     break;
 
   default:
     break;
   }
 
-  return SILC_SKR_ERROR;
+  return SILC_ERR_NOT_SUPPORTED;
+}
+
+/* Remove key from repository */
+
+SilcResult silc_skr_del_public_key(SilcSKR skr,
+                                     SilcPublicKey public_key,
+                                     void *key_context)
+{
+  SilcPKCSType type;
+
+  if (!public_key)
+    return SILC_ERR_INVALID_ARGUMENT;
+
+  type = silc_pkcs_get_type(public_key);
+
+  SILC_LOG_DEBUG(("Deleting public key %p from repository", public_key));
+
+  switch (type) {
+
+  case SILC_PKCS_SILC:
+    return silc_skr_del_silc_public_key(skr, public_key, key_context);
+    break;
+
+  case SILC_PKCS_SSH2:
+    return silc_skr_del_ssh_public_key(skr, public_key, key_context);
+    break;
+
+  default:
+    break;
+  }
+
+  return SILC_ERR_NOT_SUPPORTED;
+}
+
+/* Reference key */
+
+void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key)
+{
+  SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
+
+  silc_mutex_lock(skr->lock);
+  SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt + 1));
+  k->refcnt++;
+  silc_mutex_unlock(skr->lock);
+}
+
+/* Release key reference. */
+
+void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key)
+{
+  SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
+
+  silc_mutex_lock(skr->lock);
+
+  SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt - 1));
+  k->refcnt--;
+
+  if (k->refcnt == 0) {
+    /* If reference is zero, the key has been removed from the repository
+       already.  Just destroy the public key. */
+    silc_pkcs_public_key_free(key->key);
+    silc_free(key);
+  }
+
+  silc_mutex_unlock(skr->lock);
 }
 
 
@@ -614,7 +1015,8 @@ SilcSKRFind silc_skr_find_alloc(void)
   if (!find)
     return NULL;
 
-  find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
+  find->constr = silc_hash_table_alloc(NULL, 0, silc_hash_uint,
+                                      NULL, NULL, NULL,
                                       silc_skr_find_destructor, NULL, TRUE);
   if (!find->constr) {
     silc_skr_find_free(find);
@@ -706,6 +1108,8 @@ SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
 
 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
 {
+  if (!context)
+    return TRUE;
   return silc_hash_table_add(find->constr,
                             SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
 }
@@ -730,10 +1134,13 @@ SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
                                 SilcSKRFindCallback callback,
                                 void *callback_context)
 {
-  SilcSKRStatus status = SILC_SKR_ERROR;
+  SilcResult status = SILC_ERR;
   SilcHashTableList htl;
   SilcDList list, results = NULL;
   void *type, *ctx, *usage = NULL;
+#if defined(SILC_DEBUG)
+  char tmp[256];
+#endif /* SILC_DEBUG */
 
   SILC_LOG_DEBUG(("Finding key from repository"));
 
@@ -746,21 +1153,28 @@ SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
   silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
                       NULL, &usage);
 
+#if defined(SILC_DEBUG)
+  if (usage) {
+    memset(tmp, 0, sizeof(tmp));
+    silc_skr_type_string(SILC_SKR_FIND_USAGE, usage, tmp, sizeof(tmp) - 1);
+    SILC_LOG_DEBUG(("Finding key by %s", tmp));
+  }
+#endif /* SILC_DEBUG */
+
   silc_hash_table_list(find->constr, &htl);
   while (silc_hash_table_get(&htl, &type, &ctx)) {
 
+    /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
+    if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
+      continue;
+
 #if defined(SILC_DEBUG)
-    char tmp[256];
     memset(tmp, 0, sizeof(tmp));
     silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
                         ctx, tmp, sizeof(tmp) - 1);
     SILC_LOG_DEBUG(("Finding key by %s", tmp));
 #endif /* SILC_DEBUG */
 
-    /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
-    if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
-      continue;
-
     /* Find entries by this search constraint */
     if (!silc_skr_find_entry(skr, &status,
                             (SilcSKRFindType)SILC_32_TO_PTR(type),
@@ -796,7 +1210,7 @@ SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
     callback(skr, find, status, NULL, callback_context);
   } else {
     silc_dlist_start(results);
-    callback(skr, find, SILC_SKR_OK, results, callback_context);
+    callback(skr, find, SILC_OK, results, callback_context);
   }
 
   return NULL;