Implemented hasher_t using PKCS#11
[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
22 #include "pkcs11_manager.h"
23
24 typedef struct private_pkcs11_hasher_t private_pkcs11_hasher_t;
25
26 /**
27 * Private data of an pkcs11_hasher_t object.
28 */
29 struct private_pkcs11_hasher_t {
30
31 /**
32 * Public pkcs11_hasher_t interface.
33 */
34 pkcs11_hasher_t public;
35
36 /**
37 * PKCS#11 library
38 */
39 pkcs11_library_t *lib;
40
41 /**
42 * Mechanism for this hasher
43 */
44 CK_MECHANISM_PTR mech;
45
46 /**
47 * Token session
48 */
49 CK_SESSION_HANDLE session;
50
51 /**
52 * size of the hash
53 */
54 size_t size;
55 };
56
57 METHOD(hasher_t, get_hash_size, size_t,
58 private_pkcs11_hasher_t *this)
59 {
60 return this->size;
61 }
62
63 /**
64 * Try to handle errors
65 */
66 static void handle_error(private_pkcs11_hasher_t *this, CK_RV rv)
67 {
68 switch (rv)
69 {
70 case CKR_SESSION_CLOSED:
71 case CKR_SESSION_HANDLE_INVALID:
72 case CKR_USER_NOT_LOGGED_IN:
73 case CKR_PIN_EXPIRED:
74 case CKR_OPERATION_NOT_INITIALIZED:
75 /* reopen session if we are in DigestInit? */
76 case CKR_CRYPTOKI_NOT_INITIALIZED:
77 case CKR_ARGUMENTS_BAD:
78 case CKR_DEVICE_ERROR:
79 case CKR_DEVICE_REMOVED:
80 case CKR_GENERAL_ERROR:
81 case CKR_MECHANISM_INVALID:
82 case CKR_MECHANISM_PARAM_INVALID:
83 DBG1(DBG_CFG, "PKCS#11 hasher fatal error: %N", ck_rv_names, rv);
84 abort();
85 break;
86 case CKR_FUNCTION_CANCELED:
87 case CKR_FUNCTION_FAILED:
88 case CKR_OPERATION_ACTIVE:
89 case CKR_HOST_MEMORY:
90 case CKR_DEVICE_MEMORY:
91 DBG1(DBG_CFG, "PKCS#11 hasher critical error: %N", ck_rv_names, rv);
92 sleep(1);
93 break;
94 }
95 }
96
97 METHOD(hasher_t, reset, void,
98 private_pkcs11_hasher_t *this)
99 {
100 CK_RV rv;
101
102 while ((rv = this->lib->f->C_DigestInit(this->session,
103 this->mech)) != CKR_OK)
104 {
105 handle_error(this, rv);
106 }
107 }
108
109 METHOD(hasher_t, get_hash, void,
110 private_pkcs11_hasher_t *this, chunk_t chunk, u_int8_t *hash)
111 {
112 CK_RV rv;
113 CK_ULONG len;
114
115 if (chunk.len)
116 {
117 while ((rv = this->lib->f->C_DigestUpdate(this->session,
118 chunk.ptr, chunk.len)) != CKR_OK)
119 {
120 handle_error(this, rv);
121 }
122 }
123 if (hash)
124 {
125 len = this->size;
126 while ((rv = this->lib->f->C_DigestFinal(this->session,
127 hash, &len)) != CKR_OK)
128 {
129 handle_error(this, rv);
130 }
131 reset(this);
132 }
133 }
134
135 METHOD(hasher_t, allocate_hash, void,
136 private_pkcs11_hasher_t *this, chunk_t chunk, chunk_t *hash)
137 {
138 if (hash)
139 {
140 *hash = chunk_alloc(this->size);
141 get_hash(this, chunk, hash->ptr);
142 }
143 else
144 {
145 get_hash(this, chunk, NULL);
146 }
147 }
148
149 METHOD(hasher_t, destroy, void,
150 private_pkcs11_hasher_t *this)
151 {
152 this->lib->f->C_CloseSession(this->session);
153 free(this);
154 }
155
156 /**
157 * Get the Cryptoki mechanism for a hash algorithm
158 */
159 static CK_MECHANISM_PTR algo_to_mechanism(hash_algorithm_t algo, size_t *size)
160 {
161 static struct {
162 hash_algorithm_t algo;
163 CK_MECHANISM mechanism;
164 size_t size;
165 } mappings[] = {
166 {HASH_MD2, {CKM_MD2, NULL, 0}, HASH_SIZE_MD2},
167 {HASH_MD5, {CKM_MD5, NULL, 0}, HASH_SIZE_MD5},
168 {HASH_SHA1, {CKM_SHA_1, NULL, 0}, HASH_SIZE_SHA1},
169 {HASH_SHA256, {CKM_SHA256, NULL, 0}, HASH_SIZE_SHA256},
170 {HASH_SHA384, {CKM_SHA384, NULL, 0}, HASH_SIZE_SHA384},
171 {HASH_SHA512, {CKM_SHA512, NULL, 0}, HASH_SIZE_SHA512},
172 };
173 int i;
174
175 for (i = 0; i < countof(mappings); i++)
176 {
177 if (mappings[i].algo == algo)
178 {
179 *size = mappings[i].size;
180 return &mappings[i].mechanism;
181 }
182 }
183 return NULL;
184 }
185
186 /**
187 * Find a token we can use for a hash algorithm
188 */
189 static pkcs11_library_t* find_token(hash_algorithm_t algo,
190 CK_SESSION_HANDLE *session, CK_MECHANISM_PTR *mout, size_t *size)
191 {
192 enumerator_t *tokens, *mechs;
193 pkcs11_manager_t *manager;
194 pkcs11_library_t *current, *found = NULL;
195 CK_MECHANISM_TYPE type;
196 CK_MECHANISM_PTR mech;
197 CK_SLOT_ID slot;
198
199 mech = algo_to_mechanism(algo, size);
200 if (!mech)
201 {
202 return NULL;
203 }
204 manager = pkcs11_manager_get();
205 if (!manager)
206 {
207 return NULL;
208 }
209 tokens = manager->create_token_enumerator(manager);
210 while (tokens->enumerate(tokens, &current, &slot))
211 {
212 mechs = current->create_mechanism_enumerator(current, slot);
213 while (mechs->enumerate(mechs, &type, NULL))
214 {
215 if (type == mech->mechanism)
216 {
217 if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
218 NULL, NULL, session) == CKR_OK)
219 {
220 found = current;
221 *mout = mech;
222 break;
223 }
224 }
225 }
226 mechs->destroy(mechs);
227 if (found)
228 {
229 break;
230 }
231 }
232 tokens->destroy(tokens);
233 return found;
234 }
235
236 /**
237 * See header
238 */
239 pkcs11_hasher_t *pkcs11_hasher_create(hash_algorithm_t algo)
240 {
241 private_pkcs11_hasher_t *this;
242 CK_RV rv;
243
244 INIT(this,
245 .public.hasher = {
246 .get_hash_size = _get_hash_size,
247 .reset = _reset,
248 .get_hash = _get_hash,
249 .allocate_hash = _allocate_hash,
250 .destroy = _destroy,
251 },
252 );
253
254 this->lib = find_token(algo, &this->session, &this->mech, &this->size);
255 if (!this->lib)
256 {
257 free(this);
258 return NULL;
259 }
260 rv = this->lib->f->C_DigestInit(this->session, this->mech);
261 if (rv != CKR_OK)
262 {
263 DBG1(DBG_CFG, "C_DigestInit() failed: %N", ck_rv_names, rv);
264 destroy(this);
265 return NULL;
266 }
267 return &this->public;
268 }