23b63d2386af19a83ef64e7a02451b279f6b4c55
[strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_dh.c
1 /*
2 * Copyright (C) 2011 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
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_dh.h"
17
18 #include <utils/debug.h>
19 #include <library.h>
20 #include <asn1/asn1.h>
21 #include <asn1/oid.h>
22
23 #include "pkcs11_manager.h"
24
25 typedef struct private_pkcs11_dh_t private_pkcs11_dh_t;
26
27 /**
28 * Private data of an pkcs11_dh_t object.
29 */
30 struct private_pkcs11_dh_t {
31
32 /**
33 * Public pkcs11_dh_t interface
34 */
35 pkcs11_dh_t public;
36
37 /**
38 * PKCS#11 library
39 */
40 pkcs11_library_t *lib;
41
42 /**
43 * Session handle for this objct
44 */
45 CK_SESSION_HANDLE session;
46
47 /**
48 * Diffie Hellman group number.
49 */
50 diffie_hellman_group_t group;
51
52 /**
53 * Handle for own private value
54 */
55 CK_OBJECT_HANDLE pri_key;
56
57 /**
58 * Own public value
59 */
60 chunk_t pub_key;
61
62 /**
63 * Shared secret
64 */
65 chunk_t secret;
66
67 /**
68 * Mechanism to use to generate a key pair
69 */
70 CK_MECHANISM_TYPE mech_key;
71
72 /**
73 * Mechanism to use to derive a shared secret
74 */
75 CK_MECHANISM_TYPE mech_derive;
76
77 };
78
79 /**
80 * Derive a DH/ECDH shared secret.
81 *
82 * If this succeeds the shared secret is stored in this->secret.
83 */
84 static void derive_secret(private_pkcs11_dh_t *this, chunk_t other)
85 {
86 CK_OBJECT_CLASS klass = CKO_SECRET_KEY;
87 CK_KEY_TYPE type = CKK_GENERIC_SECRET;
88 CK_ATTRIBUTE attr[] = {
89 { CKA_CLASS, &klass, sizeof(klass) },
90 { CKA_KEY_TYPE, &type, sizeof(type) },
91 };
92 CK_MECHANISM mech = {
93 this->mech_derive,
94 other.ptr,
95 other.len,
96 };
97 CK_OBJECT_HANDLE secret;
98 CK_RV rv;
99
100 rv = this->lib->f->C_DeriveKey(this->session, &mech, this->pri_key,
101 attr, countof(attr), &secret);
102 if (rv != CKR_OK)
103 {
104 DBG1(DBG_CFG, "C_DeriveKey() error: %N", ck_rv_names, rv);
105 return;
106 }
107 if (!this->lib->get_ck_attribute(this->lib, this->session, secret,
108 CKA_VALUE, &this->secret))
109 {
110 chunk_free(&this->secret);
111 return;
112 }
113 }
114
115 METHOD(diffie_hellman_t, set_other_public_value, void,
116 private_pkcs11_dh_t *this, chunk_t value)
117 {
118 switch (this->group)
119 {
120 case ECP_192_BIT:
121 case ECP_224_BIT:
122 case ECP_256_BIT:
123 case ECP_384_BIT:
124 case ECP_521_BIT:
125 { /* we expect the public value to just be the concatenated x and y
126 * coordinates, so we tag the value as an uncompressed ECPoint */
127 chunk_t tag = chunk_from_chars(0x04);
128 chunk_t pubkey = chunk_cata("cc", tag, value);
129 CK_ECDH1_DERIVE_PARAMS params = {
130 CKD_NULL,
131 0,
132 NULL,
133 pubkey.len,
134 pubkey.ptr,
135 };
136
137 if (!lib->settings->get_bool(lib->settings,
138 "%s.ecp_x_coordinate_only", TRUE, lib->ns))
139 { /* we only get the x coordinate back */
140 return;
141 }
142 value = chunk_from_thing(params);
143 break;
144 }
145 default:
146 break;
147 }
148 derive_secret(this, value);
149 }
150
151 METHOD(diffie_hellman_t, get_my_public_value, void,
152 private_pkcs11_dh_t *this, chunk_t *value)
153 {
154 *value = chunk_clone(this->pub_key);
155 }
156
157 METHOD(diffie_hellman_t, get_shared_secret, status_t,
158 private_pkcs11_dh_t *this, chunk_t *secret)
159 {
160 if (!this->secret.ptr)
161 {
162 return FAILED;
163 }
164 *secret = chunk_clone(this->secret);
165 return SUCCESS;
166 }
167
168 METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
169 private_pkcs11_dh_t *this)
170 {
171 return this->group;
172 }
173
174 METHOD(diffie_hellman_t, destroy, void,
175 private_pkcs11_dh_t *this)
176 {
177 this->lib->f->C_CloseSession(this->session);
178 chunk_clear(&this->pub_key);
179 chunk_clear(&this->secret);
180 free(this);
181 }
182
183 /**
184 * Generate a DH/ECDH key pair.
185 *
186 * If this succeeds, this->pri_key has a handle to the private key and
187 * this->pub_key stores the public key.
188 */
189 static bool generate_key_pair(private_pkcs11_dh_t *this, CK_ATTRIBUTE_PTR pub,
190 int pub_len, CK_ATTRIBUTE_PTR pri, int pri_len,
191 CK_ATTRIBUTE_TYPE attr)
192 {
193 CK_MECHANISM mech = {
194 this->mech_key,
195 NULL,
196 0,
197 };
198 CK_OBJECT_HANDLE pub_key;
199 CK_RV rv;
200
201 rv = this->lib->f->C_GenerateKeyPair(this->session, &mech, pub, pub_len,
202 pri, pri_len, &pub_key, &this->pri_key);
203 if (rv != CKR_OK)
204 {
205 DBG1(DBG_CFG, "C_GenerateKeyPair() error: %N", ck_rv_names, rv);
206 return FALSE;
207 }
208 if (!this->lib->get_ck_attribute(this->lib, this->session, pub_key,
209 attr, &this->pub_key))
210 {
211 chunk_free(&this->pub_key);
212 return FALSE;
213 }
214 return TRUE;
215 }
216
217 /**
218 * Generate DH key pair.
219 */
220 static bool generate_key_pair_modp(private_pkcs11_dh_t *this, size_t exp_len,
221 chunk_t g, chunk_t p)
222 {
223 CK_BBOOL ck_true = CK_TRUE;
224 CK_ATTRIBUTE pub_attr[] = {
225 { CKA_DERIVE, &ck_true, sizeof(ck_true) },
226 { CKA_PRIME, p.ptr, p.len },
227 { CKA_BASE, g.ptr, g.len },
228 };
229 CK_ULONG bits = exp_len * 8;
230 CK_ATTRIBUTE pri_attr[] = {
231 { CKA_DERIVE, &ck_true, sizeof(ck_true) },
232 { CKA_VALUE_BITS, &bits, sizeof(bits) },
233 };
234 return generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr,
235 countof(pri_attr), CKA_VALUE);
236 }
237
238 /**
239 * Generate ECDH key pair.
240 */
241 static bool generate_key_pair_ecp(private_pkcs11_dh_t *this,
242 chunk_t ecparams)
243 {
244 CK_BBOOL ck_true = CK_TRUE;
245 CK_ATTRIBUTE pub_attr[] = {
246 { CKA_DERIVE, &ck_true, sizeof(ck_true) },
247 { CKA_EC_PARAMS, ecparams.ptr, ecparams.len },
248 };
249 CK_ATTRIBUTE pri_attr[] = {
250 { CKA_DERIVE, &ck_true, sizeof(ck_true) },
251 };
252 chunk_t pub_key;
253 if (!generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr,
254 countof(pri_attr), CKA_EC_POINT))
255 {
256 return FALSE;
257 }
258 if (this->pub_key.len <= 0 || this->pub_key.ptr[0] != 0x04)
259 { /* we currently only support the point in uncompressed form which
260 * looks like this: 0x04 || x || y */
261 chunk_clear(&this->pub_key);
262 return FALSE;
263 }
264 pub_key = chunk_clone(chunk_skip(this->pub_key, 1));
265 chunk_clear(&this->pub_key);
266 this->pub_key = pub_key;
267 return TRUE;
268 }
269
270 /**
271 * Find a token we can use for DH/ECDH algorithm
272 */
273 static pkcs11_library_t *find_token(private_pkcs11_dh_t *this,
274 CK_SESSION_HANDLE *session)
275 {
276 enumerator_t *tokens, *mechs;
277 pkcs11_manager_t *manager;
278 pkcs11_library_t *current, *found = NULL;
279 CK_MECHANISM_TYPE type;
280 CK_SLOT_ID slot;
281
282 manager = lib->get(lib, "pkcs11-manager");
283 if (!manager)
284 {
285 return NULL;
286 }
287 tokens = manager->create_token_enumerator(manager);
288 while (tokens->enumerate(tokens, &current, &slot))
289 {
290 mechs = current->create_mechanism_enumerator(current, slot);
291 while (mechs->enumerate(mechs, &type, NULL))
292 { /* we assume we can generate key pairs if the derive mechanism
293 * is supported */
294 if (type == this->mech_derive)
295 {
296 if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
297 NULL, NULL, session) == CKR_OK)
298 {
299 found = current;
300 break;
301 }
302 }
303 }
304 mechs->destroy(mechs);
305 if (found)
306 {
307 break;
308 }
309 }
310 tokens->destroy(tokens);
311 return found;
312 }
313
314 /**
315 * Generic internal constructor
316 */
317 static private_pkcs11_dh_t *create_generic(diffie_hellman_group_t group,
318 CK_MECHANISM_TYPE key,
319 CK_MECHANISM_TYPE derive)
320 {
321 private_pkcs11_dh_t *this;
322
323 INIT(this,
324 .public = {
325 .dh = {
326 .get_shared_secret = _get_shared_secret,
327 .set_other_public_value = _set_other_public_value,
328 .get_my_public_value = _get_my_public_value,
329 .get_dh_group = _get_dh_group,
330 .destroy = _destroy,
331 },
332 },
333 .group = group,
334 .mech_key = key,
335 .mech_derive = derive,
336 );
337
338 this->lib = find_token(this, &this->session);
339 if (!this->lib)
340 {
341 free(this);
342 return NULL;
343 }
344 return this;
345 }
346
347 static pkcs11_dh_t *create_ecp(diffie_hellman_group_t group, chunk_t ecparam)
348 {
349 private_pkcs11_dh_t *this = create_generic(group, CKM_EC_KEY_PAIR_GEN,
350 CKM_ECDH1_DERIVE);
351
352 if (this)
353 {
354 if (generate_key_pair_ecp(this, ecparam))
355 {
356 chunk_free(&ecparam);
357 return &this->public;
358 }
359 chunk_free(&ecparam);
360 free(this);
361 }
362 return NULL;
363 }
364
365 /**
366 * Constructor for MODP DH
367 */
368 static pkcs11_dh_t *create_modp(diffie_hellman_group_t group, size_t exp_len,
369 chunk_t g, chunk_t p)
370 {
371 private_pkcs11_dh_t *this = create_generic(group, CKM_DH_PKCS_KEY_PAIR_GEN,
372 CKM_DH_PKCS_DERIVE);
373
374 if (this)
375 {
376 if (generate_key_pair_modp(this, exp_len, g, p))
377 {
378 return &this->public;
379 }
380 free(this);
381 }
382 return NULL;
383 }
384
385 /**
386 * Lookup the EC params for the given group.
387 */
388 static chunk_t ecparams_lookup(diffie_hellman_group_t group)
389 {
390 switch (group)
391 {
392 case ECP_192_BIT:
393 return asn1_build_known_oid(OID_PRIME192V1);
394 case ECP_224_BIT:
395 return asn1_build_known_oid(OID_SECT224R1);
396 case ECP_256_BIT:
397 return asn1_build_known_oid(OID_PRIME256V1);
398 case ECP_384_BIT:
399 return asn1_build_known_oid(OID_SECT384R1);
400 case ECP_521_BIT:
401 return asn1_build_known_oid(OID_SECT521R1);
402 default:
403 break;
404 }
405 return chunk_empty;
406 }
407
408 /**
409 * Described in header.
410 */
411 pkcs11_dh_t *pkcs11_dh_create(diffie_hellman_group_t group,
412 chunk_t g, chunk_t p)
413 {
414 switch (group)
415 {
416 case MODP_CUSTOM:
417 {
418 return create_modp(group, p.len, g, p);
419 }
420 case ECP_192_BIT:
421 case ECP_224_BIT:
422 case ECP_256_BIT:
423 case ECP_384_BIT:
424 case ECP_521_BIT:
425 {
426 chunk_t params = ecparams_lookup(group);
427 if (params.ptr)
428 {
429 return create_ecp(group, params);
430 }
431 break;
432 }
433 default:
434 {
435 diffie_hellman_params_t *params = diffie_hellman_get_params(group);
436 if (params)
437 {
438 return create_modp(group, params->exp_len, params->generator,
439 params->prime);
440 }
441 break;
442 }
443 }
444 return NULL;
445 }
446