pkcs11: Instead of a mutex use a new session to do multipart operations.
[strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_private_key.c
1 /*
2 * Copyright (C) 2011 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2010 Martin Willi
6 * Copyright (C) 2010 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "pkcs11_private_key.h"
20
21 #include "pkcs11_library.h"
22 #include "pkcs11_manager.h"
23
24 #include <debug.h>
25
26 typedef struct private_pkcs11_private_key_t private_pkcs11_private_key_t;
27
28 /**
29 * Private data of an pkcs11_private_key_t object.
30 */
31 struct private_pkcs11_private_key_t {
32
33 /**
34 * Public pkcs11_private_key_t interface.
35 */
36 pkcs11_private_key_t public;
37
38 /**
39 * PKCS#11 module
40 */
41 pkcs11_library_t *lib;
42
43 /**
44 * Slot the token is in
45 */
46 CK_SLOT_ID slot;
47
48 /**
49 * Token session
50 */
51 CK_SESSION_HANDLE session;
52
53 /**
54 * Key object on the token
55 */
56 CK_OBJECT_HANDLE object;
57
58 /**
59 * Key requires reauthentication for each signature/decryption
60 */
61 CK_BBOOL reauth;
62
63 /**
64 * Keyid of the key we use
65 */
66 identification_t *keyid;
67
68 /**
69 * Associated public key
70 */
71 public_key_t *pubkey;
72
73 /**
74 * References to this key
75 */
76 refcount_t ref;
77 };
78
79 METHOD(private_key_t, get_type, key_type_t,
80 private_pkcs11_private_key_t *this)
81 {
82 return this->pubkey->get_type(this->pubkey);
83 }
84
85 METHOD(private_key_t, get_keysize, int,
86 private_pkcs11_private_key_t *this)
87 {
88 return this->pubkey->get_keysize(this->pubkey);
89 }
90
91 /**
92 * See header.
93 */
94 CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(signature_scheme_t scheme)
95 {
96 static struct {
97 signature_scheme_t scheme;
98 CK_MECHANISM mechanism;
99 } mappings[] = {
100 {SIGN_RSA_EMSA_PKCS1_NULL, {CKM_RSA_PKCS, NULL, 0}},
101 {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_SHA1_RSA_PKCS, NULL, 0}},
102 {SIGN_RSA_EMSA_PKCS1_SHA256, {CKM_SHA256_RSA_PKCS, NULL, 0}},
103 {SIGN_RSA_EMSA_PKCS1_SHA384, {CKM_SHA384_RSA_PKCS, NULL, 0}},
104 {SIGN_RSA_EMSA_PKCS1_SHA512, {CKM_SHA512_RSA_PKCS, NULL, 0}},
105 {SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0}},
106 };
107 int i;
108
109 for (i = 0; i < countof(mappings); i++)
110 {
111 if (mappings[i].scheme == scheme)
112 {
113 return &mappings[i].mechanism;
114 }
115 }
116 return NULL;
117 }
118
119 /**
120 * See header.
121 */
122 CK_MECHANISM_PTR pkcs11_encryption_scheme_to_mech(encryption_scheme_t scheme)
123 {
124 static struct {
125 encryption_scheme_t scheme;
126 CK_MECHANISM mechanism;
127 } mappings[] = {
128 {ENCRYPT_RSA_PKCS1, {CKM_RSA_PKCS, NULL, 0}},
129 {ENCRYPT_RSA_OAEP_SHA1, {CKM_RSA_PKCS_OAEP, NULL, 0}},
130 };
131 int i;
132
133 for (i = 0; i < countof(mappings); i++)
134 {
135 if (mappings[i].scheme == scheme)
136 {
137 return &mappings[i].mechanism;
138 }
139 }
140 return NULL;
141 }
142
143 /**
144 * Reauthenticate to do a signature
145 */
146 static bool reauth(private_pkcs11_private_key_t *this,
147 CK_SESSION_HANDLE session)
148 {
149 enumerator_t *enumerator;
150 shared_key_t *shared;
151 chunk_t pin;
152 CK_RV rv;
153 bool found = FALSE, success = FALSE;
154
155 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
156 SHARED_PIN, this->keyid, NULL);
157 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
158 {
159 found = TRUE;
160 pin = shared->get_key(shared);
161 rv = this->lib->f->C_Login(session, CKU_CONTEXT_SPECIFIC,
162 pin.ptr, pin.len);
163 if (rv == CKR_OK)
164 {
165 success = TRUE;
166 break;
167 }
168 DBG1(DBG_CFG, "reauthentication login failed: %N", ck_rv_names, rv);
169 }
170 enumerator->destroy(enumerator);
171
172 if (!found)
173 {
174 DBG1(DBG_CFG, "private key requires reauthentication, but no PIN found");
175 return FALSE;
176 }
177 return success;
178 }
179
180 METHOD(private_key_t, sign, bool,
181 private_pkcs11_private_key_t *this, signature_scheme_t scheme,
182 chunk_t data, chunk_t *signature)
183 {
184 CK_MECHANISM_PTR mechanism;
185 CK_SESSION_HANDLE session;
186 CK_BYTE_PTR buf;
187 CK_ULONG len;
188 CK_RV rv;
189
190 mechanism = pkcs11_signature_scheme_to_mech(scheme);
191 if (!mechanism)
192 {
193 DBG1(DBG_LIB, "signature scheme %N not supported",
194 signature_scheme_names, scheme);
195 return FALSE;
196 }
197 rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
198 &session);
199 if (rv != CKR_OK)
200 {
201 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
202 return FALSE;
203 }
204 rv = this->lib->f->C_SignInit(session, mechanism, this->object);
205 if (this->reauth && !reauth(this, session))
206 {
207 this->lib->f->C_CloseSession(session);
208 return FALSE;
209 }
210 if (rv != CKR_OK)
211 {
212 this->lib->f->C_CloseSession(session);
213 DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
214 return FALSE;
215 }
216 len = (get_keysize(this) + 7) / 8;
217 buf = malloc(len);
218 rv = this->lib->f->C_Sign(session, data.ptr, data.len, buf, &len);
219 this->lib->f->C_CloseSession(session);
220 if (rv != CKR_OK)
221 {
222 DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
223 free(buf);
224 return FALSE;
225 }
226 *signature = chunk_create(buf, len);
227 return TRUE;
228 }
229
230 METHOD(private_key_t, decrypt, bool,
231 private_pkcs11_private_key_t *this, encryption_scheme_t scheme,
232 chunk_t crypt, chunk_t *plain)
233 {
234 CK_MECHANISM_PTR mechanism;
235 CK_SESSION_HANDLE session;
236 CK_BYTE_PTR buf;
237 CK_ULONG len;
238 CK_RV rv;
239
240 mechanism = pkcs11_encryption_scheme_to_mech(scheme);
241 if (!mechanism)
242 {
243 DBG1(DBG_LIB, "encryption scheme %N not supported",
244 encryption_scheme_names, scheme);
245 return FALSE;
246 }
247 rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
248 &session);
249 if (rv != CKR_OK)
250 {
251 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
252 return FALSE;
253 }
254 rv = this->lib->f->C_DecryptInit(session, mechanism, this->object);
255 if (this->reauth && !reauth(this, session))
256 {
257 this->lib->f->C_CloseSession(session);
258 return FALSE;
259 }
260 if (rv != CKR_OK)
261 {
262 this->lib->f->C_CloseSession(session);
263 DBG1(DBG_LIB, "C_DecryptInit() failed: %N", ck_rv_names, rv);
264 return FALSE;
265 }
266 len = (get_keysize(this) + 7) / 8;
267 buf = malloc(len);
268 rv = this->lib->f->C_Decrypt(session, crypt.ptr, crypt.len, buf, &len);
269 this->lib->f->C_CloseSession(session);
270 if (rv != CKR_OK)
271 {
272 DBG1(DBG_LIB, "C_Decrypt() failed: %N", ck_rv_names, rv);
273 free(buf);
274 return FALSE;
275 }
276 *plain = chunk_create(buf, len);
277 return TRUE;
278 }
279
280 METHOD(private_key_t, get_public_key, public_key_t*,
281 private_pkcs11_private_key_t *this)
282 {
283 return this->pubkey->get_ref(this->pubkey);
284 }
285
286 METHOD(private_key_t, get_fingerprint, bool,
287 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
288 chunk_t *fingerprint)
289 {
290 return this->pubkey->get_fingerprint(this->pubkey, type, fingerprint);
291 }
292
293 METHOD(private_key_t, get_encoding, bool,
294 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
295 chunk_t *encoding)
296 {
297 return FALSE;
298 }
299
300 METHOD(private_key_t, get_ref, private_key_t*,
301 private_pkcs11_private_key_t *this)
302 {
303 ref_get(&this->ref);
304 return &this->public.key;
305 }
306
307 METHOD(private_key_t, destroy, void,
308 private_pkcs11_private_key_t *this)
309 {
310 if (ref_put(&this->ref))
311 {
312 if (this->pubkey)
313 {
314 this->pubkey->destroy(this->pubkey);
315 }
316 this->keyid->destroy(this->keyid);
317 this->lib->f->C_CloseSession(this->session);
318 free(this);
319 }
320 }
321
322 /**
323 * Find the PKCS#11 library by its friendly name
324 */
325 static pkcs11_library_t* find_lib(char *module)
326 {
327 pkcs11_manager_t *manager;
328 enumerator_t *enumerator;
329 pkcs11_library_t *p11, *found = NULL;
330 CK_SLOT_ID slot;
331
332 manager = lib->get(lib, "pkcs11-manager");
333 if (!manager)
334 {
335 return NULL;
336 }
337 enumerator = manager->create_token_enumerator(manager);
338 while (enumerator->enumerate(enumerator, &p11, &slot))
339 {
340 if (streq(module, p11->get_name(p11)))
341 {
342 found = p11;
343 break;
344 }
345 }
346 enumerator->destroy(enumerator);
347 return found;
348 }
349
350 /**
351 * Find the PKCS#11 lib having a keyid, and optionally a slot
352 */
353 static pkcs11_library_t* find_lib_by_keyid(chunk_t keyid, int *slot)
354 {
355 pkcs11_manager_t *manager;
356 enumerator_t *enumerator;
357 pkcs11_library_t *p11, *found = NULL;
358 CK_SLOT_ID current;
359
360 manager = lib->get(lib, "pkcs11-manager");
361 if (!manager)
362 {
363 return NULL;
364 }
365 enumerator = manager->create_token_enumerator(manager);
366 while (enumerator->enumerate(enumerator, &p11, &current))
367 {
368 if (*slot == -1 || *slot == current)
369 {
370 /* we look for a public key, it is usually readable without login */
371 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
372 CK_ATTRIBUTE tmpl[] = {
373 {CKA_CLASS, &class, sizeof(class)},
374 {CKA_ID, keyid.ptr, keyid.len},
375 };
376 CK_OBJECT_HANDLE object;
377 CK_SESSION_HANDLE session;
378 CK_RV rv;
379 enumerator_t *keys;
380
381 rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL,
382 &session);
383 if (rv != CKR_OK)
384 {
385 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
386 ck_rv_names, rv);
387 continue;
388 }
389 keys = p11->create_object_enumerator(p11, session,
390 tmpl, countof(tmpl), NULL, 0);
391 if (keys->enumerate(keys, &object))
392 {
393 DBG1(DBG_CFG, "found key on PKCS#11 token '%s':%d",
394 p11->get_name(p11), current);
395 found = p11;
396 *slot = current;
397 }
398 keys->destroy(keys);
399 p11->f->C_CloseSession(session);
400 if (found)
401 {
402 break;
403 }
404 }
405 }
406 enumerator->destroy(enumerator);
407 return found;
408 }
409
410 /**
411 * Find the key on the token
412 */
413 static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
414 {
415 CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
416 CK_ATTRIBUTE tmpl[] = {
417 {CKA_CLASS, &class, sizeof(class)},
418 {CKA_ID, keyid.ptr, keyid.len},
419 };
420 CK_OBJECT_HANDLE object;
421 CK_KEY_TYPE type;
422 CK_BBOOL reauth = FALSE;
423 CK_ATTRIBUTE attr[] = {
424 {CKA_KEY_TYPE, &type, sizeof(type)},
425 {CKA_MODULUS, NULL, 0},
426 {CKA_PUBLIC_EXPONENT, NULL, 0},
427 {CKA_ALWAYS_AUTHENTICATE, &reauth, sizeof(reauth)},
428 };
429 enumerator_t *enumerator;
430 chunk_t modulus, pubexp;
431 int count = countof(attr);
432
433 /* do not use CKA_ALWAYS_AUTHENTICATE if not supported */
434 if (!(this->lib->get_features(this->lib) & PKCS11_ALWAYS_AUTH_KEYS))
435 {
436 count--;
437 }
438 enumerator = this->lib->create_object_enumerator(this->lib,
439 this->session, tmpl, countof(tmpl), attr, count);
440 if (enumerator->enumerate(enumerator, &object))
441 {
442 switch (type)
443 {
444 case CKK_RSA:
445 if (attr[1].ulValueLen == -1 || attr[2].ulValueLen == -1)
446 {
447 DBG1(DBG_CFG, "reading modulus/exponent from PKCS#1 failed");
448 break;
449 }
450 modulus = chunk_create(attr[1].pValue, attr[1].ulValueLen);
451 pubexp = chunk_create(attr[2].pValue, attr[2].ulValueLen);
452 this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
453 KEY_RSA, BUILD_RSA_MODULUS, modulus,
454 BUILD_RSA_PUB_EXP, pubexp, BUILD_END);
455 if (!this->pubkey)
456 {
457 DBG1(DBG_CFG, "extracting public key from PKCS#11 RSA "
458 "private key failed");
459 }
460 this->reauth = reauth;
461 this->object = object;
462 break;
463 default:
464 DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type);
465 break;
466 }
467 }
468 enumerator->destroy(enumerator);
469 return this->pubkey != NULL;
470 }
471
472 /**
473 * Find a PIN and try to log in
474 */
475 static bool login(private_pkcs11_private_key_t *this, int slot)
476 {
477 enumerator_t *enumerator;
478 shared_key_t *shared;
479 chunk_t pin;
480 CK_RV rv;
481 CK_SESSION_INFO info;
482 bool found = FALSE, success = FALSE;
483
484 rv = this->lib->f->C_GetSessionInfo(this->session, &info);
485 if (rv != CKR_OK)
486 {
487 DBG1(DBG_CFG, "C_GetSessionInfo failed: %N", ck_rv_names, rv);
488 return FALSE;
489 }
490 if (info.state != CKS_RO_PUBLIC_SESSION &&
491 info.state != CKS_RW_PUBLIC_SESSION)
492 { /* already logged in with another session, skip */
493 return TRUE;
494 }
495
496 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
497 SHARED_PIN, this->keyid, NULL);
498 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
499 {
500 found = TRUE;
501 pin = shared->get_key(shared);
502 rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
503 if (rv == CKR_OK)
504 {
505 success = TRUE;
506 break;
507 }
508 DBG1(DBG_CFG, "login to '%s':%d failed: %N",
509 this->lib->get_name(this->lib), slot, ck_rv_names, rv);
510 }
511 enumerator->destroy(enumerator);
512
513 if (!found)
514 {
515 DBG1(DBG_CFG, "no PIN found for PKCS#11 key %Y", this->keyid);
516 return FALSE;
517 }
518 return success;
519 }
520
521 /**
522 * See header.
523 */
524 pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
525 {
526 private_pkcs11_private_key_t *this;
527 char *module = NULL;
528 chunk_t keyid = chunk_empty;
529 int slot = -1;
530 CK_RV rv;
531
532 while (TRUE)
533 {
534 switch (va_arg(args, builder_part_t))
535 {
536 case BUILD_PKCS11_KEYID:
537 keyid = va_arg(args, chunk_t);
538 continue;
539 case BUILD_PKCS11_SLOT:
540 slot = va_arg(args, int);
541 continue;
542 case BUILD_PKCS11_MODULE:
543 module = va_arg(args, char*);
544 continue;
545 case BUILD_END:
546 break;
547 default:
548 return NULL;
549 }
550 break;
551 }
552 if (!keyid.len)
553 {
554 return NULL;
555 }
556
557 INIT(this,
558 .public = {
559 .key = {
560 .get_type = _get_type,
561 .sign = _sign,
562 .decrypt = _decrypt,
563 .get_keysize = _get_keysize,
564 .get_public_key = _get_public_key,
565 .equals = private_key_equals,
566 .belongs_to = private_key_belongs_to,
567 .get_fingerprint = _get_fingerprint,
568 .has_fingerprint = private_key_has_fingerprint,
569 .get_encoding = _get_encoding,
570 .get_ref = _get_ref,
571 .destroy = _destroy,
572 },
573 },
574 .ref = 1,
575 );
576
577 if (module && slot != -1)
578 {
579 this->lib = find_lib(module);
580 if (!this->lib)
581 {
582 DBG1(DBG_CFG, "PKCS#11 module '%s' not found", module);
583 free(this);
584 return NULL;
585 }
586 }
587 else
588 {
589 this->lib = find_lib_by_keyid(keyid, &slot);
590 if (!this->lib)
591 {
592 DBG1(DBG_CFG, "no PKCS#11 module found having a keyid %#B", &keyid);
593 free(this);
594 return NULL;
595 }
596 }
597
598 rv = this->lib->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
599 NULL, NULL, &this->session);
600 if (rv != CKR_OK)
601 {
602 DBG1(DBG_CFG, "opening private key session on '%s':%d failed: %N",
603 module, slot, ck_rv_names, rv);
604 free(this);
605 return NULL;
606 }
607
608 this->slot = slot;
609 this->keyid = identification_create_from_encoding(ID_KEY_ID, keyid);
610
611 if (!login(this, slot))
612 {
613 destroy(this);
614 return NULL;
615 }
616
617 if (!find_key(this, keyid))
618 {
619 destroy(this);
620 return NULL;
621 }
622
623 return &this->public;
624 }