Use a seperate section for each nested struct member in INIT macro
[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 = pkcs11_manager_get();
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 = pkcs11_manager_get();
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;
405 CK_ATTRIBUTE attr[] = {
406 {CKA_KEY_TYPE, &type, sizeof(type)},
407 {CKA_ALWAYS_AUTHENTICATE, &reauth, sizeof(reauth)},
408 {CKA_MODULUS, NULL, 0},
409 {CKA_PUBLIC_EXPONENT, NULL, 0},
410 };
411 enumerator_t *enumerator;
412 chunk_t modulus, pubexp;
413
414 enumerator = this->lib->create_object_enumerator(this->lib,
415 this->session, tmpl, countof(tmpl), attr, countof(attr));
416 if (enumerator->enumerate(enumerator, &object))
417 {
418 switch (type)
419 {
420 case CKK_RSA:
421 if (attr[2].ulValueLen == -1 || attr[3].ulValueLen == -1)
422 {
423 DBG1(DBG_CFG, "reading modulus/exponent from PKCS#1 failed");
424 break;
425 }
426 modulus = chunk_create(attr[2].pValue, attr[2].ulValueLen);
427 pubexp = chunk_create(attr[3].pValue, attr[3].ulValueLen);
428 this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
429 KEY_RSA, BUILD_RSA_MODULUS, modulus,
430 BUILD_RSA_PUB_EXP, pubexp, BUILD_END);
431 if (!this->pubkey)
432 {
433 DBG1(DBG_CFG, "extracting public key from PKCS#11 RSA "
434 "private key failed");
435 }
436 this->reauth = reauth;
437 this->object = object;
438 break;
439 default:
440 DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type);
441 break;
442 }
443 }
444 enumerator->destroy(enumerator);
445 return this->pubkey != NULL;
446 }
447
448 /**
449 * Find a PIN and try to log in
450 */
451 static bool login(private_pkcs11_private_key_t *this, int slot)
452 {
453 enumerator_t *enumerator;
454 shared_key_t *shared;
455 chunk_t pin;
456 CK_RV rv;
457 CK_SESSION_INFO info;
458 bool found = FALSE, success = FALSE;
459
460 rv = this->lib->f->C_GetSessionInfo(this->session, &info);
461 if (rv != CKR_OK)
462 {
463 DBG1(DBG_CFG, "C_GetSessionInfo failed: %N", ck_rv_names, rv);
464 return FALSE;
465 }
466 if (info.state != CKS_RO_PUBLIC_SESSION &&
467 info.state != CKS_RW_PUBLIC_SESSION)
468 { /* already logged in with another session, skip */
469 return TRUE;
470 }
471
472 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
473 SHARED_PIN, this->keyid, NULL);
474 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
475 {
476 found = TRUE;
477 pin = shared->get_key(shared);
478 rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
479 if (rv == CKR_OK)
480 {
481 success = TRUE;
482 break;
483 }
484 DBG1(DBG_CFG, "login to '%s':%d failed: %N",
485 this->lib->get_name(this->lib), slot, ck_rv_names, rv);
486 }
487 enumerator->destroy(enumerator);
488
489 if (!found)
490 {
491 DBG1(DBG_CFG, "no PIN found for PKCS#11 key %Y", this->keyid);
492 return FALSE;
493 }
494 return success;
495 }
496
497 /**
498 * See header.
499 */
500 pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
501 {
502 private_pkcs11_private_key_t *this;
503 char *module = NULL;
504 chunk_t keyid = chunk_empty;
505 int slot = -1;
506 CK_RV rv;
507
508 while (TRUE)
509 {
510 switch (va_arg(args, builder_part_t))
511 {
512 case BUILD_PKCS11_KEYID:
513 keyid = va_arg(args, chunk_t);
514 continue;
515 case BUILD_PKCS11_SLOT:
516 slot = va_arg(args, int);
517 continue;
518 case BUILD_PKCS11_MODULE:
519 module = va_arg(args, char*);
520 continue;
521 case BUILD_END:
522 break;
523 default:
524 return NULL;
525 }
526 break;
527 }
528 if (!keyid.len)
529 {
530 return NULL;
531 }
532
533 INIT(this,
534 .public = {
535 .key = {
536 .get_type = _get_type,
537 .sign = _sign,
538 .decrypt = _decrypt,
539 .get_keysize = _get_keysize,
540 .get_public_key = _get_public_key,
541 .equals = private_key_equals,
542 .belongs_to = private_key_belongs_to,
543 .get_fingerprint = _get_fingerprint,
544 .has_fingerprint = private_key_has_fingerprint,
545 .get_encoding = _get_encoding,
546 .get_ref = _get_ref,
547 .destroy = _destroy,
548 },
549 },
550 .ref = 1,
551 );
552
553 if (module && slot != -1)
554 {
555 this->lib = find_lib(module);
556 if (!this->lib)
557 {
558 DBG1(DBG_CFG, "PKCS#11 module '%s' not found", module);
559 free(this);
560 return NULL;
561 }
562 }
563 else
564 {
565 this->lib = find_lib_by_keyid(keyid, &slot);
566 if (!this->lib)
567 {
568 DBG1(DBG_CFG, "no PKCS#11 module found having a keyid %#B", &keyid);
569 free(this);
570 return NULL;
571 }
572 }
573
574 rv = this->lib->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
575 NULL, NULL, &this->session);
576 if (rv != CKR_OK)
577 {
578 DBG1(DBG_CFG, "opening private key session on '%s':%d failed: %N",
579 module, slot, ck_rv_names, rv);
580 free(this);
581 return NULL;
582 }
583
584 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
585 this->keyid = identification_create_from_encoding(ID_KEY_ID, keyid);
586
587 if (!login(this, slot))
588 {
589 destroy(this);
590 return NULL;
591 }
592
593 if (!find_key(this, keyid))
594 {
595 destroy(this);
596 return NULL;
597 }
598
599 return &this->public;
600 }