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
18 #include <openssl/ec.h>
19 #include <openssl/objects.h>
21 #include "openssl_ec_diffie_hellman.h"
22 #include "openssl_util.h"
26 typedef struct private_openssl_ec_diffie_hellman_t private_openssl_ec_diffie_hellman_t
;
29 * Private data of an openssl_ec_diffie_hellman_t object.
31 struct private_openssl_ec_diffie_hellman_t
{
33 * Public openssl_ec_diffie_hellman_t interface.
35 openssl_ec_diffie_hellman_t
public;
38 * Diffie Hellman group number.
43 * EC private (public) key
50 const EC_GROUP
*ec_group
;
60 chunk_t shared_secret
;
63 * True if shared secret is computed
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.
72 static bool chunk2ecp(const EC_GROUP
*group
, chunk_t chunk
, EC_POINT
*point
)
92 if (!openssl_bn_split(chunk
, x
, y
))
97 if (!EC_POINT_set_affine_coordinates_GFp(group
, point
, x
, y
, ctx
))
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.
113 static bool ecp2chunk(const EC_GROUP
*group
, const EC_POINT
*point
, chunk_t
*chunk
)
133 if (!EC_POINT_get_affine_coordinates_GFp(group
, point
, x
, y
, ctx
))
138 if (!openssl_bn_cat(EC_FIELD_ELEMENT_LEN(group
), x
, y
, chunk
))
151 * Compute the shared secret.
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."
161 static bool compute_shared_key(private_openssl_ec_diffie_hellman_t
*this, chunk_t
*shared_secret
)
163 const BIGNUM
*priv_key
;
164 EC_POINT
*secret
= NULL
;
167 priv_key
= EC_KEY_get0_private_key(this->key
);
173 secret
= EC_POINT_new(this->ec_group
);
179 if (!EC_POINT_mul(this->ec_group
, secret
, NULL
, this->pub_key
, priv_key
, NULL
))
184 if (!ecp2chunk(this->ec_group
, secret
, shared_secret
))
193 EC_POINT_clear_free(secret
);
199 * Implementation of openssl_ec_diffie_hellman_t.set_other_public_value.
201 static void set_other_public_value(private_openssl_ec_diffie_hellman_t
*this, chunk_t value
)
203 if (!chunk2ecp(this->ec_group
, value
, this->pub_key
))
205 DBG1("ECDH public value is malformed");
209 chunk_free(&this->shared_secret
);
211 if (!compute_shared_key(this, &this->shared_secret
)) {
212 DBG1("ECDH shared secret computation failed");
216 this->computed
= TRUE
;
220 * Implementation of openssl_ec_diffie_hellman_t.get_other_public_value.
222 static status_t
get_other_public_value(private_openssl_ec_diffie_hellman_t
*this,
230 if (!ecp2chunk(this->ec_group
, this->pub_key
, value
))
238 * Implementation of openssl_ec_diffie_hellman_t.get_my_public_value.
240 static void get_my_public_value(private_openssl_ec_diffie_hellman_t
*this,chunk_t
*value
)
242 ecp2chunk(this->ec_group
, EC_KEY_get0_public_key(this->key
), value
);
246 * Implementation of openssl_ec_diffie_hellman_t.get_shared_secret.
248 static status_t
get_shared_secret(private_openssl_ec_diffie_hellman_t
*this, chunk_t
*secret
)
254 *secret
= chunk_clone(this->shared_secret
);
259 * Implementation of openssl_ec_diffie_hellman_t.get_dh_group.
261 static diffie_hellman_group_t
get_dh_group(private_openssl_ec_diffie_hellman_t
*this)
267 * Implementation of openssl_ec_diffie_hellman_t.destroy.
269 static void destroy(private_openssl_ec_diffie_hellman_t
*this)
271 EC_POINT_clear_free(this->pub_key
);
272 EC_KEY_free(this->key
);
273 chunk_free(&this->shared_secret
);
278 * Described in header.
280 openssl_ec_diffie_hellman_t
*openssl_ec_diffie_hellman_create(diffie_hellman_group_t group
)
282 private_openssl_ec_diffie_hellman_t
*this = malloc_thing(private_openssl_ec_diffie_hellman_t
);
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
;
294 this->key
= EC_KEY_new_by_curve_name(NID_X9_62_prime192v1
);
297 this->key
= EC_KEY_new_by_curve_name(NID_secp224r1
);
300 this->key
= EC_KEY_new_by_curve_name(NID_X9_62_prime256v1
);
303 this->key
= EC_KEY_new_by_curve_name(NID_secp384r1
);
306 this->key
= EC_KEY_new_by_curve_name(NID_secp521r1
);
319 /* caching the EC group */
320 this->ec_group
= EC_KEY_get0_group(this->key
);
322 this->pub_key
= EC_POINT_new(this->ec_group
);
329 /* generate an EC private (public) key */
330 if (!EC_KEY_generate_key(this->key
))
337 this->computed
= FALSE
;
339 this->shared_secret
= chunk_empty
;
341 return &this->public;