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"
25 #define COORD_LEN(group) ((EC_GROUP_get_degree(group) + 7) / 8)
27 typedef struct private_openssl_ec_diffie_hellman_t private_openssl_ec_diffie_hellman_t
;
30 * Private data of an openssl_ec_diffie_hellman_t object.
32 struct private_openssl_ec_diffie_hellman_t
{
34 * Public openssl_ec_diffie_hellman_t interface.
36 openssl_ec_diffie_hellman_t
public;
39 * Diffie Hellman group number.
44 * EC private (public) key
51 const EC_GROUP
*ec_group
;
61 chunk_t shared_secret
;
64 * True if shared secret is computed
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.
73 static bool chunk2ecp(const EC_GROUP
*group
, chunk_t chunk
, EC_POINT
*point
)
94 coord_len
= COORD_LEN(group
);
95 if (chunk
.len
!= coord_len
* 2)
100 if (!BN_bin2bn(chunk
.ptr
, coord_len
, x
) ||
101 !BN_bin2bn(chunk
.ptr
+ coord_len
, coord_len
, y
))
106 if (!EC_POINT_set_affine_coordinates_GFp(group
, point
, x
, y
, ctx
))
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.
122 static bool ecp2chunk(const EC_GROUP
*group
, const EC_POINT
*point
, chunk_t
*chunk
)
126 int coord_len
, offset
;
143 if (!EC_POINT_get_affine_coordinates_GFp(group
, point
, x
, y
, ctx
))
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
);
153 offset
= coord_len
- BN_num_bytes(x
);
154 if (!BN_bn2bin(x
, chunk
->ptr
+ offset
))
160 offset
= coord_len
- BN_num_bytes(y
);
161 if (!BN_bn2bin(y
, chunk
->ptr
+ coord_len
+ offset
))
175 * Compute the shared secret.
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."
185 static bool compute_shared_key(private_openssl_ec_diffie_hellman_t
*this, chunk_t
*shared_secret
)
187 const BIGNUM
*priv_key
;
191 priv_key
= EC_KEY_get0_private_key(this->key
);
197 secret
= EC_POINT_new(this->ec_group
);
203 if (!EC_POINT_mul(this->ec_group
, secret
, NULL
, this->pub_key
, priv_key
, NULL
))
208 if (!ecp2chunk(this->ec_group
, secret
, shared_secret
))
217 EC_POINT_clear_free(secret
);
223 * Implementation of openssl_ec_diffie_hellman_t.set_other_public_value.
225 static void set_other_public_value(private_openssl_ec_diffie_hellman_t
*this, chunk_t value
)
227 if (!chunk2ecp(this->ec_group
, value
, this->pub_key
))
229 DBG1("ECDH public value is malformed");
233 chunk_free(&this->shared_secret
);
235 if (!compute_shared_key(this, &this->shared_secret
)) {
236 DBG1("ECDH shared secret computation failed");
240 this->computed
= TRUE
;
244 * Implementation of openssl_ec_diffie_hellman_t.get_other_public_value.
246 static status_t
get_other_public_value(private_openssl_ec_diffie_hellman_t
*this,
254 if (!ecp2chunk(this->ec_group
, this->pub_key
, value
))
262 * Implementation of openssl_ec_diffie_hellman_t.get_my_public_value.
264 static void get_my_public_value(private_openssl_ec_diffie_hellman_t
*this,chunk_t
*value
)
266 ecp2chunk(this->ec_group
, EC_KEY_get0_public_key(this->key
), value
);
270 * Implementation of openssl_ec_diffie_hellman_t.get_shared_secret.
272 static status_t
get_shared_secret(private_openssl_ec_diffie_hellman_t
*this, chunk_t
*secret
)
278 *secret
= chunk_clone(this->shared_secret
);
283 * Implementation of openssl_ec_diffie_hellman_t.get_dh_group.
285 static diffie_hellman_group_t
get_dh_group(private_openssl_ec_diffie_hellman_t
*this)
291 * Implementation of openssl_ec_diffie_hellman_t.destroy.
293 static void destroy(private_openssl_ec_diffie_hellman_t
*this)
295 EC_POINT_clear_free(this->pub_key
);
296 EC_KEY_free(this->key
);
297 chunk_free(&this->shared_secret
);
302 * Described in header.
304 openssl_ec_diffie_hellman_t
*openssl_ec_diffie_hellman_create(diffie_hellman_group_t group
)
306 private_openssl_ec_diffie_hellman_t
*this = malloc_thing(private_openssl_ec_diffie_hellman_t
);
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
;
318 this->key
= EC_KEY_new_by_curve_name(NID_X9_62_prime256v1
);
321 this->key
= EC_KEY_new_by_curve_name(NID_secp384r1
);
324 this->key
= EC_KEY_new_by_curve_name(NID_secp521r1
);
337 /* caching the EC group */
338 this->ec_group
= EC_KEY_get0_group(this->key
);
340 this->pub_key
= EC_POINT_new(this->ec_group
);
347 /* generate an EC private (public) key */
348 if (!EC_KEY_generate_key(this->key
))
355 this->computed
= FALSE
;
357 this->shared_secret
= chunk_empty
;
359 return &this->public;