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