Implemented keyid discovery on all modules/slots
[strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_private_key.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_private_key.h"
17
18 #include "pkcs11_library.h"
19 #include "pkcs11_manager.h"
20
21 #include <debug.h>
22 #include <threading/mutex.h>
23
24 typedef struct private_pkcs11_private_key_t private_pkcs11_private_key_t;
25
26 /**
27 * Private data of an pkcs11_private_key_t object.
28 */
29 struct private_pkcs11_private_key_t {
30
31 /**
32 * Public pkcs11_private_key_t interface.
33 */
34 pkcs11_private_key_t public;
35
36 /**
37 * PKCS#11 module
38 */
39 pkcs11_library_t *lib;
40
41 /**
42 * Token session
43 */
44 CK_SESSION_HANDLE session;
45
46 /**
47 * Mutex to lock session
48 */
49 mutex_t *mutex;
50
51 /**
52 * Key object on the token
53 */
54 CK_OBJECT_HANDLE object;
55
56 /**
57 * Associated public key
58 */
59 public_key_t *pubkey;
60
61 /**
62 * References to this key
63 */
64 refcount_t ref;
65 };
66
67 METHOD(private_key_t, get_type, key_type_t,
68 private_pkcs11_private_key_t *this)
69 {
70 return this->pubkey->get_type(this->pubkey);
71 }
72
73 METHOD(private_key_t, get_keysize, size_t,
74 private_pkcs11_private_key_t *this)
75 {
76 return this->pubkey->get_keysize(this->pubkey);
77 }
78
79 /**
80 * Get the Cryptoki mechanism for a signature scheme
81 */
82 static CK_MECHANISM_PTR scheme_to_mechanism(signature_scheme_t scheme)
83 {
84 static struct {
85 signature_scheme_t scheme;
86 CK_MECHANISM mechanism;
87 } mappings[] = {
88 {SIGN_RSA_EMSA_PKCS1_NULL, {CKM_RSA_PKCS, NULL, 0}},
89 {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_SHA1_RSA_PKCS, NULL, 0}},
90 {SIGN_RSA_EMSA_PKCS1_SHA256, {CKM_SHA256_RSA_PKCS, NULL, 0}},
91 {SIGN_RSA_EMSA_PKCS1_SHA384, {CKM_SHA384_RSA_PKCS, NULL, 0}},
92 {SIGN_RSA_EMSA_PKCS1_SHA512, {CKM_SHA512_RSA_PKCS, NULL, 0}},
93 {SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0}},
94 };
95 int i;
96
97 for (i = 0; i < countof(mappings); i++)
98 {
99 if (mappings[i].scheme == scheme)
100 {
101 return &mappings[i].mechanism;
102 }
103 }
104 return NULL;
105 }
106
107 METHOD(private_key_t, sign, bool,
108 private_pkcs11_private_key_t *this, signature_scheme_t scheme,
109 chunk_t data, chunk_t *signature)
110 {
111 CK_MECHANISM_PTR mechanism;
112 CK_BYTE_PTR buf;
113 CK_ULONG len;
114 CK_RV rv;
115
116 mechanism = scheme_to_mechanism(scheme);
117 if (!mechanism)
118 {
119 DBG1(DBG_LIB, "signature scheme %N not supported",
120 signature_scheme_names, scheme);
121 return FALSE;
122 }
123 this->mutex->lock(this->mutex);
124 rv = this->lib->f->C_SignInit(this->session, mechanism, this->object);
125 if (rv != CKR_OK)
126 {
127 this->mutex->unlock(this->mutex);
128 DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
129 return FALSE;
130 }
131 buf = malloc(get_keysize(this));
132 rv = this->lib->f->C_Sign(this->session, data.ptr, data.len, buf, &len);
133 this->mutex->unlock(this->mutex);
134 if (rv != CKR_OK)
135 {
136 DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
137 free(buf);
138 return FALSE;
139 }
140 *signature = chunk_create(buf, len);
141 return TRUE;
142 }
143
144 METHOD(private_key_t, decrypt, bool,
145 private_pkcs11_private_key_t *this, chunk_t crypto, chunk_t *plain)
146 {
147 return FALSE;
148 }
149
150 METHOD(private_key_t, get_public_key, public_key_t*,
151 private_pkcs11_private_key_t *this)
152 {
153 return this->pubkey->get_ref(this->pubkey);
154 }
155
156 METHOD(private_key_t, get_fingerprint, bool,
157 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
158 chunk_t *fingerprint)
159 {
160 return this->pubkey->get_fingerprint(this->pubkey, type, fingerprint);
161 }
162
163 METHOD(private_key_t, get_encoding, bool,
164 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
165 chunk_t *encoding)
166 {
167 return FALSE;
168 }
169
170 METHOD(private_key_t, get_ref, private_key_t*,
171 private_pkcs11_private_key_t *this)
172 {
173 ref_get(&this->ref);
174 return &this->public.key;
175 }
176
177 METHOD(private_key_t, destroy, void,
178 private_pkcs11_private_key_t *this)
179 {
180 if (ref_put(&this->ref))
181 {
182 if (this->pubkey)
183 {
184 this->pubkey->destroy(this->pubkey);
185 }
186 this->mutex->destroy(this->mutex);
187 this->lib->f->C_CloseSession(this->session);
188 free(this);
189 }
190 }
191
192 /**
193 * Find the PKCS#11 library by its friendly name
194 */
195 static pkcs11_library_t* find_lib(char *module)
196 {
197 pkcs11_manager_t *manager;
198 enumerator_t *enumerator;
199 pkcs11_library_t *p11, *found = NULL;
200 CK_SLOT_ID slot;
201
202 manager = pkcs11_manager_get();
203 if (!manager)
204 {
205 return NULL;
206 }
207 enumerator = manager->create_token_enumerator(manager);
208 while (enumerator->enumerate(enumerator, &p11, &slot))
209 {
210 if (streq(module, p11->get_name(p11)))
211 {
212 found = p11;
213 break;
214 }
215 }
216 enumerator->destroy(enumerator);
217 return found;
218 }
219
220 /**
221 * Find the PKCS#11 lib having a keyid, and optionally a slot
222 */
223 static pkcs11_library_t* find_lib_by_keyid(chunk_t keyid, int *slot)
224 {
225 pkcs11_manager_t *manager;
226 enumerator_t *enumerator;
227 pkcs11_library_t *p11, *found = NULL;
228 CK_SLOT_ID current;
229
230 manager = pkcs11_manager_get();
231 if (!manager)
232 {
233 return NULL;
234 }
235 enumerator = manager->create_token_enumerator(manager);
236 while (enumerator->enumerate(enumerator, &p11, &current))
237 {
238 if (*slot == -1 || *slot == current)
239 {
240 /* we look for a public key, it is usually readable without login */
241 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
242 CK_ATTRIBUTE tmpl[] = {
243 {CKA_CLASS, &class, sizeof(class)},
244 {CKA_ID, keyid.ptr, keyid.len},
245 };
246 CK_OBJECT_HANDLE object;
247 CK_SESSION_HANDLE session;
248 CK_RV rv;
249 enumerator_t *keys;
250
251 rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL,
252 &session);
253 if (rv != CKR_OK)
254 {
255 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
256 ck_rv_names, rv);
257 continue;
258 }
259 keys = p11->create_object_enumerator(p11, session,
260 tmpl, countof(tmpl), NULL, 0);
261 if (keys->enumerate(keys, &object))
262 {
263 DBG1(DBG_CFG, "found key on PKCS#11 token '%s':%d",
264 p11->get_name(p11), current);
265 found = p11;
266 *slot = current;
267 }
268 keys->destroy(keys);
269 p11->f->C_CloseSession(session);
270 if (found)
271 {
272 break;
273 }
274 }
275 }
276 enumerator->destroy(enumerator);
277 return found;
278 }
279
280 /**
281 * Find the key on the token
282 */
283 static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
284 {
285 CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
286 CK_ATTRIBUTE tmpl[] = {
287 {CKA_CLASS, &class, sizeof(class)},
288 {CKA_ID, keyid.ptr, keyid.len},
289 };
290 CK_OBJECT_HANDLE object;
291 CK_KEY_TYPE type;
292 CK_ATTRIBUTE attr[] = {
293 {CKA_KEY_TYPE, &type, sizeof(type)},
294 {CKA_MODULUS, NULL, 0},
295 {CKA_PUBLIC_EXPONENT, NULL, 0},
296 };
297 enumerator_t *enumerator;
298 chunk_t modulus, pubexp;
299
300 enumerator = this->lib->create_object_enumerator(this->lib,
301 this->session, tmpl, countof(tmpl), attr, countof(attr));
302 if (enumerator->enumerate(enumerator, &object))
303 {
304 switch (type)
305 {
306 case CKK_RSA:
307 if (attr[0].ulValueLen == -1 || attr[1].ulValueLen == -1)
308 {
309 DBG1(DBG_CFG, "reading modulus/exponent from PKCS#1 failed");
310 break;
311 }
312 modulus = chunk_create(attr[1].pValue, attr[1].ulValueLen);
313 pubexp = chunk_create(attr[2].pValue, attr[2].ulValueLen);
314 this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
315 KEY_RSA, BUILD_RSA_MODULUS, modulus,
316 BUILD_RSA_PUB_EXP, pubexp, BUILD_END);
317 if (!this->pubkey)
318 {
319 DBG1(DBG_CFG, "extracting public key from PKCS#11 RSA "
320 "private key failed");
321 }
322 this->object = object;
323 break;
324 default:
325 DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type);
326 break;
327 }
328 }
329 enumerator->destroy(enumerator);
330 return this->pubkey != NULL;
331 }
332
333 /**
334 * See header.
335 */
336 pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
337 {
338 private_pkcs11_private_key_t *this;
339 char *module = NULL;
340 chunk_t keyid, pin;
341 int slot = -1;
342 CK_RV rv;
343
344 keyid = pin = chunk_empty;
345 while (TRUE)
346 {
347 switch (va_arg(args, builder_part_t))
348 {
349 case BUILD_PKCS11_KEYID:
350 keyid = va_arg(args, chunk_t);
351 continue;
352 case BUILD_PASSPHRASE:
353 pin = va_arg(args, chunk_t);
354 continue;
355 case BUILD_PKCS11_SLOT:
356 slot = va_arg(args, int);
357 continue;
358 case BUILD_PKCS11_MODULE:
359 module = va_arg(args, char*);
360 continue;
361 case BUILD_END:
362 break;
363 default:
364 return NULL;
365 }
366 break;
367 }
368 if (!keyid.len || !pin.len)
369 {
370 return NULL;
371 }
372
373 INIT(this,
374 .public.key = {
375 .get_type = _get_type,
376 .sign = _sign,
377 .decrypt = _decrypt,
378 .get_keysize = _get_keysize,
379 .get_public_key = _get_public_key,
380 .equals = private_key_equals,
381 .belongs_to = private_key_belongs_to,
382 .get_fingerprint = _get_fingerprint,
383 .has_fingerprint = private_key_has_fingerprint,
384 .get_encoding = _get_encoding,
385 .get_ref = _get_ref,
386 .destroy = _destroy,
387 },
388 .ref = 1,
389 );
390
391 if (module && slot != -1)
392 {
393 this->lib = find_lib(module);
394 if (!this->lib)
395 {
396 DBG1(DBG_CFG, "PKCS#11 module '%s' not found", module);
397 free(this);
398 return NULL;
399 }
400 }
401 else
402 {
403 this->lib = find_lib_by_keyid(keyid, &slot);
404 if (!this->lib)
405 {
406 DBG1(DBG_CFG, "no PKCS#11 module found having a keyid %#B", &keyid);
407 free(this);
408 return NULL;
409 }
410 }
411
412 rv = this->lib->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
413 NULL, NULL, &this->session);
414 if (rv != CKR_OK)
415 {
416 DBG1(DBG_CFG, "opening private key session on '%s':%d failed: %N",
417 module, slot, ck_rv_names, rv);
418 free(this);
419 return NULL;
420 }
421
422 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
423
424 rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
425 if (rv != CKR_OK)
426 {
427 DBG1(DBG_CFG, "login to '%s':%d failed: %N",
428 module, slot, ck_rv_names, rv);
429 destroy(this);
430 return NULL;
431 }
432
433 if (!find_key(this, keyid))
434 {
435 destroy(this);
436 return NULL;
437 }
438
439 return &this->public;
440 }