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