Implemented public key encryption/private key decryption in PKCS#11
[strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_public_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_public_key.h"
17
18 #include "pkcs11.h"
19 #include "pkcs11_private_key.h"
20 #include "pkcs11_manager.h"
21
22 #include <debug.h>
23 #include <threading/mutex.h>
24
25 typedef struct private_pkcs11_public_key_t private_pkcs11_public_key_t;
26
27 /**
28 * Private data of an pkcs11_public_key_t object.
29 */
30 struct private_pkcs11_public_key_t {
31
32 /**
33 * Public pkcs11_public_key_t interface.
34 */
35 pkcs11_public_key_t public;
36
37 /**
38 * Type of the key
39 */
40 key_type_t type;
41
42 /**
43 * Key size in bytes
44 */
45 size_t k;
46
47 /**
48 * PKCS#11 library this key uses
49 */
50 pkcs11_library_t *lib;
51
52 /**
53 * Slot the token is in
54 */
55 CK_SLOT_ID slot;
56
57 /**
58 * Session we use
59 */
60 CK_SESSION_HANDLE session;
61
62 /**
63 * Object handle to the key
64 */
65 CK_OBJECT_HANDLE object;
66
67 /**
68 * Mutex to lock session
69 */
70 mutex_t *mutex;
71
72 /**
73 * References to this key
74 */
75 refcount_t ref;
76 };
77
78 METHOD(public_key_t, get_type, key_type_t,
79 private_pkcs11_public_key_t *this)
80 {
81 return this->type;
82 }
83
84 METHOD(public_key_t, get_keysize, int,
85 private_pkcs11_public_key_t *this)
86 {
87 return this->k * 8;
88 }
89
90 METHOD(public_key_t, verify, bool,
91 private_pkcs11_public_key_t *this, signature_scheme_t scheme,
92 chunk_t data, chunk_t sig)
93 {
94 CK_MECHANISM_PTR mechanism;
95 CK_RV rv;
96
97 mechanism = pkcs11_signature_scheme_to_mech(scheme);
98 if (!mechanism)
99 {
100 DBG1(DBG_LIB, "signature scheme %N not supported",
101 signature_scheme_names, scheme);
102 return FALSE;
103 }
104 if (sig.len && sig.ptr[0] == 0)
105 { /* trim leading zero byte in sig */
106 sig = chunk_skip(sig, 1);
107 }
108 this->mutex->lock(this->mutex);
109 rv = this->lib->f->C_VerifyInit(this->session, mechanism, this->object);
110 if (rv != CKR_OK)
111 {
112 this->mutex->unlock(this->mutex);
113 DBG1(DBG_LIB, "C_VerifyInit() failed: %N", ck_rv_names, rv);
114 return FALSE;
115 }
116 rv = this->lib->f->C_Verify(this->session, data.ptr, data.len,
117 sig.ptr, sig.len);
118 this->mutex->unlock(this->mutex);
119 if (rv != CKR_OK)
120 {
121 DBG1(DBG_LIB, "C_Verify() failed: %N", ck_rv_names, rv);
122 return FALSE;
123 }
124 return TRUE;
125 }
126
127 METHOD(public_key_t, encrypt, bool,
128 private_pkcs11_public_key_t *this, encryption_scheme_t scheme,
129 chunk_t plain, chunk_t *crypt)
130 {
131 CK_MECHANISM_PTR mechanism;
132 CK_BYTE_PTR buf;
133 CK_ULONG len;
134 CK_RV rv;
135
136 mechanism = pkcs11_encryption_scheme_to_mech(scheme);
137 if (!mechanism)
138 {
139 DBG1(DBG_LIB, "encryption scheme %N not supported",
140 encryption_scheme_names, scheme);
141 return FALSE;
142 }
143 this->mutex->lock(this->mutex);
144 rv = this->lib->f->C_EncryptInit(this->session, mechanism, this->object);
145 if (rv != CKR_OK)
146 {
147 this->mutex->unlock(this->mutex);
148 DBG1(DBG_LIB, "C_EncryptInit() failed: %N", ck_rv_names, rv);
149 return FALSE;
150 }
151 len = (get_keysize(this) + 7) / 8;
152 buf = malloc(len);
153 rv = this->lib->f->C_Encrypt(this->session, plain.ptr, plain.len, buf, &len);
154 this->mutex->unlock(this->mutex);
155 if (rv != CKR_OK)
156 {
157 DBG1(DBG_LIB, "C_Encrypt() failed: %N", ck_rv_names, rv);
158 free(buf);
159 return FALSE;
160 }
161 *crypt = chunk_create(buf, len);
162 return TRUE;
163 }
164
165 /**
166 * Encode RSA key using a given encoding type
167 */
168 static bool encode_rsa(private_pkcs11_public_key_t *this,
169 cred_encoding_type_t type, void *cache, chunk_t *encoding)
170 {
171 CK_RV rv;
172 bool success = FALSE;
173 chunk_t n, e;
174 CK_ATTRIBUTE attr[] = {
175 {CKA_MODULUS, NULL, 0},
176 {CKA_PUBLIC_EXPONENT, NULL, 0},
177 };
178
179 rv = this->lib->f->C_GetAttributeValue(this->session, this->object,
180 attr, countof(attr));
181 if (rv != CKR_OK ||
182 attr[0].ulValueLen == 0 || attr[0].ulValueLen == -1 ||
183 attr[1].ulValueLen == 0 || attr[1].ulValueLen == -1)
184 {
185 return FALSE;
186 }
187 attr[0].pValue = malloc(attr[0].ulValueLen);
188 attr[1].pValue = malloc(attr[1].ulValueLen);
189 rv = this->lib->f->C_GetAttributeValue(this->session, this->object,
190 attr, countof(attr));
191 if (rv == CKR_OK)
192 {
193 n = chunk_create(attr[0].pValue, attr[0].ulValueLen);
194 e = chunk_create(attr[1].pValue, attr[1].ulValueLen);
195 success = lib->encoding->encode(lib->encoding, type, cache, encoding,
196 CRED_PART_RSA_MODULUS, n, CRED_PART_RSA_PUB_EXP, e, CRED_PART_END);
197 }
198 free(attr[0].pValue);
199 free(attr[1].pValue);
200 return success;
201 }
202
203 METHOD(public_key_t, get_encoding, bool,
204 private_pkcs11_public_key_t *this, cred_encoding_type_t type,
205 chunk_t *encoding)
206 {
207 switch (this->type)
208 {
209 case KEY_RSA:
210 return encode_rsa(this, type, NULL, encoding);
211 default:
212 return FALSE;
213 }
214 }
215
216 METHOD(public_key_t, get_fingerprint, bool,
217 private_pkcs11_public_key_t *this, cred_encoding_type_t type, chunk_t *fp)
218 {
219 if (lib->encoding->get_cache(lib->encoding, type, this, fp))
220 {
221 return TRUE;
222 }
223 switch (this->type)
224 {
225 case KEY_RSA:
226 return encode_rsa(this, type, this, fp);
227 default:
228 return FALSE;
229 }
230 }
231
232 METHOD(public_key_t, get_ref, public_key_t*,
233 private_pkcs11_public_key_t *this)
234 {
235 ref_get(&this->ref);
236 return &this->public.key;
237 }
238
239 METHOD(public_key_t, destroy, void,
240 private_pkcs11_public_key_t *this)
241 {
242 if (ref_put(&this->ref))
243 {
244 lib->encoding->clear_cache(lib->encoding, this);
245 this->lib->f->C_CloseSession(this->session);
246 this->mutex->destroy(this->mutex);
247 free(this);
248 }
249 }
250
251 /**
252 * Create an empty PKCS#11 public key
253 */
254 static private_pkcs11_public_key_t *create(key_type_t type, size_t k,
255 pkcs11_library_t *p11, CK_SLOT_ID slot,
256 CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object)
257 {
258 private_pkcs11_public_key_t *this;
259
260 INIT(this,
261 .public = {
262 .key = {
263 .get_type = _get_type,
264 .verify = _verify,
265 .encrypt = _encrypt,
266 .equals = public_key_equals,
267 .get_keysize = _get_keysize,
268 .get_fingerprint = _get_fingerprint,
269 .has_fingerprint = public_key_has_fingerprint,
270 .get_encoding = _get_encoding,
271 .get_ref = _get_ref,
272 .destroy = _destroy,
273 },
274 },
275 .type = type,
276 .k = k,
277 .lib = p11,
278 .slot = slot,
279 .session = session,
280 .object = object,
281 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
282 .ref = 1,
283 );
284
285 return this;
286 }
287
288 /**
289 * Find a key object, including PKCS11 library and slot
290 */
291 static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e)
292 {
293 private_pkcs11_public_key_t *this = NULL;
294 pkcs11_manager_t *manager;
295 enumerator_t *enumerator, *keys;
296 pkcs11_library_t *p11;
297 CK_SLOT_ID slot;
298
299 manager = pkcs11_manager_get();
300 if (!manager)
301 {
302 return NULL;
303 }
304
305 enumerator = manager->create_token_enumerator(manager);
306 while (enumerator->enumerate(enumerator, &p11, &slot))
307 {
308 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
309 CK_KEY_TYPE type = CKK_RSA;
310 CK_ATTRIBUTE tmpl[] = {
311 {CKA_CLASS, &class, sizeof(class)},
312 {CKA_KEY_TYPE, &type, sizeof(type)},
313 {CKA_MODULUS, n.ptr, n.len},
314 {CKA_PUBLIC_EXPONENT, e.ptr, e.len},
315 };
316 CK_OBJECT_HANDLE object;
317 CK_SESSION_HANDLE session;
318 CK_RV rv;
319
320 rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL,
321 &session);
322 if (rv != CKR_OK)
323 {
324 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
325 continue;
326 }
327 keys = p11->create_object_enumerator(p11, session,
328 tmpl, countof(tmpl), NULL, 0);
329 if (keys->enumerate(keys, &object))
330 {
331 this = create(KEY_RSA, n.len, p11, slot, session, object);
332 keys->destroy(keys);
333 break;
334 }
335 keys->destroy(keys);
336 p11->f->C_CloseSession(session);
337 }
338 enumerator->destroy(enumerator);
339 return this;
340 }
341
342 /**
343 * Create a key object in a suitable token session
344 */
345 static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e)
346 {
347 private_pkcs11_public_key_t *this = NULL;
348 pkcs11_manager_t *manager;
349 enumerator_t *enumerator, *mechs;
350 pkcs11_library_t *p11;
351 CK_SLOT_ID slot;
352
353 manager = pkcs11_manager_get();
354 if (!manager)
355 {
356 return NULL;
357 }
358
359 enumerator = manager->create_token_enumerator(manager);
360 while (enumerator->enumerate(enumerator, &p11, &slot))
361 {
362 CK_MECHANISM_TYPE mech;
363 CK_MECHANISM_INFO info;
364 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
365 CK_KEY_TYPE type = CKK_RSA;
366 CK_ATTRIBUTE tmpl[] = {
367 {CKA_CLASS, &class, sizeof(class)},
368 {CKA_KEY_TYPE, &type, sizeof(type)},
369 {CKA_MODULUS, n.ptr, n.len},
370 {CKA_PUBLIC_EXPONENT, e.ptr, e.len}
371 };
372 CK_OBJECT_HANDLE object;
373 CK_SESSION_HANDLE session;
374 CK_RV rv;
375
376 mechs = p11->create_mechanism_enumerator(p11, slot);
377 while (mechs->enumerate(mechs, &mech, &info))
378 {
379 if (!(info.flags & CKF_VERIFY))
380 {
381 continue;
382 }
383 switch (mech)
384 {
385 case CKM_RSA_PKCS:
386 case CKM_SHA1_RSA_PKCS:
387 case CKM_SHA256_RSA_PKCS:
388 case CKM_SHA384_RSA_PKCS:
389 case CKM_SHA512_RSA_PKCS:
390 case CKM_MD5_RSA_PKCS:
391 break;
392 default:
393 continue;
394 }
395 rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL,
396 &session);
397 if (rv != CKR_OK)
398 {
399 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
400 ck_rv_names, rv);
401 continue;
402 }
403 rv = p11->f->C_CreateObject(session, tmpl, countof(tmpl), &object);
404 if (rv == CKR_OK)
405 {
406 this = create(KEY_RSA, n.len, p11, slot, session, object);
407 DBG2(DBG_CFG, "created RSA public key on token '%s':%d ",
408 p11->get_name(p11), slot);
409 break;
410 }
411 else
412 {
413 DBG1(DBG_CFG, "creating RSA public key on token '%s':%d "
414 "failed: %N", p11->get_name(p11), slot, ck_rv_names, rv);
415 p11->f->C_CloseSession(session);
416 }
417 }
418 mechs->destroy(mechs);
419 if (this)
420 {
421 break;
422 }
423 }
424 enumerator->destroy(enumerator);
425 return this;
426 }
427
428 /**
429 * See header
430 */
431 pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args)
432 {
433 private_pkcs11_public_key_t *this;
434 chunk_t n, e;
435
436 n = e = chunk_empty;
437 while (TRUE)
438 {
439 switch (va_arg(args, builder_part_t))
440 {
441 case BUILD_RSA_MODULUS:
442 n = va_arg(args, chunk_t);
443 continue;
444 case BUILD_RSA_PUB_EXP:
445 e = va_arg(args, chunk_t);
446 continue;
447 case BUILD_END:
448 break;
449 default:
450 return NULL;
451 }
452 break;
453 }
454 if (type == KEY_RSA && e.ptr && n.ptr)
455 {
456 if (n.len && n.ptr[0] == 0)
457 { /* trim leading zero byte in modulus */
458 n = chunk_skip(n, 1);
459 }
460 this = find_rsa_key(n, e);
461 if (this)
462 {
463 return &this->public;
464 }
465 this = create_rsa_key(n, e);
466 if (this)
467 {
468 return &this->public;
469 }
470 }
471 return NULL;
472 }
473