Add a return value to hasher_t.get_hash()
[strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_hasher.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "pkcs11_hasher.h"
17
18 #include <unistd.h>
19
20 #include <debug.h>
21 #include <threading/mutex.h>
22
23 #include "pkcs11_manager.h"
24
25 typedef struct private_pkcs11_hasher_t private_pkcs11_hasher_t;
26
27 /**
28 * Private data of an pkcs11_hasher_t object.
29 */
30 struct private_pkcs11_hasher_t {
31
32 /**
33 * Public pkcs11_hasher_t interface.
34 */
35 pkcs11_hasher_t public;
36
37 /**
38 * PKCS#11 library
39 */
40 pkcs11_library_t *lib;
41
42 /**
43 * Mechanism for this hasher
44 */
45 CK_MECHANISM_PTR mech;
46
47 /**
48 * Token session
49 */
50 CK_SESSION_HANDLE session;
51
52 /**
53 * size of the hash
54 */
55 size_t size;
56
57 /**
58 * Mutex to lock the tokens hashing engine
59 */
60 mutex_t *mutex;
61
62 /**
63 * do we have an initialized state?
64 */
65 bool have_state;
66
67 /**
68 * state buffer
69 */
70 CK_BYTE_PTR state;
71
72 /**
73 * Length of the state buffer
74 */
75 CK_ULONG state_len;
76 };
77
78 METHOD(hasher_t, get_hash_size, size_t,
79 private_pkcs11_hasher_t *this)
80 {
81 return this->size;
82 }
83
84 /**
85 * Save the Operation state to host memory
86 */
87 static bool save_state(private_pkcs11_hasher_t *this)
88 {
89 CK_RV rv;
90
91 while (TRUE)
92 {
93 if (!this->state)
94 {
95 rv = this->lib->f->C_GetOperationState(this->session, NULL,
96 &this->state_len);
97 if (rv != CKR_OK)
98 {
99 break;
100 }
101 this->state = malloc(this->state_len);
102 }
103 rv = this->lib->f->C_GetOperationState(this->session, this->state,
104 &this->state_len);
105 switch (rv)
106 {
107 case CKR_BUFFER_TOO_SMALL:
108 free(this->state);
109 this->state = NULL;
110 continue;
111 case CKR_OK:
112 this->have_state = TRUE;
113 return TRUE;
114 default:
115 break;
116 }
117 break;
118 }
119 DBG1(DBG_CFG, "C_GetOperationState() failed: %N", ck_rv_names, rv);
120 return FALSE;
121 }
122
123 /**
124 * Load the Operation state from host memory
125 */
126 static bool load_state(private_pkcs11_hasher_t *this)
127 {
128 CK_RV rv;
129
130 rv = this->lib->f->C_SetOperationState(this->session, this->state,
131 this->state_len, CK_INVALID_HANDLE, CK_INVALID_HANDLE);
132 if (rv != CKR_OK)
133 {
134 DBG1(DBG_CFG, "C_SetOperationState() failed: %N", ck_rv_names, rv);
135 return FALSE;
136 }
137 this->have_state = FALSE;
138 return TRUE;
139 }
140
141 METHOD(hasher_t, reset, void,
142 private_pkcs11_hasher_t *this)
143 {
144 this->have_state = FALSE;
145 }
146
147 METHOD(hasher_t, get_hash, bool,
148 private_pkcs11_hasher_t *this, chunk_t chunk, u_int8_t *hash)
149 {
150 CK_RV rv;
151 CK_ULONG len;
152
153 this->mutex->lock(this->mutex);
154 if (this->have_state)
155 {
156 if (!load_state(this))
157 {
158 this->mutex->unlock(this->mutex);
159 return FALSE;
160 }
161 }
162 else
163 {
164 rv = this->lib->f->C_DigestInit(this->session, this->mech);
165 if (rv != CKR_OK)
166 {
167 DBG1(DBG_CFG, "C_DigestInit() failed: %N", ck_rv_names, rv);
168 this->mutex->unlock(this->mutex);
169 return FALSE;
170 }
171 }
172 if (chunk.len)
173 {
174 rv = this->lib->f->C_DigestUpdate(this->session, chunk.ptr, chunk.len);
175 if (rv != CKR_OK)
176 {
177 DBG1(DBG_CFG, "C_DigestUpdate() failed: %N", ck_rv_names, rv);
178 this->mutex->unlock(this->mutex);
179 return FALSE;
180 }
181 }
182 if (hash)
183 {
184 len = this->size;
185 rv = this->lib->f->C_DigestFinal(this->session,
186 hash, &len);
187 if (rv != CKR_OK)
188 {
189 DBG1(DBG_CFG, "C_DigestFinal() failed: %N", ck_rv_names, rv);
190 this->mutex->unlock(this->mutex);
191 return FALSE;
192 }
193 }
194 else
195 {
196 if (!save_state(this))
197 {
198 this->mutex->unlock(this->mutex);
199 return FALSE;
200 }
201 }
202 this->mutex->unlock(this->mutex);
203 return TRUE;
204 }
205
206 METHOD(hasher_t, allocate_hash, void,
207 private_pkcs11_hasher_t *this, chunk_t chunk, chunk_t *hash)
208 {
209 if (hash)
210 {
211 *hash = chunk_alloc(this->size);
212 get_hash(this, chunk, hash->ptr);
213 }
214 else
215 {
216 get_hash(this, chunk, NULL);
217 }
218 }
219
220 METHOD(hasher_t, destroy, void,
221 private_pkcs11_hasher_t *this)
222 {
223 this->lib->f->C_CloseSession(this->session);
224 this->mutex->destroy(this->mutex);
225 free(this);
226 }
227
228 /**
229 * Get the Cryptoki mechanism for a hash algorithm
230 */
231 static CK_MECHANISM_PTR algo_to_mechanism(hash_algorithm_t algo, size_t *size)
232 {
233 static struct {
234 hash_algorithm_t algo;
235 CK_MECHANISM mechanism;
236 size_t size;
237 } mappings[] = {
238 {HASH_MD2, {CKM_MD2, NULL, 0}, HASH_SIZE_MD2},
239 {HASH_MD5, {CKM_MD5, NULL, 0}, HASH_SIZE_MD5},
240 {HASH_SHA1, {CKM_SHA_1, NULL, 0}, HASH_SIZE_SHA1},
241 {HASH_SHA256, {CKM_SHA256, NULL, 0}, HASH_SIZE_SHA256},
242 {HASH_SHA384, {CKM_SHA384, NULL, 0}, HASH_SIZE_SHA384},
243 {HASH_SHA512, {CKM_SHA512, NULL, 0}, HASH_SIZE_SHA512},
244 };
245 int i;
246
247 for (i = 0; i < countof(mappings); i++)
248 {
249 if (mappings[i].algo == algo)
250 {
251 *size = mappings[i].size;
252 return &mappings[i].mechanism;
253 }
254 }
255 return NULL;
256 }
257
258 /**
259 * Find a token we can use for a hash algorithm
260 */
261 static pkcs11_library_t* find_token(hash_algorithm_t algo,
262 CK_SESSION_HANDLE *session, CK_MECHANISM_PTR *mout, size_t *size)
263 {
264 enumerator_t *tokens, *mechs;
265 pkcs11_manager_t *manager;
266 pkcs11_library_t *current, *found = NULL;
267 CK_MECHANISM_TYPE type;
268 CK_MECHANISM_PTR mech;
269 CK_SLOT_ID slot;
270
271 mech = algo_to_mechanism(algo, size);
272 if (!mech)
273 {
274 return NULL;
275 }
276 manager = lib->get(lib, "pkcs11-manager");
277 if (!manager)
278 {
279 return NULL;
280 }
281 tokens = manager->create_token_enumerator(manager);
282 while (tokens->enumerate(tokens, &current, &slot))
283 {
284 mechs = current->create_mechanism_enumerator(current, slot);
285 while (mechs->enumerate(mechs, &type, NULL))
286 {
287 if (type == mech->mechanism)
288 {
289 if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
290 NULL, NULL, session) == CKR_OK)
291 {
292 found = current;
293 *mout = mech;
294 break;
295 }
296 }
297 }
298 mechs->destroy(mechs);
299 if (found)
300 {
301 break;
302 }
303 }
304 tokens->destroy(tokens);
305 return found;
306 }
307
308 /**
309 * See header
310 */
311 pkcs11_hasher_t *pkcs11_hasher_create(hash_algorithm_t algo)
312 {
313 private_pkcs11_hasher_t *this;
314
315 INIT(this,
316 .public = {
317 .hasher = {
318 .get_hash_size = _get_hash_size,
319 .reset = _reset,
320 .get_hash = _get_hash,
321 .allocate_hash = _allocate_hash,
322 .destroy = _destroy,
323 },
324 },
325 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
326 );
327
328 this->lib = find_token(algo, &this->session, &this->mech, &this->size);
329 if (!this->lib)
330 {
331 this->mutex->destroy(this->mutex);
332 free(this);
333 return NULL;
334 }
335
336 return &this->public;
337 }