56d0024b4dc780777d8e5e6b8f0d2757b5d6600e
[crypto.git] / lib / silcskr / silcskr.c
1 /*
2
3   silcskr.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21 #include "silcskr.h"
22
23 /************************** Types and definitions ***************************/
24
25 /* Search constraints */
26 typedef enum {
27   SILC_SKR_FIND_PKCS_TYPE,
28   SILC_SKR_FIND_USERNAME,
29   SILC_SKR_FIND_HOST,
30   SILC_SKR_FIND_REALNAME,
31   SILC_SKR_FIND_EMAIL,
32   SILC_SKR_FIND_ORG,
33   SILC_SKR_FIND_COUNTRY,
34   SILC_SKR_FIND_PUBLIC_KEY,
35   SILC_SKR_FIND_CONTEXT,
36   SILC_SKR_FIND_USAGE,          /* Never added as key specific */
37 } SilcSKRFindType;
38
39 /* Hash table key context */
40 typedef struct {
41   SilcSKRFindType type;         /* Type of key */
42   void *data;                   /* Hash table key */
43 } *SilcSKREntry, SilcSKREntryStruct;
44
45 /* Foreach user context when finding entries from hash table */
46 typedef struct {
47   SilcDList list;
48   void *key_context;
49   SilcSKRKeyUsage usage;
50 } SilcSKRFindForeach;
51
52 #if defined(SILC_DEBUG)
53 static const char *find_name[] = {
54   "PKCS TYPE",
55   "USERNAME",
56   "HOST",
57   "REALNAME",
58   "EMAIL",
59   "ORG",
60   "COUNTRY",
61   "PUBLIC KEY",
62   "CONTEXT",
63   "USAGE",
64   NULL
65 };
66 #endif /* SILC_DEBUG */
67
68 /************************ Static utility functions **************************/
69
70 #if defined(SILC_DEBUG)
71
72 /* Returns search constraint string */
73
74 static void silc_skr_type_string(SilcSKRFindType type, void *data,
75                                  char *retbuf, SilcUInt32 retbuf_size)
76 {
77   switch (type) {
78   case SILC_SKR_FIND_PKCS_TYPE:
79   case SILC_SKR_FIND_USAGE:
80     silc_snprintf(retbuf, retbuf_size, "[%s] [%d]", find_name[type],
81              (int)SILC_PTR_TO_32(data));
82     break;
83
84   case SILC_SKR_FIND_PUBLIC_KEY:
85   case SILC_SKR_FIND_CONTEXT:
86     silc_snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
87     break;
88
89   default:
90     silc_snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
91              (char *)data);
92   }
93 }
94 #endif /* SILC_DEBUG */
95
96 /* Hash table destructor for search constraints */
97
98 static void silc_skr_find_destructor(void *key, void *context,
99                                      void *user_context)
100 {
101   SilcSKRFindType type = SILC_PTR_TO_32(key);
102   SilcPKCSType pkcs_type = SILC_PTR_TO_32(user_context);
103
104   switch (type) {
105   case SILC_SKR_FIND_PKCS_TYPE:
106   case SILC_SKR_FIND_USAGE:
107   case SILC_SKR_FIND_CONTEXT:
108     break;
109
110   case SILC_SKR_FIND_PUBLIC_KEY:
111     silc_pkcs_public_key_free(context);
112     break;
113
114   default:
115     /* In SILC Public key all entries are referenced from the public key
116        so don't free them.  This test is valid only when removing key
117        from the repository. */
118     if (pkcs_type == SILC_PKCS_SILC)
119       break;
120
121     silc_free(context);
122   }
123 }
124
125 /* Hash table destructor for key entries */
126
127 static void silc_skr_destructor(void *key, void *context, void *user_context)
128 {
129   SilcSKREntry type = key;
130   SilcSKRKeyInternal entry = context;
131   SilcPKCSType pkcs_type = silc_pkcs_get_type(entry->key.key);
132
133   /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
134      shares same context with the key entry. */
135   if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
136     silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data,
137                              SILC_32_TO_PTR(pkcs_type));
138   silc_free(type);
139
140   /* Destroy key */
141   entry->refcnt--;
142   if (entry->refcnt > 0)
143     return;
144
145   SILC_LOG_DEBUG(("Freeing public key %p", entry->key.key));
146
147   silc_pkcs_public_key_free(entry->key.key);
148   silc_free(entry);
149 }
150
151 /* Hash table hash function for key entries */
152
153 static SilcUInt32 silc_skr_hash(void *key, void *user_context)
154 {
155   SilcSKREntry type = key;
156
157   switch (type->type) {
158   case SILC_SKR_FIND_PKCS_TYPE:
159   case SILC_SKR_FIND_CONTEXT:
160     return type->type + (type->type ^ SILC_PTR_TO_32(type->data));
161     break;
162
163   case SILC_SKR_FIND_PUBLIC_KEY:
164     return type->type + silc_hash_public_key(type->data, user_context);
165     break;
166
167   default:
168     break;
169   }
170
171   return type->type + silc_hash_string(type->data, user_context);
172 }
173
174 /* Hash table comparison function for key entries */
175
176 static SilcBool silc_skr_compare(void *key1, void *key2, void *user_context)
177 {
178   SilcSKREntry type1 = key1;
179   SilcSKREntry type2 = key2;
180
181   if (type1->type != type2->type)
182     return FALSE;
183
184   switch (type1->type) {
185   case SILC_SKR_FIND_PKCS_TYPE:
186   case SILC_SKR_FIND_CONTEXT:
187     return type1->data == type2->data;
188     break;
189
190   case SILC_SKR_FIND_PUBLIC_KEY:
191     return silc_hash_public_key_compare(type1->data, type2->data,
192                                         user_context);
193     break;
194
195   default:
196     break;
197   }
198
199   return silc_utf8_strcasecmp((const char *)type1->data,
200                               (const char *)type2->data);
201 }
202
203 /* Foreach function for finding entries in the repository */
204
205 static void silc_skr_find_foreach(void *key, void *context,
206                                   void *user_context)
207 {
208   SilcSKRFindForeach *f = user_context;
209   SilcSKRKeyInternal k = context;
210
211   if (k) {
212     /* If key context is present, it must match the context in the key.
213        This is used only internally when adding keys, to check if the key
214        is added with same context. */
215     if (f->key_context && f->key_context != k->key.key_context)
216       return;
217
218     /* Check for usage bits.  At least one usage bit must be set. */
219     if (f->usage && k->key.usage && (f->usage & k->key.usage) == 0)
220       return;
221
222     silc_dlist_add(f->list, k);
223   }
224 }
225
226 /* Finds entry from repository by search constraint type and data */
227
228 static SilcBool silc_skr_find_entry(SilcSKR skr,
229                                     SilcSKRStatus *status,
230                                     SilcSKRFindType type,
231                                     void *type_data,
232                                     SilcDList *results,
233                                     void *key_context,
234                                     SilcSKRKeyUsage usage)
235 {
236   SilcSKREntryStruct find;
237   SilcSKRFindForeach f;
238
239   f.list = silc_dlist_init();
240   if (!f.list) {
241     *status |= SILC_SKR_NO_MEMORY;
242     return FALSE;
243   }
244   f.key_context = key_context;
245   f.usage = usage;
246
247   find.type = type;
248   find.data = type_data;
249
250   silc_hash_table_find_foreach(skr->keys, (void *)&find,
251                                silc_skr_find_foreach, &f);
252
253   if (!silc_dlist_count(f.list)) {
254     *status |= SILC_SKR_NOT_FOUND;
255     silc_dlist_uninit(f.list);
256     return FALSE;
257   }
258
259   if (results)
260     *results = f.list;
261   else
262     silc_dlist_uninit(f.list);
263
264   return TRUE;
265 }
266
267 /* Add a key by search constraint type to repository */
268
269 static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
270                                    void *type_data, SilcSKRKeyInternal key)
271 {
272   SilcSKREntry entry;
273
274   entry = silc_calloc(1, sizeof(*entry));
275   if (!entry)
276     return FALSE;
277
278   entry->type = type;
279   entry->data = type_data;
280
281   return silc_hash_table_add(skr->keys, entry, key);
282 }
283
284 /* Delete a key by search constraint type from repository */
285
286 static SilcBool silc_skr_del_entry(SilcSKR skr, SilcSKRFindType type,
287                                    void *type_data, SilcSKRKeyInternal key)
288 {
289   SilcSKREntryStruct entry;
290
291   if (!type_data)
292     return FALSE;
293
294   entry.type = type;
295   entry.data = type_data;
296
297   return silc_hash_table_del_by_context(skr->keys, &entry, key);
298 }
299
300 /* This performs AND operation.  Any entry already in `results' that is not
301    in `list' will be removed from `results'. */
302
303 static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
304                                      SilcDList *results)
305 {
306   SilcSKRKeyInternal entry, r;
307
308   if (*results == NULL) {
309     *results = silc_dlist_init();
310     if (*results == NULL) {
311       *status |= SILC_SKR_NO_MEMORY;
312       return FALSE;
313     }
314   }
315
316   /* If results is empty, just add all entries from list to results */
317   if (!silc_dlist_count(*results)) {
318     silc_dlist_start(list);
319     while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
320       silc_dlist_add(*results, entry);
321
322     return TRUE;
323   }
324
325   silc_dlist_start(*results);
326   while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
327
328     /* Check if this entry is in list  */
329     silc_dlist_start(list);
330     while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
331       if (r == entry)
332         break;
333     }
334     if (r != SILC_LIST_END)
335       continue;
336
337     /* Remove from results */
338     silc_dlist_del(*results, entry);
339   }
340
341   /* If results became empty, we did not find any key */
342   if (!silc_dlist_count(*results)) {
343     SILC_LOG_DEBUG(("Not all search constraints found"));
344     *status |= SILC_SKR_NOT_FOUND;
345     return FALSE;
346   }
347
348   return TRUE;
349 }
350
351
352 /***************************** SILC Public Key ******************************/
353
354 /* Add SILC style public key to repository */
355
356 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
357                                        SilcPublicKey public_key,
358                                        SilcSKRKeyUsage usage,
359                                        void *key_context,
360                                        SilcSKRKey *return_key)
361 {
362   SilcSKRKeyInternal key;
363   SilcSKRStatus status = SILC_SKR_ERROR;
364   SilcPublicKeyIdentifier ident;
365   SilcSILCPublicKey silc_pubkey;
366
367   /* Get the SILC public key */
368   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
369   ident = &silc_pubkey->identifier;
370
371   SILC_LOG_DEBUG(("Adding SILC public key [%s]", ident->username));
372
373   silc_mutex_lock(skr->lock);
374
375   /* Check that this key hasn't been added already */
376   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
377                           public_key, NULL, key_context, 0)) {
378     silc_mutex_unlock(skr->lock);
379     SILC_LOG_DEBUG(("Key already added"));
380     return status | SILC_SKR_ALREADY_EXIST;
381   }
382
383   /* Allocate key entry */
384   key = silc_calloc(1, sizeof(*key));
385   if (!key) {
386     silc_mutex_unlock(skr->lock);
387     return status | SILC_SKR_NO_MEMORY;
388   }
389
390   key->key.usage = usage;
391   key->key.key = public_key;
392   key->key.key_context = key_context;
393
394   /* Add key specifics */
395
396   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
397                           public_key, key))
398     goto err;
399   key->refcnt++;
400
401   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
402                           SILC_32_TO_PTR(SILC_PKCS_SILC), key))
403     goto err;
404   key->refcnt++;
405
406   if (ident->username) {
407     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
408                             ident->username, key))
409       goto err;
410     key->refcnt++;
411   }
412
413   if (ident->host) {
414     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_HOST,
415                             ident->host, key))
416       goto err;
417     key->refcnt++;
418   }
419
420   if (ident->realname) {
421     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_REALNAME,
422                             ident->realname, key))
423       goto err;
424     key->refcnt++;
425   }
426
427   if (ident->email) {
428     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_EMAIL,
429                             ident->email, key))
430       goto err;
431     key->refcnt++;
432   }
433
434   if (ident->org) {
435     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_ORG,
436                             ident->org, key))
437       goto err;
438     key->refcnt++;
439   }
440
441   if (ident->country) {
442     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_COUNTRY,
443                             ident->country, key))
444       goto err;
445     key->refcnt++;
446   }
447
448   if (key_context) {
449     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
450                             key_context, key))
451       goto err;
452     key->refcnt++;
453   }
454
455   silc_mutex_unlock(skr->lock);
456
457   if (return_key)
458     *return_key = (SilcSKRKey)key;
459
460   return SILC_SKR_OK;
461
462  err:
463   silc_mutex_unlock(skr->lock);
464   return status;
465 }
466
467 /* Add SILC style public key to repository, and only the public key, not
468    other details from the key. */
469
470 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
471                                               SilcPublicKey public_key,
472                                               SilcSKRKeyUsage usage,
473                                               void *key_context,
474                                               SilcSKRKey *return_key)
475 {
476   SilcSKRKeyInternal key;
477   SilcSKRStatus status = SILC_SKR_ERROR;
478
479   SILC_LOG_DEBUG(("Adding SILC public key"));
480
481   silc_mutex_lock(skr->lock);
482
483   /* Check that this key hasn't been added already */
484   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
485                           public_key, NULL, key_context, 0)) {
486     silc_mutex_unlock(skr->lock);
487     SILC_LOG_DEBUG(("Key already added"));
488     return status | SILC_SKR_ALREADY_EXIST;
489   }
490
491   /* Allocate key entry */
492   key = silc_calloc(1, sizeof(*key));
493   if (!key) {
494     silc_mutex_unlock(skr->lock);
495     return status | SILC_SKR_NO_MEMORY;
496   }
497
498   key->key.usage = usage;
499   key->key.key = public_key;
500   key->key.key_context = key_context;
501
502   /* Add key specifics */
503
504   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
505                           public_key, key))
506     goto err;
507   key->refcnt++;
508
509   if (key_context) {
510     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
511                             key_context, key))
512       goto err;
513     key->refcnt++;
514   }
515
516   silc_mutex_unlock(skr->lock);
517
518   if (return_key)
519     *return_key = (SilcSKRKey)key;
520
521   return SILC_SKR_OK;
522
523  err:
524   silc_mutex_unlock(skr->lock);
525   return status;
526 }
527
528 /* Deletes SILC public key from repository */
529
530 static SilcSKRStatus silc_skr_del_silc_public_key(SilcSKR skr,
531                                                   SilcPublicKey public_key,
532                                                   void *key_context)
533 {
534   SilcSKRStatus status = SILC_SKR_ERROR;
535   SilcPublicKeyIdentifier ident;
536   SilcSILCPublicKey silc_pubkey;
537   SilcSKRKeyInternal key;
538   SilcDList entry;
539
540   /* Get the SILC public key */
541   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
542   ident = &silc_pubkey->identifier;
543
544   SILC_LOG_DEBUG(("Deleting SILC public key [%s]", ident->username));
545
546   silc_mutex_lock(skr->lock);
547
548   /* Check that this key exists */
549   if (!silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
550                            public_key, &entry, key_context, 0)) {
551     silc_mutex_unlock(skr->lock);
552     SILC_LOG_DEBUG(("Key does not exist"));
553     return status | SILC_SKR_NOT_FOUND;
554   }
555
556   silc_dlist_start(entry);
557   key = silc_dlist_get(entry);
558   silc_dlist_uninit(entry);
559
560   silc_skr_del_entry(skr, SILC_SKR_FIND_PUBLIC_KEY, public_key, key);
561   silc_skr_del_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
562                      SILC_32_TO_PTR(SILC_PKCS_SILC), key);
563   silc_skr_del_entry(skr, SILC_SKR_FIND_USERNAME, ident->username, key);
564   silc_skr_del_entry(skr, SILC_SKR_FIND_HOST, ident->host, key);
565   silc_skr_del_entry(skr, SILC_SKR_FIND_REALNAME, ident->realname, key);
566   silc_skr_del_entry(skr, SILC_SKR_FIND_EMAIL, ident->email, key);
567   silc_skr_del_entry(skr, SILC_SKR_FIND_ORG, ident->org, key);
568   silc_skr_del_entry(skr, SILC_SKR_FIND_COUNTRY, ident->country, key);
569   silc_skr_del_entry(skr, SILC_SKR_FIND_CONTEXT, key_context, key);
570
571   silc_mutex_unlock(skr->lock);
572
573   return SILC_SKR_OK;
574 }
575
576
577 /**************************** Key Repository API ****************************/
578
579 /* Allocate key repository */
580
581 SilcSKR silc_skr_alloc(void)
582 {
583   SilcSKR skr;
584
585   skr = silc_calloc(1, sizeof(*skr));
586   if (!skr)
587     return NULL;
588
589   if (!silc_skr_init(skr)) {
590     silc_skr_free(skr);
591     return NULL;
592   }
593
594   return skr;
595 }
596
597 /* Free key repository */
598
599 void silc_skr_free(SilcSKR skr)
600 {
601   silc_skr_uninit(skr);
602   silc_free(skr);
603 }
604
605 /* Initializes key repository */
606
607 SilcBool silc_skr_init(SilcSKR skr)
608 {
609   if (!silc_mutex_alloc(&skr->lock))
610     return FALSE;
611
612   skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
613                                     silc_skr_compare, NULL,
614                                     silc_skr_destructor, NULL, TRUE);
615   if (!skr->keys)
616     return FALSE;
617
618   return TRUE;
619 }
620
621 /* Uninitializes key repository */
622
623 void silc_skr_uninit(SilcSKR skr)
624 {
625   if (skr->keys)
626     silc_hash_table_free(skr->keys);
627   silc_mutex_free(skr->lock);
628 }
629
630 /* Adds public key to key repository */
631
632 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
633                                       SilcPublicKey public_key,
634                                       SilcSKRKeyUsage usage,
635                                       void *key_context,
636                                       SilcSKRKey *return_key)
637 {
638   SilcPKCSType type;
639
640   if (!public_key)
641     return SILC_SKR_ERROR;
642
643   type = silc_pkcs_get_type(public_key);
644
645   SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
646
647   switch (type) {
648
649   case SILC_PKCS_SILC:
650     return silc_skr_add_silc(skr, public_key, usage, key_context, return_key);
651     break;
652
653   default:
654     break;
655   }
656
657   return SILC_SKR_ERROR;
658 }
659
660 /* Adds public key to repository. */
661
662 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
663                                              SilcPublicKey public_key,
664                                              SilcSKRKeyUsage usage,
665                                              void *key_context,
666                                              SilcSKRKey *return_key)
667 {
668   SilcPKCSType type;
669
670   if (!public_key)
671     return SILC_SKR_ERROR;
672
673   type = silc_pkcs_get_type(public_key);
674
675   SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
676
677   switch (type) {
678
679   case SILC_PKCS_SILC:
680     return silc_skr_add_silc_simple(skr, public_key, usage, key_context,
681                                     return_key);
682     break;
683
684   default:
685     break;
686   }
687
688   return SILC_SKR_ERROR;
689 }
690
691 /* Remove key from repository */
692
693 SilcSKRStatus silc_skr_del_public_key(SilcSKR skr,
694                                       SilcPublicKey public_key,
695                                       void *key_context)
696 {
697   SilcPKCSType type;
698
699   if (!public_key)
700     return SILC_SKR_ERROR;
701
702   type = silc_pkcs_get_type(public_key);
703
704   SILC_LOG_DEBUG(("Deleting public key %p from repository", public_key));
705
706   switch (type) {
707
708   case SILC_PKCS_SILC:
709     return silc_skr_del_silc_public_key(skr, public_key, key_context);
710     break;
711
712   default:
713     break;
714   }
715
716   return SILC_SKR_ERROR;
717 }
718
719 /* Reference key */
720
721 void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key)
722 {
723   SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
724
725   silc_mutex_lock(skr->lock);
726   SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt + 1));
727   k->refcnt++;
728   silc_mutex_unlock(skr->lock);
729 }
730
731 /* Release key reference. */
732
733 void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key)
734 {
735   SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
736
737   silc_mutex_lock(skr->lock);
738
739   SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt - 1));
740   k->refcnt--;
741
742   if (k->refcnt == 0) {
743     /* If reference is zero, the key has been removed from the repository
744        already.  Just destroy the public key. */
745     silc_pkcs_public_key_free(key->key);
746     silc_free(key);
747   }
748
749   silc_mutex_unlock(skr->lock);
750 }
751
752
753 /************************** Search Constraints API **************************/
754
755 /* Allocate search constraints */
756
757 SilcSKRFind silc_skr_find_alloc(void)
758 {
759   SilcSKRFind find;
760
761   find = silc_calloc(1, sizeof(*find));
762   if (!find)
763     return NULL;
764
765   find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
766                                        silc_skr_find_destructor, NULL, TRUE);
767   if (!find->constr) {
768     silc_skr_find_free(find);
769     return NULL;
770   }
771
772   return find;
773 }
774
775 /* Free search constraints */
776
777 void silc_skr_find_free(SilcSKRFind find)
778 {
779   if (find->constr)
780     silc_hash_table_free(find->constr);
781   silc_free(find);
782 }
783
784 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
785 {
786   return silc_hash_table_add(find->constr,
787                              SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
788                              SILC_32_TO_PTR(type));
789 }
790
791 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
792 {
793   void *c = silc_memdup(username, strlen(username));
794   if (!c)
795     return FALSE;
796   return silc_hash_table_add(find->constr,
797                              SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
798 }
799
800 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
801 {
802   void *c = silc_memdup(host, strlen(host));
803   if (!c)
804     return FALSE;
805   return silc_hash_table_add(find->constr,
806                              SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
807 }
808
809 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
810 {
811   void *c = silc_memdup(realname, strlen(realname));
812   if (!c)
813     return FALSE;
814   return silc_hash_table_add(find->constr,
815                              SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
816 }
817
818 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
819 {
820   void *c = silc_memdup(email, strlen(email));
821   if (!c)
822     return FALSE;
823   return silc_hash_table_add(find->constr,
824                              SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
825 }
826
827 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
828 {
829   void *c = silc_memdup(org, strlen(org));
830   if (!c)
831     return FALSE;
832   return silc_hash_table_add(find->constr,
833                              SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
834 }
835
836 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
837 {
838   void *c = silc_memdup(country, strlen(country));
839   if (!c)
840     return FALSE;
841   return silc_hash_table_add(find->constr,
842                              SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
843 }
844
845 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
846                                       SilcPublicKey public_key)
847 {
848   SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
849   if (!pk)
850     return FALSE;
851   return silc_hash_table_add(find->constr,
852                              SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
853 }
854
855 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
856 {
857   return silc_hash_table_add(find->constr,
858                              SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
859 }
860
861 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
862 {
863   if (!usage)
864     return TRUE;
865   return silc_hash_table_add(find->constr,
866                              SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
867                              SILC_32_TO_PTR(usage));
868 }
869
870 /******************************** Search API ********************************/
871
872 /* Finds key(s) by the set search constraints.  The callback will be called
873    once keys has been found. */
874 /* This is now synchronous function but may later change async */
875
876 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
877                                  SilcSKRFind find,
878                                  SilcSKRFindCallback callback,
879                                  void *callback_context)
880 {
881   SilcSKRStatus status = SILC_SKR_ERROR;
882   SilcHashTableList htl;
883   SilcDList list, results = NULL;
884   void *type, *ctx, *usage = NULL;
885
886   SILC_LOG_DEBUG(("Finding key from repository"));
887
888   if (!find || !callback)
889     return NULL;
890
891   silc_mutex_lock(skr->lock);
892
893   /* Get usage bits, if searching by them */
894   silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
895                        NULL, &usage);
896
897   silc_hash_table_list(find->constr, &htl);
898   while (silc_hash_table_get(&htl, &type, &ctx)) {
899
900 #if defined(SILC_DEBUG)
901     char tmp[256];
902     memset(tmp, 0, sizeof(tmp));
903     silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
904                          ctx, tmp, sizeof(tmp) - 1);
905     SILC_LOG_DEBUG(("Finding key by %s", tmp));
906 #endif /* SILC_DEBUG */
907
908     /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
909     if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
910       continue;
911
912     /* Find entries by this search constraint */
913     if (!silc_skr_find_entry(skr, &status,
914                              (SilcSKRFindType)SILC_32_TO_PTR(type),
915                              ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
916       SILC_LOG_DEBUG(("Not found"));
917       if (results) {
918         silc_dlist_uninit(results);
919         results = NULL;
920       }
921       break;
922     }
923
924     /* For now, our logic rule is AND.  All constraints must be found
925        to find the key.  Later OR might be added also. */
926     if (!silc_skr_results_and(list, &status, &results)) {
927       SILC_LOG_DEBUG(("Not found"));
928       if (results) {
929         silc_dlist_uninit(results);
930         results = NULL;
931       }
932       silc_dlist_uninit(list);
933       break;
934     }
935
936     silc_dlist_uninit(list);
937   }
938   silc_hash_table_list_reset(&htl);
939
940   silc_mutex_unlock(skr->lock);
941
942   /* Return results */
943   if (!results) {
944     callback(skr, find, status, NULL, callback_context);
945   } else {
946     silc_dlist_start(results);
947     callback(skr, find, SILC_SKR_OK, results, callback_context);
948   }
949
950   return NULL;
951 }