f7f7d3f7964ba297732866e0f25501598445fccb
[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 * Type of this private key
80 */
81 key_type_t type;
82 };
83
84 /**
85 * Implemented in pkcs11_public_key.c
86 */
87 public_key_t *pkcs11_public_key_connect(pkcs11_library_t *p11,
88 int slot, key_type_t type, chunk_t keyid);
89
90
91 METHOD(private_key_t, get_type, key_type_t,
92 private_pkcs11_private_key_t *this)
93 {
94 return this->type;
95 }
96
97 METHOD(private_key_t, get_keysize, int,
98 private_pkcs11_private_key_t *this)
99 {
100 return this->pubkey->get_keysize(this->pubkey);
101 }
102
103 /**
104 * See header.
105 */
106 CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(signature_scheme_t scheme,
107 key_type_t type, size_t keylen,
108 hash_algorithm_t *hash)
109 {
110 static struct {
111 signature_scheme_t scheme;
112 CK_MECHANISM mechanism;
113 key_type_t type;
114 size_t keylen;
115 hash_algorithm_t hash;
116 } mappings[] = {
117 {SIGN_RSA_EMSA_PKCS1_NULL, {CKM_RSA_PKCS, NULL, 0},
118 KEY_RSA, 0, HASH_UNKNOWN},
119 {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_SHA1_RSA_PKCS, NULL, 0},
120 KEY_RSA, 0, HASH_UNKNOWN},
121 {SIGN_RSA_EMSA_PKCS1_SHA256, {CKM_SHA256_RSA_PKCS, NULL, 0},
122 KEY_RSA, 0, HASH_UNKNOWN},
123 {SIGN_RSA_EMSA_PKCS1_SHA384, {CKM_SHA384_RSA_PKCS, NULL, 0},
124 KEY_RSA, 0, HASH_UNKNOWN},
125 {SIGN_RSA_EMSA_PKCS1_SHA512, {CKM_SHA512_RSA_PKCS, NULL, 0},
126 KEY_RSA, 0, HASH_UNKNOWN},
127 {SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0},
128 KEY_RSA, 0, HASH_UNKNOWN},
129 {SIGN_ECDSA_WITH_NULL, {CKM_ECDSA, NULL, 0},
130 KEY_ECDSA, 0, HASH_UNKNOWN},
131 {SIGN_ECDSA_WITH_SHA1_DER, {CKM_ECDSA_SHA1, NULL, 0},
132 KEY_ECDSA, 0, HASH_UNKNOWN},
133 {SIGN_ECDSA_WITH_SHA256_DER, {CKM_ECDSA, NULL, 0},
134 KEY_ECDSA, 0, HASH_SHA256},
135 {SIGN_ECDSA_WITH_SHA384_DER, {CKM_ECDSA, NULL, 0},
136 KEY_ECDSA, 0, HASH_SHA384},
137 {SIGN_ECDSA_WITH_SHA512_DER, {CKM_ECDSA, NULL, 0},
138 KEY_ECDSA, 0, HASH_SHA512},
139 {SIGN_ECDSA_256, {CKM_ECDSA, NULL, 0},
140 KEY_ECDSA, 256, HASH_SHA256},
141 {SIGN_ECDSA_384, {CKM_ECDSA, NULL, 0},
142 KEY_ECDSA, 384, HASH_SHA384},
143 {SIGN_ECDSA_521, {CKM_ECDSA, NULL, 0},
144 KEY_ECDSA, 521, HASH_SHA512},
145 };
146 int i;
147
148 for (i = 0; i < countof(mappings); i++)
149 {
150 if (mappings[i].scheme == scheme)
151 {
152 size_t len = mappings[i].keylen;
153 if (mappings[i].type != type || (len && keylen != len))
154 {
155 return NULL;
156 }
157 if (hash)
158 {
159 *hash = mappings[i].hash;
160 }
161 return &mappings[i].mechanism;
162 }
163 }
164 return NULL;
165 }
166
167 /**
168 * See header.
169 */
170 CK_MECHANISM_PTR pkcs11_encryption_scheme_to_mech(encryption_scheme_t scheme)
171 {
172 static struct {
173 encryption_scheme_t scheme;
174 CK_MECHANISM mechanism;
175 } mappings[] = {
176 {ENCRYPT_RSA_PKCS1, {CKM_RSA_PKCS, NULL, 0}},
177 {ENCRYPT_RSA_OAEP_SHA1, {CKM_RSA_PKCS_OAEP, NULL, 0}},
178 };
179 int i;
180
181 for (i = 0; i < countof(mappings); i++)
182 {
183 if (mappings[i].scheme == scheme)
184 {
185 return &mappings[i].mechanism;
186 }
187 }
188 return NULL;
189 }
190
191 /**
192 * Reauthenticate to do a signature
193 */
194 static bool reauth(private_pkcs11_private_key_t *this,
195 CK_SESSION_HANDLE session)
196 {
197 enumerator_t *enumerator;
198 shared_key_t *shared;
199 chunk_t pin;
200 CK_RV rv;
201 bool found = FALSE, success = FALSE;
202
203 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
204 SHARED_PIN, this->keyid, NULL);
205 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
206 {
207 found = TRUE;
208 pin = shared->get_key(shared);
209 rv = this->lib->f->C_Login(session, CKU_CONTEXT_SPECIFIC,
210 pin.ptr, pin.len);
211 if (rv == CKR_OK)
212 {
213 success = TRUE;
214 break;
215 }
216 DBG1(DBG_CFG, "reauthentication login failed: %N", ck_rv_names, rv);
217 }
218 enumerator->destroy(enumerator);
219
220 if (!found)
221 {
222 DBG1(DBG_CFG, "private key requires reauthentication, but no PIN found");
223 return FALSE;
224 }
225 return success;
226 }
227
228 METHOD(private_key_t, sign, bool,
229 private_pkcs11_private_key_t *this, signature_scheme_t scheme,
230 chunk_t data, chunk_t *signature)
231 {
232 CK_MECHANISM_PTR mechanism;
233 CK_SESSION_HANDLE session;
234 CK_BYTE_PTR buf;
235 CK_ULONG len;
236 CK_RV rv;
237 hash_algorithm_t hash_alg;
238 chunk_t hash = chunk_empty;
239
240 mechanism = pkcs11_signature_scheme_to_mech(scheme, this->type,
241 get_keysize(this), &hash_alg);
242 if (!mechanism)
243 {
244 DBG1(DBG_LIB, "signature scheme %N not supported",
245 signature_scheme_names, scheme);
246 return FALSE;
247 }
248 rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
249 &session);
250 if (rv != CKR_OK)
251 {
252 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
253 return FALSE;
254 }
255 rv = this->lib->f->C_SignInit(session, mechanism, this->object);
256 if (this->reauth && !reauth(this, session))
257 {
258 this->lib->f->C_CloseSession(session);
259 return FALSE;
260 }
261 if (rv != CKR_OK)
262 {
263 this->lib->f->C_CloseSession(session);
264 DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
265 return FALSE;
266 }
267 if (hash_alg != HASH_UNKNOWN)
268 {
269 hasher_t *hasher;
270
271 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
272 if (!hasher || !hasher->allocate_hash(hasher, data, &hash))
273 {
274 DESTROY_IF(hasher);
275 this->lib->f->C_CloseSession(session);
276 return FALSE;
277 }
278 hasher->destroy(hasher);
279 data = hash;
280 }
281 len = (get_keysize(this) + 7) / 8;
282 if (this->type == KEY_ECDSA)
283 { /* signature is twice the length of the base point order */
284 len *= 2;
285 }
286 buf = malloc(len);
287 rv = this->lib->f->C_Sign(session, data.ptr, data.len, buf, &len);
288 this->lib->f->C_CloseSession(session);
289 chunk_free(&hash);
290 if (rv != CKR_OK)
291 {
292 DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
293 free(buf);
294 return FALSE;
295 }
296 *signature = chunk_create(buf, len);
297 return TRUE;
298 }
299
300 METHOD(private_key_t, decrypt, bool,
301 private_pkcs11_private_key_t *this, encryption_scheme_t scheme,
302 chunk_t crypt, chunk_t *plain)
303 {
304 CK_MECHANISM_PTR mechanism;
305 CK_SESSION_HANDLE session;
306 CK_BYTE_PTR buf;
307 CK_ULONG len;
308 CK_RV rv;
309
310 mechanism = pkcs11_encryption_scheme_to_mech(scheme);
311 if (!mechanism)
312 {
313 DBG1(DBG_LIB, "encryption scheme %N not supported",
314 encryption_scheme_names, scheme);
315 return FALSE;
316 }
317 rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
318 &session);
319 if (rv != CKR_OK)
320 {
321 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
322 return FALSE;
323 }
324 rv = this->lib->f->C_DecryptInit(session, mechanism, this->object);
325 if (this->reauth && !reauth(this, session))
326 {
327 this->lib->f->C_CloseSession(session);
328 return FALSE;
329 }
330 if (rv != CKR_OK)
331 {
332 this->lib->f->C_CloseSession(session);
333 DBG1(DBG_LIB, "C_DecryptInit() failed: %N", ck_rv_names, rv);
334 return FALSE;
335 }
336 len = (get_keysize(this) + 7) / 8;
337 buf = malloc(len);
338 rv = this->lib->f->C_Decrypt(session, crypt.ptr, crypt.len, buf, &len);
339 this->lib->f->C_CloseSession(session);
340 if (rv != CKR_OK)
341 {
342 DBG1(DBG_LIB, "C_Decrypt() failed: %N", ck_rv_names, rv);
343 free(buf);
344 return FALSE;
345 }
346 *plain = chunk_create(buf, len);
347 return TRUE;
348 }
349
350 METHOD(private_key_t, get_public_key, public_key_t*,
351 private_pkcs11_private_key_t *this)
352 {
353 return this->pubkey->get_ref(this->pubkey);
354 }
355
356 METHOD(private_key_t, get_fingerprint, bool,
357 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
358 chunk_t *fingerprint)
359 {
360 return this->pubkey->get_fingerprint(this->pubkey, type, fingerprint);
361 }
362
363 METHOD(private_key_t, get_encoding, bool,
364 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
365 chunk_t *encoding)
366 {
367 return FALSE;
368 }
369
370 METHOD(private_key_t, get_ref, private_key_t*,
371 private_pkcs11_private_key_t *this)
372 {
373 ref_get(&this->ref);
374 return &this->public.key;
375 }
376
377 METHOD(private_key_t, destroy, void,
378 private_pkcs11_private_key_t *this)
379 {
380 if (ref_put(&this->ref))
381 {
382 if (this->pubkey)
383 {
384 this->pubkey->destroy(this->pubkey);
385 }
386 this->keyid->destroy(this->keyid);
387 this->lib->f->C_CloseSession(this->session);
388 free(this);
389 }
390 }
391
392 /**
393 * Find the PKCS#11 library by its friendly name
394 */
395 static pkcs11_library_t* find_lib(char *module)
396 {
397 pkcs11_manager_t *manager;
398 enumerator_t *enumerator;
399 pkcs11_library_t *p11, *found = NULL;
400 CK_SLOT_ID slot;
401
402 manager = lib->get(lib, "pkcs11-manager");
403 if (!manager)
404 {
405 return NULL;
406 }
407 enumerator = manager->create_token_enumerator(manager);
408 while (enumerator->enumerate(enumerator, &p11, &slot))
409 {
410 if (streq(module, p11->get_name(p11)))
411 {
412 found = p11;
413 break;
414 }
415 }
416 enumerator->destroy(enumerator);
417 return found;
418 }
419
420 /**
421 * Find the PKCS#11 lib having a keyid, and optionally a slot
422 */
423 static pkcs11_library_t* find_lib_by_keyid(chunk_t keyid, int *slot)
424 {
425 pkcs11_manager_t *manager;
426 enumerator_t *enumerator;
427 pkcs11_library_t *p11, *found = NULL;
428 CK_SLOT_ID current;
429
430 manager = lib->get(lib, "pkcs11-manager");
431 if (!manager)
432 {
433 return NULL;
434 }
435 enumerator = manager->create_token_enumerator(manager);
436 while (enumerator->enumerate(enumerator, &p11, &current))
437 {
438 if (*slot == -1 || *slot == current)
439 {
440 /* we look for a public key, it is usually readable without login */
441 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
442 CK_ATTRIBUTE tmpl[] = {
443 {CKA_CLASS, &class, sizeof(class)},
444 {CKA_ID, keyid.ptr, keyid.len},
445 };
446 CK_OBJECT_HANDLE object;
447 CK_SESSION_HANDLE session;
448 CK_RV rv;
449 enumerator_t *keys;
450
451 rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL,
452 &session);
453 if (rv != CKR_OK)
454 {
455 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
456 ck_rv_names, rv);
457 continue;
458 }
459 keys = p11->create_object_enumerator(p11, session,
460 tmpl, countof(tmpl), NULL, 0);
461 if (keys->enumerate(keys, &object))
462 {
463 DBG1(DBG_CFG, "found key on PKCS#11 token '%s':%d",
464 p11->get_name(p11), current);
465 found = p11;
466 *slot = current;
467 }
468 keys->destroy(keys);
469 p11->f->C_CloseSession(session);
470 if (found)
471 {
472 break;
473 }
474 }
475 }
476 enumerator->destroy(enumerator);
477 return found;
478 }
479
480 /**
481 * Find the key on the token
482 */
483 static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
484 {
485 CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
486 CK_ATTRIBUTE tmpl[] = {
487 {CKA_CLASS, &class, sizeof(class)},
488 {CKA_ID, keyid.ptr, keyid.len},
489 };
490 CK_OBJECT_HANDLE object;
491 CK_KEY_TYPE type;
492 CK_BBOOL reauth = FALSE;
493 CK_ATTRIBUTE attr[] = {
494 {CKA_KEY_TYPE, &type, sizeof(type)},
495 {CKA_ALWAYS_AUTHENTICATE, &reauth, sizeof(reauth)},
496 };
497 enumerator_t *enumerator;
498 int count = countof(attr);
499 bool found = FALSE;
500
501 /* do not use CKA_ALWAYS_AUTHENTICATE if not supported */
502 if (!(this->lib->get_features(this->lib) & PKCS11_ALWAYS_AUTH_KEYS))
503 {
504 count--;
505 }
506 enumerator = this->lib->create_object_enumerator(this->lib,
507 this->session, tmpl, countof(tmpl), attr, count);
508 if (enumerator->enumerate(enumerator, &object))
509 {
510 this->type = KEY_RSA;
511 switch (type)
512 {
513 case CKK_ECDSA:
514 this->type = KEY_ECDSA;
515 /* fall-through */
516 case CKK_RSA:
517 this->reauth = reauth;
518 this->object = object;
519 found = TRUE;
520 break;
521 default:
522 DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type);
523 break;
524 }
525 }
526 enumerator->destroy(enumerator);
527 return found;
528 }
529
530 /**
531 * Find a PIN and try to log in
532 */
533 static bool login(private_pkcs11_private_key_t *this, int slot)
534 {
535 enumerator_t *enumerator;
536 shared_key_t *shared;
537 chunk_t pin;
538 CK_RV rv;
539 CK_SESSION_INFO info;
540 bool found = FALSE, success = FALSE;
541
542 rv = this->lib->f->C_GetSessionInfo(this->session, &info);
543 if (rv != CKR_OK)
544 {
545 DBG1(DBG_CFG, "C_GetSessionInfo failed: %N", ck_rv_names, rv);
546 return FALSE;
547 }
548 if (info.state != CKS_RO_PUBLIC_SESSION &&
549 info.state != CKS_RW_PUBLIC_SESSION)
550 { /* already logged in with another session, skip */
551 return TRUE;
552 }
553
554 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
555 SHARED_PIN, this->keyid, NULL);
556 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
557 {
558 found = TRUE;
559 pin = shared->get_key(shared);
560 rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
561 if (rv == CKR_OK)
562 {
563 success = TRUE;
564 break;
565 }
566 DBG1(DBG_CFG, "login to '%s':%d failed: %N",
567 this->lib->get_name(this->lib), slot, ck_rv_names, rv);
568 }
569 enumerator->destroy(enumerator);
570
571 if (!found)
572 {
573 DBG1(DBG_CFG, "no PIN found for PKCS#11 key %Y", this->keyid);
574 return FALSE;
575 }
576 return success;
577 }
578
579 /**
580 * See header.
581 */
582 pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
583 {
584 private_pkcs11_private_key_t *this;
585 char *module = NULL;
586 chunk_t keyid = chunk_empty;
587 int slot = -1;
588 CK_RV rv;
589
590 while (TRUE)
591 {
592 switch (va_arg(args, builder_part_t))
593 {
594 case BUILD_PKCS11_KEYID:
595 keyid = va_arg(args, chunk_t);
596 continue;
597 case BUILD_PKCS11_SLOT:
598 slot = va_arg(args, int);
599 continue;
600 case BUILD_PKCS11_MODULE:
601 module = va_arg(args, char*);
602 continue;
603 case BUILD_END:
604 break;
605 default:
606 return NULL;
607 }
608 break;
609 }
610 if (!keyid.len)
611 {
612 return NULL;
613 }
614
615 INIT(this,
616 .public = {
617 .key = {
618 .get_type = _get_type,
619 .sign = _sign,
620 .decrypt = _decrypt,
621 .get_keysize = _get_keysize,
622 .get_public_key = _get_public_key,
623 .equals = private_key_equals,
624 .belongs_to = private_key_belongs_to,
625 .get_fingerprint = _get_fingerprint,
626 .has_fingerprint = private_key_has_fingerprint,
627 .get_encoding = _get_encoding,
628 .get_ref = _get_ref,
629 .destroy = _destroy,
630 },
631 },
632 .ref = 1,
633 );
634
635 if (module && slot != -1)
636 {
637 this->lib = find_lib(module);
638 if (!this->lib)
639 {
640 DBG1(DBG_CFG, "PKCS#11 module '%s' not found", module);
641 free(this);
642 return NULL;
643 }
644 }
645 else
646 {
647 this->lib = find_lib_by_keyid(keyid, &slot);
648 if (!this->lib)
649 {
650 DBG1(DBG_CFG, "no PKCS#11 module found having a keyid %#B", &keyid);
651 free(this);
652 return NULL;
653 }
654 }
655
656 rv = this->lib->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
657 NULL, NULL, &this->session);
658 if (rv != CKR_OK)
659 {
660 DBG1(DBG_CFG, "opening private key session on '%s':%d failed: %N",
661 module, slot, ck_rv_names, rv);
662 free(this);
663 return NULL;
664 }
665
666 this->slot = slot;
667 this->keyid = identification_create_from_encoding(ID_KEY_ID, keyid);
668
669 if (!login(this, slot))
670 {
671 destroy(this);
672 return NULL;
673 }
674
675 if (!find_key(this, keyid))
676 {
677 destroy(this);
678 return NULL;
679 }
680
681 this->pubkey = pkcs11_public_key_connect(this->lib, slot, this->type,
682 keyid);
683 if (!this->pubkey)
684 {
685 destroy(this);
686 return NULL;
687 }
688
689 return &this->public;
690 }