]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c
Correctly install DNS servers on Android if frontend is not used.
[thirdparty/strongswan.git] / src / libstrongswan / plugins / openssl / openssl_ec_diffie_hellman.c
CommitLineData
fc1a31d5
TB
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.
fc1a31d5
TB
14 */
15
5a367e99
TB
16#include <openssl/opensslconf.h>
17
18#ifndef OPENSSL_NO_EC
19
fc1a31d5
TB
20#include <openssl/ec.h>
21#include <openssl/objects.h>
22
23#include "openssl_ec_diffie_hellman.h"
a57e0580 24#include "openssl_util.h"
fc1a31d5
TB
25
26#include <debug.h>
27
fc1a31d5
TB
28typedef 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 */
33struct private_openssl_ec_diffie_hellman_t {
34 /**
35 * Public openssl_ec_diffie_hellman_t interface.
36 */
37 openssl_ec_diffie_hellman_t public;
7daf5226 38
fc1a31d5
TB
39 /**
40 * Diffie Hellman group number.
41 */
42 u_int16_t group;
7daf5226 43
fc1a31d5
TB
44 /**
45 * EC private (public) key
46 */
47 EC_KEY *key;
7daf5226 48
fc1a31d5
TB
49 /**
50 * EC group
51 */
52 const EC_GROUP *ec_group;
7daf5226 53
fc1a31d5
TB
54 /**
55 * Other public key
56 */
57 EC_POINT *pub_key;
7daf5226 58
fc1a31d5
TB
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 */
74static bool chunk2ecp(const EC_GROUP *group, chunk_t chunk, EC_POINT *point)
75{
76 BN_CTX *ctx;
77 BIGNUM *x, *y;
fc1a31d5 78 bool ret = FALSE;
7daf5226 79
fc1a31d5
TB
80 ctx = BN_CTX_new();
81 if (!ctx)
82 {
83 return FALSE;
84 }
7daf5226 85
fc1a31d5
TB
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 }
7daf5226 93
a57e0580 94 if (!openssl_bn_split(chunk, x, y))
fc1a31d5
TB
95 {
96 goto error;
97 }
7daf5226 98
fc1a31d5
TB
99 if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
100 {
101 goto error;
102 }
7daf5226 103
fc1a31d5
TB
104 ret = TRUE;
105error:
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 */
21863d63
AS
115static bool ecp2chunk(const EC_GROUP *group, const EC_POINT *point,
116 chunk_t *chunk, bool x_coordinate_only)
fc1a31d5
TB
117{
118 BN_CTX *ctx;
119 BIGNUM *x, *y;
fc1a31d5 120 bool ret = FALSE;
7daf5226 121
fc1a31d5
TB
122 ctx = BN_CTX_new();
123 if (!ctx)
124 {
125 return FALSE;
126 }
7daf5226 127
fc1a31d5
TB
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 }
7daf5226 135
fc1a31d5
TB
136 if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx))
137 {
138 goto error;
139 }
7daf5226 140
21863d63
AS
141 if (x_coordinate_only)
142 {
143 y = NULL;
144 }
a57e0580 145 if (!openssl_bn_cat(EC_FIELD_ELEMENT_LEN(group), x, y, chunk))
fc1a31d5 146 {
fc1a31d5
TB
147 goto error;
148 }
7daf5226 149
fc1a31d5
TB
150 ret = TRUE;
151error:
152 BN_CTX_end(ctx);
153 BN_CTX_free(ctx);
154 return ret;
155}
156
157/**
158 * Compute the shared secret.
7daf5226 159 *
fc1a31d5
TB
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 */
57202484
MW
168static bool compute_shared_key(private_openssl_ec_diffie_hellman_t *this,
169 chunk_t *shared_secret)
fc1a31d5
TB
170{
171 const BIGNUM *priv_key;
5e17e35c 172 EC_POINT *secret = NULL;
21863d63 173 bool x_coordinate_only, ret = FALSE;
7daf5226 174
fc1a31d5
TB
175 priv_key = EC_KEY_get0_private_key(this->key);
176 if (!priv_key)
177 {
178 goto error;
179 }
7daf5226 180
fc1a31d5
TB
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 }
7daf5226 191
21863d63 192 /*
eebecebe
AS
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
21863d63
AS
196 */
197 x_coordinate_only = lib->settings->get_bool(lib->settings,
eebecebe 198 "libstrongswan.ecp_x_coordinate_only", TRUE);
21863d63 199 if (!ecp2chunk(this->ec_group, secret, shared_secret, x_coordinate_only))
fc1a31d5
TB
200 {
201 goto error;
202 }
7daf5226 203
fc1a31d5
TB
204 ret = TRUE;
205error:
206 if (secret)
207 {
208 EC_POINT_clear_free(secret);
209 }
210 return ret;
211}
212
57202484
MW
213METHOD(diffie_hellman_t, set_other_public_value, void,
214 private_openssl_ec_diffie_hellman_t *this, chunk_t value)
fc1a31d5
TB
215{
216 if (!chunk2ecp(this->ec_group, value, this->pub_key))
217 {
8b0e0910 218 DBG1(DBG_LIB, "ECDH public value is malformed");
fc1a31d5
TB
219 return;
220 }
7daf5226 221
fc1a31d5 222 chunk_free(&this->shared_secret);
7daf5226 223
fc1a31d5 224 if (!compute_shared_key(this, &this->shared_secret)) {
8b0e0910 225 DBG1(DBG_LIB, "ECDH shared secret computation failed");
fc1a31d5
TB
226 return;
227 }
7daf5226 228
fc1a31d5
TB
229 this->computed = TRUE;
230}
231
57202484
MW
232METHOD(diffie_hellman_t, get_my_public_value, void,
233 private_openssl_ec_diffie_hellman_t *this,chunk_t *value)
fc1a31d5 234{
21863d63 235 ecp2chunk(this->ec_group, EC_KEY_get0_public_key(this->key), value, FALSE);
fc1a31d5
TB
236}
237
57202484
MW
238METHOD(diffie_hellman_t, get_shared_secret, status_t,
239 private_openssl_ec_diffie_hellman_t *this, chunk_t *secret)
fc1a31d5
TB
240{
241 if (!this->computed)
242 {
243 return FAILED;
244 }
245 *secret = chunk_clone(this->shared_secret);
246 return SUCCESS;
247}
248
57202484
MW
249METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
250 private_openssl_ec_diffie_hellman_t *this)
fc1a31d5
TB
251{
252 return this->group;
253}
254
57202484
MW
255METHOD(diffie_hellman_t, destroy, void,
256 private_openssl_ec_diffie_hellman_t *this)
fc1a31d5
TB
257{
258 EC_POINT_clear_free(this->pub_key);
259 EC_KEY_free(this->key);
f7812f64 260 chunk_clear(&this->shared_secret);
fc1a31d5
TB
261 free(this);
262}
263
264/*
265 * Described in header.
266 */
267openssl_ec_diffie_hellman_t *openssl_ec_diffie_hellman_create(diffie_hellman_group_t group)
268{
57202484
MW
269 private_openssl_ec_diffie_hellman_t *this;
270
271 INIT(this,
ba31fe1f
MW
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 },
57202484
MW
280 },
281 .group = group,
282 );
7daf5226 283
fc1a31d5
TB
284 switch (group)
285 {
346e9c57
TB
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;
fc1a31d5
TB
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 }
7daf5226 305
fc1a31d5
TB
306 if (!this->key)
307 {
308 free(this);
309 return NULL;
310 }
7daf5226 311
fc1a31d5
TB
312 /* caching the EC group */
313 this->ec_group = EC_KEY_get0_group(this->key);
7daf5226 314
fc1a31d5
TB
315 this->pub_key = EC_POINT_new(this->ec_group);
316 if (!this->pub_key)
317 {
318 free(this);
319 return NULL;
320 }
7daf5226 321
fc1a31d5
TB
322 /* generate an EC private (public) key */
323 if (!EC_KEY_generate_key(this->key))
324 {
325 free(this);
326 return NULL;
327 }
7daf5226 328
fc1a31d5
TB
329 return &this->public;
330}
5a367e99
TB
331#endif /* OPENSSL_NO_EC */
332