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