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