wolfssl: Fixes, code style changes and some refactorings
[strongswan.git] / src / libstrongswan / plugins / wolfssl / wolfssl_ec_diffie_hellman.c
1 /*
2 * Copyright (C) 2019 Sean Parkinson, wolfSSL Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23 #include "wolfssl_common.h"
24
25 #ifdef HAVE_ECC_DHE
26
27 #include <wolfssl/wolfcrypt/ecc.h>
28
29 #include "wolfssl_ec_diffie_hellman.h"
30 #include "wolfssl_util.h"
31
32 #include <utils/debug.h>
33
34 typedef struct private_wolfssl_ec_diffie_hellman_t private_wolfssl_ec_diffie_hellman_t;
35
36 /**
37 * Private data of an wolfssl_ec_diffie_hellman_t object.
38 */
39 struct private_wolfssl_ec_diffie_hellman_t {
40 /**
41 * Public wolfssl_ec_diffie_hellman_t interface.
42 */
43 wolfssl_ec_diffie_hellman_t public;
44
45 /**
46 * Diffie Hellman group number.
47 */
48 diffie_hellman_group_t group;
49
50 /**
51 * EC curve id for creating keys
52 */
53 ecc_curve_id curve_id;
54
55 /**
56 * Size of an ordinate in bytes
57 */
58 int keysize;
59
60 /**
61 * EC private (public) key
62 */
63 ecc_key key;
64
65 /**
66 * Shared secret
67 */
68 chunk_t shared_secret;
69 };
70
71 /**
72 * Convert a chunk to an ecc_point (which must already exist). The x and y
73 * coordinates of the point have to be concatenated in the chunk.
74 */
75 static bool chunk2ecp(chunk_t chunk, ecc_point *point)
76 {
77 if (!wolfssl_mp_split(chunk, point->x, point->y))
78 {
79 return FALSE;
80 }
81 if (mp_set(point->z, 1) != 0)
82 {
83 return FALSE;
84 }
85 return TRUE;
86 }
87
88 /**
89 * Convert an ec_point to a chunk by concatenating the x and y coordinates of
90 * the point. This function allocates memory for the chunk.
91 */
92 static bool ecp2chunk(int keysize, ecc_point *point, chunk_t *chunk,
93 bool x_coordinate_only)
94 {
95 mp_int *y = NULL;
96
97 if (!x_coordinate_only)
98 {
99 keysize *= 2;
100 y = point->y;
101 }
102 return wolfssl_mp_cat(keysize, point->x, y, chunk);
103 }
104
105 /**
106 * Perform the elliptic curve scalar multiplication.
107 */
108 static bool wolfssl_ecc_multiply(const ecc_set_type *ecc_set, mp_int *scalar,
109 ecc_point *point, ecc_point *r)
110 {
111 mp_int a, prime;
112 int ret;
113
114 if (mp_init(&a) != 0)
115 {
116 return FALSE;
117 }
118 if (mp_init(&prime) != 0)
119 {
120 mp_free(&a);
121 return FALSE;
122 }
123
124 ret = mp_read_radix(&a, ecc_set->Af, MP_RADIX_HEX);
125 if (ret == 0)
126 {
127 ret = mp_read_radix(&prime, ecc_set->prime, MP_RADIX_HEX);
128 }
129 if (ret == 0)
130 {
131 /* multiply the point by our secret */
132 ret = wc_ecc_mulmod(scalar, point, r, &a, &prime, 1);
133 }
134
135 mp_free(&prime);
136 mp_free(&a);
137
138 return ret == 0;
139 }
140
141 /**
142 * Compute the shared secret.
143 *
144 * We cannot use the function wc_ecc_shared_secret() because that returns only
145 * the x coordinate of the shared secret point (which is defined, for instance,
146 * in 'NIST SP 800-56A').
147 * However, we need both coordinates as RFC 4753 says: "The Diffie-Hellman
148 * public value is obtained by concatenating the x and y values. The format
149 * of the Diffie-Hellman shared secret value is the same as that of the
150 * Diffie-Hellman public value."
151 */
152 static bool compute_shared_key(private_wolfssl_ec_diffie_hellman_t *this,
153 ecc_point *pub_key, chunk_t *shared_secret)
154 {
155 ecc_point* secret;
156 bool x_coordinate_only;
157 bool success = FALSE;
158
159 if ((secret = wc_ecc_new_point()) == NULL)
160 {
161 return FALSE;
162 }
163
164 if (wolfssl_ecc_multiply(this->key.dp, &this->key.k, pub_key, secret))
165 {
166 /*
167 * The default setting ecp_x_coordinate_only = TRUE
168 * applies the following errata for RFC 4753:
169 * http://www.rfc-editor.org/errata_search.php?eid=9
170 */
171 x_coordinate_only = lib->settings->get_bool(lib->settings,
172 "%s.ecp_x_coordinate_only", TRUE, lib->ns);
173 success = ecp2chunk(this->keysize, secret, shared_secret,
174 x_coordinate_only);
175 }
176
177 wc_ecc_del_point(secret);
178 return success;
179 }
180
181 METHOD(diffie_hellman_t, set_other_public_value, bool,
182 private_wolfssl_ec_diffie_hellman_t *this, chunk_t value)
183 {
184 ecc_point *pub_key;
185
186 if (!diffie_hellman_verify_value(this->group, value))
187 {
188 return FALSE;
189 }
190
191 if ((pub_key = wc_ecc_new_point()) == NULL)
192 {
193 return FALSE;
194 }
195
196 if (!chunk2ecp(value, pub_key))
197 {
198 DBG1(DBG_LIB, "ECDH public value is malformed");
199 wc_ecc_del_point(pub_key);
200 return FALSE;
201 }
202
203 chunk_clear(&this->shared_secret);
204
205 if (!compute_shared_key(this, pub_key, &this->shared_secret))
206 {
207 DBG1(DBG_LIB, "ECDH shared secret computation failed");
208 chunk_clear(&this->shared_secret);
209 wc_ecc_del_point(pub_key);
210 return FALSE;
211 }
212 wc_ecc_del_point(pub_key);
213 return TRUE;
214 }
215
216 METHOD(diffie_hellman_t, get_my_public_value, bool,
217 private_wolfssl_ec_diffie_hellman_t *this,chunk_t *value)
218 {
219 return ecp2chunk(this->keysize, &this->key.pubkey, value, FALSE);
220 }
221
222 METHOD(diffie_hellman_t, set_private_value, bool,
223 private_wolfssl_ec_diffie_hellman_t *this, chunk_t value)
224 {
225 bool success = FALSE;
226 ecc_point *base;
227 int ret;
228
229 if ((base = wc_ecc_new_point()) == NULL)
230 {
231 return FALSE;
232 }
233
234 ret = mp_read_unsigned_bin(&this->key.k, value.ptr, value.len);
235 /* get base point */
236 if (ret == 0)
237 {
238 ret = mp_read_radix(base->x, this->key.dp->Gx, MP_RADIX_HEX);
239 }
240 if (ret == 0)
241 {
242 ret = mp_read_radix(base->y, this->key.dp->Gy, MP_RADIX_HEX);
243 }
244 if (ret == 0)
245 {
246 ret = mp_set(base->z, 1);
247 }
248 if (ret == 0)
249 {
250 /* calculate public key */
251 success = wolfssl_ecc_multiply(this->key.dp, &this->key.k, base,
252 &this->key.pubkey);
253 }
254
255 wc_ecc_del_point(base);
256
257 return success;
258 }
259
260 METHOD(diffie_hellman_t, get_shared_secret, bool,
261 private_wolfssl_ec_diffie_hellman_t *this, chunk_t *secret)
262 {
263 if (!this->shared_secret.len)
264 {
265 return FALSE;
266 }
267 *secret = chunk_clone(this->shared_secret);
268 return TRUE;
269 }
270
271 METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
272 private_wolfssl_ec_diffie_hellman_t *this)
273 {
274 return this->group;
275 }
276
277 METHOD(diffie_hellman_t, destroy, void,
278 private_wolfssl_ec_diffie_hellman_t *this)
279 {
280 wc_ecc_free(&this->key);
281 chunk_clear(&this->shared_secret);
282 free(this);
283 }
284
285 /*
286 * Described in header
287 */
288 wolfssl_ec_diffie_hellman_t *wolfssl_ec_diffie_hellman_create(diffie_hellman_group_t group)
289 {
290 private_wolfssl_ec_diffie_hellman_t *this;
291 WC_RNG rng;
292
293 INIT(this,
294 .public = {
295 .dh = {
296 .get_shared_secret = _get_shared_secret,
297 .set_other_public_value = _set_other_public_value,
298 .get_my_public_value = _get_my_public_value,
299 .set_private_value = _set_private_value,
300 .get_dh_group = _get_dh_group,
301 .destroy = _destroy,
302 },
303 },
304 .group = group,
305 );
306
307 if (wc_ecc_init(&this->key) != 0)
308 {
309 DBG1(DBG_LIB, "key init failed, ecdh create failed");
310 free(this);
311 return NULL;
312 }
313
314 switch (group)
315 {
316 case ECP_192_BIT:
317 this->curve_id = ECC_SECP192R1;
318 this->keysize = 192 / 8;
319 break;
320 case ECP_224_BIT:
321 this->curve_id = ECC_SECP224R1;
322 this->keysize = 224 / 8;
323 break;
324 case ECP_256_BIT:
325 this->curve_id = ECC_SECP256R1;
326 this->keysize = 256 / 8;
327 break;
328 case ECP_384_BIT:
329 this->curve_id = ECC_SECP384R1;
330 this->keysize = 384 / 8;
331 break;
332 case ECP_521_BIT:
333 this->curve_id = ECC_SECP521R1;
334 this->keysize = (521 + 7) / 8;
335 break;
336 #ifdef HAVE_BRAINPOOL
337 case ECP_224_BP:
338 this->curve_id = ECC_BRAINPOOLP224R1;
339 this->keysize = 224 / 8;
340 break;
341 case ECP_256_BP:
342 this->curve_id = ECC_BRAINPOOLP256R1;
343 this->keysize = 256 / 8;
344 break;
345 case ECP_384_BP:
346 this->curve_id = ECC_BRAINPOOLP384R1;
347 this->keysize = 384 / 8;
348 break;
349 case ECP_512_BP:
350 this->curve_id = ECC_BRAINPOOLP512R1;
351 this->keysize = 512 / 8;
352 break;
353 #endif
354 default:
355 destroy(this);
356 return NULL;
357 }
358
359 if (wc_InitRng(&rng) != 0)
360 {
361 DBG1(DBG_LIB, "RNG init failed, ecdh create failed");
362 destroy(this);
363 return NULL;
364 }
365
366 /* generate an EC private (public) key */
367 if (wc_ecc_make_key_ex(&rng, this->keysize, &this->key,
368 this->curve_id) != 0)
369 {
370 DBG1(DBG_LIB, "make key failed, wolfssl ECDH create failed");
371 destroy(this);
372 return NULL;
373 }
374 wc_FreeRng(&rng);
375
376 return &this->public;
377 }
378 #endif /* HAVE_ECC_DHE */