Save/Load state of PKCS#11 hasher
[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 void 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;
114 default:
115 break;
116 }
117 break;
118 }
119 DBG1(DBG_CFG, "C_GetOperationState() failed: %N", ck_rv_names, rv);
120 abort();
121 }
122
123 /**
124 * Load the Operation state from host memory
125 */
126 static void 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 abort();
136 }
137 this->have_state = FALSE;
138 }
139
140 METHOD(hasher_t, reset, void,
141 private_pkcs11_hasher_t *this)
142 {
143 this->have_state = FALSE;
144 }
145
146 METHOD(hasher_t, get_hash, void,
147 private_pkcs11_hasher_t *this, chunk_t chunk, u_int8_t *hash)
148 {
149 CK_RV rv;
150 CK_ULONG len;
151
152 this->mutex->lock(this->mutex);
153 if (this->have_state)
154 {
155 load_state(this);
156 }
157 else
158 {
159 rv = this->lib->f->C_DigestInit(this->session, this->mech);
160 if (rv != CKR_OK)
161 {
162 DBG1(DBG_CFG, "C_DigestInit() failed: %N", ck_rv_names, rv);
163 abort();
164 }
165 }
166 if (chunk.len)
167 {
168 rv = this->lib->f->C_DigestUpdate(this->session, chunk.ptr, chunk.len);
169 if (rv != CKR_OK)
170 {
171 DBG1(DBG_CFG, "C_DigestUpdate() failed: %N", ck_rv_names, rv);
172 abort();
173 }
174 }
175 if (hash)
176 {
177 len = this->size;
178 rv = this->lib->f->C_DigestFinal(this->session,
179 hash, &len);
180 if (rv != CKR_OK)
181 {
182 DBG1(DBG_CFG, "C_DigestFinal() failed: %N", ck_rv_names, rv);
183 abort();
184 }
185 }
186 else
187 {
188 save_state(this);
189 }
190 this->mutex->unlock(this->mutex);
191 }
192
193 METHOD(hasher_t, allocate_hash, void,
194 private_pkcs11_hasher_t *this, chunk_t chunk, chunk_t *hash)
195 {
196 if (hash)
197 {
198 *hash = chunk_alloc(this->size);
199 get_hash(this, chunk, hash->ptr);
200 }
201 else
202 {
203 get_hash(this, chunk, NULL);
204 }
205 }
206
207 METHOD(hasher_t, destroy, void,
208 private_pkcs11_hasher_t *this)
209 {
210 this->lib->f->C_CloseSession(this->session);
211 this->mutex->destroy(this->mutex);
212 free(this);
213 }
214
215 /**
216 * Get the Cryptoki mechanism for a hash algorithm
217 */
218 static CK_MECHANISM_PTR algo_to_mechanism(hash_algorithm_t algo, size_t *size)
219 {
220 static struct {
221 hash_algorithm_t algo;
222 CK_MECHANISM mechanism;
223 size_t size;
224 } mappings[] = {
225 {HASH_MD2, {CKM_MD2, NULL, 0}, HASH_SIZE_MD2},
226 {HASH_MD5, {CKM_MD5, NULL, 0}, HASH_SIZE_MD5},
227 {HASH_SHA1, {CKM_SHA_1, NULL, 0}, HASH_SIZE_SHA1},
228 {HASH_SHA256, {CKM_SHA256, NULL, 0}, HASH_SIZE_SHA256},
229 {HASH_SHA384, {CKM_SHA384, NULL, 0}, HASH_SIZE_SHA384},
230 {HASH_SHA512, {CKM_SHA512, NULL, 0}, HASH_SIZE_SHA512},
231 };
232 int i;
233
234 for (i = 0; i < countof(mappings); i++)
235 {
236 if (mappings[i].algo == algo)
237 {
238 *size = mappings[i].size;
239 return &mappings[i].mechanism;
240 }
241 }
242 return NULL;
243 }
244
245 /**
246 * Find a token we can use for a hash algorithm
247 */
248 static pkcs11_library_t* find_token(hash_algorithm_t algo,
249 CK_SESSION_HANDLE *session, CK_MECHANISM_PTR *mout, size_t *size)
250 {
251 enumerator_t *tokens, *mechs;
252 pkcs11_manager_t *manager;
253 pkcs11_library_t *current, *found = NULL;
254 CK_MECHANISM_TYPE type;
255 CK_MECHANISM_PTR mech;
256 CK_SLOT_ID slot;
257
258 mech = algo_to_mechanism(algo, size);
259 if (!mech)
260 {
261 return NULL;
262 }
263 manager = pkcs11_manager_get();
264 if (!manager)
265 {
266 return NULL;
267 }
268 tokens = manager->create_token_enumerator(manager);
269 while (tokens->enumerate(tokens, &current, &slot))
270 {
271 mechs = current->create_mechanism_enumerator(current, slot);
272 while (mechs->enumerate(mechs, &type, NULL))
273 {
274 if (type == mech->mechanism)
275 {
276 if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
277 NULL, NULL, session) == CKR_OK)
278 {
279 found = current;
280 *mout = mech;
281 break;
282 }
283 }
284 }
285 mechs->destroy(mechs);
286 if (found)
287 {
288 break;
289 }
290 }
291 tokens->destroy(tokens);
292 return found;
293 }
294
295 /**
296 * See header
297 */
298 pkcs11_hasher_t *pkcs11_hasher_create(hash_algorithm_t algo)
299 {
300 private_pkcs11_hasher_t *this;
301
302 INIT(this,
303 .public.hasher = {
304 .get_hash_size = _get_hash_size,
305 .reset = _reset,
306 .get_hash = _get_hash,
307 .allocate_hash = _allocate_hash,
308 .destroy = _destroy,
309 },
310 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
311 );
312
313 this->lib = find_token(algo, &this->session, &this->mech, &this->size);
314 if (!this->lib)
315 {
316 free(this);
317 return NULL;
318 }
319
320 return &this->public;
321 }