]>
Commit | Line | Data |
---|---|---|
fed9407b | 1 | /* |
b0319fe8 TB |
2 | * Copyright (C) 2011 Tobias Brunner |
3 | * Hochschule fuer Technik Rapperswil | |
4 | * | |
fed9407b 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 | */ | |
18 | ||
19 | #include "pkcs11_public_key.h" | |
20 | ||
21 | #include "pkcs11.h" | |
22 | #include "pkcs11_private_key.h" | |
23 | #include "pkcs11_manager.h" | |
24 | ||
36d1627f TB |
25 | #include <asn1/oid.h> |
26 | #include <asn1/asn1.h> | |
27 | #include <asn1/asn1_parser.h> | |
fed9407b | 28 | #include <debug.h> |
fed9407b MW |
29 | |
30 | typedef struct private_pkcs11_public_key_t private_pkcs11_public_key_t; | |
31 | ||
32 | /** | |
33 | * Private data of an pkcs11_public_key_t object. | |
34 | */ | |
35 | struct private_pkcs11_public_key_t { | |
36 | ||
37 | /** | |
38 | * Public pkcs11_public_key_t interface. | |
39 | */ | |
40 | pkcs11_public_key_t public; | |
41 | ||
42 | /** | |
43 | * Type of the key | |
44 | */ | |
45 | key_type_t type; | |
46 | ||
47 | /** | |
a8084ee0 | 48 | * Key size in bits |
fed9407b MW |
49 | */ |
50 | size_t k; | |
51 | ||
52 | /** | |
53 | * PKCS#11 library this key uses | |
54 | */ | |
55 | pkcs11_library_t *lib; | |
56 | ||
57 | /** | |
58 | * Slot the token is in | |
59 | */ | |
60 | CK_SLOT_ID slot; | |
61 | ||
62 | /** | |
63 | * Session we use | |
64 | */ | |
65 | CK_SESSION_HANDLE session; | |
66 | ||
67 | /** | |
68 | * Object handle to the key | |
69 | */ | |
70 | CK_OBJECT_HANDLE object; | |
71 | ||
fed9407b MW |
72 | /** |
73 | * References to this key | |
74 | */ | |
75 | refcount_t ref; | |
76 | }; | |
77 | ||
36d1627f TB |
78 | /** |
79 | * Helper function that returns the base point order length in bits of the | |
80 | * given named curve. | |
81 | * | |
82 | * Currently only a subset of defined curves is supported (namely the 5 curves | |
83 | * over Fp recommended by NIST). IKEv2 only supports 3 out of these. | |
84 | * | |
85 | * 0 is returned if the given curve is not supported. | |
86 | */ | |
87 | static size_t basepoint_order_len(int oid) | |
88 | { | |
89 | switch (oid) | |
90 | { | |
91 | case OID_PRIME192V1: | |
92 | return 192; | |
93 | case OID_SECT224R1: | |
94 | return 224; | |
95 | case OID_PRIME256V1: | |
96 | return 256; | |
97 | case OID_SECT384R1: | |
98 | return 384; | |
99 | case OID_SECT521R1: | |
100 | return 521; | |
101 | default: | |
102 | return 0; | |
103 | } | |
104 | } | |
105 | ||
106 | /** | |
107 | * Parses the given ecParameters (ASN.1) and returns the key length. | |
108 | */ | |
109 | static bool keylen_from_ecparams(chunk_t ecparams, size_t *keylen) | |
110 | { | |
111 | if (!asn1_parse_simple_object(&ecparams, ASN1_OID, 0, "named curve")) | |
112 | { | |
113 | return FALSE; | |
114 | } | |
115 | *keylen = basepoint_order_len(asn1_known_oid(ecparams)); | |
116 | return *keylen > 0; | |
117 | } | |
118 | ||
119 | /** | |
120 | * ASN.1 definition of a subjectPublicKeyInfo structure when used with ECDSA | |
121 | * we currently only support named curves. | |
122 | */ | |
123 | static const asn1Object_t pkinfoObjects[] = { | |
124 | { 0, "subjectPublicKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ | |
125 | { 1, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ | |
126 | { 2, "algorithm", ASN1_OID, ASN1_BODY }, /* 2 */ | |
127 | { 2, "namedCurve", ASN1_OID, ASN1_RAW }, /* 3 */ | |
128 | { 1, "subjectPublicKey", ASN1_BIT_STRING, ASN1_BODY }, /* 4 */ | |
129 | { 0, "exit", ASN1_EOC, ASN1_EXIT } | |
130 | }; | |
131 | #define PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM 2 | |
132 | #define PKINFO_SUBJECT_PUBLIC_KEY_NAMEDCURVE 3 | |
133 | #define PKINFO_SUBJECT_PUBLIC_KEY 4 | |
134 | ||
135 | /** | |
136 | * Extract the DER encoded Parameters and ECPoint from the given DER encoded | |
137 | * subjectPublicKeyInfo. | |
138 | */ | |
139 | static bool parse_ecdsa_public_key(chunk_t blob, chunk_t *ecparams, | |
140 | chunk_t *ecpoint, size_t *keylen) | |
141 | { | |
142 | asn1_parser_t *parser; | |
143 | chunk_t object; | |
144 | int objectID; | |
145 | bool success = FALSE; | |
146 | ||
147 | parser = asn1_parser_create(pkinfoObjects, blob); | |
148 | ||
149 | while (parser->iterate(parser, &objectID, &object)) | |
150 | { | |
151 | switch (objectID) | |
152 | { | |
153 | case PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM: | |
154 | { | |
155 | if (asn1_known_oid(object) != OID_EC_PUBLICKEY) | |
156 | { | |
157 | goto end; | |
158 | } | |
159 | break; | |
160 | } | |
161 | case PKINFO_SUBJECT_PUBLIC_KEY_NAMEDCURVE: | |
162 | { | |
163 | *ecparams = object; | |
164 | if (!keylen_from_ecparams(object, keylen)) | |
165 | { | |
166 | goto end; | |
167 | } | |
168 | break; | |
169 | } | |
170 | case PKINFO_SUBJECT_PUBLIC_KEY: | |
171 | { | |
172 | if (object.len > 0 && *object.ptr == 0x00) | |
173 | { /* skip initial bit string octet defining 0 unused bits */ | |
174 | object = chunk_skip(object, 1); | |
175 | } | |
176 | *ecpoint = object; | |
177 | break; | |
178 | } | |
179 | } | |
180 | } | |
181 | success = parser->success(parser); | |
182 | end: | |
183 | parser->destroy(parser); | |
184 | return success; | |
185 | } | |
186 | ||
187 | ||
fed9407b MW |
188 | METHOD(public_key_t, get_type, key_type_t, |
189 | private_pkcs11_public_key_t *this) | |
190 | { | |
191 | return this->type; | |
192 | } | |
193 | ||
01e4f5f3 MW |
194 | METHOD(public_key_t, get_keysize, int, |
195 | private_pkcs11_public_key_t *this) | |
196 | { | |
a8084ee0 | 197 | return this->k; |
01e4f5f3 MW |
198 | } |
199 | ||
fed9407b MW |
200 | METHOD(public_key_t, verify, bool, |
201 | private_pkcs11_public_key_t *this, signature_scheme_t scheme, | |
202 | chunk_t data, chunk_t sig) | |
203 | { | |
204 | CK_MECHANISM_PTR mechanism; | |
b0319fe8 | 205 | CK_SESSION_HANDLE session; |
fed9407b | 206 | CK_RV rv; |
fd48b220 TB |
207 | hash_algorithm_t hash_alg; |
208 | chunk_t hash = chunk_empty; | |
fed9407b | 209 | |
5b85b94e TB |
210 | mechanism = pkcs11_signature_scheme_to_mech(scheme, this->type, this->k, |
211 | &hash_alg); | |
fed9407b MW |
212 | if (!mechanism) |
213 | { | |
214 | DBG1(DBG_LIB, "signature scheme %N not supported", | |
215 | signature_scheme_names, scheme); | |
216 | return FALSE; | |
217 | } | |
7c03d707 MW |
218 | if (sig.len && sig.ptr[0] == 0) |
219 | { /* trim leading zero byte in sig */ | |
220 | sig = chunk_skip(sig, 1); | |
221 | } | |
b0319fe8 TB |
222 | rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL, |
223 | &session); | |
224 | if (rv != CKR_OK) | |
225 | { | |
226 | DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv); | |
227 | return FALSE; | |
228 | } | |
229 | rv = this->lib->f->C_VerifyInit(session, mechanism, this->object); | |
fed9407b MW |
230 | if (rv != CKR_OK) |
231 | { | |
b0319fe8 | 232 | this->lib->f->C_CloseSession(session); |
fed9407b MW |
233 | DBG1(DBG_LIB, "C_VerifyInit() failed: %N", ck_rv_names, rv); |
234 | return FALSE; | |
235 | } | |
fd48b220 TB |
236 | if (hash_alg != HASH_UNKNOWN) |
237 | { | |
87dd205b MW |
238 | hasher_t *hasher; |
239 | ||
240 | hasher = lib->crypto->create_hasher(lib->crypto, hash_alg); | |
241 | if (!hasher || !hasher->allocate_hash(hasher, data, &hash)) | |
fd48b220 | 242 | { |
87dd205b | 243 | DESTROY_IF(hasher); |
fd48b220 TB |
244 | this->lib->f->C_CloseSession(session); |
245 | return FALSE; | |
246 | } | |
fd48b220 TB |
247 | hasher->destroy(hasher); |
248 | data = hash; | |
249 | } | |
b0319fe8 TB |
250 | rv = this->lib->f->C_Verify(session, data.ptr, data.len, sig.ptr, sig.len); |
251 | this->lib->f->C_CloseSession(session); | |
fd48b220 | 252 | chunk_free(&hash); |
fed9407b MW |
253 | if (rv != CKR_OK) |
254 | { | |
255 | DBG1(DBG_LIB, "C_Verify() failed: %N", ck_rv_names, rv); | |
256 | return FALSE; | |
257 | } | |
258 | return TRUE; | |
259 | } | |
260 | ||
261 | METHOD(public_key_t, encrypt, bool, | |
33ddaaab | 262 | private_pkcs11_public_key_t *this, encryption_scheme_t scheme, |
01e4f5f3 | 263 | chunk_t plain, chunk_t *crypt) |
fed9407b | 264 | { |
01e4f5f3 | 265 | CK_MECHANISM_PTR mechanism; |
b0319fe8 | 266 | CK_SESSION_HANDLE session; |
01e4f5f3 MW |
267 | CK_BYTE_PTR buf; |
268 | CK_ULONG len; | |
269 | CK_RV rv; | |
fed9407b | 270 | |
01e4f5f3 MW |
271 | mechanism = pkcs11_encryption_scheme_to_mech(scheme); |
272 | if (!mechanism) | |
273 | { | |
274 | DBG1(DBG_LIB, "encryption scheme %N not supported", | |
275 | encryption_scheme_names, scheme); | |
276 | return FALSE; | |
277 | } | |
b0319fe8 TB |
278 | rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL, |
279 | &session); | |
280 | if (rv != CKR_OK) | |
281 | { | |
282 | DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv); | |
283 | return FALSE; | |
284 | } | |
285 | rv = this->lib->f->C_EncryptInit(session, mechanism, this->object); | |
01e4f5f3 MW |
286 | if (rv != CKR_OK) |
287 | { | |
b0319fe8 | 288 | this->lib->f->C_CloseSession(session); |
01e4f5f3 MW |
289 | DBG1(DBG_LIB, "C_EncryptInit() failed: %N", ck_rv_names, rv); |
290 | return FALSE; | |
291 | } | |
292 | len = (get_keysize(this) + 7) / 8; | |
293 | buf = malloc(len); | |
b0319fe8 TB |
294 | rv = this->lib->f->C_Encrypt(session, plain.ptr, plain.len, buf, &len); |
295 | this->lib->f->C_CloseSession(session); | |
01e4f5f3 MW |
296 | if (rv != CKR_OK) |
297 | { | |
298 | DBG1(DBG_LIB, "C_Encrypt() failed: %N", ck_rv_names, rv); | |
299 | free(buf); | |
300 | return FALSE; | |
301 | } | |
302 | *crypt = chunk_create(buf, len); | |
303 | return TRUE; | |
fed9407b MW |
304 | } |
305 | ||
9e3b1e14 TB |
306 | /** |
307 | * Encode ECDSA key using a given encoding type | |
308 | */ | |
309 | static bool encode_ecdsa(private_pkcs11_public_key_t *this, | |
310 | cred_encoding_type_t type, chunk_t *encoding) | |
311 | { | |
312 | enumerator_t *enumerator; | |
313 | bool success = FALSE; | |
314 | CK_ATTRIBUTE attr[] = { | |
315 | {CKA_EC_PARAMS, NULL, 0}, | |
316 | {CKA_EC_POINT, NULL, 0}, | |
317 | }; | |
318 | ||
319 | if (type != PUBKEY_SPKI_ASN1_DER && type != PUBKEY_PEM) | |
320 | { | |
321 | return FALSE; | |
322 | } | |
323 | ||
324 | enumerator = this->lib->create_object_attr_enumerator(this->lib, | |
325 | this->session, this->object, attr, countof(attr)); | |
326 | if (enumerator && enumerator->enumerate(enumerator, NULL) && | |
327 | attr[0].ulValueLen > 0 && attr[1].ulValueLen > 0) | |
328 | { | |
329 | chunk_t ecparams, ecpoint; | |
330 | ecparams = chunk_create(attr[0].pValue, attr[0].ulValueLen); | |
331 | ecpoint = chunk_create(attr[1].pValue, attr[1].ulValueLen); | |
332 | /* encode as subjectPublicKeyInfo */ | |
333 | *encoding = asn1_wrap(ASN1_SEQUENCE, "mm", | |
334 | asn1_wrap(ASN1_SEQUENCE, "mc", | |
335 | asn1_build_known_oid(OID_EC_PUBLICKEY), ecparams), | |
336 | asn1_bitstring("c", ecpoint)); | |
337 | success = TRUE; | |
338 | if (type == PUBKEY_PEM) | |
339 | { | |
340 | chunk_t asn1 = *encoding; | |
341 | success = lib->encoding->encode(lib->encoding, PUBKEY_PEM, | |
342 | NULL, encoding, CRED_PART_ECDSA_PUB_ASN1_DER, | |
343 | asn1, CRED_PART_END); | |
344 | chunk_clear(&asn1); | |
345 | } | |
346 | } | |
347 | DESTROY_IF(enumerator); | |
348 | return success; | |
349 | } | |
350 | ||
351 | /** | |
352 | * Compute fingerprint of an ECDSA key | |
353 | */ | |
354 | static bool fingerprint_ecdsa(private_pkcs11_public_key_t *this, | |
355 | cred_encoding_type_t type, chunk_t *fp) | |
356 | { | |
357 | hasher_t *hasher; | |
358 | chunk_t asn1; | |
359 | ||
360 | switch (type) | |
361 | { | |
362 | case KEYID_PUBKEY_SHA1: | |
363 | if (!this->lib->get_ck_attribute(this->lib, this->session, | |
364 | this->object, CKA_EC_POINT, &asn1)) | |
365 | { | |
366 | return FALSE; | |
367 | } | |
368 | break; | |
369 | case KEYID_PUBKEY_INFO_SHA1: | |
370 | if (!encode_ecdsa(this, PUBKEY_SPKI_ASN1_DER, &asn1)) | |
371 | { | |
372 | return FALSE; | |
373 | } | |
374 | break; | |
375 | default: | |
376 | return FALSE; | |
377 | } | |
378 | hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); | |
87dd205b | 379 | if (!hasher || !hasher->allocate_hash(hasher, asn1, fp)) |
9e3b1e14 | 380 | { |
87dd205b | 381 | DESTROY_IF(hasher); |
9e3b1e14 TB |
382 | chunk_clear(&asn1); |
383 | return FALSE; | |
384 | } | |
9e3b1e14 TB |
385 | hasher->destroy(hasher); |
386 | chunk_clear(&asn1); | |
387 | lib->encoding->cache(lib->encoding, type, this, *fp); | |
388 | return TRUE; | |
389 | } | |
390 | ||
fed9407b MW |
391 | /** |
392 | * Encode RSA key using a given encoding type | |
393 | */ | |
394 | static bool encode_rsa(private_pkcs11_public_key_t *this, | |
395 | cred_encoding_type_t type, void *cache, chunk_t *encoding) | |
396 | { | |
dae19d44 | 397 | enumerator_t *enumerator; |
fed9407b | 398 | bool success = FALSE; |
fed9407b MW |
399 | CK_ATTRIBUTE attr[] = { |
400 | {CKA_MODULUS, NULL, 0}, | |
401 | {CKA_PUBLIC_EXPONENT, NULL, 0}, | |
402 | }; | |
403 | ||
dae19d44 TB |
404 | enumerator = this->lib->create_object_attr_enumerator(this->lib, |
405 | this->session, this->object, attr, countof(attr)); | |
406 | if (enumerator && enumerator->enumerate(enumerator, NULL) && | |
407 | attr[0].ulValueLen > 0 && attr[1].ulValueLen > 0) | |
fed9407b | 408 | { |
dae19d44 | 409 | chunk_t n, e; |
fed9407b | 410 | n = chunk_create(attr[0].pValue, attr[0].ulValueLen); |
8859c1f2 TB |
411 | if (n.ptr[0] & 0x80) |
412 | { /* add leading 0x00, encoders expect it already like this */ | |
413 | n = chunk_cata("cc", chunk_from_chars(0x00), n); | |
414 | } | |
fed9407b MW |
415 | e = chunk_create(attr[1].pValue, attr[1].ulValueLen); |
416 | success = lib->encoding->encode(lib->encoding, type, cache, encoding, | |
417 | CRED_PART_RSA_MODULUS, n, CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); | |
418 | } | |
dae19d44 | 419 | DESTROY_IF(enumerator); |
fed9407b MW |
420 | return success; |
421 | } | |
422 | ||
423 | METHOD(public_key_t, get_encoding, bool, | |
424 | private_pkcs11_public_key_t *this, cred_encoding_type_t type, | |
425 | chunk_t *encoding) | |
426 | { | |
427 | switch (this->type) | |
428 | { | |
429 | case KEY_RSA: | |
430 | return encode_rsa(this, type, NULL, encoding); | |
9e3b1e14 TB |
431 | case KEY_ECDSA: |
432 | return encode_ecdsa(this, type, encoding); | |
fed9407b MW |
433 | default: |
434 | return FALSE; | |
435 | } | |
436 | } | |
437 | ||
438 | METHOD(public_key_t, get_fingerprint, bool, | |
439 | private_pkcs11_public_key_t *this, cred_encoding_type_t type, chunk_t *fp) | |
440 | { | |
441 | if (lib->encoding->get_cache(lib->encoding, type, this, fp)) | |
442 | { | |
443 | return TRUE; | |
444 | } | |
445 | switch (this->type) | |
446 | { | |
447 | case KEY_RSA: | |
448 | return encode_rsa(this, type, this, fp); | |
9e3b1e14 TB |
449 | case KEY_ECDSA: |
450 | return fingerprint_ecdsa(this, type, fp); | |
fed9407b MW |
451 | default: |
452 | return FALSE; | |
453 | } | |
454 | } | |
455 | ||
456 | METHOD(public_key_t, get_ref, public_key_t*, | |
457 | private_pkcs11_public_key_t *this) | |
458 | { | |
459 | ref_get(&this->ref); | |
460 | return &this->public.key; | |
461 | } | |
462 | ||
463 | METHOD(public_key_t, destroy, void, | |
464 | private_pkcs11_public_key_t *this) | |
465 | { | |
466 | if (ref_put(&this->ref)) | |
467 | { | |
468 | lib->encoding->clear_cache(lib->encoding, this); | |
469 | this->lib->f->C_CloseSession(this->session); | |
fed9407b MW |
470 | free(this); |
471 | } | |
472 | } | |
473 | ||
474 | /** | |
475 | * Create an empty PKCS#11 public key | |
476 | */ | |
477 | static private_pkcs11_public_key_t *create(key_type_t type, size_t k, | |
478 | pkcs11_library_t *p11, CK_SLOT_ID slot, | |
479 | CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object) | |
480 | { | |
481 | private_pkcs11_public_key_t *this; | |
482 | ||
483 | INIT(this, | |
484 | .public = { | |
485 | .key = { | |
486 | .get_type = _get_type, | |
487 | .verify = _verify, | |
488 | .encrypt = _encrypt, | |
489 | .equals = public_key_equals, | |
490 | .get_keysize = _get_keysize, | |
491 | .get_fingerprint = _get_fingerprint, | |
492 | .has_fingerprint = public_key_has_fingerprint, | |
493 | .get_encoding = _get_encoding, | |
494 | .get_ref = _get_ref, | |
495 | .destroy = _destroy, | |
496 | }, | |
497 | }, | |
498 | .type = type, | |
57426116 | 499 | .k = k, |
fed9407b MW |
500 | .lib = p11, |
501 | .slot = slot, | |
502 | .session = session, | |
503 | .object = object, | |
7c03d707 | 504 | .ref = 1, |
fed9407b MW |
505 | ); |
506 | ||
507 | return this; | |
508 | } | |
509 | ||
510 | /** | |
511 | * Find a key object, including PKCS11 library and slot | |
512 | */ | |
57426116 TB |
513 | static private_pkcs11_public_key_t* find_key(key_type_t type, size_t keylen, |
514 | CK_ATTRIBUTE_PTR tmpl, int count) | |
fed9407b MW |
515 | { |
516 | private_pkcs11_public_key_t *this = NULL; | |
517 | pkcs11_manager_t *manager; | |
518 | enumerator_t *enumerator, *keys; | |
519 | pkcs11_library_t *p11; | |
520 | CK_SLOT_ID slot; | |
521 | ||
07190323 | 522 | manager = lib->get(lib, "pkcs11-manager"); |
fed9407b MW |
523 | if (!manager) |
524 | { | |
525 | return NULL; | |
526 | } | |
527 | ||
fed9407b MW |
528 | enumerator = manager->create_token_enumerator(manager); |
529 | while (enumerator->enumerate(enumerator, &p11, &slot)) | |
530 | { | |
fed9407b MW |
531 | CK_OBJECT_HANDLE object; |
532 | CK_SESSION_HANDLE session; | |
533 | CK_RV rv; | |
534 | ||
535 | rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL, | |
536 | &session); | |
537 | if (rv != CKR_OK) | |
538 | { | |
539 | DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv); | |
540 | continue; | |
541 | } | |
57426116 TB |
542 | keys = p11->create_object_enumerator(p11, session, tmpl, count, |
543 | NULL, 0); | |
fed9407b MW |
544 | if (keys->enumerate(keys, &object)) |
545 | { | |
57426116 | 546 | this = create(type, keylen, p11, slot, session, object); |
fed9407b MW |
547 | keys->destroy(keys); |
548 | break; | |
549 | } | |
550 | keys->destroy(keys); | |
551 | p11->f->C_CloseSession(session); | |
552 | } | |
553 | enumerator->destroy(enumerator); | |
554 | return this; | |
555 | } | |
556 | ||
57426116 TB |
557 | /** |
558 | * Find an RSA key object | |
559 | */ | |
560 | static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e, | |
561 | size_t keylen) | |
562 | { | |
563 | CK_OBJECT_CLASS class = CKO_PUBLIC_KEY; | |
564 | CK_KEY_TYPE type = CKK_RSA; | |
565 | CK_ATTRIBUTE tmpl[] = { | |
566 | {CKA_CLASS, &class, sizeof(class)}, | |
567 | {CKA_KEY_TYPE, &type, sizeof(type)}, | |
568 | {CKA_MODULUS, n.ptr, n.len}, | |
569 | {CKA_PUBLIC_EXPONENT, e.ptr, e.len}, | |
570 | }; | |
571 | return find_key(KEY_RSA, keylen, tmpl, countof(tmpl)); | |
572 | } | |
573 | ||
36d1627f TB |
574 | /** |
575 | * Find an ECDSA key object | |
576 | */ | |
577 | static private_pkcs11_public_key_t* find_ecdsa_key(chunk_t ecparams, | |
578 | chunk_t ecpoint, | |
579 | size_t keylen) | |
580 | { | |
581 | CK_OBJECT_CLASS class = CKO_PUBLIC_KEY; | |
582 | CK_KEY_TYPE type = CKK_ECDSA; | |
583 | CK_ATTRIBUTE tmpl[] = { | |
584 | {CKA_CLASS, &class, sizeof(class)}, | |
585 | {CKA_KEY_TYPE, &type, sizeof(type)}, | |
586 | {CKA_EC_PARAMS, ecparams.ptr, ecparams.len}, | |
587 | {CKA_EC_POINT, ecpoint.ptr, ecpoint.len}, | |
588 | }; | |
589 | return find_key(KEY_ECDSA, keylen, tmpl, countof(tmpl)); | |
590 | } | |
591 | ||
7c03d707 MW |
592 | /** |
593 | * Create a key object in a suitable token session | |
594 | */ | |
57426116 TB |
595 | static private_pkcs11_public_key_t* create_key(key_type_t type, size_t keylen, |
596 | CK_MECHANISM_TYPE_PTR mechanisms, int mcount, | |
597 | CK_ATTRIBUTE_PTR tmpl, int count) | |
7c03d707 MW |
598 | { |
599 | private_pkcs11_public_key_t *this = NULL; | |
600 | pkcs11_manager_t *manager; | |
601 | enumerator_t *enumerator, *mechs; | |
602 | pkcs11_library_t *p11; | |
603 | CK_SLOT_ID slot; | |
604 | ||
07190323 | 605 | manager = lib->get(lib, "pkcs11-manager"); |
7c03d707 MW |
606 | if (!manager) |
607 | { | |
608 | return NULL; | |
609 | } | |
610 | ||
611 | enumerator = manager->create_token_enumerator(manager); | |
612 | while (enumerator->enumerate(enumerator, &p11, &slot)) | |
613 | { | |
614 | CK_MECHANISM_TYPE mech; | |
615 | CK_MECHANISM_INFO info; | |
7c03d707 MW |
616 | CK_OBJECT_HANDLE object; |
617 | CK_SESSION_HANDLE session; | |
618 | CK_RV rv; | |
619 | ||
620 | mechs = p11->create_mechanism_enumerator(p11, slot); | |
621 | while (mechs->enumerate(mechs, &mech, &info)) | |
622 | { | |
57426116 TB |
623 | bool found = FALSE; |
624 | int i; | |
7c03d707 MW |
625 | if (!(info.flags & CKF_VERIFY)) |
626 | { | |
627 | continue; | |
628 | } | |
57426116 | 629 | for (i = 0; i < mcount; i++) |
7c03d707 | 630 | { |
57426116 TB |
631 | if (mechanisms[i] == mech) |
632 | { | |
633 | found = TRUE; | |
7c03d707 | 634 | break; |
57426116 TB |
635 | } |
636 | } | |
637 | if (!found) | |
638 | { | |
639 | continue; | |
7c03d707 MW |
640 | } |
641 | rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL, | |
642 | &session); | |
643 | if (rv != CKR_OK) | |
644 | { | |
645 | DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", | |
646 | ck_rv_names, rv); | |
647 | continue; | |
648 | } | |
57426116 | 649 | rv = p11->f->C_CreateObject(session, tmpl, count, &object); |
7c03d707 MW |
650 | if (rv == CKR_OK) |
651 | { | |
57426116 TB |
652 | this = create(type, keylen, p11, slot, session, object); |
653 | DBG2(DBG_CFG, "created %N public key on token '%s':%d ", | |
654 | key_type_names, type, p11->get_name(p11), slot); | |
7c03d707 MW |
655 | } |
656 | else | |
657 | { | |
57426116 TB |
658 | DBG1(DBG_CFG, "creating %N public key on token '%s':%d " |
659 | "failed: %N", key_type_names, type, p11->get_name(p11), | |
660 | slot, ck_rv_names, rv); | |
7c03d707 MW |
661 | p11->f->C_CloseSession(session); |
662 | } | |
57426116 | 663 | break; |
7c03d707 MW |
664 | } |
665 | mechs->destroy(mechs); | |
666 | if (this) | |
667 | { | |
668 | break; | |
669 | } | |
670 | } | |
671 | enumerator->destroy(enumerator); | |
672 | return this; | |
673 | } | |
674 | ||
57426116 TB |
675 | /** |
676 | * Create an RSA key object in a suitable token session | |
677 | */ | |
678 | static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e, | |
679 | size_t keylen) | |
680 | { | |
681 | CK_OBJECT_CLASS class = CKO_PUBLIC_KEY; | |
682 | CK_KEY_TYPE type = CKK_RSA; | |
683 | CK_ATTRIBUTE tmpl[] = { | |
684 | {CKA_CLASS, &class, sizeof(class)}, | |
685 | {CKA_KEY_TYPE, &type, sizeof(type)}, | |
686 | {CKA_MODULUS, n.ptr, n.len}, | |
687 | {CKA_PUBLIC_EXPONENT, e.ptr, e.len}, | |
688 | }; | |
689 | CK_MECHANISM_TYPE mechs[] = { | |
690 | CKM_RSA_PKCS, | |
691 | CKM_SHA1_RSA_PKCS, | |
692 | CKM_SHA256_RSA_PKCS, | |
693 | CKM_SHA384_RSA_PKCS, | |
694 | CKM_SHA512_RSA_PKCS, | |
695 | CKM_MD5_RSA_PKCS, | |
696 | }; | |
697 | return create_key(KEY_RSA, keylen, mechs, countof(mechs), tmpl, | |
698 | countof(tmpl)); | |
699 | } | |
700 | ||
36d1627f TB |
701 | /** |
702 | * Create an ECDSA key object in a suitable token session | |
703 | */ | |
704 | static private_pkcs11_public_key_t* create_ecdsa_key(chunk_t ecparams, | |
705 | chunk_t ecpoint, | |
706 | size_t keylen) | |
707 | { | |
708 | CK_OBJECT_CLASS class = CKO_PUBLIC_KEY; | |
709 | CK_KEY_TYPE type = CKK_ECDSA; | |
710 | CK_ATTRIBUTE tmpl[] = { | |
711 | {CKA_CLASS, &class, sizeof(class)}, | |
712 | {CKA_KEY_TYPE, &type, sizeof(type)}, | |
713 | {CKA_EC_PARAMS, ecparams.ptr, ecparams.len}, | |
714 | {CKA_EC_POINT, ecpoint.ptr, ecpoint.len}, | |
715 | }; | |
716 | CK_MECHANISM_TYPE mechs[] = { | |
717 | CKM_ECDSA, | |
718 | CKM_ECDSA_SHA1, | |
719 | }; | |
720 | return create_key(KEY_ECDSA, keylen, mechs, | |
721 | countof(mechs), tmpl, countof(tmpl)); | |
722 | } | |
723 | ||
fed9407b MW |
724 | /** |
725 | * See header | |
726 | */ | |
727 | pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args) | |
728 | { | |
729 | private_pkcs11_public_key_t *this; | |
36d1627f | 730 | chunk_t n, e, blob; |
a190ec0a | 731 | size_t keylen = 0; |
fed9407b | 732 | |
36d1627f | 733 | n = e = blob = chunk_empty; |
fed9407b MW |
734 | while (TRUE) |
735 | { | |
736 | switch (va_arg(args, builder_part_t)) | |
737 | { | |
36d1627f TB |
738 | case BUILD_BLOB_ASN1_DER: |
739 | blob = va_arg(args, chunk_t); | |
740 | continue; | |
fed9407b MW |
741 | case BUILD_RSA_MODULUS: |
742 | n = va_arg(args, chunk_t); | |
743 | continue; | |
744 | case BUILD_RSA_PUB_EXP: | |
745 | e = va_arg(args, chunk_t); | |
746 | continue; | |
747 | case BUILD_END: | |
748 | break; | |
749 | default: | |
750 | return NULL; | |
751 | } | |
752 | break; | |
753 | } | |
754 | if (type == KEY_RSA && e.ptr && n.ptr) | |
755 | { | |
7c03d707 MW |
756 | if (n.len && n.ptr[0] == 0) |
757 | { /* trim leading zero byte in modulus */ | |
758 | n = chunk_skip(n, 1); | |
759 | } | |
57426116 TB |
760 | keylen = n.len * 8; |
761 | this = find_rsa_key(n, e, keylen); | |
fed9407b MW |
762 | if (this) |
763 | { | |
764 | return &this->public; | |
765 | } | |
57426116 | 766 | this = create_rsa_key(n, e, keylen); |
7c03d707 MW |
767 | if (this) |
768 | { | |
769 | return &this->public; | |
770 | } | |
fed9407b | 771 | } |
36d1627f TB |
772 | else if (type == KEY_ECDSA && blob.ptr) |
773 | { | |
774 | chunk_t ecparams, ecpoint; | |
775 | ecparams = ecpoint = chunk_empty; | |
776 | if (parse_ecdsa_public_key(blob, &ecparams, &ecpoint, &keylen)) | |
777 | { | |
778 | this = find_ecdsa_key(ecparams, ecpoint, keylen); | |
779 | if (this) | |
780 | { | |
781 | return &this->public; | |
782 | } | |
783 | this = create_ecdsa_key(ecparams, ecpoint, keylen); | |
784 | if (this) | |
785 | { | |
786 | return &this->public; | |
787 | } | |
788 | } | |
789 | } | |
fed9407b MW |
790 | return NULL; |
791 | } | |
792 | ||
30a3ede8 TB |
793 | static private_pkcs11_public_key_t *find_key_by_keyid(pkcs11_library_t *p11, |
794 | int slot, key_type_t key_type, | |
795 | chunk_t keyid) | |
796 | { | |
797 | CK_OBJECT_CLASS class = CKO_PUBLIC_KEY; | |
798 | CK_KEY_TYPE type; | |
799 | CK_ATTRIBUTE tmpl[] = { | |
800 | {CKA_CLASS, &class, sizeof(class)}, | |
801 | {CKA_ID, keyid.ptr, keyid.len}, | |
802 | {CKA_KEY_TYPE, &type, sizeof(type)}, | |
803 | }; | |
804 | CK_OBJECT_HANDLE object; | |
805 | CK_ATTRIBUTE attr[] = { | |
806 | {CKA_KEY_TYPE, &type, sizeof(type)}, | |
807 | }; | |
808 | CK_SESSION_HANDLE session; | |
809 | CK_RV rv; | |
810 | enumerator_t *enumerator; | |
811 | int count = countof(tmpl); | |
812 | bool found = FALSE; | |
813 | size_t keylen; | |
814 | ||
4de8f280 | 815 | switch (key_type) |
30a3ede8 TB |
816 | { |
817 | case KEY_RSA: | |
818 | type = CKK_RSA; | |
819 | break; | |
820 | case KEY_ECDSA: | |
821 | type = CKK_ECDSA; | |
822 | break; | |
823 | default: | |
824 | /* don't specify key type on KEY_ANY */ | |
825 | count--; | |
826 | break; | |
827 | } | |
828 | ||
829 | rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL, &session); | |
830 | if (rv != CKR_OK) | |
831 | { | |
832 | DBG1(DBG_CFG, "opening public key session on '%s':%d failed: %N", | |
833 | p11->get_name(p11), slot, ck_rv_names, rv); | |
834 | return NULL; | |
835 | } | |
836 | ||
837 | enumerator = p11->create_object_enumerator(p11, session, tmpl, count, attr, | |
838 | countof(attr)); | |
839 | if (enumerator->enumerate(enumerator, &object)) | |
840 | { | |
841 | switch (type) | |
842 | { | |
843 | case CKK_ECDSA: | |
844 | { | |
845 | chunk_t ecparams; | |
846 | if (p11->get_ck_attribute(p11, session, object, CKA_EC_PARAMS, | |
847 | &ecparams) && | |
848 | keylen_from_ecparams(ecparams, &keylen)) | |
849 | { | |
850 | chunk_free(&ecparams); | |
851 | key_type = KEY_ECDSA; | |
852 | found = TRUE; | |
853 | } | |
854 | break; | |
855 | } | |
856 | case CKK_RSA: | |
857 | { | |
858 | chunk_t n; | |
859 | if (p11->get_ck_attribute(p11, session, object, CKA_MODULUS, | |
860 | &n) && n.len > 0) | |
861 | { | |
862 | keylen = n.len * 8; | |
863 | chunk_free(&n); | |
864 | key_type = KEY_RSA; | |
865 | found = TRUE; | |
866 | } | |
867 | break; | |
868 | } | |
869 | default: | |
870 | DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type); | |
871 | break; | |
872 | } | |
873 | } | |
874 | enumerator->destroy(enumerator); | |
875 | ||
876 | if (found) | |
877 | { | |
878 | return create(key_type, keylen, p11, slot, session, object); | |
879 | } | |
880 | p11->f->C_CloseSession(session); | |
881 | return NULL; | |
882 | } | |
883 | ||
884 | /** | |
885 | * Find a public key on the given token with a specific keyid. | |
886 | * | |
887 | * Used by pkcs11_private_key_t. | |
888 | * | |
889 | * TODO: if no public key is found, we should perhaps search for a certificate | |
890 | * with the given keyid and extract the key from there | |
891 | * | |
892 | * @param p11 PKCS#11 module | |
893 | * @param slot slot id | |
894 | * @param type type of the key | |
895 | * @param keyid key id | |
896 | */ | |
897 | pkcs11_public_key_t *pkcs11_public_key_connect(pkcs11_library_t *p11, | |
898 | int slot, key_type_t type, chunk_t keyid) | |
899 | { | |
900 | private_pkcs11_public_key_t *this; | |
901 | ||
902 | this = find_key_by_keyid(p11, slot, type, keyid); | |
903 | if (!this) | |
904 | { | |
905 | return NULL; | |
906 | } | |
907 | return &this->public; | |
908 | } |