/src/samba/third_party/heimdal/lib/hcrypto/dh.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan |
3 | | * (Royal Institute of Technology, Stockholm, Sweden). |
4 | | * All rights reserved. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions |
8 | | * are met: |
9 | | * |
10 | | * 1. Redistributions of source code must retain the above copyright |
11 | | * notice, this list of conditions and the following disclaimer. |
12 | | * |
13 | | * 2. Redistributions in binary form must reproduce the above copyright |
14 | | * notice, this list of conditions and the following disclaimer in the |
15 | | * documentation and/or other materials provided with the distribution. |
16 | | * |
17 | | * 3. Neither the name of the Institute nor the names of its contributors |
18 | | * may be used to endorse or promote products derived from this software |
19 | | * without specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND |
22 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
24 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE |
25 | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
26 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
27 | | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
28 | | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
29 | | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
30 | | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
31 | | * SUCH DAMAGE. |
32 | | */ |
33 | | |
34 | | #ifdef HAVE_CONFIG_H |
35 | | #include <config.h> |
36 | | #endif |
37 | | #include <roken.h> |
38 | | |
39 | | #include <krb5-types.h> |
40 | | #include <rfc2459_asn1.h> |
41 | | |
42 | | #include <dh.h> |
43 | | |
44 | | /** |
45 | | * @page page_dh DH - Diffie-Hellman key exchange |
46 | | * |
47 | | * Diffie-Hellman key exchange is a protocol that allows two parties |
48 | | * to establish a shared secret key. |
49 | | * |
50 | | * Include and example how to use DH_new() and friends here. |
51 | | * |
52 | | * See the library functions here: @ref hcrypto_dh |
53 | | */ |
54 | | |
55 | | /** |
56 | | * Create a new DH object using DH_new_method(NULL), see DH_new_method(). |
57 | | * |
58 | | * @return a newly allocated DH object. |
59 | | * |
60 | | * @ingroup hcrypto_dh |
61 | | */ |
62 | | |
63 | | DH * |
64 | | DH_new(void) |
65 | 0 | { |
66 | 0 | return DH_new_method(NULL); |
67 | 0 | } |
68 | | |
69 | | /** |
70 | | * Create a new DH object from the given engine, if the NULL is used, |
71 | | * the default engine is used. Free the DH object with DH_free(). |
72 | | * |
73 | | * @param engine The engine to use to allocate the DH object. |
74 | | * |
75 | | * @return a newly allocated DH object. |
76 | | * |
77 | | * @ingroup hcrypto_dh |
78 | | */ |
79 | | |
80 | | DH * |
81 | | DH_new_method(ENGINE *engine) |
82 | 0 | { |
83 | 0 | DH *dh; |
84 | |
|
85 | 0 | dh = calloc(1, sizeof(*dh)); |
86 | 0 | if (dh == NULL) |
87 | 0 | return NULL; |
88 | | |
89 | 0 | dh->references = 1; |
90 | |
|
91 | 0 | if (engine) { |
92 | 0 | ENGINE_up_ref(engine); |
93 | 0 | dh->engine = engine; |
94 | 0 | } else { |
95 | 0 | dh->engine = ENGINE_get_default_DH(); |
96 | 0 | } |
97 | |
|
98 | 0 | if (dh->engine) { |
99 | 0 | dh->meth = ENGINE_get_DH(dh->engine); |
100 | 0 | if (dh->meth == NULL) { |
101 | 0 | ENGINE_finish(dh->engine); |
102 | 0 | free(dh); |
103 | 0 | return 0; |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | 0 | if (dh->meth == NULL) |
108 | 0 | dh->meth = DH_get_default_method(); |
109 | |
|
110 | 0 | (*dh->meth->init)(dh); |
111 | |
|
112 | 0 | return dh; |
113 | 0 | } |
114 | | |
115 | | /** |
116 | | * Free a DH object and release related resources, like ENGINE, that |
117 | | * the object was using. |
118 | | * |
119 | | * @param dh object to be freed. |
120 | | * |
121 | | * @ingroup hcrypto_dh |
122 | | */ |
123 | | |
124 | | void |
125 | | DH_free(DH *dh) |
126 | 0 | { |
127 | 0 | if (dh->references <= 0) |
128 | 0 | abort(); |
129 | | |
130 | 0 | if (--dh->references > 0) |
131 | 0 | return; |
132 | | |
133 | 0 | (*dh->meth->finish)(dh); |
134 | |
|
135 | 0 | if (dh->engine) |
136 | 0 | ENGINE_finish(dh->engine); |
137 | |
|
138 | 0 | #define free_if(f) if (f) { BN_free(f); } |
139 | 0 | free_if(dh->p); |
140 | 0 | free_if(dh->g); |
141 | 0 | free_if(dh->pub_key); |
142 | 0 | free_if(dh->priv_key); |
143 | 0 | free_if(dh->q); |
144 | 0 | free_if(dh->j); |
145 | 0 | free_if(dh->counter); |
146 | 0 | #undef free_if |
147 | |
|
148 | 0 | memset_s(dh, sizeof(*dh), 0, sizeof(*dh)); |
149 | 0 | free(dh); |
150 | 0 | } |
151 | | |
152 | | /** |
153 | | * Add a reference to the DH object. The object should be free with |
154 | | * DH_free() to drop the reference. |
155 | | * |
156 | | * @param dh the object to increase the reference count too. |
157 | | * |
158 | | * @return the updated reference count, can't safely be used except |
159 | | * for debug printing. |
160 | | * |
161 | | * @ingroup hcrypto_dh |
162 | | */ |
163 | | |
164 | | int |
165 | | DH_up_ref(DH *dh) |
166 | 0 | { |
167 | 0 | return ++dh->references; |
168 | 0 | } |
169 | | |
170 | | /** |
171 | | * The maximum output size of the DH_compute_key() function. |
172 | | * |
173 | | * @param dh The DH object to get the size from. |
174 | | * |
175 | | * @return the maximum size in bytes of the out data. |
176 | | * |
177 | | * @ingroup hcrypto_dh |
178 | | */ |
179 | | |
180 | | int |
181 | | DH_size(const DH *dh) |
182 | 0 | { |
183 | 0 | return BN_num_bytes(dh->p); |
184 | 0 | } |
185 | | |
186 | | /** |
187 | | * Set the data index idx in the DH object to data. |
188 | | * |
189 | | * @param dh DH object. |
190 | | * @param idx index to set the data for. |
191 | | * @param data data to store for the index idx. |
192 | | * |
193 | | * @return 1 on success. |
194 | | * |
195 | | * @ingroup hcrypto_dh |
196 | | */ |
197 | | |
198 | | int |
199 | | DH_set_ex_data(DH *dh, int idx, void *data) |
200 | 0 | { |
201 | 0 | dh->ex_data.sk = data; |
202 | 0 | return 1; |
203 | 0 | } |
204 | | |
205 | | /** |
206 | | * Get the data for index idx in the DH object. |
207 | | * |
208 | | * @param dh DH object. |
209 | | * @param idx index to get the data for. |
210 | | * |
211 | | * @return the object store in index idx |
212 | | * |
213 | | * @ingroup hcrypto_dh |
214 | | */ |
215 | | |
216 | | void * |
217 | | DH_get_ex_data(DH *dh, int idx) |
218 | 0 | { |
219 | 0 | return dh->ex_data.sk; |
220 | 0 | } |
221 | | |
222 | | /** |
223 | | * Generate DH parameters for the DH object give parameters. |
224 | | * |
225 | | * @param dh The DH object to generate parameters for. |
226 | | * @param prime_len length of the prime |
227 | | * @param generator generator, g |
228 | | * @param cb Callback parameters to show progress, can be NULL. |
229 | | * |
230 | | * @return the maximum size in bytes of the out data. |
231 | | * |
232 | | * @ingroup hcrypto_dh |
233 | | */ |
234 | | |
235 | | int |
236 | | DH_generate_parameters_ex(DH *dh, int prime_len, int generator, BN_GENCB *cb) |
237 | 0 | { |
238 | 0 | if (dh->meth->generate_params) |
239 | 0 | return dh->meth->generate_params(dh, prime_len, generator, cb); |
240 | 0 | return 0; |
241 | 0 | } |
242 | | |
243 | | /** |
244 | | * Check that the public key is sane. |
245 | | * |
246 | | * @param dh the local peer DH parameters. |
247 | | * @param pub_key the remote peer public key parameters. |
248 | | * @param codes return that the failures of the pub_key are. |
249 | | * |
250 | | * @return 1 on success, 0 on failure and *codes is set the the |
251 | | * combined fail check for the public key |
252 | | * |
253 | | * @ingroup hcrypto_dh |
254 | | */ |
255 | | |
256 | | int |
257 | | DH_check_pubkey(const DH *dh, const BIGNUM *pub_key, int *codes) |
258 | 0 | { |
259 | 0 | BIGNUM *bn = NULL, *sum = NULL; |
260 | 0 | int ret = 0; |
261 | |
|
262 | 0 | *codes = 0; |
263 | | |
264 | | /** |
265 | | * Checks that the function performs are: |
266 | | * - pub_key is not negative |
267 | | */ |
268 | |
|
269 | 0 | if (BN_is_negative(pub_key)) |
270 | 0 | goto out; |
271 | | |
272 | | /** |
273 | | * - pub_key > 1 and pub_key < p - 1, |
274 | | * to avoid small subgroups attack. |
275 | | */ |
276 | | |
277 | 0 | bn = BN_new(); |
278 | 0 | if (bn == NULL) |
279 | 0 | goto out; |
280 | | |
281 | 0 | if (!BN_set_word(bn, 1)) |
282 | 0 | goto out; |
283 | | |
284 | 0 | if (BN_cmp(bn, pub_key) >= 0) |
285 | 0 | *codes |= DH_CHECK_PUBKEY_TOO_SMALL; |
286 | |
|
287 | 0 | sum = BN_new(); |
288 | 0 | if (sum == NULL) |
289 | 0 | goto out; |
290 | | |
291 | 0 | BN_uadd(sum, pub_key, bn); |
292 | |
|
293 | 0 | if (BN_cmp(sum, dh->p) >= 0) |
294 | 0 | *codes |= DH_CHECK_PUBKEY_TOO_LARGE; |
295 | | |
296 | | /** |
297 | | * - if g == 2, pub_key have more then one bit set, |
298 | | * if bits set is 1, log_2(pub_key) is trival |
299 | | */ |
300 | |
|
301 | 0 | if (!BN_set_word(bn, 2)) |
302 | 0 | goto out; |
303 | | |
304 | 0 | if (BN_cmp(bn, dh->g) == 0) { |
305 | 0 | unsigned i, n = BN_num_bits(pub_key); |
306 | 0 | unsigned bits = 0; |
307 | |
|
308 | 0 | for (i = 0; i < n; i++) |
309 | 0 | if (BN_is_bit_set(pub_key, i)) |
310 | 0 | bits++; |
311 | |
|
312 | 0 | if (bits < 2) { |
313 | 0 | *codes |= DH_CHECK_PUBKEY_TOO_SMALL; |
314 | 0 | goto out; |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | 0 | ret = 1; |
319 | 0 | out: |
320 | 0 | if (bn) |
321 | 0 | BN_free(bn); |
322 | 0 | if (sum) |
323 | 0 | BN_free(sum); |
324 | |
|
325 | 0 | return ret; |
326 | 0 | } |
327 | | |
328 | | /** |
329 | | * Generate a new DH private-public key pair. The dh parameter must be |
330 | | * allocted first with DH_new(). dh->p and dp->g must be set. |
331 | | * |
332 | | * @param dh dh parameter. |
333 | | * |
334 | | * @return 1 on success. |
335 | | * |
336 | | * @ingroup hcrypto_dh |
337 | | */ |
338 | | |
339 | | int |
340 | | DH_generate_key(DH *dh) |
341 | 0 | { |
342 | 0 | return dh->meth->generate_key(dh); |
343 | 0 | } |
344 | | |
345 | | /** |
346 | | * Complute the shared secret key. |
347 | | * |
348 | | * @param shared_key the resulting shared key, need to be at least |
349 | | * DH_size() large. |
350 | | * @param peer_pub_key the peer's public key. |
351 | | * @param dh the dh key pair. |
352 | | * |
353 | | * @return 1 on success. |
354 | | * |
355 | | * @ingroup hcrypto_dh |
356 | | */ |
357 | | |
358 | | int |
359 | | DH_compute_key(unsigned char *shared_key, |
360 | | const BIGNUM *peer_pub_key, DH *dh) |
361 | 0 | { |
362 | 0 | int codes; |
363 | | |
364 | | /** |
365 | | * Checks that the pubkey passed in is valid using |
366 | | * DH_check_pubkey(). |
367 | | */ |
368 | |
|
369 | 0 | if (!DH_check_pubkey(dh, peer_pub_key, &codes) || codes != 0) |
370 | 0 | return -1; |
371 | | |
372 | 0 | return dh->meth->compute_key(shared_key, peer_pub_key, dh); |
373 | 0 | } |
374 | | |
375 | | /** |
376 | | * Set a new method for the DH keypair. |
377 | | * |
378 | | * @param dh dh parameter. |
379 | | * @param method the new method for the DH parameter. |
380 | | * |
381 | | * @return 1 on success. |
382 | | * |
383 | | * @ingroup hcrypto_dh |
384 | | */ |
385 | | |
386 | | int |
387 | | DH_set_method(DH *dh, const DH_METHOD *method) |
388 | 0 | { |
389 | 0 | (*dh->meth->finish)(dh); |
390 | 0 | if (dh->engine) { |
391 | 0 | ENGINE_finish(dh->engine); |
392 | 0 | dh->engine = NULL; |
393 | 0 | } |
394 | 0 | dh->meth = method; |
395 | 0 | (*dh->meth->init)(dh); |
396 | 0 | return 1; |
397 | 0 | } |
398 | | |
399 | | /* |
400 | | * |
401 | | */ |
402 | | |
403 | | static int |
404 | | dh_null_generate_key(DH *dh) |
405 | 0 | { |
406 | 0 | return 0; |
407 | 0 | } |
408 | | |
409 | | static int |
410 | | dh_null_compute_key(unsigned char *shared,const BIGNUM *pub, DH *dh) |
411 | 0 | { |
412 | 0 | return 0; |
413 | 0 | } |
414 | | |
415 | | static int |
416 | | dh_null_init(DH *dh) |
417 | 0 | { |
418 | 0 | return 1; |
419 | 0 | } |
420 | | |
421 | | static int |
422 | | dh_null_finish(DH *dh) |
423 | 0 | { |
424 | 0 | return 1; |
425 | 0 | } |
426 | | |
427 | | static int |
428 | | dh_null_generate_params(DH *dh, int prime_num, int len, BN_GENCB *cb) |
429 | 0 | { |
430 | 0 | return 0; |
431 | 0 | } |
432 | | |
433 | | static const DH_METHOD dh_null_method = { |
434 | | "hcrypto null DH", |
435 | | dh_null_generate_key, |
436 | | dh_null_compute_key, |
437 | | NULL, |
438 | | dh_null_init, |
439 | | dh_null_finish, |
440 | | 0, |
441 | | NULL, |
442 | | dh_null_generate_params |
443 | | }; |
444 | | |
445 | | extern const DH_METHOD _hc_dh_ltm_method; |
446 | | static const DH_METHOD *dh_default_method = &_hc_dh_ltm_method; |
447 | | |
448 | | /** |
449 | | * Return the dummy DH implementation. |
450 | | * |
451 | | * @return pointer to a DH_METHOD. |
452 | | * |
453 | | * @ingroup hcrypto_dh |
454 | | */ |
455 | | |
456 | | const DH_METHOD * |
457 | | DH_null_method(void) |
458 | 0 | { |
459 | 0 | return &dh_null_method; |
460 | 0 | } |
461 | | |
462 | | /** |
463 | | * Set the default DH implementation. |
464 | | * |
465 | | * @param meth pointer to a DH_METHOD. |
466 | | * |
467 | | * @ingroup hcrypto_dh |
468 | | */ |
469 | | |
470 | | void |
471 | | DH_set_default_method(const DH_METHOD *meth) |
472 | 0 | { |
473 | 0 | dh_default_method = meth; |
474 | 0 | } |
475 | | |
476 | | /** |
477 | | * Return the default DH implementation. |
478 | | * |
479 | | * @return pointer to a DH_METHOD. |
480 | | * |
481 | | * @ingroup hcrypto_dh |
482 | | */ |
483 | | |
484 | | const DH_METHOD * |
485 | | DH_get_default_method(void) |
486 | 0 | { |
487 | 0 | return dh_default_method; |
488 | 0 | } |
489 | | |
490 | | /* |
491 | | * |
492 | | */ |
493 | | |
494 | | static int |
495 | | bn2heim_int(BIGNUM *bn, heim_integer *integer) |
496 | 0 | { |
497 | 0 | integer->length = BN_num_bytes(bn); |
498 | 0 | integer->data = malloc(integer->length); |
499 | 0 | if (integer->data == NULL) { |
500 | 0 | integer->length = 0; |
501 | 0 | return ENOMEM; |
502 | 0 | } |
503 | 0 | BN_bn2bin(bn, integer->data); |
504 | 0 | integer->negative = BN_is_negative(bn); |
505 | 0 | return 0; |
506 | 0 | } |
507 | | |
508 | | /** |
509 | | * |
510 | | */ |
511 | | |
512 | | int |
513 | | i2d_DHparams(DH *dh, unsigned char **pp) |
514 | 0 | { |
515 | 0 | DHParameter data; |
516 | 0 | size_t size; |
517 | 0 | int ret; |
518 | |
|
519 | 0 | memset(&data, 0, sizeof(data)); |
520 | |
|
521 | 0 | if (bn2heim_int(dh->p, &data.prime) || |
522 | 0 | bn2heim_int(dh->g, &data.base)) |
523 | 0 | { |
524 | 0 | free_DHParameter(&data); |
525 | 0 | return -1; |
526 | 0 | } |
527 | | |
528 | 0 | if (pp == NULL) { |
529 | 0 | size = length_DHParameter(&data); |
530 | 0 | free_DHParameter(&data); |
531 | 0 | } else { |
532 | 0 | void *p; |
533 | 0 | size_t len; |
534 | |
|
535 | 0 | ASN1_MALLOC_ENCODE(DHParameter, p, len, &data, &size, ret); |
536 | 0 | free_DHParameter(&data); |
537 | 0 | if (ret) |
538 | 0 | return -1; |
539 | 0 | if (len != size) { |
540 | 0 | abort(); |
541 | 0 | return -1; |
542 | 0 | } |
543 | | |
544 | 0 | memcpy(*pp, p, size); |
545 | 0 | free(p); |
546 | |
|
547 | 0 | *pp += size; |
548 | 0 | } |
549 | | |
550 | 0 | return size; |
551 | 0 | } |