]>
Commit | Line | Data |
---|---|---|
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 | |
49 | typedef struct private_pkcs11_private_key_t private_pkcs11_private_key_t; | |
50 | ||
51 | /** | |
52 | * Private data of an pkcs11_private_key_t object. | |
53 | */ | |
54 | struct 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 |
108 | METHOD(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 | 114 | METHOD(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 | */ | |
123 | static 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 |
145 | CK_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 | */ | |
221 | CK_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 |
245 | static 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 | 279 | METHOD(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 | ||
389 | METHOD(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 | ||
439 | METHOD(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 | ||
445 | METHOD(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 | ||
452 | METHOD(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 | ||
459 | METHOD(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 | ||
466 | METHOD(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 | */ | |
484 | static 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 |
512 | static 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, ¤t)) | |
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 | */ | |
573 | static 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, ¤t)) | |
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 | */ | |
686 | static 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 | 736 | static 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 | */ | |
785 | static 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 | */ | |
829 | pkcs11_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 | } |