fda6ae39254d392c686a6386a3fa7547bdd85e5e
[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 {
108 static struct {
109 signature_scheme_t scheme;
110 CK_MECHANISM mechanism;
111 } mappings[] = {
112 {SIGN_RSA_EMSA_PKCS1_NULL, {CKM_RSA_PKCS, NULL, 0}},
113 {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_SHA1_RSA_PKCS, NULL, 0}},
114 {SIGN_RSA_EMSA_PKCS1_SHA256, {CKM_SHA256_RSA_PKCS, NULL, 0}},
115 {SIGN_RSA_EMSA_PKCS1_SHA384, {CKM_SHA384_RSA_PKCS, NULL, 0}},
116 {SIGN_RSA_EMSA_PKCS1_SHA512, {CKM_SHA512_RSA_PKCS, NULL, 0}},
117 {SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0}},
118 };
119 int i;
120
121 for (i = 0; i < countof(mappings); i++)
122 {
123 if (mappings[i].scheme == scheme)
124 {
125 return &mappings[i].mechanism;
126 }
127 }
128 return NULL;
129 }
130
131 /**
132 * See header.
133 */
134 CK_MECHANISM_PTR pkcs11_encryption_scheme_to_mech(encryption_scheme_t scheme)
135 {
136 static struct {
137 encryption_scheme_t scheme;
138 CK_MECHANISM mechanism;
139 } mappings[] = {
140 {ENCRYPT_RSA_PKCS1, {CKM_RSA_PKCS, NULL, 0}},
141 {ENCRYPT_RSA_OAEP_SHA1, {CKM_RSA_PKCS_OAEP, NULL, 0}},
142 };
143 int i;
144
145 for (i = 0; i < countof(mappings); i++)
146 {
147 if (mappings[i].scheme == scheme)
148 {
149 return &mappings[i].mechanism;
150 }
151 }
152 return NULL;
153 }
154
155 /**
156 * Reauthenticate to do a signature
157 */
158 static bool reauth(private_pkcs11_private_key_t *this,
159 CK_SESSION_HANDLE session)
160 {
161 enumerator_t *enumerator;
162 shared_key_t *shared;
163 chunk_t pin;
164 CK_RV rv;
165 bool found = FALSE, success = FALSE;
166
167 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
168 SHARED_PIN, this->keyid, NULL);
169 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
170 {
171 found = TRUE;
172 pin = shared->get_key(shared);
173 rv = this->lib->f->C_Login(session, CKU_CONTEXT_SPECIFIC,
174 pin.ptr, pin.len);
175 if (rv == CKR_OK)
176 {
177 success = TRUE;
178 break;
179 }
180 DBG1(DBG_CFG, "reauthentication login failed: %N", ck_rv_names, rv);
181 }
182 enumerator->destroy(enumerator);
183
184 if (!found)
185 {
186 DBG1(DBG_CFG, "private key requires reauthentication, but no PIN found");
187 return FALSE;
188 }
189 return success;
190 }
191
192 METHOD(private_key_t, sign, bool,
193 private_pkcs11_private_key_t *this, signature_scheme_t scheme,
194 chunk_t data, chunk_t *signature)
195 {
196 CK_MECHANISM_PTR mechanism;
197 CK_SESSION_HANDLE session;
198 CK_BYTE_PTR buf;
199 CK_ULONG len;
200 CK_RV rv;
201
202 mechanism = pkcs11_signature_scheme_to_mech(scheme);
203 if (!mechanism)
204 {
205 DBG1(DBG_LIB, "signature scheme %N not supported",
206 signature_scheme_names, scheme);
207 return FALSE;
208 }
209 rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
210 &session);
211 if (rv != CKR_OK)
212 {
213 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
214 return FALSE;
215 }
216 rv = this->lib->f->C_SignInit(session, mechanism, this->object);
217 if (this->reauth && !reauth(this, session))
218 {
219 this->lib->f->C_CloseSession(session);
220 return FALSE;
221 }
222 if (rv != CKR_OK)
223 {
224 this->lib->f->C_CloseSession(session);
225 DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
226 return FALSE;
227 }
228 len = (get_keysize(this) + 7) / 8;
229 buf = malloc(len);
230 rv = this->lib->f->C_Sign(session, data.ptr, data.len, buf, &len);
231 this->lib->f->C_CloseSession(session);
232 if (rv != CKR_OK)
233 {
234 DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
235 free(buf);
236 return FALSE;
237 }
238 *signature = chunk_create(buf, len);
239 return TRUE;
240 }
241
242 METHOD(private_key_t, decrypt, bool,
243 private_pkcs11_private_key_t *this, encryption_scheme_t scheme,
244 chunk_t crypt, chunk_t *plain)
245 {
246 CK_MECHANISM_PTR mechanism;
247 CK_SESSION_HANDLE session;
248 CK_BYTE_PTR buf;
249 CK_ULONG len;
250 CK_RV rv;
251
252 mechanism = pkcs11_encryption_scheme_to_mech(scheme);
253 if (!mechanism)
254 {
255 DBG1(DBG_LIB, "encryption scheme %N not supported",
256 encryption_scheme_names, scheme);
257 return FALSE;
258 }
259 rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
260 &session);
261 if (rv != CKR_OK)
262 {
263 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
264 return FALSE;
265 }
266 rv = this->lib->f->C_DecryptInit(session, mechanism, this->object);
267 if (this->reauth && !reauth(this, session))
268 {
269 this->lib->f->C_CloseSession(session);
270 return FALSE;
271 }
272 if (rv != CKR_OK)
273 {
274 this->lib->f->C_CloseSession(session);
275 DBG1(DBG_LIB, "C_DecryptInit() failed: %N", ck_rv_names, rv);
276 return FALSE;
277 }
278 len = (get_keysize(this) + 7) / 8;
279 buf = malloc(len);
280 rv = this->lib->f->C_Decrypt(session, crypt.ptr, crypt.len, buf, &len);
281 this->lib->f->C_CloseSession(session);
282 if (rv != CKR_OK)
283 {
284 DBG1(DBG_LIB, "C_Decrypt() failed: %N", ck_rv_names, rv);
285 free(buf);
286 return FALSE;
287 }
288 *plain = chunk_create(buf, len);
289 return TRUE;
290 }
291
292 METHOD(private_key_t, get_public_key, public_key_t*,
293 private_pkcs11_private_key_t *this)
294 {
295 return this->pubkey->get_ref(this->pubkey);
296 }
297
298 METHOD(private_key_t, get_fingerprint, bool,
299 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
300 chunk_t *fingerprint)
301 {
302 return this->pubkey->get_fingerprint(this->pubkey, type, fingerprint);
303 }
304
305 METHOD(private_key_t, get_encoding, bool,
306 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
307 chunk_t *encoding)
308 {
309 return FALSE;
310 }
311
312 METHOD(private_key_t, get_ref, private_key_t*,
313 private_pkcs11_private_key_t *this)
314 {
315 ref_get(&this->ref);
316 return &this->public.key;
317 }
318
319 METHOD(private_key_t, destroy, void,
320 private_pkcs11_private_key_t *this)
321 {
322 if (ref_put(&this->ref))
323 {
324 if (this->pubkey)
325 {
326 this->pubkey->destroy(this->pubkey);
327 }
328 this->keyid->destroy(this->keyid);
329 this->lib->f->C_CloseSession(this->session);
330 free(this);
331 }
332 }
333
334 /**
335 * Find the PKCS#11 library by its friendly name
336 */
337 static pkcs11_library_t* find_lib(char *module)
338 {
339 pkcs11_manager_t *manager;
340 enumerator_t *enumerator;
341 pkcs11_library_t *p11, *found = NULL;
342 CK_SLOT_ID slot;
343
344 manager = lib->get(lib, "pkcs11-manager");
345 if (!manager)
346 {
347 return NULL;
348 }
349 enumerator = manager->create_token_enumerator(manager);
350 while (enumerator->enumerate(enumerator, &p11, &slot))
351 {
352 if (streq(module, p11->get_name(p11)))
353 {
354 found = p11;
355 break;
356 }
357 }
358 enumerator->destroy(enumerator);
359 return found;
360 }
361
362 /**
363 * Find the PKCS#11 lib having a keyid, and optionally a slot
364 */
365 static pkcs11_library_t* find_lib_by_keyid(chunk_t keyid, int *slot)
366 {
367 pkcs11_manager_t *manager;
368 enumerator_t *enumerator;
369 pkcs11_library_t *p11, *found = NULL;
370 CK_SLOT_ID current;
371
372 manager = lib->get(lib, "pkcs11-manager");
373 if (!manager)
374 {
375 return NULL;
376 }
377 enumerator = manager->create_token_enumerator(manager);
378 while (enumerator->enumerate(enumerator, &p11, &current))
379 {
380 if (*slot == -1 || *slot == current)
381 {
382 /* we look for a public key, it is usually readable without login */
383 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
384 CK_ATTRIBUTE tmpl[] = {
385 {CKA_CLASS, &class, sizeof(class)},
386 {CKA_ID, keyid.ptr, keyid.len},
387 };
388 CK_OBJECT_HANDLE object;
389 CK_SESSION_HANDLE session;
390 CK_RV rv;
391 enumerator_t *keys;
392
393 rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL,
394 &session);
395 if (rv != CKR_OK)
396 {
397 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
398 ck_rv_names, rv);
399 continue;
400 }
401 keys = p11->create_object_enumerator(p11, session,
402 tmpl, countof(tmpl), NULL, 0);
403 if (keys->enumerate(keys, &object))
404 {
405 DBG1(DBG_CFG, "found key on PKCS#11 token '%s':%d",
406 p11->get_name(p11), current);
407 found = p11;
408 *slot = current;
409 }
410 keys->destroy(keys);
411 p11->f->C_CloseSession(session);
412 if (found)
413 {
414 break;
415 }
416 }
417 }
418 enumerator->destroy(enumerator);
419 return found;
420 }
421
422 /**
423 * Find the key on the token
424 */
425 static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
426 {
427 CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
428 CK_ATTRIBUTE tmpl[] = {
429 {CKA_CLASS, &class, sizeof(class)},
430 {CKA_ID, keyid.ptr, keyid.len},
431 };
432 CK_OBJECT_HANDLE object;
433 CK_KEY_TYPE type;
434 CK_BBOOL reauth = FALSE;
435 CK_ATTRIBUTE attr[] = {
436 {CKA_KEY_TYPE, &type, sizeof(type)},
437 {CKA_ALWAYS_AUTHENTICATE, &reauth, sizeof(reauth)},
438 };
439 enumerator_t *enumerator;
440 int count = countof(attr);
441 bool found = FALSE;
442
443 /* do not use CKA_ALWAYS_AUTHENTICATE if not supported */
444 if (!(this->lib->get_features(this->lib) & PKCS11_ALWAYS_AUTH_KEYS))
445 {
446 count--;
447 }
448 enumerator = this->lib->create_object_enumerator(this->lib,
449 this->session, tmpl, countof(tmpl), attr, count);
450 if (enumerator->enumerate(enumerator, &object))
451 {
452 this->type = KEY_RSA;
453 switch (type)
454 {
455 case CKK_ECDSA:
456 this->type = KEY_ECDSA;
457 /* fall-through */
458 case CKK_RSA:
459 this->reauth = reauth;
460 this->object = object;
461 found = TRUE;
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 found;
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 this->pubkey = pkcs11_public_key_connect(this->lib, slot, this->type,
624 keyid);
625 if (!this->pubkey)
626 {
627 destroy(this);
628 return NULL;
629 }
630
631 return &this->public;
632 }