]>
Commit | Line | Data |
---|---|---|
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 |
28 | typedef 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 | */ | |
33 | struct 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 | */ | |
74 | static 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; |
105 | error: | |
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 |
115 | static 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; |
151 | error: | |
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 |
168 | static 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; |
205 | error: | |
206 | if (secret) | |
207 | { | |
208 | EC_POINT_clear_free(secret); | |
209 | } | |
210 | return ret; | |
211 | } | |
212 | ||
57202484 MW |
213 | METHOD(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 |
232 | METHOD(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 |
238 | METHOD(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 |
249 | METHOD(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 |
255 | METHOD(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 | */ | |
267 | openssl_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 |