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