pkcs11: Added support for DH.
[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
21 #include "pkcs11_manager.h"
22
23 typedef struct private_pkcs11_dh_t private_pkcs11_dh_t;
24
25 /**
26 * Private data of an pkcs11_dh_t object.
27 */
28 struct private_pkcs11_dh_t {
29
30 /**
31 * Public pkcs11_dh_t interface
32 */
33 pkcs11_dh_t public;
34
35 /**
36 * PKCS#11 library
37 */
38 pkcs11_library_t *lib;
39
40 /**
41 * Session handle for this objct
42 */
43 CK_SESSION_HANDLE session;
44
45 /**
46 * Diffie Hellman group number.
47 */
48 u_int16_t group;
49
50 /**
51 * Handle for own private value
52 */
53 CK_OBJECT_HANDLE pri_key;
54
55 /**
56 * Own public value
57 */
58 chunk_t pub_key;
59
60 /**
61 * Shared secret
62 */
63 chunk_t secret;
64
65 };
66
67 /**
68 * Retrieve a CKA_VALUE from a CK_OBJECT_HANDLE, memory gets allocated
69 */
70 static bool get_cka_value(private_pkcs11_dh_t *this, CK_OBJECT_HANDLE obj,
71 chunk_t *value)
72 {
73 CK_ATTRIBUTE attr = { CKA_VALUE, NULL, 0 };
74 CK_RV rv;
75 rv = this->lib->f->C_GetAttributeValue(this->session, obj, &attr, 1);
76 if (rv != CKR_OK)
77 {
78 DBG1(DBG_CFG, "C_GetAttributeValue(NULL) error: %N", ck_rv_names, rv);
79 return FALSE;
80 }
81 *value = chunk_alloc(attr.ulValueLen);
82 attr.pValue = value->ptr;
83 rv = this->lib->f->C_GetAttributeValue(this->session, obj, &attr, 1);
84 if (rv != CKR_OK)
85 {
86 DBG1(DBG_CFG, "C_GetAttributeValue() error: %N", ck_rv_names, rv);
87 chunk_free(value);
88 return FALSE;
89 }
90 return TRUE;
91 }
92
93 METHOD(diffie_hellman_t, set_other_public_value, void,
94 private_pkcs11_dh_t *this, chunk_t value)
95 {
96 CK_ATTRIBUTE attr[] = {
97 };
98 CK_MECHANISM mech = {
99 CKM_DH_PKCS_DERIVE,
100 value.ptr,
101 value.len,
102 };
103 CK_OBJECT_HANDLE secret;
104 CK_RV rv;
105
106 rv = this->lib->f->C_DeriveKey(this->session, &mech, this->pri_key,
107 attr, countof(attr), &secret);
108 if (rv != CKR_OK)
109 {
110 DBG1(DBG_CFG, "C_DeriveKey() error: %N", ck_rv_names, rv);
111 return;
112 }
113 if (!get_cka_value(this, secret, &this->secret))
114 {
115 return;
116 }
117 }
118
119 METHOD(diffie_hellman_t, get_my_public_value, void,
120 private_pkcs11_dh_t *this, chunk_t *value)
121 {
122 *value = chunk_clone(this->pub_key);
123 }
124
125 METHOD(diffie_hellman_t, get_shared_secret, status_t,
126 private_pkcs11_dh_t *this, chunk_t *secret)
127 {
128 if (!this->secret.ptr)
129 {
130 return FAILED;
131 }
132 *secret = chunk_clone(this->secret);
133 return SUCCESS;
134 }
135
136 METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
137 private_pkcs11_dh_t *this)
138 {
139 return this->group;
140 }
141
142 METHOD(diffie_hellman_t, destroy, void,
143 private_pkcs11_dh_t *this)
144 {
145 this->lib->f->C_CloseSession(this->session);
146 chunk_clear(&this->pub_key);
147 chunk_clear(&this->secret);
148 free(this);
149 }
150
151 /**
152 * Generate DH key pair
153 */
154 static bool generate_key_pair(private_pkcs11_dh_t *this, size_t exp_len,
155 chunk_t g, chunk_t p)
156 {
157 CK_ULONG bits = exp_len * 8;
158 CK_ATTRIBUTE pub_attr[] = {
159 { CKA_PRIME, p.ptr, p.len },
160 { CKA_BASE, g.ptr, g.len },
161 };
162 CK_ATTRIBUTE pri_attr[] = {
163 { CKA_VALUE_BITS, &bits, sizeof(bits) },
164 };
165 CK_MECHANISM mech = {
166 CKM_DH_PKCS_KEY_PAIR_GEN,
167 NULL,
168 0,
169 };
170 CK_OBJECT_HANDLE pub_key;
171 CK_RV rv;
172
173 rv = this->lib->f->C_GenerateKeyPair(this->session, &mech, pub_attr,
174 countof(pub_attr), pri_attr, countof(pri_attr),
175 &pub_key, &this->pri_key);
176 if (rv != CKR_OK)
177 {
178 DBG1(DBG_CFG, "C_GenerateKeyPair() error: %N", ck_rv_names, rv);
179 return FALSE;
180 }
181
182 if (!get_cka_value(this, pub_key, &this->pub_key))
183 {
184 return FALSE;
185 }
186 return TRUE;
187 }
188
189 /**
190 * Find a token we can use for DH algorithm
191 */
192 static pkcs11_library_t *find_token(CK_SESSION_HANDLE *session)
193 {
194 enumerator_t *tokens, *mechs;
195 pkcs11_manager_t *manager;
196 pkcs11_library_t *current, *found = NULL;
197 CK_MECHANISM_TYPE type;
198 CK_SLOT_ID slot;
199
200 manager = lib->get(lib, "pkcs11-manager");
201 if (!manager)
202 {
203 return NULL;
204 }
205 tokens = manager->create_token_enumerator(manager);
206 while (tokens->enumerate(tokens, &current, &slot))
207 {
208 mechs = current->create_mechanism_enumerator(current, slot);
209 while (mechs->enumerate(mechs, &type, NULL))
210 {
211 /* we assume CKM_DH_PKCS_DERIVE is supported too */
212 if (type == CKM_DH_PKCS_KEY_PAIR_GEN)
213 {
214 if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
215 NULL, NULL, session) == CKR_OK)
216 {
217 found = current;
218 break;
219 }
220 }
221 }
222 mechs->destroy(mechs);
223 if (found)
224 {
225 break;
226 }
227 }
228 tokens->destroy(tokens);
229 return found;
230 }
231
232 /*
233 * Generic internal constructor
234 */
235 pkcs11_dh_t *create_generic(diffie_hellman_group_t group, size_t exp_len,
236 chunk_t g, chunk_t p)
237 {
238 private_pkcs11_dh_t *this;
239
240 INIT(this,
241 .public = {
242 .dh = {
243 .get_shared_secret = _get_shared_secret,
244 .set_other_public_value = _set_other_public_value,
245 .get_my_public_value = _get_my_public_value,
246 .get_dh_group = _get_dh_group,
247 .destroy = _destroy,
248 },
249 },
250 .group = group,
251 );
252
253 this->lib = find_token(&this->session);
254 if (!this->lib)
255 {
256 free(this);
257 return NULL;
258 }
259
260 if (!generate_key_pair(this, exp_len, g, p))
261 {
262 free(this);
263 return NULL;
264 }
265 return &this->public;
266 }
267
268
269 /*
270 * Described in header.
271 */
272 pkcs11_dh_t *pkcs11_dh_create(diffie_hellman_group_t group)
273 {
274
275 diffie_hellman_params_t *params;
276
277 params = diffie_hellman_get_params(group);
278 if (!params)
279 {
280 return NULL;
281 }
282 return create_generic(group, params->exp_len,
283 params->generator, params->prime);
284 }
285
286 /*
287 * Described in header.
288 */
289 pkcs11_dh_t *pkcs11_dh_create_custom(diffie_hellman_group_t group,
290 chunk_t g, chunk_t p)
291 {
292 if (group == MODP_CUSTOM)
293 {
294 return create_generic(group, p.len, g, p);
295 }
296 return NULL;
297 }