]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | */ | |
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 | */ | |
84 | static 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 |
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, | |
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 | 151 | METHOD(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 | 158 | METHOD(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 | ||
169 | METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, | |
170 | private_pkcs11_dh_t *this) | |
171 | { | |
172 | return this->group; | |
173 | } | |
174 | ||
175 | METHOD(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 |
190 | static 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 | */ | |
221 | static 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 |
242 | static 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 | */ | |
274 | static 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, ¤t, &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 |
318 | static 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 |
348 | static 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 |
369 | static 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 | */ | |
389 | static 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 | */ | |
412 | pkcs11_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 |