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