]>
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 | */ | |
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 | ||
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 | ||
1bb5d7c3 TB |
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 | /** | |
10b82be6 TB |
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. | |
1bb5d7c3 | 188 | */ |
10b82be6 TB |
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) | |
1bb5d7c3 | 192 | { |
1bb5d7c3 | 193 | CK_MECHANISM mech = { |
10b82be6 | 194 | this->mech_key, |
1bb5d7c3 TB |
195 | NULL, |
196 | 0, | |
197 | }; | |
198 | CK_OBJECT_HANDLE pub_key; | |
199 | CK_RV rv; | |
200 | ||
10b82be6 TB |
201 | rv = this->lib->f->C_GenerateKeyPair(this->session, &mech, pub, pub_len, |
202 | pri, pri_len, &pub_key, &this->pri_key); | |
1bb5d7c3 TB |
203 | if (rv != CKR_OK) |
204 | { | |
205 | DBG1(DBG_CFG, "C_GenerateKeyPair() error: %N", ck_rv_names, rv); | |
206 | return FALSE; | |
207 | } | |
cac68531 | 208 | if (!this->lib->get_ck_attribute(this->lib, this->session, pub_key, |
10b82be6 | 209 | attr, &this->pub_key)) |
1bb5d7c3 | 210 | { |
cac68531 | 211 | chunk_free(&this->pub_key); |
1bb5d7c3 TB |
212 | return FALSE; |
213 | } | |
214 | return TRUE; | |
215 | } | |
216 | ||
217 | /** | |
10b82be6 TB |
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 | { | |
cf9d45ea | 223 | CK_BBOOL ck_true = CK_TRUE; |
10b82be6 | 224 | CK_ATTRIBUTE pub_attr[] = { |
cf9d45ea | 225 | { CKA_DERIVE, &ck_true, sizeof(ck_true) }, |
10b82be6 TB |
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[] = { | |
cf9d45ea | 231 | { CKA_DERIVE, &ck_true, sizeof(ck_true) }, |
10b82be6 TB |
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. | |
1bb5d7c3 | 240 | */ |
10b82be6 TB |
241 | static bool generate_key_pair_ecp(private_pkcs11_dh_t *this, |
242 | chunk_t ecparams) | |
243 | { | |
cf9d45ea | 244 | CK_BBOOL ck_true = CK_TRUE; |
10b82be6 | 245 | CK_ATTRIBUTE pub_attr[] = { |
cf9d45ea | 246 | { CKA_DERIVE, &ck_true, sizeof(ck_true) }, |
10b82be6 TB |
247 | { CKA_EC_PARAMS, ecparams.ptr, ecparams.len }, |
248 | }; | |
cf9d45ea TB |
249 | CK_ATTRIBUTE pri_attr[] = { |
250 | { CKA_DERIVE, &ck_true, sizeof(ck_true) }, | |
251 | }; | |
43cd036a | 252 | chunk_t pub_key; |
cf9d45ea TB |
253 | if (!generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr, |
254 | countof(pri_attr), CKA_EC_POINT)) | |
10b82be6 TB |
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 | } | |
43cd036a TB |
264 | pub_key = chunk_clone(chunk_skip(this->pub_key, 1)); |
265 | chunk_clear(&this->pub_key); | |
266 | this->pub_key = pub_key; | |
10b82be6 TB |
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) | |
1bb5d7c3 TB |
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, ¤t, &slot)) | |
289 | { | |
290 | mechs = current->create_mechanism_enumerator(current, slot); | |
291 | while (mechs->enumerate(mechs, &type, NULL)) | |
10b82be6 TB |
292 | { /* we assume we can generate key pairs if the derive mechanism |
293 | * is supported */ | |
294 | if (type == this->mech_derive) | |
1bb5d7c3 TB |
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 | ||
10b82be6 | 314 | /** |
1bb5d7c3 TB |
315 | * Generic internal constructor |
316 | */ | |
10b82be6 TB |
317 | static private_pkcs11_dh_t *create_generic(diffie_hellman_group_t group, |
318 | CK_MECHANISM_TYPE key, | |
319 | CK_MECHANISM_TYPE derive) | |
1bb5d7c3 TB |
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, | |
10b82be6 TB |
334 | .mech_key = key, |
335 | .mech_derive = derive, | |
1bb5d7c3 TB |
336 | ); |
337 | ||
10b82be6 | 338 | this->lib = find_token(this, &this->session); |
1bb5d7c3 TB |
339 | if (!this->lib) |
340 | { | |
341 | free(this); | |
342 | return NULL; | |
343 | } | |
10b82be6 TB |
344 | return this; |
345 | } | |
1bb5d7c3 | 346 | |
10b82be6 TB |
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) | |
1bb5d7c3 | 353 | { |
10b82be6 TB |
354 | if (generate_key_pair_ecp(this, ecparam)) |
355 | { | |
817d165c | 356 | chunk_free(&ecparam); |
10b82be6 TB |
357 | return &this->public; |
358 | } | |
817d165c | 359 | chunk_free(&ecparam); |
1bb5d7c3 | 360 | free(this); |
1bb5d7c3 | 361 | } |
10b82be6 | 362 | return NULL; |
1bb5d7c3 TB |
363 | } |
364 | ||
10b82be6 TB |
365 | /** |
366 | * Constructor for MODP DH | |
1bb5d7c3 | 367 | */ |
10b82be6 TB |
368 | static pkcs11_dh_t *create_modp(diffie_hellman_group_t group, size_t exp_len, |
369 | chunk_t g, chunk_t p) | |
1bb5d7c3 | 370 | { |
10b82be6 TB |
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 | } | |
1bb5d7c3 | 384 | |
10b82be6 TB |
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) | |
b730fd6f | 391 | { |
10b82be6 TB |
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; | |
b730fd6f | 404 | } |
10b82be6 TB |
405 | return chunk_empty; |
406 | } | |
b730fd6f | 407 | |
10b82be6 TB |
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) | |
1bb5d7c3 | 415 | { |
10b82be6 TB |
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 | } | |
1bb5d7c3 | 443 | } |
10b82be6 | 444 | return NULL; |
1bb5d7c3 TB |
445 | } |
446 |