Use chunk_clear to memwipe shared secret
[strongswan.git] / src / libstrongswan / plugins / openssl / openssl_ec_diffie_hellman.c
1 /*
2 * Copyright (C) 2008 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 <openssl/opensslconf.h>
17
18 #ifndef OPENSSL_NO_EC
19
20 #include <openssl/ec.h>
21 #include <openssl/objects.h>
22
23 #include "openssl_ec_diffie_hellman.h"
24 #include "openssl_util.h"
25
26 #include <debug.h>
27
28 typedef struct private_openssl_ec_diffie_hellman_t private_openssl_ec_diffie_hellman_t;
29
30 /**
31 * Private data of an openssl_ec_diffie_hellman_t object.
32 */
33 struct private_openssl_ec_diffie_hellman_t {
34 /**
35 * Public openssl_ec_diffie_hellman_t interface.
36 */
37 openssl_ec_diffie_hellman_t public;
38
39 /**
40 * Diffie Hellman group number.
41 */
42 u_int16_t group;
43
44 /**
45 * EC private (public) key
46 */
47 EC_KEY *key;
48
49 /**
50 * EC group
51 */
52 const EC_GROUP *ec_group;
53
54 /**
55 * Other public key
56 */
57 EC_POINT *pub_key;
58
59 /**
60 * Shared secret
61 */
62 chunk_t shared_secret;
63
64 /**
65 * True if shared secret is computed
66 */
67 bool computed;
68 };
69
70 /**
71 * Convert a chunk to an EC_POINT (which must already exist). The x and y
72 * coordinates of the point have to be concatenated in the chunk.
73 */
74 static bool chunk2ecp(const EC_GROUP *group, chunk_t chunk, EC_POINT *point)
75 {
76 BN_CTX *ctx;
77 BIGNUM *x, *y;
78 bool ret = FALSE;
79
80 ctx = BN_CTX_new();
81 if (!ctx)
82 {
83 return FALSE;
84 }
85
86 BN_CTX_start(ctx);
87 x = BN_CTX_get(ctx);
88 y = BN_CTX_get(ctx);
89 if (!x || !y)
90 {
91 goto error;
92 }
93
94 if (!openssl_bn_split(chunk, x, y))
95 {
96 goto error;
97 }
98
99 if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
100 {
101 goto error;
102 }
103
104 ret = TRUE;
105 error:
106 BN_CTX_end(ctx);
107 BN_CTX_free(ctx);
108 return ret;
109 }
110
111 /**
112 * Convert an EC_POINT to a chunk by concatenating the x and y coordinates of
113 * the point. This function allocates memory for the chunk.
114 */
115 static bool ecp2chunk(const EC_GROUP *group, const EC_POINT *point,
116 chunk_t *chunk, bool x_coordinate_only)
117 {
118 BN_CTX *ctx;
119 BIGNUM *x, *y;
120 bool ret = FALSE;
121
122 ctx = BN_CTX_new();
123 if (!ctx)
124 {
125 return FALSE;
126 }
127
128 BN_CTX_start(ctx);
129 x = BN_CTX_get(ctx);
130 y = BN_CTX_get(ctx);
131 if (!x || !y)
132 {
133 goto error;
134 }
135
136 if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx))
137 {
138 goto error;
139 }
140
141 if (x_coordinate_only)
142 {
143 y = NULL;
144 }
145 if (!openssl_bn_cat(EC_FIELD_ELEMENT_LEN(group), x, y, chunk))
146 {
147 goto error;
148 }
149
150 ret = TRUE;
151 error:
152 BN_CTX_end(ctx);
153 BN_CTX_free(ctx);
154 return ret;
155 }
156
157 /**
158 * Compute the shared secret.
159 *
160 * We cannot use the function ECDH_compute_key() because that returns only the
161 * x coordinate of the shared secret point (which is defined, for instance, in
162 * 'NIST SP 800-56A').
163 * However, we need both coordinates as RFC 4753 says: "The Diffie-Hellman
164 * public value is obtained by concatenating the x and y values. The format
165 * of the Diffie-Hellman shared secret value is the same as that of the
166 * Diffie-Hellman public value."
167 */
168 static bool compute_shared_key(private_openssl_ec_diffie_hellman_t *this,
169 chunk_t *shared_secret)
170 {
171 const BIGNUM *priv_key;
172 EC_POINT *secret = NULL;
173 bool x_coordinate_only, ret = FALSE;
174
175 priv_key = EC_KEY_get0_private_key(this->key);
176 if (!priv_key)
177 {
178 goto error;
179 }
180
181 secret = EC_POINT_new(this->ec_group);
182 if (!secret)
183 {
184 goto error;
185 }
186
187 if (!EC_POINT_mul(this->ec_group, secret, NULL, this->pub_key, priv_key, NULL))
188 {
189 goto error;
190 }
191
192 /*
193 * The default setting ecp_x_coordinate_only = TRUE
194 * applies the following errata for RFC 4753:
195 * http://www.rfc-editor.org/errata_search.php?eid=9
196 */
197 x_coordinate_only = lib->settings->get_bool(lib->settings,
198 "libstrongswan.ecp_x_coordinate_only", TRUE);
199 if (!ecp2chunk(this->ec_group, secret, shared_secret, x_coordinate_only))
200 {
201 goto error;
202 }
203
204 ret = TRUE;
205 error:
206 if (secret)
207 {
208 EC_POINT_clear_free(secret);
209 }
210 return ret;
211 }
212
213 METHOD(diffie_hellman_t, set_other_public_value, void,
214 private_openssl_ec_diffie_hellman_t *this, chunk_t value)
215 {
216 if (!chunk2ecp(this->ec_group, value, this->pub_key))
217 {
218 DBG1(DBG_LIB, "ECDH public value is malformed");
219 return;
220 }
221
222 chunk_clear(&this->shared_secret);
223
224 if (!compute_shared_key(this, &this->shared_secret)) {
225 DBG1(DBG_LIB, "ECDH shared secret computation failed");
226 return;
227 }
228
229 this->computed = TRUE;
230 }
231
232 METHOD(diffie_hellman_t, get_my_public_value, void,
233 private_openssl_ec_diffie_hellman_t *this,chunk_t *value)
234 {
235 ecp2chunk(this->ec_group, EC_KEY_get0_public_key(this->key), value, FALSE);
236 }
237
238 METHOD(diffie_hellman_t, get_shared_secret, status_t,
239 private_openssl_ec_diffie_hellman_t *this, chunk_t *secret)
240 {
241 if (!this->computed)
242 {
243 return FAILED;
244 }
245 *secret = chunk_clone(this->shared_secret);
246 return SUCCESS;
247 }
248
249 METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
250 private_openssl_ec_diffie_hellman_t *this)
251 {
252 return this->group;
253 }
254
255 METHOD(diffie_hellman_t, destroy, void,
256 private_openssl_ec_diffie_hellman_t *this)
257 {
258 EC_POINT_clear_free(this->pub_key);
259 EC_KEY_free(this->key);
260 chunk_clear(&this->shared_secret);
261 free(this);
262 }
263
264 /*
265 * Described in header.
266 */
267 openssl_ec_diffie_hellman_t *openssl_ec_diffie_hellman_create(diffie_hellman_group_t group)
268 {
269 private_openssl_ec_diffie_hellman_t *this;
270
271 INIT(this,
272 .public = {
273 .dh = {
274 .get_shared_secret = _get_shared_secret,
275 .set_other_public_value = _set_other_public_value,
276 .get_my_public_value = _get_my_public_value,
277 .get_dh_group = _get_dh_group,
278 .destroy = _destroy,
279 },
280 },
281 .group = group,
282 );
283
284 switch (group)
285 {
286 case ECP_192_BIT:
287 this->key = EC_KEY_new_by_curve_name(NID_X9_62_prime192v1);
288 break;
289 case ECP_224_BIT:
290 this->key = EC_KEY_new_by_curve_name(NID_secp224r1);
291 break;
292 case ECP_256_BIT:
293 this->key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
294 break;
295 case ECP_384_BIT:
296 this->key = EC_KEY_new_by_curve_name(NID_secp384r1);
297 break;
298 case ECP_521_BIT:
299 this->key = EC_KEY_new_by_curve_name(NID_secp521r1);
300 break;
301 default:
302 this->key = NULL;
303 break;
304 }
305
306 if (!this->key)
307 {
308 free(this);
309 return NULL;
310 }
311
312 /* caching the EC group */
313 this->ec_group = EC_KEY_get0_group(this->key);
314
315 this->pub_key = EC_POINT_new(this->ec_group);
316 if (!this->pub_key)
317 {
318 free(this);
319 return NULL;
320 }
321
322 /* generate an EC private (public) key */
323 if (!EC_KEY_generate_key(this->key))
324 {
325 free(this);
326 return NULL;
327 }
328
329 return &this->public;
330 }
331 #endif /* OPENSSL_NO_EC */
332