blob: 305a5af79141b5802ab120ce3f24b33f92262f34 [file] [log] [blame]
David Benjamin4298d772015-12-19 00:18:25 -05001/* Copyright (c) 2015, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15#include <openssl/ssl.h>
16
17#include <assert.h>
18#include <string.h>
19
20#include <openssl/bn.h>
21#include <openssl/bytestring.h>
22#include <openssl/curve25519.h>
23#include <openssl/ec.h>
24#include <openssl/err.h>
25#include <openssl/mem.h>
David Benjamin98193672016-03-25 18:07:11 -040026#include <openssl/nid.h>
David Benjamin4298d772015-12-19 00:18:25 -050027
28#include "internal.h"
29
30
31/* |EC_POINT| implementation. */
32
33static void ssl_ec_point_cleanup(SSL_ECDH_CTX *ctx) {
34 BIGNUM *private_key = (BIGNUM *)ctx->data;
35 BN_clear_free(private_key);
36}
37
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -070038static int ssl_ec_point_offer(SSL_ECDH_CTX *ctx, CBB *out) {
David Benjamin4298d772015-12-19 00:18:25 -050039 assert(ctx->data == NULL);
40 BIGNUM *private_key = BN_new();
41 if (private_key == NULL) {
42 return 0;
43 }
44 ctx->data = private_key;
45
46 /* Set up a shared |BN_CTX| for all operations. */
47 BN_CTX *bn_ctx = BN_CTX_new();
48 if (bn_ctx == NULL) {
49 return 0;
50 }
51 BN_CTX_start(bn_ctx);
52
53 int ret = 0;
54 EC_POINT *public_key = NULL;
55 EC_GROUP *group = EC_GROUP_new_by_curve_name(ctx->method->nid);
56 if (group == NULL) {
57 goto err;
58 }
59
60 /* Generate a private key. */
61 const BIGNUM *order = EC_GROUP_get0_order(group);
62 do {
63 if (!BN_rand_range(private_key, order)) {
64 goto err;
65 }
66 } while (BN_is_zero(private_key));
67
David Benjamin6014ea62015-12-30 21:39:34 -050068 /* Compute the corresponding public key and serialize it. */
David Benjamin4298d772015-12-19 00:18:25 -050069 public_key = EC_POINT_new(group);
70 if (public_key == NULL ||
David Benjamin6014ea62015-12-30 21:39:34 -050071 !EC_POINT_mul(group, public_key, private_key, NULL, NULL, bn_ctx) ||
72 !EC_POINT_point2cbb(out, group, public_key, POINT_CONVERSION_UNCOMPRESSED,
73 bn_ctx)) {
David Benjamin4298d772015-12-19 00:18:25 -050074 goto err;
75 }
76
77 ret = 1;
78
79err:
80 EC_GROUP_free(group);
81 EC_POINT_free(public_key);
82 BN_CTX_end(bn_ctx);
83 BN_CTX_free(bn_ctx);
84 return ret;
85}
86
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -070087static int ssl_ec_point_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
88 size_t *out_secret_len, uint8_t *out_alert,
89 const uint8_t *peer_key, size_t peer_key_len) {
David Benjamin4298d772015-12-19 00:18:25 -050090 BIGNUM *private_key = (BIGNUM *)ctx->data;
91 assert(private_key != NULL);
92 *out_alert = SSL_AD_INTERNAL_ERROR;
93
94 /* Set up a shared |BN_CTX| for all operations. */
95 BN_CTX *bn_ctx = BN_CTX_new();
96 if (bn_ctx == NULL) {
97 return 0;
98 }
99 BN_CTX_start(bn_ctx);
100
101 int ret = 0;
102 EC_GROUP *group = EC_GROUP_new_by_curve_name(ctx->method->nid);
103 EC_POINT *peer_point = NULL, *result = NULL;
104 uint8_t *secret = NULL;
105 if (group == NULL) {
106 goto err;
107 }
108
109 /* Compute the x-coordinate of |peer_key| * |private_key|. */
110 peer_point = EC_POINT_new(group);
111 result = EC_POINT_new(group);
112 if (peer_point == NULL || result == NULL) {
113 goto err;
114 }
115 BIGNUM *x = BN_CTX_get(bn_ctx);
116 if (x == NULL) {
117 goto err;
118 }
119 if (!EC_POINT_oct2point(group, peer_point, peer_key, peer_key_len, bn_ctx)) {
120 *out_alert = SSL_AD_DECODE_ERROR;
121 goto err;
122 }
123 if (!EC_POINT_mul(group, result, NULL, peer_point, private_key, bn_ctx) ||
124 !EC_POINT_get_affine_coordinates_GFp(group, result, x, NULL, bn_ctx)) {
125 goto err;
126 }
127
128 /* Encode the x-coordinate left-padded with zeros. */
129 size_t secret_len = (EC_GROUP_get_degree(group) + 7) / 8;
130 secret = OPENSSL_malloc(secret_len);
131 if (secret == NULL || !BN_bn2bin_padded(secret, secret_len, x)) {
132 goto err;
133 }
134
135 *out_secret = secret;
136 *out_secret_len = secret_len;
137 secret = NULL;
138 ret = 1;
139
140err:
141 EC_GROUP_free(group);
142 EC_POINT_free(peer_point);
143 EC_POINT_free(result);
144 BN_CTX_end(bn_ctx);
145 BN_CTX_free(bn_ctx);
146 OPENSSL_free(secret);
147 return ret;
148}
149
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700150static int ssl_ec_point_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
151 uint8_t **out_secret, size_t *out_secret_len,
152 uint8_t *out_alert, const uint8_t *peer_key,
153 size_t peer_key_len) {
154 *out_alert = SSL_AD_INTERNAL_ERROR;
155 if (!ssl_ec_point_offer(ctx, out_public_key) ||
156 !ssl_ec_point_finish(ctx, out_secret, out_secret_len, out_alert, peer_key,
157 peer_key_len)) {
158 return 0;
159 }
160 return 1;
161}
David Benjamin4298d772015-12-19 00:18:25 -0500162
163/* X25119 implementation. */
164
165static void ssl_x25519_cleanup(SSL_ECDH_CTX *ctx) {
166 if (ctx->data == NULL) {
167 return;
168 }
169 OPENSSL_cleanse(ctx->data, 32);
170 OPENSSL_free(ctx->data);
171}
172
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700173static int ssl_x25519_offer(SSL_ECDH_CTX *ctx, CBB *out) {
David Benjamin4298d772015-12-19 00:18:25 -0500174 assert(ctx->data == NULL);
175
176 ctx->data = OPENSSL_malloc(32);
177 if (ctx->data == NULL) {
178 OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
179 return 0;
180 }
181 uint8_t public_key[32];
182 X25519_keypair(public_key, (uint8_t *)ctx->data);
183 return CBB_add_bytes(out, public_key, sizeof(public_key));
184}
185
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700186static int ssl_x25519_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
187 size_t *out_secret_len, uint8_t *out_alert,
188 const uint8_t *peer_key, size_t peer_key_len) {
David Benjamin4298d772015-12-19 00:18:25 -0500189 assert(ctx->data != NULL);
190 *out_alert = SSL_AD_INTERNAL_ERROR;
191
192 uint8_t *secret = OPENSSL_malloc(32);
193 if (secret == NULL) {
194 return 0;
195 }
196
197 if (peer_key_len != 32 ||
198 !X25519(secret, (uint8_t *)ctx->data, peer_key)) {
199 OPENSSL_free(secret);
200 *out_alert = SSL_AD_DECODE_ERROR;
201 OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
202 return 0;
203 }
204
205 *out_secret = secret;
206 *out_secret_len = 32;
207 return 1;
208}
209
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700210static int ssl_x25519_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
211 uint8_t **out_secret, size_t *out_secret_len,
212 uint8_t *out_alert, const uint8_t *peer_key,
213 size_t peer_key_len) {
214 *out_alert = SSL_AD_INTERNAL_ERROR;
215 if (!ssl_x25519_offer(ctx, out_public_key) ||
216 !ssl_x25519_finish(ctx, out_secret, out_secret_len, out_alert, peer_key,
217 peer_key_len)) {
218 return 0;
219 }
220 return 1;
221}
David Benjamin4298d772015-12-19 00:18:25 -0500222
David Benjamin974c7ba2015-12-19 16:58:04 -0500223/* Legacy DHE-based implementation. */
224
225static void ssl_dhe_cleanup(SSL_ECDH_CTX *ctx) {
226 DH_free((DH *)ctx->data);
227}
228
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700229static int ssl_dhe_offer(SSL_ECDH_CTX *ctx, CBB *out) {
David Benjamin974c7ba2015-12-19 16:58:04 -0500230 DH *dh = (DH *)ctx->data;
231 /* The group must have been initialized already, but not the key. */
232 assert(dh != NULL);
233 assert(dh->priv_key == NULL);
234
235 /* Due to a bug in yaSSL, the public key must be zero padded to the size of
236 * the prime. */
237 return DH_generate_key(dh) &&
238 BN_bn2cbb_padded(out, BN_num_bytes(dh->p), dh->pub_key);
239}
240
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700241static int ssl_dhe_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
242 size_t *out_secret_len, uint8_t *out_alert,
243 const uint8_t *peer_key, size_t peer_key_len) {
David Benjamin974c7ba2015-12-19 16:58:04 -0500244 DH *dh = (DH *)ctx->data;
245 assert(dh != NULL);
246 assert(dh->priv_key != NULL);
247 *out_alert = SSL_AD_INTERNAL_ERROR;
248
249 int secret_len = 0;
250 uint8_t *secret = NULL;
251 BIGNUM *peer_point = BN_bin2bn(peer_key, peer_key_len, NULL);
252 if (peer_point == NULL) {
253 goto err;
254 }
255
256 secret = OPENSSL_malloc(DH_size(dh));
257 if (secret == NULL) {
258 goto err;
259 }
260 secret_len = DH_compute_key(secret, peer_point, dh);
261 if (secret_len <= 0) {
262 goto err;
263 }
264
265 *out_secret = secret;
266 *out_secret_len = (size_t)secret_len;
267 BN_free(peer_point);
268 return 1;
269
270err:
271 if (secret_len > 0) {
272 OPENSSL_cleanse(secret, (size_t)secret_len);
273 }
274 OPENSSL_free(secret);
275 BN_free(peer_point);
276 return 0;
277}
278
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700279static int ssl_dhe_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
280 uint8_t **out_secret, size_t *out_secret_len,
281 uint8_t *out_alert, const uint8_t *peer_key,
282 size_t peer_key_len) {
283 *out_alert = SSL_AD_INTERNAL_ERROR;
284 if (!ssl_dhe_offer(ctx, out_public_key) ||
285 !ssl_dhe_finish(ctx, out_secret, out_secret_len, out_alert, peer_key,
286 peer_key_len)) {
287 return 0;
288 }
289 return 1;
290}
291
David Benjamin974c7ba2015-12-19 16:58:04 -0500292static const SSL_ECDH_METHOD kDHEMethod = {
293 NID_undef, 0, "",
294 ssl_dhe_cleanup,
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700295 ssl_dhe_offer,
296 ssl_dhe_accept,
297 ssl_dhe_finish,
David Benjamin974c7ba2015-12-19 16:58:04 -0500298};
299
David Benjamin4298d772015-12-19 00:18:25 -0500300static const SSL_ECDH_METHOD kMethods[] = {
301 {
302 NID_X9_62_prime256v1,
303 SSL_CURVE_SECP256R1,
304 "P-256",
305 ssl_ec_point_cleanup,
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700306 ssl_ec_point_offer,
307 ssl_ec_point_accept,
308 ssl_ec_point_finish,
David Benjamin4298d772015-12-19 00:18:25 -0500309 },
310 {
311 NID_secp384r1,
312 SSL_CURVE_SECP384R1,
313 "P-384",
314 ssl_ec_point_cleanup,
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700315 ssl_ec_point_offer,
316 ssl_ec_point_accept,
317 ssl_ec_point_finish,
David Benjamin4298d772015-12-19 00:18:25 -0500318 },
319 {
320 NID_secp521r1,
321 SSL_CURVE_SECP521R1,
322 "P-521",
323 ssl_ec_point_cleanup,
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700324 ssl_ec_point_offer,
325 ssl_ec_point_accept,
326 ssl_ec_point_finish,
David Benjamin4298d772015-12-19 00:18:25 -0500327 },
328 {
David Benjaminad004af2016-03-05 14:35:35 -0500329 NID_X25519,
David Benjaminfc6e5a72016-01-19 15:36:43 -0500330 SSL_CURVE_X25519,
David Benjamin4298d772015-12-19 00:18:25 -0500331 "X25519",
332 ssl_x25519_cleanup,
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700333 ssl_x25519_offer,
334 ssl_x25519_accept,
335 ssl_x25519_finish,
David Benjamin4298d772015-12-19 00:18:25 -0500336 },
337};
338
339static const SSL_ECDH_METHOD *method_from_curve_id(uint16_t curve_id) {
340 size_t i;
341 for (i = 0; i < sizeof(kMethods) / sizeof(kMethods[0]); i++) {
342 if (kMethods[i].curve_id == curve_id) {
343 return &kMethods[i];
344 }
345 }
346 return NULL;
347}
348
349static const SSL_ECDH_METHOD *method_from_nid(int nid) {
350 size_t i;
351 for (i = 0; i < sizeof(kMethods) / sizeof(kMethods[0]); i++) {
352 if (kMethods[i].nid == nid) {
353 return &kMethods[i];
354 }
355 }
356 return NULL;
357}
358
359const char* SSL_get_curve_name(uint16_t curve_id) {
360 const SSL_ECDH_METHOD *method = method_from_curve_id(curve_id);
361 if (method == NULL) {
362 return NULL;
363 }
364 return method->name;
365}
366
367int ssl_nid_to_curve_id(uint16_t *out_curve_id, int nid) {
368 const SSL_ECDH_METHOD *method = method_from_nid(nid);
369 if (method == NULL) {
370 return 0;
371 }
372 *out_curve_id = method->curve_id;
373 return 1;
374}
375
376int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t curve_id) {
377 SSL_ECDH_CTX_cleanup(ctx);
378
379 const SSL_ECDH_METHOD *method = method_from_curve_id(curve_id);
380 if (method == NULL) {
381 OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE);
382 return 0;
383 }
384 ctx->method = method;
385 return 1;
386}
387
David Benjamin974c7ba2015-12-19 16:58:04 -0500388void SSL_ECDH_CTX_init_for_dhe(SSL_ECDH_CTX *ctx, DH *params) {
389 SSL_ECDH_CTX_cleanup(ctx);
390
391 ctx->method = &kDHEMethod;
392 ctx->data = params;
393}
394
David Benjamin4298d772015-12-19 00:18:25 -0500395void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx) {
396 if (ctx->method == NULL) {
397 return;
398 }
399 ctx->method->cleanup(ctx);
400 ctx->method = NULL;
401 ctx->data = NULL;
402}
403
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700404int SSL_ECDH_CTX_offer(SSL_ECDH_CTX *ctx, CBB *out_public_key) {
405 return ctx->method->offer(ctx, out_public_key);
David Benjamin4298d772015-12-19 00:18:25 -0500406}
407
Matt Braithwaitef4ce8e52016-05-16 14:27:14 -0700408int SSL_ECDH_CTX_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
409 uint8_t **out_secret, size_t *out_secret_len,
410 uint8_t *out_alert, const uint8_t *peer_key,
411 size_t peer_key_len) {
412 return ctx->method->accept(ctx, out_public_key, out_secret, out_secret_len,
413 out_alert, peer_key, peer_key_len);
414}
415
416int SSL_ECDH_CTX_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
417 size_t *out_secret_len, uint8_t *out_alert,
418 const uint8_t *peer_key, size_t peer_key_len) {
419 return ctx->method->finish(ctx, out_secret, out_secret_len, out_alert,
420 peer_key, peer_key_len);
David Benjamin4298d772015-12-19 00:18:25 -0500421}