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