affd8cafc3268048d704b21e6b4e6232497e47ad
[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, verify, bool,
85 private_pkcs11_public_key_t *this, signature_scheme_t scheme,
86 chunk_t data, chunk_t sig)
87 {
88 CK_MECHANISM_PTR mechanism;
89 CK_RV rv;
90
91 mechanism = pkcs11_scheme_to_mechanism(scheme);
92 if (!mechanism)
93 {
94 DBG1(DBG_LIB, "signature scheme %N not supported",
95 signature_scheme_names, scheme);
96 return FALSE;
97 }
98 if (sig.len && sig.ptr[0] == 0)
99 { /* trim leading zero byte in sig */
100 sig = chunk_skip(sig, 1);
101 }
102 this->mutex->lock(this->mutex);
103 rv = this->lib->f->C_VerifyInit(this->session, mechanism, this->object);
104 if (rv != CKR_OK)
105 {
106 this->mutex->unlock(this->mutex);
107 DBG1(DBG_LIB, "C_VerifyInit() failed: %N", ck_rv_names, rv);
108 return FALSE;
109 }
110 rv = this->lib->f->C_Verify(this->session, data.ptr, data.len,
111 sig.ptr, sig.len);
112 this->mutex->unlock(this->mutex);
113 if (rv != CKR_OK)
114 {
115 DBG1(DBG_LIB, "C_Verify() failed: %N", ck_rv_names, rv);
116 return FALSE;
117 }
118 return TRUE;
119 }
120
121 METHOD(public_key_t, encrypt, bool,
122 private_pkcs11_public_key_t *this, encryption_scheme_t scheme,
123 chunk_t plain, chunk_t *crypto)
124 {
125 return FALSE;
126 }
127
128 METHOD(public_key_t, get_keysize, int,
129 private_pkcs11_public_key_t *this)
130 {
131 return this->k * 8;
132 }
133
134 /**
135 * Encode RSA key using a given encoding type
136 */
137 static bool encode_rsa(private_pkcs11_public_key_t *this,
138 cred_encoding_type_t type, void *cache, chunk_t *encoding)
139 {
140 CK_RV rv;
141 bool success = FALSE;
142 chunk_t n, e;
143 CK_ATTRIBUTE attr[] = {
144 {CKA_MODULUS, NULL, 0},
145 {CKA_PUBLIC_EXPONENT, NULL, 0},
146 };
147
148 rv = this->lib->f->C_GetAttributeValue(this->session, this->object,
149 attr, countof(attr));
150 if (rv != CKR_OK ||
151 attr[0].ulValueLen == 0 || attr[0].ulValueLen == -1 ||
152 attr[1].ulValueLen == 0 || attr[1].ulValueLen == -1)
153 {
154 return FALSE;
155 }
156 attr[0].pValue = malloc(attr[0].ulValueLen);
157 attr[1].pValue = malloc(attr[1].ulValueLen);
158 rv = this->lib->f->C_GetAttributeValue(this->session, this->object,
159 attr, countof(attr));
160 if (rv == CKR_OK)
161 {
162 n = chunk_create(attr[0].pValue, attr[0].ulValueLen);
163 e = chunk_create(attr[1].pValue, attr[1].ulValueLen);
164 success = lib->encoding->encode(lib->encoding, type, cache, encoding,
165 CRED_PART_RSA_MODULUS, n, CRED_PART_RSA_PUB_EXP, e, CRED_PART_END);
166 }
167 free(attr[0].pValue);
168 free(attr[1].pValue);
169 return success;
170 }
171
172 METHOD(public_key_t, get_encoding, bool,
173 private_pkcs11_public_key_t *this, cred_encoding_type_t type,
174 chunk_t *encoding)
175 {
176 switch (this->type)
177 {
178 case KEY_RSA:
179 return encode_rsa(this, type, NULL, encoding);
180 default:
181 return FALSE;
182 }
183 }
184
185 METHOD(public_key_t, get_fingerprint, bool,
186 private_pkcs11_public_key_t *this, cred_encoding_type_t type, chunk_t *fp)
187 {
188 if (lib->encoding->get_cache(lib->encoding, type, this, fp))
189 {
190 return TRUE;
191 }
192 switch (this->type)
193 {
194 case KEY_RSA:
195 return encode_rsa(this, type, this, fp);
196 default:
197 return FALSE;
198 }
199 }
200
201 METHOD(public_key_t, get_ref, public_key_t*,
202 private_pkcs11_public_key_t *this)
203 {
204 ref_get(&this->ref);
205 return &this->public.key;
206 }
207
208 METHOD(public_key_t, destroy, void,
209 private_pkcs11_public_key_t *this)
210 {
211 if (ref_put(&this->ref))
212 {
213 lib->encoding->clear_cache(lib->encoding, this);
214 this->lib->f->C_CloseSession(this->session);
215 this->mutex->destroy(this->mutex);
216 free(this);
217 }
218 }
219
220 /**
221 * Create an empty PKCS#11 public key
222 */
223 static private_pkcs11_public_key_t *create(key_type_t type, size_t k,
224 pkcs11_library_t *p11, CK_SLOT_ID slot,
225 CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object)
226 {
227 private_pkcs11_public_key_t *this;
228
229 INIT(this,
230 .public = {
231 .key = {
232 .get_type = _get_type,
233 .verify = _verify,
234 .encrypt = _encrypt,
235 .equals = public_key_equals,
236 .get_keysize = _get_keysize,
237 .get_fingerprint = _get_fingerprint,
238 .has_fingerprint = public_key_has_fingerprint,
239 .get_encoding = _get_encoding,
240 .get_ref = _get_ref,
241 .destroy = _destroy,
242 },
243 },
244 .type = type,
245 .k = k,
246 .lib = p11,
247 .slot = slot,
248 .session = session,
249 .object = object,
250 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
251 .ref = 1,
252 );
253
254 return this;
255 }
256
257 /**
258 * Find a key object, including PKCS11 library and slot
259 */
260 static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e)
261 {
262 private_pkcs11_public_key_t *this = NULL;
263 pkcs11_manager_t *manager;
264 enumerator_t *enumerator, *keys;
265 pkcs11_library_t *p11;
266 CK_SLOT_ID slot;
267
268 manager = pkcs11_manager_get();
269 if (!manager)
270 {
271 return NULL;
272 }
273
274 enumerator = manager->create_token_enumerator(manager);
275 while (enumerator->enumerate(enumerator, &p11, &slot))
276 {
277 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
278 CK_KEY_TYPE type = CKK_RSA;
279 CK_ATTRIBUTE tmpl[] = {
280 {CKA_CLASS, &class, sizeof(class)},
281 {CKA_KEY_TYPE, &type, sizeof(type)},
282 {CKA_MODULUS, n.ptr, n.len},
283 {CKA_PUBLIC_EXPONENT, e.ptr, e.len},
284 };
285 CK_OBJECT_HANDLE object;
286 CK_SESSION_HANDLE session;
287 CK_RV rv;
288
289 rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL,
290 &session);
291 if (rv != CKR_OK)
292 {
293 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
294 continue;
295 }
296 keys = p11->create_object_enumerator(p11, session,
297 tmpl, countof(tmpl), NULL, 0);
298 if (keys->enumerate(keys, &object))
299 {
300 this = create(KEY_RSA, n.len, p11, slot, session, object);
301 keys->destroy(keys);
302 break;
303 }
304 keys->destroy(keys);
305 p11->f->C_CloseSession(session);
306 }
307 enumerator->destroy(enumerator);
308 return this;
309 }
310
311 /**
312 * Create a key object in a suitable token session
313 */
314 static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e)
315 {
316 private_pkcs11_public_key_t *this = NULL;
317 pkcs11_manager_t *manager;
318 enumerator_t *enumerator, *mechs;
319 pkcs11_library_t *p11;
320 CK_SLOT_ID slot;
321
322 manager = pkcs11_manager_get();
323 if (!manager)
324 {
325 return NULL;
326 }
327
328 enumerator = manager->create_token_enumerator(manager);
329 while (enumerator->enumerate(enumerator, &p11, &slot))
330 {
331 CK_MECHANISM_TYPE mech;
332 CK_MECHANISM_INFO info;
333 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
334 CK_KEY_TYPE type = CKK_RSA;
335 CK_ATTRIBUTE tmpl[] = {
336 {CKA_CLASS, &class, sizeof(class)},
337 {CKA_KEY_TYPE, &type, sizeof(type)},
338 {CKA_MODULUS, n.ptr, n.len},
339 {CKA_PUBLIC_EXPONENT, e.ptr, e.len}
340 };
341 CK_OBJECT_HANDLE object;
342 CK_SESSION_HANDLE session;
343 CK_RV rv;
344
345 mechs = p11->create_mechanism_enumerator(p11, slot);
346 while (mechs->enumerate(mechs, &mech, &info))
347 {
348 if (!(info.flags & CKF_VERIFY))
349 {
350 continue;
351 }
352 switch (mech)
353 {
354 case CKM_RSA_PKCS:
355 case CKM_SHA1_RSA_PKCS:
356 case CKM_SHA256_RSA_PKCS:
357 case CKM_SHA384_RSA_PKCS:
358 case CKM_SHA512_RSA_PKCS:
359 case CKM_MD5_RSA_PKCS:
360 break;
361 default:
362 continue;
363 }
364 rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL,
365 &session);
366 if (rv != CKR_OK)
367 {
368 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
369 ck_rv_names, rv);
370 continue;
371 }
372 rv = p11->f->C_CreateObject(session, tmpl, countof(tmpl), &object);
373 if (rv == CKR_OK)
374 {
375 this = create(KEY_RSA, n.len, p11, slot, session, object);
376 DBG2(DBG_CFG, "created RSA public key on token '%s':%d ",
377 p11->get_name(p11), slot);
378 break;
379 }
380 else
381 {
382 DBG1(DBG_CFG, "creating RSA public key on token '%s':%d "
383 "failed: %N", p11->get_name(p11), slot, ck_rv_names, rv);
384 p11->f->C_CloseSession(session);
385 }
386 }
387 mechs->destroy(mechs);
388 if (this)
389 {
390 break;
391 }
392 }
393 enumerator->destroy(enumerator);
394 return this;
395 }
396
397 /**
398 * See header
399 */
400 pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args)
401 {
402 private_pkcs11_public_key_t *this;
403 chunk_t n, e;
404
405 n = e = chunk_empty;
406 while (TRUE)
407 {
408 switch (va_arg(args, builder_part_t))
409 {
410 case BUILD_RSA_MODULUS:
411 n = va_arg(args, chunk_t);
412 continue;
413 case BUILD_RSA_PUB_EXP:
414 e = va_arg(args, chunk_t);
415 continue;
416 case BUILD_END:
417 break;
418 default:
419 return NULL;
420 }
421 break;
422 }
423 if (type == KEY_RSA && e.ptr && n.ptr)
424 {
425 if (n.len && n.ptr[0] == 0)
426 { /* trim leading zero byte in modulus */
427 n = chunk_skip(n, 1);
428 }
429 this = find_rsa_key(n, e);
430 if (this)
431 {
432 return &this->public;
433 }
434 this = create_rsa_key(n, e);
435 if (this)
436 {
437 return &this->public;
438 }
439 }
440 return NULL;
441 }
442