f3fc0303dcc6212233f6fd0cb9d339be24922096
[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 <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 u_int16_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 "libstrongswan.ecp_x_coordinate_only", TRUE))
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 if (!generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr,
253 countof(pri_attr), CKA_EC_POINT))
254 {
255 return FALSE;
256 }
257 if (this->pub_key.len <= 0 || this->pub_key.ptr[0] != 0x04)
258 { /* we currently only support the point in uncompressed form which
259 * looks like this: 0x04 || x || y */
260 chunk_clear(&this->pub_key);
261 return FALSE;
262 }
263 this->pub_key = chunk_skip(this->pub_key, 1);
264 return TRUE;
265 }
266
267 /**
268 * Find a token we can use for DH/ECDH algorithm
269 */
270 static pkcs11_library_t *find_token(private_pkcs11_dh_t *this,
271 CK_SESSION_HANDLE *session)
272 {
273 enumerator_t *tokens, *mechs;
274 pkcs11_manager_t *manager;
275 pkcs11_library_t *current, *found = NULL;
276 CK_MECHANISM_TYPE type;
277 CK_SLOT_ID slot;
278
279 manager = lib->get(lib, "pkcs11-manager");
280 if (!manager)
281 {
282 return NULL;
283 }
284 tokens = manager->create_token_enumerator(manager);
285 while (tokens->enumerate(tokens, &current, &slot))
286 {
287 mechs = current->create_mechanism_enumerator(current, slot);
288 while (mechs->enumerate(mechs, &type, NULL))
289 { /* we assume we can generate key pairs if the derive mechanism
290 * is supported */
291 if (type == this->mech_derive)
292 {
293 if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
294 NULL, NULL, session) == CKR_OK)
295 {
296 found = current;
297 break;
298 }
299 }
300 }
301 mechs->destroy(mechs);
302 if (found)
303 {
304 break;
305 }
306 }
307 tokens->destroy(tokens);
308 return found;
309 }
310
311 /**
312 * Generic internal constructor
313 */
314 static private_pkcs11_dh_t *create_generic(diffie_hellman_group_t group,
315 CK_MECHANISM_TYPE key,
316 CK_MECHANISM_TYPE derive)
317 {
318 private_pkcs11_dh_t *this;
319
320 INIT(this,
321 .public = {
322 .dh = {
323 .get_shared_secret = _get_shared_secret,
324 .set_other_public_value = _set_other_public_value,
325 .get_my_public_value = _get_my_public_value,
326 .get_dh_group = _get_dh_group,
327 .destroy = _destroy,
328 },
329 },
330 .group = group,
331 .mech_key = key,
332 .mech_derive = derive,
333 );
334
335 this->lib = find_token(this, &this->session);
336 if (!this->lib)
337 {
338 free(this);
339 return NULL;
340 }
341 return this;
342 }
343
344 static pkcs11_dh_t *create_ecp(diffie_hellman_group_t group, chunk_t ecparam)
345 {
346 private_pkcs11_dh_t *this = create_generic(group, CKM_EC_KEY_PAIR_GEN,
347 CKM_ECDH1_DERIVE);
348
349 if (this)
350 {
351 if (generate_key_pair_ecp(this, ecparam))
352 {
353 return &this->public;
354 }
355 free(this);
356 }
357 return NULL;
358 }
359
360 /**
361 * Constructor for MODP DH
362 */
363 static pkcs11_dh_t *create_modp(diffie_hellman_group_t group, size_t exp_len,
364 chunk_t g, chunk_t p)
365 {
366 private_pkcs11_dh_t *this = create_generic(group, CKM_DH_PKCS_KEY_PAIR_GEN,
367 CKM_DH_PKCS_DERIVE);
368
369 if (this)
370 {
371 if (generate_key_pair_modp(this, exp_len, g, p))
372 {
373 return &this->public;
374 }
375 free(this);
376 }
377 return NULL;
378 }
379
380 /**
381 * Lookup the EC params for the given group.
382 */
383 static chunk_t ecparams_lookup(diffie_hellman_group_t group)
384 {
385 switch (group)
386 {
387 case ECP_192_BIT:
388 return asn1_build_known_oid(OID_PRIME192V1);
389 case ECP_224_BIT:
390 return asn1_build_known_oid(OID_SECT224R1);
391 case ECP_256_BIT:
392 return asn1_build_known_oid(OID_PRIME256V1);
393 case ECP_384_BIT:
394 return asn1_build_known_oid(OID_SECT384R1);
395 case ECP_521_BIT:
396 return asn1_build_known_oid(OID_SECT521R1);
397 default:
398 break;
399 }
400 return chunk_empty;
401 }
402
403 /**
404 * Described in header.
405 */
406 pkcs11_dh_t *pkcs11_dh_create(diffie_hellman_group_t group,
407 chunk_t g, chunk_t p)
408 {
409 switch (group)
410 {
411 case MODP_CUSTOM:
412 {
413 return create_modp(group, p.len, g, p);
414 }
415 case ECP_192_BIT:
416 case ECP_224_BIT:
417 case ECP_256_BIT:
418 case ECP_384_BIT:
419 case ECP_521_BIT:
420 {
421 chunk_t params = ecparams_lookup(group);
422 if (params.ptr)
423 {
424 return create_ecp(group, params);
425 }
426 break;
427 }
428 default:
429 {
430 diffie_hellman_params_t *params = diffie_hellman_get_params(group);
431 if (params)
432 {
433 return create_modp(group, params->exp_len, params->generator,
434 params->prime);
435 }
436 break;
437 }
438 }
439 return NULL;
440 }
441