]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c
pkcs11: Optionally hash data for PKCS#1 v1.5 RSA signatures in software
[thirdparty/strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_private_key.c
CommitLineData
5f1e4438 1/*
9a704963 2 * Copyright (C) 2011-2016 Tobias Brunner
1b671669 3 * HSR Hochschule fuer Technik Rapperswil
b0319fe8 4 *
5f1e4438
MW
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 */
9a704963
RG
18/*
19 * Copyright (C) 2016 EDF S.A.
20 *
21 * Permission is hereby granted, free of charge, to any person obtaining a copy
22 * of this software and associated documentation files (the "Software"), to deal
23 * in the Software without restriction, including without limitation the rights
24 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 * copies of the Software, and to permit persons to whom the Software is
26 * furnished to do so, subject to the following conditions:
27 *
28 * The above copyright notice and this permission notice shall be included in
29 * all copies or substantial portions of the Software.
30 *
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
37 * THE SOFTWARE.
38 */
5f1e4438
MW
39
40#include "pkcs11_private_key.h"
41
42#include "pkcs11_library.h"
43#include "pkcs11_manager.h"
44fdc62f 44#include "pkcs11_public_key.h"
5f1e4438 45
f05b4272 46#include <utils/debug.h>
b258ed01 47#include <asn1/asn1.h>
5f1e4438
MW
48
49typedef struct private_pkcs11_private_key_t private_pkcs11_private_key_t;
50
51/**
52 * Private data of an pkcs11_private_key_t object.
53 */
54struct private_pkcs11_private_key_t {
55
56 /**
57 * Public pkcs11_private_key_t interface.
58 */
59 pkcs11_private_key_t public;
60
61 /**
62 * PKCS#11 module
63 */
64 pkcs11_library_t *lib;
65
66 /**
b0319fe8 67 * Slot the token is in
5f1e4438 68 */
b0319fe8 69 CK_SLOT_ID slot;
5f1e4438
MW
70
71 /**
b0319fe8 72 * Token session
5f1e4438 73 */
b0319fe8 74 CK_SESSION_HANDLE session;
5f1e4438
MW
75
76 /**
77 * Key object on the token
78 */
79 CK_OBJECT_HANDLE object;
80
af007ed6
MW
81 /**
82 * Key requires reauthentication for each signature/decryption
83 */
84 CK_BBOOL reauth;
85
86 /**
87 * Keyid of the key we use
88 */
89 identification_t *keyid;
90
5f1e4438
MW
91 /**
92 * Associated public key
93 */
94 public_key_t *pubkey;
95
96 /**
97 * References to this key
98 */
99 refcount_t ref;
5d2fccf4
TB
100
101 /**
102 * Type of this private key
103 */
104 key_type_t type;
5f1e4438
MW
105};
106
30a3ede8 107
5f1e4438
MW
108METHOD(private_key_t, get_type, key_type_t,
109 private_pkcs11_private_key_t *this)
110{
5d2fccf4 111 return this->type;
5f1e4438
MW
112}
113
a944d209 114METHOD(private_key_t, get_keysize, int,
5f1e4438
MW
115 private_pkcs11_private_key_t *this)
116{
117 return this->pubkey->get_keysize(this->pubkey);
118}
119
120/**
b6fcdc71
TB
121 * Check if a token supports the given mechanism.
122 */
123static bool is_mechanism_supported(pkcs11_library_t *p11, CK_SLOT_ID slot,
124 const CK_MECHANISM_PTR mech)
125{
126 enumerator_t *mechs;
127 CK_MECHANISM_TYPE type;
128
129 mechs = p11->create_mechanism_enumerator(p11, slot);
130 while (mechs->enumerate(mechs, &type, NULL))
131 {
132 if (type == mech->mechanism)
133 {
134 mechs->destroy(mechs);
135 return TRUE;
136 }
137 }
138 mechs->destroy(mechs);
139 return FALSE;
140}
141
142/*
143 * Described in header
5f1e4438 144 */
b6fcdc71
TB
145CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(pkcs11_library_t *p11,
146 CK_SLOT_ID slot,
147 signature_scheme_t scheme,
5b85b94e 148 key_type_t type, size_t keylen,
fd48b220 149 hash_algorithm_t *hash)
5f1e4438
MW
150{
151 static struct {
152 signature_scheme_t scheme;
153 CK_MECHANISM mechanism;
5b85b94e
TB
154 key_type_t type;
155 size_t keylen;
fd48b220 156 hash_algorithm_t hash;
5f1e4438 157 } mappings[] = {
fd48b220 158 {SIGN_RSA_EMSA_PKCS1_NULL, {CKM_RSA_PKCS, NULL, 0},
5b85b94e 159 KEY_RSA, 0, HASH_UNKNOWN},
40f2589a 160 {SIGN_RSA_EMSA_PKCS1_SHA2_256, {CKM_SHA256_RSA_PKCS, NULL, 0},
5b85b94e 161 KEY_RSA, 0, HASH_UNKNOWN},
b6fcdc71
TB
162 {SIGN_RSA_EMSA_PKCS1_SHA2_256, {CKM_RSA_PKCS, NULL, 0},
163 KEY_RSA, 0, HASH_SHA256},
40f2589a 164 {SIGN_RSA_EMSA_PKCS1_SHA2_384, {CKM_SHA384_RSA_PKCS, NULL, 0},
5b85b94e 165 KEY_RSA, 0, HASH_UNKNOWN},
b6fcdc71
TB
166 {SIGN_RSA_EMSA_PKCS1_SHA2_384, {CKM_RSA_PKCS, NULL, 0},
167 KEY_RSA, 0, HASH_SHA384},
40f2589a 168 {SIGN_RSA_EMSA_PKCS1_SHA2_512, {CKM_SHA512_RSA_PKCS, NULL, 0},
5b85b94e 169 KEY_RSA, 0, HASH_UNKNOWN},
b6fcdc71
TB
170 {SIGN_RSA_EMSA_PKCS1_SHA2_512, {CKM_RSA_PKCS, NULL, 0},
171 KEY_RSA, 0, HASH_SHA512},
40f2589a 172 {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_SHA1_RSA_PKCS, NULL, 0},
5b85b94e 173 KEY_RSA, 0, HASH_UNKNOWN},
b6fcdc71
TB
174 {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_RSA_PKCS, NULL, 0},
175 KEY_RSA, 0, HASH_SHA1},
fd48b220 176 {SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0},
5b85b94e 177 KEY_RSA, 0, HASH_UNKNOWN},
fd48b220 178 {SIGN_ECDSA_WITH_NULL, {CKM_ECDSA, NULL, 0},
5b85b94e 179 KEY_ECDSA, 0, HASH_UNKNOWN},
fd48b220 180 {SIGN_ECDSA_WITH_SHA1_DER, {CKM_ECDSA_SHA1, NULL, 0},
5b85b94e 181 KEY_ECDSA, 0, HASH_UNKNOWN},
fd48b220 182 {SIGN_ECDSA_WITH_SHA256_DER, {CKM_ECDSA, NULL, 0},
5b85b94e 183 KEY_ECDSA, 0, HASH_SHA256},
fd48b220 184 {SIGN_ECDSA_WITH_SHA384_DER, {CKM_ECDSA, NULL, 0},
5b85b94e 185 KEY_ECDSA, 0, HASH_SHA384},
fd48b220 186 {SIGN_ECDSA_WITH_SHA512_DER, {CKM_ECDSA, NULL, 0},
5b85b94e 187 KEY_ECDSA, 0, HASH_SHA512},
fd48b220 188 {SIGN_ECDSA_256, {CKM_ECDSA, NULL, 0},
5b85b94e 189 KEY_ECDSA, 256, HASH_SHA256},
fd48b220 190 {SIGN_ECDSA_384, {CKM_ECDSA, NULL, 0},
5b85b94e 191 KEY_ECDSA, 384, HASH_SHA384},
fd48b220 192 {SIGN_ECDSA_521, {CKM_ECDSA, NULL, 0},
5b85b94e 193 KEY_ECDSA, 521, HASH_SHA512},
5f1e4438
MW
194 };
195 int i;
196
197 for (i = 0; i < countof(mappings); i++)
198 {
199 if (mappings[i].scheme == scheme)
200 {
5b85b94e 201 size_t len = mappings[i].keylen;
b6fcdc71
TB
202
203 if (mappings[i].type != type || (len && keylen != len) ||
204 !is_mechanism_supported(p11, slot, &mappings[i].mechanism))
5b85b94e 205 {
b6fcdc71 206 continue;
5b85b94e 207 }
fd48b220
TB
208 if (hash)
209 {
210 *hash = mappings[i].hash;
211 }
5f1e4438
MW
212 return &mappings[i].mechanism;
213 }
214 }
215 return NULL;
216}
217
01e4f5f3
MW
218/**
219 * See header.
220 */
221CK_MECHANISM_PTR pkcs11_encryption_scheme_to_mech(encryption_scheme_t scheme)
222{
223 static struct {
224 encryption_scheme_t scheme;
225 CK_MECHANISM mechanism;
226 } mappings[] = {
227 {ENCRYPT_RSA_PKCS1, {CKM_RSA_PKCS, NULL, 0}},
228 {ENCRYPT_RSA_OAEP_SHA1, {CKM_RSA_PKCS_OAEP, NULL, 0}},
229 };
230 int i;
231
232 for (i = 0; i < countof(mappings); i++)
233 {
234 if (mappings[i].scheme == scheme)
235 {
236 return &mappings[i].mechanism;
237 }
238 }
239 return NULL;
240}
241
af007ed6
MW
242/**
243 * Reauthenticate to do a signature
244 */
b0319fe8
TB
245static bool reauth(private_pkcs11_private_key_t *this,
246 CK_SESSION_HANDLE session)
af007ed6
MW
247{
248 enumerator_t *enumerator;
249 shared_key_t *shared;
250 chunk_t pin;
251 CK_RV rv;
252 bool found = FALSE, success = FALSE;
253
254 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
255 SHARED_PIN, this->keyid, NULL);
256 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
257 {
258 found = TRUE;
259 pin = shared->get_key(shared);
b0319fe8 260 rv = this->lib->f->C_Login(session, CKU_CONTEXT_SPECIFIC,
af007ed6
MW
261 pin.ptr, pin.len);
262 if (rv == CKR_OK)
263 {
264 success = TRUE;
265 break;
266 }
267 DBG1(DBG_CFG, "reauthentication login failed: %N", ck_rv_names, rv);
268 }
269 enumerator->destroy(enumerator);
270
271 if (!found)
272 {
273 DBG1(DBG_CFG, "private key requires reauthentication, but no PIN found");
274 return FALSE;
275 }
276 return success;
277}
278
5f1e4438 279METHOD(private_key_t, sign, bool,
de280c2e 280 private_pkcs11_private_key_t *this, signature_scheme_t scheme, void *params,
5f1e4438
MW
281 chunk_t data, chunk_t *signature)
282{
283 CK_MECHANISM_PTR mechanism;
b0319fe8 284 CK_SESSION_HANDLE session;
5f1e4438
MW
285 CK_BYTE_PTR buf;
286 CK_ULONG len;
287 CK_RV rv;
fd48b220
TB
288 hash_algorithm_t hash_alg;
289 chunk_t hash = chunk_empty;
5f1e4438 290
b6fcdc71
TB
291 mechanism = pkcs11_signature_scheme_to_mech(this->lib, this->slot, scheme,
292 this->type, get_keysize(this),
293 &hash_alg);
5f1e4438
MW
294 if (!mechanism)
295 {
296 DBG1(DBG_LIB, "signature scheme %N not supported",
297 signature_scheme_names, scheme);
298 return FALSE;
299 }
b0319fe8
TB
300 rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
301 &session);
302 if (rv != CKR_OK)
303 {
304 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
305 return FALSE;
306 }
307 rv = this->lib->f->C_SignInit(session, mechanism, this->object);
308 if (this->reauth && !reauth(this, session))
af007ed6 309 {
b0319fe8 310 this->lib->f->C_CloseSession(session);
af007ed6
MW
311 return FALSE;
312 }
5f1e4438
MW
313 if (rv != CKR_OK)
314 {
b0319fe8 315 this->lib->f->C_CloseSession(session);
5f1e4438
MW
316 DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
317 return FALSE;
318 }
fd48b220
TB
319 if (hash_alg != HASH_UNKNOWN)
320 {
87dd205b
MW
321 hasher_t *hasher;
322
323 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
324 if (!hasher || !hasher->allocate_hash(hasher, data, &hash))
fd48b220 325 {
87dd205b 326 DESTROY_IF(hasher);
fd48b220
TB
327 this->lib->f->C_CloseSession(session);
328 return FALSE;
329 }
fd48b220 330 hasher->destroy(hasher);
b6fcdc71
TB
331 switch (scheme)
332 {
333 case SIGN_RSA_EMSA_PKCS1_SHA1:
334 case SIGN_RSA_EMSA_PKCS1_SHA2_256:
335 case SIGN_RSA_EMSA_PKCS1_SHA2_384:
336 case SIGN_RSA_EMSA_PKCS1_SHA2_512:
337 /* encode PKCS#1 digestInfo if the token does not support it */
338 hash = asn1_wrap(ASN1_SEQUENCE, "mm",
339 asn1_algorithmIdentifier(
340 hasher_algorithm_to_oid(hash_alg)),
341 asn1_wrap(ASN1_OCTET_STRING, "m", hash));
342 break;
343 default:
344 break;
345 }
fd48b220
TB
346 data = hash;
347 }
a944d209 348 len = (get_keysize(this) + 7) / 8;
fd48b220
TB
349 if (this->type == KEY_ECDSA)
350 { /* signature is twice the length of the base point order */
351 len *= 2;
352 }
af007ed6 353 buf = malloc(len);
b0319fe8
TB
354 rv = this->lib->f->C_Sign(session, data.ptr, data.len, buf, &len);
355 this->lib->f->C_CloseSession(session);
fd48b220 356 chunk_free(&hash);
5f1e4438
MW
357 if (rv != CKR_OK)
358 {
359 DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
360 free(buf);
361 return FALSE;
362 }
b258ed01
TB
363 switch (scheme)
364 {
365 case SIGN_ECDSA_WITH_SHA1_DER:
366 case SIGN_ECDSA_WITH_SHA256_DER:
367 case SIGN_ECDSA_WITH_SHA384_DER:
368 case SIGN_ECDSA_WITH_SHA512_DER:
7316a13b
TB
369 {
370 chunk_t r, s;
371
372 /* return an ASN.1 encoded sequence of integers r and s, removing
373 * any zero-padding */
b258ed01 374 len /= 2;
7316a13b
TB
375 r = chunk_skip_zero(chunk_create(buf, len));
376 s = chunk_skip_zero(chunk_create(buf+len, len));
b258ed01 377 *signature = asn1_wrap(ASN1_SEQUENCE, "mm",
7316a13b 378 asn1_integer("c", r), asn1_integer("c", s));
b258ed01
TB
379 free(buf);
380 break;
7316a13b 381 }
b258ed01
TB
382 default:
383 *signature = chunk_create(buf, len);
384 break;
385 }
5f1e4438
MW
386 return TRUE;
387}
388
389METHOD(private_key_t, decrypt, bool,
33ddaaab 390 private_pkcs11_private_key_t *this, encryption_scheme_t scheme,
01e4f5f3 391 chunk_t crypt, chunk_t *plain)
5f1e4438 392{
01e4f5f3 393 CK_MECHANISM_PTR mechanism;
b0319fe8 394 CK_SESSION_HANDLE session;
01e4f5f3
MW
395 CK_BYTE_PTR buf;
396 CK_ULONG len;
397 CK_RV rv;
398
399 mechanism = pkcs11_encryption_scheme_to_mech(scheme);
400 if (!mechanism)
401 {
402 DBG1(DBG_LIB, "encryption scheme %N not supported",
403 encryption_scheme_names, scheme);
404 return FALSE;
405 }
b0319fe8
TB
406 rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
407 &session);
408 if (rv != CKR_OK)
409 {
410 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
411 return FALSE;
412 }
413 rv = this->lib->f->C_DecryptInit(session, mechanism, this->object);
414 if (this->reauth && !reauth(this, session))
01e4f5f3 415 {
b0319fe8 416 this->lib->f->C_CloseSession(session);
01e4f5f3
MW
417 return FALSE;
418 }
419 if (rv != CKR_OK)
420 {
b0319fe8 421 this->lib->f->C_CloseSession(session);
01e4f5f3
MW
422 DBG1(DBG_LIB, "C_DecryptInit() failed: %N", ck_rv_names, rv);
423 return FALSE;
424 }
425 len = (get_keysize(this) + 7) / 8;
426 buf = malloc(len);
b0319fe8
TB
427 rv = this->lib->f->C_Decrypt(session, crypt.ptr, crypt.len, buf, &len);
428 this->lib->f->C_CloseSession(session);
01e4f5f3
MW
429 if (rv != CKR_OK)
430 {
431 DBG1(DBG_LIB, "C_Decrypt() failed: %N", ck_rv_names, rv);
432 free(buf);
433 return FALSE;
434 }
435 *plain = chunk_create(buf, len);
436 return TRUE;
5f1e4438
MW
437}
438
439METHOD(private_key_t, get_public_key, public_key_t*,
440 private_pkcs11_private_key_t *this)
441{
442 return this->pubkey->get_ref(this->pubkey);
443}
444
445METHOD(private_key_t, get_fingerprint, bool,
446 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
447 chunk_t *fingerprint)
448{
449 return this->pubkey->get_fingerprint(this->pubkey, type, fingerprint);
450}
451
452METHOD(private_key_t, get_encoding, bool,
453 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
454 chunk_t *encoding)
455{
456 return FALSE;
457}
458
459METHOD(private_key_t, get_ref, private_key_t*,
460 private_pkcs11_private_key_t *this)
461{
462 ref_get(&this->ref);
463 return &this->public.key;
464}
465
466METHOD(private_key_t, destroy, void,
467 private_pkcs11_private_key_t *this)
468{
469 if (ref_put(&this->ref))
470 {
471 if (this->pubkey)
472 {
473 this->pubkey->destroy(this->pubkey);
474 }
af007ed6 475 this->keyid->destroy(this->keyid);
5f1e4438
MW
476 this->lib->f->C_CloseSession(this->session);
477 free(this);
478 }
479}
480
481/**
482 * Find the PKCS#11 library by its friendly name
483 */
484static pkcs11_library_t* find_lib(char *module)
485{
486 pkcs11_manager_t *manager;
487 enumerator_t *enumerator;
488 pkcs11_library_t *p11, *found = NULL;
489 CK_SLOT_ID slot;
490
07190323 491 manager = lib->get(lib, "pkcs11-manager");
5f1e4438
MW
492 if (!manager)
493 {
494 return NULL;
495 }
496 enumerator = manager->create_token_enumerator(manager);
497 while (enumerator->enumerate(enumerator, &p11, &slot))
498 {
499 if (streq(module, p11->get_name(p11)))
500 {
501 found = p11;
502 break;
503 }
504 }
505 enumerator->destroy(enumerator);
506 return found;
507}
508
7afc00d0
MW
509/**
510 * Find the PKCS#11 lib having a keyid, and optionally a slot
511 */
712e8130
MW
512static pkcs11_library_t* find_lib_by_keyid(chunk_t keyid, int *slot,
513 CK_OBJECT_CLASS class)
7afc00d0
MW
514{
515 pkcs11_manager_t *manager;
516 enumerator_t *enumerator;
517 pkcs11_library_t *p11, *found = NULL;
518 CK_SLOT_ID current;
519
07190323 520 manager = lib->get(lib, "pkcs11-manager");
7afc00d0
MW
521 if (!manager)
522 {
523 return NULL;
524 }
525 enumerator = manager->create_token_enumerator(manager);
526 while (enumerator->enumerate(enumerator, &p11, &current))
527 {
528 if (*slot == -1 || *slot == current)
529 {
712e8130 530 /* look for a pubkey/cert, it is usually readable without login */
7afc00d0
MW
531 CK_ATTRIBUTE tmpl[] = {
532 {CKA_CLASS, &class, sizeof(class)},
533 {CKA_ID, keyid.ptr, keyid.len},
534 };
535 CK_OBJECT_HANDLE object;
536 CK_SESSION_HANDLE session;
537 CK_RV rv;
538 enumerator_t *keys;
539
540 rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL,
541 &session);
542 if (rv != CKR_OK)
543 {
544 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
545 ck_rv_names, rv);
546 continue;
547 }
548 keys = p11->create_object_enumerator(p11, session,
549 tmpl, countof(tmpl), NULL, 0);
550 if (keys->enumerate(keys, &object))
551 {
552 DBG1(DBG_CFG, "found key on PKCS#11 token '%s':%d",
553 p11->get_name(p11), current);
554 found = p11;
555 *slot = current;
556 }
557 keys->destroy(keys);
558 p11->f->C_CloseSession(session);
559 if (found)
560 {
561 break;
562 }
563 }
564 }
565 enumerator->destroy(enumerator);
566 return found;
567}
568
9a704963
RG
569/**
570 * Find the PKCS#11 lib and CKA_ID of the certificate object of a given
571 * subjectKeyIdentifier and optional slot
572 */
573static pkcs11_library_t* find_lib_and_keyid_by_skid(chunk_t keyid_chunk,
574 chunk_t *ckaid, int *slot)
575{
576 CK_OBJECT_CLASS class = CKO_CERTIFICATE;
577 CK_CERTIFICATE_TYPE type = CKC_X_509;
578 CK_ATTRIBUTE tmpl[] = {
579 {CKA_CLASS, &class, sizeof(class)},
580 {CKA_CERTIFICATE_TYPE, &type, sizeof(type)},
581 };
582 CK_ATTRIBUTE attr[] = {
583 {CKA_VALUE, NULL, 0},
584 {CKA_ID, NULL, 0},
585 };
586 CK_OBJECT_HANDLE object;
587 CK_SESSION_HANDLE session;
588 CK_RV rv;
589 pkcs11_manager_t *manager;
590 enumerator_t *enumerator, *certs;
591 identification_t *keyid;
592 pkcs11_library_t *p11, *found = NULL;
593 CK_SLOT_ID current;
594 linked_list_t *raw;
595 certificate_t *cert;
596 struct {
597 chunk_t value;
598 chunk_t ckaid;
599 } *entry;
600
601 manager = lib->get(lib, "pkcs11-manager");
602 if (!manager)
603 {
604 return NULL;
605 }
606
607 keyid = identification_create_from_encoding(ID_KEY_ID, keyid_chunk);
608 /* store result in a temporary list, avoid recursive operation */
609 raw = linked_list_create();
610
611 enumerator = manager->create_token_enumerator(manager);
612 while (enumerator->enumerate(enumerator, &p11, &current))
613 {
614 if (*slot != -1 && *slot != current)
615 {
616 continue;
617 }
618 rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL,
619 &session);
620 if (rv != CKR_OK)
621 {
622 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
623 ck_rv_names, rv);
624 continue;
625 }
626 certs = p11->create_object_enumerator(p11, session, tmpl, countof(tmpl),
627 attr, countof(attr));
628 while (certs->enumerate(certs, &object))
629 {
630 INIT(entry,
631 .value = chunk_clone(
632 chunk_create(attr[0].pValue, attr[0].ulValueLen)),
633 .ckaid = chunk_clone(
634 chunk_create(attr[1].pValue, attr[1].ulValueLen)),
635 );
636 raw->insert_last(raw, entry);
637 }
638 certs->destroy(certs);
639
640 while (raw->remove_first(raw, (void**)&entry) == SUCCESS)
641 {
642 if (!found)
643 {
644 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
645 CERT_X509, BUILD_BLOB_ASN1_DER,
646 entry->value, BUILD_END);
647 if (cert)
648 {
649 if (cert->has_subject(cert, keyid))
650 {
651 DBG1(DBG_CFG, "found cert with keyid '%#B' on PKCS#11 "
652 "token '%s':%d", &keyid_chunk, p11->get_name(p11),
653 current);
654 found = p11;
655 *ckaid = chunk_clone(entry->ckaid);
656 *slot = current;
657 }
658 cert->destroy(cert);
659 }
660 else
661 {
662 DBG1(DBG_CFG, "parsing cert with CKA_ID '%#B' on PKCS#11 "
663 "token '%s':%d failed", &entry->ckaid,
664 p11->get_name(p11), current);
665 }
666 }
667 chunk_free(&entry->value);
668 chunk_free(&entry->ckaid);
669 free(entry);
670 }
671 p11->f->C_CloseSession(session);
672 if (found)
673 {
674 break;
675 }
676 }
677 enumerator->destroy(enumerator);
678 keyid->destroy(keyid);
679 raw->destroy(raw);
680 return found;
681}
682
5f1e4438
MW
683/**
684 * Find the key on the token
685 */
686static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
687{
688 CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
689 CK_ATTRIBUTE tmpl[] = {
690 {CKA_CLASS, &class, sizeof(class)},
691 {CKA_ID, keyid.ptr, keyid.len},
692 };
693 CK_OBJECT_HANDLE object;
694 CK_KEY_TYPE type;
b78ca4b0 695 CK_BBOOL reauth = FALSE;
5f1e4438
MW
696 CK_ATTRIBUTE attr[] = {
697 {CKA_KEY_TYPE, &type, sizeof(type)},
b78ca4b0 698 {CKA_ALWAYS_AUTHENTICATE, &reauth, sizeof(reauth)},
5f1e4438
MW
699 };
700 enumerator_t *enumerator;
b78ca4b0 701 int count = countof(attr);
5d2fccf4 702 bool found = FALSE;
5f1e4438 703
b78ca4b0
MW
704 /* do not use CKA_ALWAYS_AUTHENTICATE if not supported */
705 if (!(this->lib->get_features(this->lib) & PKCS11_ALWAYS_AUTH_KEYS))
706 {
707 count--;
708 }
5f1e4438 709 enumerator = this->lib->create_object_enumerator(this->lib,
b78ca4b0 710 this->session, tmpl, countof(tmpl), attr, count);
5f1e4438
MW
711 if (enumerator->enumerate(enumerator, &object))
712 {
5d2fccf4 713 this->type = KEY_RSA;
5f1e4438
MW
714 switch (type)
715 {
5d2fccf4
TB
716 case CKK_ECDSA:
717 this->type = KEY_ECDSA;
718 /* fall-through */
5f1e4438 719 case CKK_RSA:
af007ed6 720 this->reauth = reauth;
5f1e4438 721 this->object = object;
5d2fccf4 722 found = TRUE;
5f1e4438
MW
723 break;
724 default:
725 DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type);
726 break;
727 }
728 }
729 enumerator->destroy(enumerator);
5d2fccf4 730 return found;
5f1e4438
MW
731}
732
a0bdd5d6 733/**
62be9236 734 * Find a PIN and try to log in
a0bdd5d6 735 */
af007ed6 736static bool login(private_pkcs11_private_key_t *this, int slot)
a0bdd5d6 737{
0556667d 738 enumerator_t *enumerator;
62be9236
MW
739 shared_key_t *shared;
740 chunk_t pin;
a0bdd5d6 741 CK_RV rv;
199b1712 742 CK_SESSION_INFO info;
0556667d 743 bool found = FALSE, success = FALSE;
a0bdd5d6 744
199b1712
MW
745 rv = this->lib->f->C_GetSessionInfo(this->session, &info);
746 if (rv != CKR_OK)
747 {
748 DBG1(DBG_CFG, "C_GetSessionInfo failed: %N", ck_rv_names, rv);
749 return FALSE;
750 }
751 if (info.state != CKS_RO_PUBLIC_SESSION &&
752 info.state != CKS_RW_PUBLIC_SESSION)
753 { /* already logged in with another session, skip */
754 return TRUE;
755 }
756
0556667d 757 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
af007ed6 758 SHARED_PIN, this->keyid, NULL);
0556667d 759 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
a0bdd5d6 760 {
0556667d
MW
761 found = TRUE;
762 pin = shared->get_key(shared);
763 rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
764 if (rv == CKR_OK)
765 {
766 success = TRUE;
767 break;
768 }
a0bdd5d6
MW
769 DBG1(DBG_CFG, "login to '%s':%d failed: %N",
770 this->lib->get_name(this->lib), slot, ck_rv_names, rv);
0556667d
MW
771 }
772 enumerator->destroy(enumerator);
0556667d
MW
773
774 if (!found)
775 {
af007ed6 776 DBG1(DBG_CFG, "no PIN found for PKCS#11 key %Y", this->keyid);
a0bdd5d6
MW
777 return FALSE;
778 }
0556667d 779 return success;
a0bdd5d6
MW
780}
781
ffe42fa4
MW
782/**
783 * Get a public key from a certificate with a given key ID.
784 */
785static public_key_t* find_pubkey_in_certs(private_pkcs11_private_key_t *this,
786 chunk_t keyid)
787{
788 CK_OBJECT_CLASS class = CKO_CERTIFICATE;
789 CK_CERTIFICATE_TYPE type = CKC_X_509;
790 CK_ATTRIBUTE tmpl[] = {
791 {CKA_CLASS, &class, sizeof(class)},
792 {CKA_CERTIFICATE_TYPE, &type, sizeof(type)},
793 {CKA_ID, keyid.ptr, keyid.len},
794 };
795 CK_OBJECT_HANDLE object;
796 CK_ATTRIBUTE attr[] = {
797 {CKA_VALUE, NULL, 0},
798 };
799 enumerator_t *enumerator;
800 chunk_t data = chunk_empty;
801 public_key_t *key = NULL;
802 certificate_t *cert;
803
804 enumerator = this->lib->create_object_enumerator(this->lib, this->session,
805 tmpl, countof(tmpl), attr, countof(attr));
806 if (enumerator->enumerate(enumerator, &object))
807 {
808 data = chunk_clone(chunk_create(attr[0].pValue, attr[0].ulValueLen));
809 }
810 enumerator->destroy(enumerator);
811
812 if (data.ptr)
813 {
814 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
815 BUILD_BLOB_ASN1_DER, data, BUILD_END);
816 free(data.ptr);
817 if (cert)
818 {
819 key = cert->get_public_key(cert);
820 cert->destroy(cert);
821 }
822 }
823 return key;
824}
825
5f1e4438
MW
826/**
827 * See header.
828 */
829pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
830{
831 private_pkcs11_private_key_t *this;
0b8b6640 832 char *module = NULL;
9a704963 833 chunk_t keyid = chunk_empty, ckaid = chunk_empty;
62be9236 834 int slot = -1;
5f1e4438 835 CK_RV rv;
5f1e4438
MW
836
837 while (TRUE)
838 {
839 switch (va_arg(args, builder_part_t))
840 {
841 case BUILD_PKCS11_KEYID:
0b8b6640 842 keyid = va_arg(args, chunk_t);
5f1e4438 843 continue;
5f1e4438
MW
844 case BUILD_PKCS11_SLOT:
845 slot = va_arg(args, int);
846 continue;
847 case BUILD_PKCS11_MODULE:
848 module = va_arg(args, char*);
849 continue;
850 case BUILD_END:
851 break;
852 default:
853 return NULL;
854 }
855 break;
856 }
62be9236 857 if (!keyid.len)
7afc00d0 858 {
5f1e4438
MW
859 return NULL;
860 }
861
862 INIT(this,
ba31fe1f
MW
863 .public = {
864 .key = {
865 .get_type = _get_type,
866 .sign = _sign,
867 .decrypt = _decrypt,
868 .get_keysize = _get_keysize,
869 .get_public_key = _get_public_key,
870 .equals = private_key_equals,
871 .belongs_to = private_key_belongs_to,
872 .get_fingerprint = _get_fingerprint,
873 .has_fingerprint = private_key_has_fingerprint,
874 .get_encoding = _get_encoding,
875 .get_ref = _get_ref,
876 .destroy = _destroy,
877 },
5f1e4438
MW
878 },
879 .ref = 1,
880 );
881
7afc00d0 882 if (module && slot != -1)
5f1e4438 883 {
7afc00d0
MW
884 this->lib = find_lib(module);
885 if (!this->lib)
886 {
887 DBG1(DBG_CFG, "PKCS#11 module '%s' not found", module);
888 free(this);
889 return NULL;
890 }
891 }
892 else
893 {
712e8130
MW
894 this->lib = find_lib_by_keyid(keyid, &slot, CKO_PUBLIC_KEY);
895 if (!this->lib)
896 {
897 this->lib = find_lib_by_keyid(keyid, &slot, CKO_CERTIFICATE);
898 }
7afc00d0 899 if (!this->lib)
9a704963
RG
900 {
901 this->lib = find_lib_and_keyid_by_skid(keyid, &ckaid, &slot);
902 }
903 if (!this->lib)
7afc00d0
MW
904 {
905 DBG1(DBG_CFG, "no PKCS#11 module found having a keyid %#B", &keyid);
906 free(this);
907 return NULL;
908 }
5f1e4438
MW
909 }
910
911 rv = this->lib->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
912 NULL, NULL, &this->session);
913 if (rv != CKR_OK)
914 {
915 DBG1(DBG_CFG, "opening private key session on '%s':%d failed: %N",
916 module, slot, ck_rv_names, rv);
917 free(this);
918 return NULL;
919 }
920
b0319fe8 921 this->slot = slot;
af007ed6 922 this->keyid = identification_create_from_encoding(ID_KEY_ID, keyid);
5f1e4438 923
af007ed6 924 if (!login(this, slot))
a0bdd5d6 925 {
62be9236
MW
926 destroy(this);
927 return NULL;
5f1e4438
MW
928 }
929
9a704963
RG
930 if (ckaid.ptr)
931 {
932 DBG1(DBG_CFG, "using CKA_ID '%#B' for key with keyid '%#B'",
933 &ckaid, &keyid);
934 keyid = ckaid;
935 }
936
0b8b6640 937 if (!find_key(this, keyid))
5f1e4438 938 {
9a704963
RG
939 DBG1(DBG_CFG, "did not find the key with %s '%#B'",
940 ckaid.ptr ? "CKA_ID" : "keyid", &keyid);
5f1e4438
MW
941 destroy(this);
942 return NULL;
943 }
5f1e4438 944
ffe42fa4 945 this->pubkey = pkcs11_public_key_connect(this->lib, slot, this->type, keyid);
5d2fccf4
TB
946 if (!this->pubkey)
947 {
ffe42fa4
MW
948 this->pubkey = find_pubkey_in_certs(this, keyid);
949 if (!this->pubkey)
950 {
951 DBG1(DBG_CFG, "no public key or certificate found for private key "
9a704963
RG
952 "(%s '%#B') on '%s':%d", ckaid.ptr ? "CKA_ID" : "keyid",
953 &keyid, module, slot);
ffe42fa4
MW
954 destroy(this);
955 return NULL;
956 }
5d2fccf4 957 }
5f1e4438
MW
958 return &this->public;
959}