2 * Copyright (C) 2008 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include <openssl/opensslconf.h>
20 #include <openssl/ec.h>
21 #include <openssl/objects.h>
23 #include "openssl_ec_diffie_hellman.h"
24 #include "openssl_util.h"
28 typedef struct private_openssl_ec_diffie_hellman_t private_openssl_ec_diffie_hellman_t
;
31 * Private data of an openssl_ec_diffie_hellman_t object.
33 struct private_openssl_ec_diffie_hellman_t
{
35 * Public openssl_ec_diffie_hellman_t interface.
37 openssl_ec_diffie_hellman_t
public;
40 * Diffie Hellman group number.
45 * EC private (public) key
52 const EC_GROUP
*ec_group
;
62 chunk_t shared_secret
;
65 * True if shared secret is computed
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.
74 static bool chunk2ecp(const EC_GROUP
*group
, chunk_t chunk
, EC_POINT
*point
)
94 if (!openssl_bn_split(chunk
, x
, y
))
99 if (!EC_POINT_set_affine_coordinates_GFp(group
, point
, x
, y
, ctx
))
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.
115 static bool ecp2chunk(const EC_GROUP
*group
, const EC_POINT
*point
,
116 chunk_t
*chunk
, bool x_coordinate_only
)
136 if (!EC_POINT_get_affine_coordinates_GFp(group
, point
, x
, y
, ctx
))
141 if (x_coordinate_only
)
145 if (!openssl_bn_cat(EC_FIELD_ELEMENT_LEN(group
), x
, y
, chunk
))
158 * Compute the shared secret.
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."
168 static bool compute_shared_key(private_openssl_ec_diffie_hellman_t
*this,
169 chunk_t
*shared_secret
)
171 const BIGNUM
*priv_key
;
172 EC_POINT
*secret
= NULL
;
173 bool x_coordinate_only
, ret
= FALSE
;
175 priv_key
= EC_KEY_get0_private_key(this->key
);
181 secret
= EC_POINT_new(this->ec_group
);
187 if (!EC_POINT_mul(this->ec_group
, secret
, NULL
, this->pub_key
, priv_key
, NULL
))
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
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
))
208 EC_POINT_clear_free(secret
);
213 METHOD(diffie_hellman_t
, set_other_public_value
, void,
214 private_openssl_ec_diffie_hellman_t
*this, chunk_t value
)
216 if (!chunk2ecp(this->ec_group
, value
, this->pub_key
))
218 DBG1(DBG_LIB
, "ECDH public value is malformed");
222 chunk_free(&this->shared_secret
);
224 if (!compute_shared_key(this, &this->shared_secret
)) {
225 DBG1(DBG_LIB
, "ECDH shared secret computation failed");
229 this->computed
= TRUE
;
232 METHOD(diffie_hellman_t
, get_my_public_value
, void,
233 private_openssl_ec_diffie_hellman_t
*this,chunk_t
*value
)
235 ecp2chunk(this->ec_group
, EC_KEY_get0_public_key(this->key
), value
, FALSE
);
238 METHOD(diffie_hellman_t
, get_shared_secret
, status_t
,
239 private_openssl_ec_diffie_hellman_t
*this, chunk_t
*secret
)
245 *secret
= chunk_clone(this->shared_secret
);
249 METHOD(diffie_hellman_t
, get_dh_group
, diffie_hellman_group_t
,
250 private_openssl_ec_diffie_hellman_t
*this)
255 METHOD(diffie_hellman_t
, destroy
, void,
256 private_openssl_ec_diffie_hellman_t
*this)
258 EC_POINT_clear_free(this->pub_key
);
259 EC_KEY_free(this->key
);
260 chunk_clear(&this->shared_secret
);
265 * Described in header.
267 openssl_ec_diffie_hellman_t
*openssl_ec_diffie_hellman_create(diffie_hellman_group_t group
)
269 private_openssl_ec_diffie_hellman_t
*this;
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
,
287 this->key
= EC_KEY_new_by_curve_name(NID_X9_62_prime192v1
);
290 this->key
= EC_KEY_new_by_curve_name(NID_secp224r1
);
293 this->key
= EC_KEY_new_by_curve_name(NID_X9_62_prime256v1
);
296 this->key
= EC_KEY_new_by_curve_name(NID_secp384r1
);
299 this->key
= EC_KEY_new_by_curve_name(NID_secp521r1
);
312 /* caching the EC group */
313 this->ec_group
= EC_KEY_get0_group(this->key
);
315 this->pub_key
= EC_POINT_new(this->ec_group
);
322 /* generate an EC private (public) key */
323 if (!EC_KEY_generate_key(this->key
))
329 return &this->public;
331 #endif /* OPENSSL_NO_EC */