]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/plugins/pkcs11/pkcs11_dh.c
diffie-hellman: Add a bool return value to get_my_public_value()
[thirdparty/strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_dh.c
CommitLineData
1bb5d7c3
TB
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
f05b4272 18#include <utils/debug.h>
1bb5d7c3 19#include <library.h>
10b82be6
TB
20#include <asn1/asn1.h>
21#include <asn1/oid.h>
1bb5d7c3
TB
22
23#include "pkcs11_manager.h"
24
25typedef struct private_pkcs11_dh_t private_pkcs11_dh_t;
26
27/**
28 * Private data of an pkcs11_dh_t object.
29 */
30struct 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 */
e13ef5c4 50 diffie_hellman_group_t group;
1bb5d7c3
TB
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
10b82be6
TB
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
1bb5d7c3
TB
77};
78
10b82be6
TB
79/**
80 * Derive a DH/ECDH shared secret.
81 *
82 * If this succeeds the shared secret is stored in this->secret.
83 */
84static void derive_secret(private_pkcs11_dh_t *this, chunk_t other)
1bb5d7c3 85{
7c78a6e6
TB
86 CK_OBJECT_CLASS klass = CKO_SECRET_KEY;
87 CK_KEY_TYPE type = CKK_GENERIC_SECRET;
1bb5d7c3 88 CK_ATTRIBUTE attr[] = {
7c78a6e6
TB
89 { CKA_CLASS, &klass, sizeof(klass) },
90 { CKA_KEY_TYPE, &type, sizeof(type) },
1bb5d7c3
TB
91 };
92 CK_MECHANISM mech = {
10b82be6
TB
93 this->mech_derive,
94 other.ptr,
95 other.len,
1bb5d7c3
TB
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 }
cac68531
TB
107 if (!this->lib->get_ck_attribute(this->lib, this->session, secret,
108 CKA_VALUE, &this->secret))
1bb5d7c3 109 {
cac68531 110 chunk_free(&this->secret);
1bb5d7c3
TB
111 return;
112 }
113}
114
10b82be6
TB
115METHOD(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,
8dc6e716 138 "%s.ecp_x_coordinate_only", TRUE, lib->ns))
10b82be6
TB
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
42431690 151METHOD(diffie_hellman_t, get_my_public_value, bool,
1bb5d7c3
TB
152 private_pkcs11_dh_t *this, chunk_t *value)
153{
154 *value = chunk_clone(this->pub_key);
42431690 155 return TRUE;
1bb5d7c3
TB
156}
157
bace1d64 158METHOD(diffie_hellman_t, get_shared_secret, bool,
1bb5d7c3
TB
159 private_pkcs11_dh_t *this, chunk_t *secret)
160{
161 if (!this->secret.ptr)
162 {
bace1d64 163 return FALSE;
1bb5d7c3
TB
164 }
165 *secret = chunk_clone(this->secret);
bace1d64 166 return TRUE;
1bb5d7c3
TB
167}
168
169METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
170 private_pkcs11_dh_t *this)
171{
172 return this->group;
173}
174
175METHOD(diffie_hellman_t, destroy, void,
176 private_pkcs11_dh_t *this)
177{
178 this->lib->f->C_CloseSession(this->session);
179 chunk_clear(&this->pub_key);
180 chunk_clear(&this->secret);
181 free(this);
182}
183
184/**
10b82be6
TB
185 * Generate a DH/ECDH key pair.
186 *
187 * If this succeeds, this->pri_key has a handle to the private key and
188 * this->pub_key stores the public key.
1bb5d7c3 189 */
10b82be6
TB
190static bool generate_key_pair(private_pkcs11_dh_t *this, CK_ATTRIBUTE_PTR pub,
191 int pub_len, CK_ATTRIBUTE_PTR pri, int pri_len,
192 CK_ATTRIBUTE_TYPE attr)
1bb5d7c3 193{
1bb5d7c3 194 CK_MECHANISM mech = {
10b82be6 195 this->mech_key,
1bb5d7c3
TB
196 NULL,
197 0,
198 };
199 CK_OBJECT_HANDLE pub_key;
200 CK_RV rv;
201
10b82be6
TB
202 rv = this->lib->f->C_GenerateKeyPair(this->session, &mech, pub, pub_len,
203 pri, pri_len, &pub_key, &this->pri_key);
1bb5d7c3
TB
204 if (rv != CKR_OK)
205 {
206 DBG1(DBG_CFG, "C_GenerateKeyPair() error: %N", ck_rv_names, rv);
207 return FALSE;
208 }
cac68531 209 if (!this->lib->get_ck_attribute(this->lib, this->session, pub_key,
10b82be6 210 attr, &this->pub_key))
1bb5d7c3 211 {
cac68531 212 chunk_free(&this->pub_key);
1bb5d7c3
TB
213 return FALSE;
214 }
215 return TRUE;
216}
217
218/**
10b82be6
TB
219 * Generate DH key pair.
220 */
221static bool generate_key_pair_modp(private_pkcs11_dh_t *this, size_t exp_len,
222 chunk_t g, chunk_t p)
223{
cf9d45ea 224 CK_BBOOL ck_true = CK_TRUE;
10b82be6 225 CK_ATTRIBUTE pub_attr[] = {
cf9d45ea 226 { CKA_DERIVE, &ck_true, sizeof(ck_true) },
10b82be6
TB
227 { CKA_PRIME, p.ptr, p.len },
228 { CKA_BASE, g.ptr, g.len },
229 };
230 CK_ULONG bits = exp_len * 8;
231 CK_ATTRIBUTE pri_attr[] = {
cf9d45ea 232 { CKA_DERIVE, &ck_true, sizeof(ck_true) },
10b82be6
TB
233 { CKA_VALUE_BITS, &bits, sizeof(bits) },
234 };
235 return generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr,
236 countof(pri_attr), CKA_VALUE);
237}
238
239/**
240 * Generate ECDH key pair.
1bb5d7c3 241 */
10b82be6
TB
242static bool generate_key_pair_ecp(private_pkcs11_dh_t *this,
243 chunk_t ecparams)
244{
cf9d45ea 245 CK_BBOOL ck_true = CK_TRUE;
10b82be6 246 CK_ATTRIBUTE pub_attr[] = {
cf9d45ea 247 { CKA_DERIVE, &ck_true, sizeof(ck_true) },
10b82be6
TB
248 { CKA_EC_PARAMS, ecparams.ptr, ecparams.len },
249 };
cf9d45ea
TB
250 CK_ATTRIBUTE pri_attr[] = {
251 { CKA_DERIVE, &ck_true, sizeof(ck_true) },
252 };
43cd036a 253 chunk_t pub_key;
cf9d45ea
TB
254 if (!generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr,
255 countof(pri_attr), CKA_EC_POINT))
10b82be6
TB
256 {
257 return FALSE;
258 }
259 if (this->pub_key.len <= 0 || this->pub_key.ptr[0] != 0x04)
260 { /* we currently only support the point in uncompressed form which
261 * looks like this: 0x04 || x || y */
262 chunk_clear(&this->pub_key);
263 return FALSE;
264 }
43cd036a
TB
265 pub_key = chunk_clone(chunk_skip(this->pub_key, 1));
266 chunk_clear(&this->pub_key);
267 this->pub_key = pub_key;
10b82be6
TB
268 return TRUE;
269}
270
271/**
272 * Find a token we can use for DH/ECDH algorithm
273 */
274static pkcs11_library_t *find_token(private_pkcs11_dh_t *this,
275 CK_SESSION_HANDLE *session)
1bb5d7c3
TB
276{
277 enumerator_t *tokens, *mechs;
278 pkcs11_manager_t *manager;
279 pkcs11_library_t *current, *found = NULL;
280 CK_MECHANISM_TYPE type;
281 CK_SLOT_ID slot;
282
283 manager = lib->get(lib, "pkcs11-manager");
284 if (!manager)
285 {
286 return NULL;
287 }
288 tokens = manager->create_token_enumerator(manager);
289 while (tokens->enumerate(tokens, &current, &slot))
290 {
291 mechs = current->create_mechanism_enumerator(current, slot);
292 while (mechs->enumerate(mechs, &type, NULL))
10b82be6
TB
293 { /* we assume we can generate key pairs if the derive mechanism
294 * is supported */
295 if (type == this->mech_derive)
1bb5d7c3
TB
296 {
297 if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
298 NULL, NULL, session) == CKR_OK)
299 {
300 found = current;
301 break;
302 }
303 }
304 }
305 mechs->destroy(mechs);
306 if (found)
307 {
308 break;
309 }
310 }
311 tokens->destroy(tokens);
312 return found;
313}
314
10b82be6 315/**
1bb5d7c3
TB
316 * Generic internal constructor
317 */
10b82be6
TB
318static private_pkcs11_dh_t *create_generic(diffie_hellman_group_t group,
319 CK_MECHANISM_TYPE key,
320 CK_MECHANISM_TYPE derive)
1bb5d7c3
TB
321{
322 private_pkcs11_dh_t *this;
323
324 INIT(this,
325 .public = {
326 .dh = {
327 .get_shared_secret = _get_shared_secret,
328 .set_other_public_value = _set_other_public_value,
329 .get_my_public_value = _get_my_public_value,
330 .get_dh_group = _get_dh_group,
331 .destroy = _destroy,
332 },
333 },
334 .group = group,
10b82be6
TB
335 .mech_key = key,
336 .mech_derive = derive,
1bb5d7c3
TB
337 );
338
10b82be6 339 this->lib = find_token(this, &this->session);
1bb5d7c3
TB
340 if (!this->lib)
341 {
342 free(this);
343 return NULL;
344 }
10b82be6
TB
345 return this;
346}
1bb5d7c3 347
10b82be6
TB
348static pkcs11_dh_t *create_ecp(diffie_hellman_group_t group, chunk_t ecparam)
349{
350 private_pkcs11_dh_t *this = create_generic(group, CKM_EC_KEY_PAIR_GEN,
351 CKM_ECDH1_DERIVE);
352
353 if (this)
1bb5d7c3 354 {
10b82be6
TB
355 if (generate_key_pair_ecp(this, ecparam))
356 {
817d165c 357 chunk_free(&ecparam);
10b82be6
TB
358 return &this->public;
359 }
817d165c 360 chunk_free(&ecparam);
1bb5d7c3 361 free(this);
1bb5d7c3 362 }
10b82be6 363 return NULL;
1bb5d7c3
TB
364}
365
10b82be6
TB
366/**
367 * Constructor for MODP DH
1bb5d7c3 368 */
10b82be6
TB
369static pkcs11_dh_t *create_modp(diffie_hellman_group_t group, size_t exp_len,
370 chunk_t g, chunk_t p)
1bb5d7c3 371{
10b82be6
TB
372 private_pkcs11_dh_t *this = create_generic(group, CKM_DH_PKCS_KEY_PAIR_GEN,
373 CKM_DH_PKCS_DERIVE);
374
375 if (this)
376 {
377 if (generate_key_pair_modp(this, exp_len, g, p))
378 {
379 return &this->public;
380 }
381 free(this);
382 }
383 return NULL;
384}
1bb5d7c3 385
10b82be6
TB
386/**
387 * Lookup the EC params for the given group.
388 */
389static chunk_t ecparams_lookup(diffie_hellman_group_t group)
390{
391 switch (group)
b730fd6f 392 {
10b82be6
TB
393 case ECP_192_BIT:
394 return asn1_build_known_oid(OID_PRIME192V1);
395 case ECP_224_BIT:
396 return asn1_build_known_oid(OID_SECT224R1);
397 case ECP_256_BIT:
398 return asn1_build_known_oid(OID_PRIME256V1);
399 case ECP_384_BIT:
400 return asn1_build_known_oid(OID_SECT384R1);
401 case ECP_521_BIT:
402 return asn1_build_known_oid(OID_SECT521R1);
403 default:
404 break;
b730fd6f 405 }
10b82be6
TB
406 return chunk_empty;
407}
b730fd6f 408
10b82be6
TB
409/**
410 * Described in header.
411 */
412pkcs11_dh_t *pkcs11_dh_create(diffie_hellman_group_t group,
413 chunk_t g, chunk_t p)
414{
415 switch (group)
1bb5d7c3 416 {
10b82be6
TB
417 case MODP_CUSTOM:
418 {
419 return create_modp(group, p.len, g, p);
420 }
421 case ECP_192_BIT:
422 case ECP_224_BIT:
423 case ECP_256_BIT:
424 case ECP_384_BIT:
425 case ECP_521_BIT:
426 {
427 chunk_t params = ecparams_lookup(group);
428 if (params.ptr)
429 {
430 return create_ecp(group, params);
431 }
432 break;
433 }
434 default:
435 {
436 diffie_hellman_params_t *params = diffie_hellman_get_params(group);
437 if (params)
438 {
439 return create_modp(group, params->exp_len, params->generator,
440 params->prime);
441 }
442 break;
443 }
1bb5d7c3 444 }
10b82be6 445 return NULL;
1bb5d7c3
TB
446}
447