Export scheme_to_mechanism conversion function
[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, size_t,
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_scheme_to_mechanism(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 * Reauthenticate to do a signature
119 */
120 static bool reauth(private_pkcs11_private_key_t *this)
121 {
122 enumerator_t *enumerator;
123 shared_key_t *shared;
124 chunk_t pin;
125 CK_RV rv;
126 bool found = FALSE, success = FALSE;
127
128 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
129 SHARED_PIN, this->keyid, NULL);
130 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
131 {
132 found = TRUE;
133 pin = shared->get_key(shared);
134 rv = this->lib->f->C_Login(this->session, CKU_CONTEXT_SPECIFIC,
135 pin.ptr, pin.len);
136 if (rv == CKR_OK)
137 {
138 success = TRUE;
139 break;
140 }
141 DBG1(DBG_CFG, "reauthentication login failed: %N", ck_rv_names, rv);
142 }
143 enumerator->destroy(enumerator);
144
145 if (!found)
146 {
147 DBG1(DBG_CFG, "private key requires reauthentication, but no PIN found");
148 return FALSE;
149 }
150 return success;
151 }
152
153 METHOD(private_key_t, sign, bool,
154 private_pkcs11_private_key_t *this, signature_scheme_t scheme,
155 chunk_t data, chunk_t *signature)
156 {
157 CK_MECHANISM_PTR mechanism;
158 CK_BYTE_PTR buf;
159 CK_ULONG len;
160 CK_RV rv;
161
162 mechanism = pkcs11_scheme_to_mechanism(scheme);
163 if (!mechanism)
164 {
165 DBG1(DBG_LIB, "signature scheme %N not supported",
166 signature_scheme_names, scheme);
167 return FALSE;
168 }
169 this->mutex->lock(this->mutex);
170 rv = this->lib->f->C_SignInit(this->session, mechanism, this->object);
171 if (this->reauth && !reauth(this))
172 {
173 return FALSE;
174 }
175 if (rv != CKR_OK)
176 {
177 this->mutex->unlock(this->mutex);
178 DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
179 return FALSE;
180 }
181 len = get_keysize(this);
182 buf = malloc(len);
183 rv = this->lib->f->C_Sign(this->session, data.ptr, data.len, buf, &len);
184 this->mutex->unlock(this->mutex);
185 if (rv != CKR_OK)
186 {
187 DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
188 free(buf);
189 return FALSE;
190 }
191 *signature = chunk_create(buf, len);
192 return TRUE;
193 }
194
195 METHOD(private_key_t, decrypt, bool,
196 private_pkcs11_private_key_t *this, chunk_t crypto, chunk_t *plain)
197 {
198 return FALSE;
199 }
200
201 METHOD(private_key_t, get_public_key, public_key_t*,
202 private_pkcs11_private_key_t *this)
203 {
204 return this->pubkey->get_ref(this->pubkey);
205 }
206
207 METHOD(private_key_t, get_fingerprint, bool,
208 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
209 chunk_t *fingerprint)
210 {
211 return this->pubkey->get_fingerprint(this->pubkey, type, fingerprint);
212 }
213
214 METHOD(private_key_t, get_encoding, bool,
215 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
216 chunk_t *encoding)
217 {
218 return FALSE;
219 }
220
221 METHOD(private_key_t, get_ref, private_key_t*,
222 private_pkcs11_private_key_t *this)
223 {
224 ref_get(&this->ref);
225 return &this->public.key;
226 }
227
228 METHOD(private_key_t, destroy, void,
229 private_pkcs11_private_key_t *this)
230 {
231 if (ref_put(&this->ref))
232 {
233 if (this->pubkey)
234 {
235 this->pubkey->destroy(this->pubkey);
236 }
237 this->mutex->destroy(this->mutex);
238 this->keyid->destroy(this->keyid);
239 this->lib->f->C_CloseSession(this->session);
240 free(this);
241 }
242 }
243
244 /**
245 * Find the PKCS#11 library by its friendly name
246 */
247 static pkcs11_library_t* find_lib(char *module)
248 {
249 pkcs11_manager_t *manager;
250 enumerator_t *enumerator;
251 pkcs11_library_t *p11, *found = NULL;
252 CK_SLOT_ID slot;
253
254 manager = pkcs11_manager_get();
255 if (!manager)
256 {
257 return NULL;
258 }
259 enumerator = manager->create_token_enumerator(manager);
260 while (enumerator->enumerate(enumerator, &p11, &slot))
261 {
262 if (streq(module, p11->get_name(p11)))
263 {
264 found = p11;
265 break;
266 }
267 }
268 enumerator->destroy(enumerator);
269 return found;
270 }
271
272 /**
273 * Find the PKCS#11 lib having a keyid, and optionally a slot
274 */
275 static pkcs11_library_t* find_lib_by_keyid(chunk_t keyid, int *slot)
276 {
277 pkcs11_manager_t *manager;
278 enumerator_t *enumerator;
279 pkcs11_library_t *p11, *found = NULL;
280 CK_SLOT_ID current;
281
282 manager = pkcs11_manager_get();
283 if (!manager)
284 {
285 return NULL;
286 }
287 enumerator = manager->create_token_enumerator(manager);
288 while (enumerator->enumerate(enumerator, &p11, &current))
289 {
290 if (*slot == -1 || *slot == current)
291 {
292 /* we look for a public key, it is usually readable without login */
293 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
294 CK_ATTRIBUTE tmpl[] = {
295 {CKA_CLASS, &class, sizeof(class)},
296 {CKA_ID, keyid.ptr, keyid.len},
297 };
298 CK_OBJECT_HANDLE object;
299 CK_SESSION_HANDLE session;
300 CK_RV rv;
301 enumerator_t *keys;
302
303 rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL,
304 &session);
305 if (rv != CKR_OK)
306 {
307 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
308 ck_rv_names, rv);
309 continue;
310 }
311 keys = p11->create_object_enumerator(p11, session,
312 tmpl, countof(tmpl), NULL, 0);
313 if (keys->enumerate(keys, &object))
314 {
315 DBG1(DBG_CFG, "found key on PKCS#11 token '%s':%d",
316 p11->get_name(p11), current);
317 found = p11;
318 *slot = current;
319 }
320 keys->destroy(keys);
321 p11->f->C_CloseSession(session);
322 if (found)
323 {
324 break;
325 }
326 }
327 }
328 enumerator->destroy(enumerator);
329 return found;
330 }
331
332 /**
333 * Find the key on the token
334 */
335 static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
336 {
337 CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
338 CK_ATTRIBUTE tmpl[] = {
339 {CKA_CLASS, &class, sizeof(class)},
340 {CKA_ID, keyid.ptr, keyid.len},
341 };
342 CK_OBJECT_HANDLE object;
343 CK_KEY_TYPE type;
344 CK_BBOOL reauth;
345 CK_ATTRIBUTE attr[] = {
346 {CKA_KEY_TYPE, &type, sizeof(type)},
347 {CKA_ALWAYS_AUTHENTICATE, &reauth, sizeof(reauth)},
348 {CKA_MODULUS, NULL, 0},
349 {CKA_PUBLIC_EXPONENT, NULL, 0},
350 };
351 enumerator_t *enumerator;
352 chunk_t modulus, pubexp;
353
354 enumerator = this->lib->create_object_enumerator(this->lib,
355 this->session, tmpl, countof(tmpl), attr, countof(attr));
356 if (enumerator->enumerate(enumerator, &object))
357 {
358 switch (type)
359 {
360 case CKK_RSA:
361 if (attr[2].ulValueLen == -1 || attr[3].ulValueLen == -1)
362 {
363 DBG1(DBG_CFG, "reading modulus/exponent from PKCS#1 failed");
364 break;
365 }
366 modulus = chunk_create(attr[2].pValue, attr[2].ulValueLen);
367 pubexp = chunk_create(attr[3].pValue, attr[3].ulValueLen);
368 this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
369 KEY_RSA, BUILD_RSA_MODULUS, modulus,
370 BUILD_RSA_PUB_EXP, pubexp, BUILD_END);
371 if (!this->pubkey)
372 {
373 DBG1(DBG_CFG, "extracting public key from PKCS#11 RSA "
374 "private key failed");
375 }
376 this->reauth = reauth;
377 this->object = object;
378 break;
379 default:
380 DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type);
381 break;
382 }
383 }
384 enumerator->destroy(enumerator);
385 return this->pubkey != NULL;
386 }
387
388 /**
389 * Find a PIN and try to log in
390 */
391 static bool login(private_pkcs11_private_key_t *this, int slot)
392 {
393 enumerator_t *enumerator;
394 shared_key_t *shared;
395 chunk_t pin;
396 CK_RV rv;
397 CK_SESSION_INFO info;
398 bool found = FALSE, success = FALSE;
399
400 rv = this->lib->f->C_GetSessionInfo(this->session, &info);
401 if (rv != CKR_OK)
402 {
403 DBG1(DBG_CFG, "C_GetSessionInfo failed: %N", ck_rv_names, rv);
404 return FALSE;
405 }
406 if (info.state != CKS_RO_PUBLIC_SESSION &&
407 info.state != CKS_RW_PUBLIC_SESSION)
408 { /* already logged in with another session, skip */
409 return TRUE;
410 }
411
412 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
413 SHARED_PIN, this->keyid, NULL);
414 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
415 {
416 found = TRUE;
417 pin = shared->get_key(shared);
418 rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
419 if (rv == CKR_OK)
420 {
421 success = TRUE;
422 break;
423 }
424 DBG1(DBG_CFG, "login to '%s':%d failed: %N",
425 this->lib->get_name(this->lib), slot, ck_rv_names, rv);
426 }
427 enumerator->destroy(enumerator);
428
429 if (!found)
430 {
431 DBG1(DBG_CFG, "no PIN found for PKCS#11 key %Y", this->keyid);
432 return FALSE;
433 }
434 return success;
435 }
436
437 /**
438 * See header.
439 */
440 pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
441 {
442 private_pkcs11_private_key_t *this;
443 char *module = NULL;
444 chunk_t keyid = chunk_empty;
445 int slot = -1;
446 CK_RV rv;
447
448 while (TRUE)
449 {
450 switch (va_arg(args, builder_part_t))
451 {
452 case BUILD_PKCS11_KEYID:
453 keyid = va_arg(args, chunk_t);
454 continue;
455 case BUILD_PKCS11_SLOT:
456 slot = va_arg(args, int);
457 continue;
458 case BUILD_PKCS11_MODULE:
459 module = va_arg(args, char*);
460 continue;
461 case BUILD_END:
462 break;
463 default:
464 return NULL;
465 }
466 break;
467 }
468 if (!keyid.len)
469 {
470 return NULL;
471 }
472
473 INIT(this,
474 .public.key = {
475 .get_type = _get_type,
476 .sign = _sign,
477 .decrypt = _decrypt,
478 .get_keysize = _get_keysize,
479 .get_public_key = _get_public_key,
480 .equals = private_key_equals,
481 .belongs_to = private_key_belongs_to,
482 .get_fingerprint = _get_fingerprint,
483 .has_fingerprint = private_key_has_fingerprint,
484 .get_encoding = _get_encoding,
485 .get_ref = _get_ref,
486 .destroy = _destroy,
487 },
488 .ref = 1,
489 );
490
491 if (module && slot != -1)
492 {
493 this->lib = find_lib(module);
494 if (!this->lib)
495 {
496 DBG1(DBG_CFG, "PKCS#11 module '%s' not found", module);
497 free(this);
498 return NULL;
499 }
500 }
501 else
502 {
503 this->lib = find_lib_by_keyid(keyid, &slot);
504 if (!this->lib)
505 {
506 DBG1(DBG_CFG, "no PKCS#11 module found having a keyid %#B", &keyid);
507 free(this);
508 return NULL;
509 }
510 }
511
512 rv = this->lib->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
513 NULL, NULL, &this->session);
514 if (rv != CKR_OK)
515 {
516 DBG1(DBG_CFG, "opening private key session on '%s':%d failed: %N",
517 module, slot, ck_rv_names, rv);
518 free(this);
519 return NULL;
520 }
521
522 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
523 this->keyid = identification_create_from_encoding(ID_KEY_ID, keyid);
524
525 if (!login(this, slot))
526 {
527 destroy(this);
528 return NULL;
529 }
530
531 if (!find_key(this, keyid))
532 {
533 destroy(this);
534 return NULL;
535 }
536
537 return &this->public;
538 }