Add support for Salsa20.
[libgcrypt.git] / src / module.c
1 /* module.c - Module management for libgcrypt.
2  * Copyright (C) 2003, 2008 Free Software Foundation, Inc.
3  *
4  * This file is part of Libgcrypt.
5  *
6  * Libgcrypt is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser general Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Libgcrypt is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <errno.h>
22 #include "g10lib.h"
23
24 /* Please match these numbers with the allocated algorithm
25    numbers.  */
26 #define MODULE_ID_MIN 600
27 #define MODULE_ID_LAST 65500
28 #define MODULE_ID_USER GCRY_MODULE_ID_USER
29 #define MODULE_ID_USER_LAST GCRY_MODULE_ID_USER_LAST
30
31 #if MODULE_ID_MIN >= MODULE_ID_USER
32 #error Need to implement a different search strategy
33 #endif
34
35 /* Internal function.  Generate a new, unique module ID for a module
36    that should be inserted into the module chain starting at
37    MODULES.  */
38 static gcry_err_code_t
39 _gcry_module_id_new (gcry_module_t modules, unsigned int *id_new)
40 {
41   unsigned int mod_id;
42   gcry_err_code_t err = GPG_ERR_NO_ERROR;
43   gcry_module_t module;
44
45   /* Search for unused ID.  */
46   for (mod_id = MODULE_ID_MIN; mod_id < MODULE_ID_LAST; mod_id++)
47     {
48       if (mod_id == MODULE_ID_USER)
49         {
50           mod_id = MODULE_ID_USER_LAST;
51           continue;
52         }
53
54       /* Search for a module with the current ID.  */
55       for (module = modules; module; module = module->next)
56         if (mod_id == module->mod_id)
57           break;
58
59       if (! module)
60         /* None found -> the ID is available for use.  */
61         break;
62     }
63
64   if (mod_id < MODULE_ID_LAST)
65     /* Done.  */
66     *id_new = mod_id;
67   else
68     /* No free ID found.  */
69     err = GPG_ERR_INTERNAL;
70
71   return err;
72 }
73
74 /* Add a module specification to the list ENTRIES.  The new module has
75    it's use-counter set to one.  */
76 gcry_err_code_t
77 _gcry_module_add (gcry_module_t *entries, unsigned int mod_id,
78                   void *spec, void *extraspec, gcry_module_t *module)
79 {
80   gcry_err_code_t err = 0;
81   gcry_module_t entry;
82
83   if (! mod_id)
84     err = _gcry_module_id_new (*entries, &mod_id);
85
86   if (! err)
87     {
88       entry = gcry_malloc (sizeof (struct gcry_module));
89       if (! entry)
90         err = gpg_err_code_from_errno (errno);
91     }
92
93   if (! err)
94     {
95       /* Fill new module entry.  */
96       entry->flags = 0;
97       entry->counter = 1;
98       entry->spec = spec;
99       entry->extraspec = extraspec;
100       entry->mod_id = mod_id;
101
102       /* Link it into the list.  */
103       entry->next = *entries;
104       entry->prevp = entries;
105       if (*entries)
106         (*entries)->prevp = &entry->next;
107       *entries = entry;
108
109       /* And give it to the caller.  */
110       if (module)
111         *module = entry;
112     }
113   return err;
114 }
115
116 /* Internal function.  Unlink CIPHER_ENTRY from the list of registered
117    ciphers and destroy it.  */
118 static void
119 _gcry_module_drop (gcry_module_t entry)
120 {
121   *entry->prevp = entry->next;
122   if (entry->next)
123     entry->next->prevp = entry->prevp;
124
125   gcry_free (entry);
126 }
127
128 /* Lookup a module specification by it's ID.  After a successful
129    lookup, the module has it's resource counter incremented.  */
130 gcry_module_t
131 _gcry_module_lookup_id (gcry_module_t entries, unsigned int mod_id)
132 {
133   gcry_module_t entry;
134
135   for (entry = entries; entry; entry = entry->next)
136     if (entry->mod_id == mod_id)
137       {
138         entry->counter++;
139         break;
140       }
141
142   return entry;
143 }
144
145 /* Lookup a module specification.  After a successful lookup, the
146    module has it's resource counter incremented.  FUNC is a function
147    provided by the caller, which is responsible for identifying the
148    wanted module.  */
149 gcry_module_t
150 _gcry_module_lookup (gcry_module_t entries, void *data,
151                      gcry_module_lookup_t func)
152 {
153   gcry_module_t entry;
154
155   for (entry = entries; entry; entry = entry->next)
156     if ((*func) (entry->spec, data))
157       {
158         entry->counter++;
159         break;
160       }
161
162   return entry;
163 }
164
165 /* Release a module.  In case the use-counter reaches zero, destroy
166    the module.  Passing MODULE as NULL is a dummy operation (similar
167    to free()). */
168 void
169 _gcry_module_release (gcry_module_t module)
170 {
171   if (module && ! --module->counter)
172     _gcry_module_drop (module);
173 }
174
175 /* Add a reference to a module.  */
176 void
177 _gcry_module_use (gcry_module_t module)
178 {
179   ++module->counter;
180 }
181
182 /* If LIST is zero, write the number of modules identified by MODULES
183    to LIST_LENGTH and return.  If LIST is non-zero, the first
184    *LIST_LENGTH algorithm IDs are stored in LIST, which must be of
185    according size.  In case there are less cipher modules than
186    *LIST_LENGTH, *LIST_LENGTH is updated to the correct number.  */
187 gcry_err_code_t
188 _gcry_module_list (gcry_module_t modules,
189                    int *list, int *list_length)
190 {
191   gcry_err_code_t err = GPG_ERR_NO_ERROR;
192   gcry_module_t module;
193   int length, i;
194
195   for (module = modules, length = 0; module; module = module->next, length++);
196
197   if (list)
198     {
199       if (length > *list_length)
200         length = *list_length;
201
202       for (module = modules, i = 0; i < length; module = module->next, i++)
203         list[i] = module->mod_id;
204
205       if (length < *list_length)
206         *list_length = length;
207     }
208   else
209     *list_length = length;
210
211   return err;
212 }