1 /* 2 * Diffie-Hellman-Merkle key exchange 3 * 4 * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved 5 * SPDX-License-Identifier: GPL-2.0 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * This file is part of mbed TLS (https://tls.mbed.org) 22 */ 23 /* 24 * The following sources were referenced in the design of this implementation 25 * of the Diffie-Hellman-Merkle algorithm: 26 * 27 * [1] Handbook of Applied Cryptography - 1997, Chapter 12 28 * Menezes, van Oorschot and Vanstone 29 * 30 */ 31 32 #if !defined(MBEDTLS_CONFIG_FILE) 33 #include "mbedtls/config.h" 34 #else 35 #include MBEDTLS_CONFIG_FILE 36 #endif 37 38 #if defined(MBEDTLS_DHM_C) 39 40 #include "mbedtls/dhm.h" 41 42 #include <string.h> 43 44 #if defined(MBEDTLS_PEM_PARSE_C) 45 #include "mbedtls/pem.h" 46 #endif 47 48 #if defined(MBEDTLS_ASN1_PARSE_C) 49 #include "mbedtls/asn1.h" 50 #endif 51 52 #if defined(MBEDTLS_PLATFORM_C) 53 #include "mbedtls/platform.h" 54 #else 55 #include <stdlib.h> 56 #include <stdio.h> 57 #define mbedtls_printf printf 58 #define mbedtls_calloc calloc 59 #define mbedtls_free free 60 #endif 61 62 /* Implementation that should never be optimized out by the compiler */ 63 static void mbedtls_zeroize( void *v, size_t n ) { 64 volatile unsigned char *p = v; while( n-- ) *p++ = 0; 65 } 66 67 /* 68 * helper to validate the mbedtls_mpi size and import it 69 */ 70 static int dhm_read_bignum( mbedtls_mpi *X, 71 unsigned char **p, 72 const unsigned char *end ) 73 { 74 int ret, n; 75 76 if( end - *p < 2 ) 77 return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA ); 78 79 n = ( (*p)[0] << 8 ) | (*p)[1]; 80 (*p) += 2; 81 82 if( (int)( end - *p ) < n ) 83 return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA ); 84 85 if( ( ret = mbedtls_mpi_read_binary( X, *p, n ) ) != 0 ) 86 return( MBEDTLS_ERR_DHM_READ_PARAMS_FAILED + ret ); 87 88 (*p) += n; 89 90 return( 0 ); 91 } 92 93 /* 94 * Verify sanity of parameter with regards to P 95 * 96 * Parameter should be: 2 <= public_param <= P - 2 97 * 98 * For more information on the attack, see: 99 * http://www.cl.cam.ac.uk/~rja14/Papers/psandqs.pdf 100 * http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2005-2643 101 */ 102 static int dhm_check_range( const mbedtls_mpi *param, const mbedtls_mpi *P ) 103 { 104 mbedtls_mpi L, U; 105 int ret = MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 106 107 mbedtls_mpi_init( &L ); mbedtls_mpi_init( &U ); 108 109 MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &L, 2 ) ); 110 MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &U, P, 2 ) ); 111 112 if( mbedtls_mpi_cmp_mpi( param, &L ) >= 0 && 113 mbedtls_mpi_cmp_mpi( param, &U ) <= 0 ) 114 { 115 ret = 0; 116 } 117 118 cleanup: 119 mbedtls_mpi_free( &L ); mbedtls_mpi_free( &U ); 120 return( ret ); 121 } 122 123 void mbedtls_dhm_init( mbedtls_dhm_context *ctx ) 124 { 125 memset( ctx, 0, sizeof( mbedtls_dhm_context ) ); 126 } 127 128 /* 129 * Parse the ServerKeyExchange parameters 130 */ 131 int mbedtls_dhm_read_params( mbedtls_dhm_context *ctx, 132 unsigned char **p, 133 const unsigned char *end ) 134 { 135 int ret; 136 137 if( ( ret = dhm_read_bignum( &ctx->P, p, end ) ) != 0 || 138 ( ret = dhm_read_bignum( &ctx->G, p, end ) ) != 0 || 139 ( ret = dhm_read_bignum( &ctx->GY, p, end ) ) != 0 ) 140 return( ret ); 141 142 if( ( ret = dhm_check_range( &ctx->GY, &ctx->P ) ) != 0 ) 143 return( ret ); 144 145 ctx->len = mbedtls_mpi_size( &ctx->P ); 146 147 return( 0 ); 148 } 149 150 /* 151 * Setup and write the ServerKeyExchange parameters 152 */ 153 int mbedtls_dhm_make_params( mbedtls_dhm_context *ctx, int x_size, 154 unsigned char *output, size_t *olen, 155 int (*f_rng)(void *, unsigned char *, size_t), 156 void *p_rng ) 157 { 158 int ret, count = 0; 159 size_t n1, n2, n3; 160 unsigned char *p; 161 162 if( mbedtls_mpi_cmp_int( &ctx->P, 0 ) == 0 ) 163 return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA ); 164 165 /* 166 * Generate X as large as possible ( < P ) 167 */ 168 do 169 { 170 MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &ctx->X, x_size, f_rng, p_rng ) ); 171 172 while( mbedtls_mpi_cmp_mpi( &ctx->X, &ctx->P ) >= 0 ) 173 MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &ctx->X, 1 ) ); 174 175 if( count++ > 10 ) 176 return( MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED ); 177 } 178 while( dhm_check_range( &ctx->X, &ctx->P ) != 0 ); 179 180 /* 181 * Calculate GX = G^X mod P 182 */ 183 MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &ctx->GX, &ctx->G, &ctx->X, 184 &ctx->P , &ctx->RP ) ); 185 186 if( ( ret = dhm_check_range( &ctx->GX, &ctx->P ) ) != 0 ) 187 return( ret ); 188 189 /* 190 * export P, G, GX 191 */ 192 #define DHM_MPI_EXPORT(X,n) \ 193 MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( X, p + 2, n ) ); \ 194 *p++ = (unsigned char)( n >> 8 ); \ 195 *p++ = (unsigned char)( n ); p += n; 196 197 n1 = mbedtls_mpi_size( &ctx->P ); 198 n2 = mbedtls_mpi_size( &ctx->G ); 199 n3 = mbedtls_mpi_size( &ctx->GX ); 200 201 p = output; 202 DHM_MPI_EXPORT( &ctx->P , n1 ); 203 DHM_MPI_EXPORT( &ctx->G , n2 ); 204 DHM_MPI_EXPORT( &ctx->GX, n3 ); 205 206 *olen = p - output; 207 208 ctx->len = n1; 209 210 cleanup: 211 212 if( ret != 0 ) 213 return( MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED + ret ); 214 215 return( 0 ); 216 } 217 218 /* 219 * Import the peer's public value G^Y 220 */ 221 int mbedtls_dhm_read_public( mbedtls_dhm_context *ctx, 222 const unsigned char *input, size_t ilen ) 223 { 224 int ret; 225 226 if( ctx == NULL || ilen < 1 || ilen > ctx->len ) 227 return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA ); 228 229 if( ( ret = mbedtls_mpi_read_binary( &ctx->GY, input, ilen ) ) != 0 ) 230 return( MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED + ret ); 231 232 return( 0 ); 233 } 234 235 /* 236 * Create own private value X and export G^X 237 */ 238 int mbedtls_dhm_make_public( mbedtls_dhm_context *ctx, int x_size, 239 unsigned char *output, size_t olen, 240 int (*f_rng)(void *, unsigned char *, size_t), 241 void *p_rng ) 242 { 243 int ret, count = 0; 244 245 if( ctx == NULL || olen < 1 || olen > ctx->len ) 246 return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA ); 247 248 if( mbedtls_mpi_cmp_int( &ctx->P, 0 ) == 0 ) 249 return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA ); 250 251 /* 252 * generate X and calculate GX = G^X mod P 253 */ 254 do 255 { 256 MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &ctx->X, x_size, f_rng, p_rng ) ); 257 258 while( mbedtls_mpi_cmp_mpi( &ctx->X, &ctx->P ) >= 0 ) 259 MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &ctx->X, 1 ) ); 260 261 if( count++ > 10 ) 262 return( MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED ); 263 } 264 while( dhm_check_range( &ctx->X, &ctx->P ) != 0 ); 265 266 MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &ctx->GX, &ctx->G, &ctx->X, 267 &ctx->P , &ctx->RP ) ); 268 269 if( ( ret = dhm_check_range( &ctx->GX, &ctx->P ) ) != 0 ) 270 return( ret ); 271 272 MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &ctx->GX, output, olen ) ); 273 274 cleanup: 275 276 if( ret != 0 ) 277 return( MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED + ret ); 278 279 return( 0 ); 280 } 281 282 /* 283 * Use the blinding method and optimisation suggested in section 10 of: 284 * KOCHER, Paul C. Timing attacks on implementations of Diffie-Hellman, RSA, 285 * DSS, and other systems. In : Advances in Cryptology-CRYPTO'96. Springer 286 * Berlin Heidelberg, 1996. p. 104-113. 287 */ 288 static int dhm_update_blinding( mbedtls_dhm_context *ctx, 289 int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) 290 { 291 int ret, count; 292 293 /* 294 * Don't use any blinding the first time a particular X is used, 295 * but remember it to use blinding next time. 296 */ 297 if( mbedtls_mpi_cmp_mpi( &ctx->X, &ctx->pX ) != 0 ) 298 { 299 MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &ctx->pX, &ctx->X ) ); 300 MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &ctx->Vi, 1 ) ); 301 MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &ctx->Vf, 1 ) ); 302 303 return( 0 ); 304 } 305 306 /* 307 * Ok, we need blinding. Can we re-use existing values? 308 * If yes, just update them by squaring them. 309 */ 310 if( mbedtls_mpi_cmp_int( &ctx->Vi, 1 ) != 0 ) 311 { 312 MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &ctx->Vi, &ctx->Vi, &ctx->Vi ) ); 313 MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &ctx->Vi, &ctx->Vi, &ctx->P ) ); 314 315 MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &ctx->Vf, &ctx->Vf, &ctx->Vf ) ); 316 MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &ctx->Vf, &ctx->Vf, &ctx->P ) ); 317 318 return( 0 ); 319 } 320 321 /* 322 * We need to generate blinding values from scratch 323 */ 324 325 /* Vi = random( 2, P-1 ) */ 326 count = 0; 327 do 328 { 329 MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &ctx->Vi, mbedtls_mpi_size( &ctx->P ), f_rng, p_rng ) ); 330 331 while( mbedtls_mpi_cmp_mpi( &ctx->Vi, &ctx->P ) >= 0 ) 332 MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &ctx->Vi, 1 ) ); 333 334 if( count++ > 10 ) 335 return( MBEDTLS_ERR_MPI_NOT_ACCEPTABLE ); 336 } 337 while( mbedtls_mpi_cmp_int( &ctx->Vi, 1 ) <= 0 ); 338 339 /* Vf = Vi^-X mod P */ 340 MBEDTLS_MPI_CHK( mbedtls_mpi_inv_mod( &ctx->Vf, &ctx->Vi, &ctx->P ) ); 341 MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &ctx->Vf, &ctx->Vf, &ctx->X, &ctx->P, &ctx->RP ) ); 342 343 cleanup: 344 return( ret ); 345 } 346 347 /* 348 * Derive and export the shared secret (G^Y)^X mod P 349 */ 350 int mbedtls_dhm_calc_secret( mbedtls_dhm_context *ctx, 351 unsigned char *output, size_t output_size, size_t *olen, 352 int (*f_rng)(void *, unsigned char *, size_t), 353 void *p_rng ) 354 { 355 int ret; 356 mbedtls_mpi GYb; 357 358 if( ctx == NULL || output_size < ctx->len ) 359 return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA ); 360 361 if( ( ret = dhm_check_range( &ctx->GY, &ctx->P ) ) != 0 ) 362 return( ret ); 363 364 mbedtls_mpi_init( &GYb ); 365 366 /* Blind peer's value */ 367 if( f_rng != NULL ) 368 { 369 MBEDTLS_MPI_CHK( dhm_update_blinding( ctx, f_rng, p_rng ) ); 370 MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &GYb, &ctx->GY, &ctx->Vi ) ); 371 MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &GYb, &GYb, &ctx->P ) ); 372 } 373 else 374 MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &GYb, &ctx->GY ) ); 375 376 /* Do modular exponentiation */ 377 MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &ctx->K, &GYb, &ctx->X, 378 &ctx->P, &ctx->RP ) ); 379 380 /* Unblind secret value */ 381 if( f_rng != NULL ) 382 { 383 MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &ctx->K, &ctx->K, &ctx->Vf ) ); 384 MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &ctx->K, &ctx->K, &ctx->P ) ); 385 } 386 387 *olen = mbedtls_mpi_size( &ctx->K ); 388 389 MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &ctx->K, output, *olen ) ); 390 391 cleanup: 392 mbedtls_mpi_free( &GYb ); 393 394 if( ret != 0 ) 395 return( MBEDTLS_ERR_DHM_CALC_SECRET_FAILED + ret ); 396 397 return( 0 ); 398 } 399 400 /* 401 * Free the components of a DHM key 402 */ 403 void mbedtls_dhm_free( mbedtls_dhm_context *ctx ) 404 { 405 mbedtls_mpi_free( &ctx->pX); mbedtls_mpi_free( &ctx->Vf ); mbedtls_mpi_free( &ctx->Vi ); 406 mbedtls_mpi_free( &ctx->RP ); mbedtls_mpi_free( &ctx->K ); mbedtls_mpi_free( &ctx->GY ); 407 mbedtls_mpi_free( &ctx->GX ); mbedtls_mpi_free( &ctx->X ); mbedtls_mpi_free( &ctx->G ); 408 mbedtls_mpi_free( &ctx->P ); 409 410 mbedtls_zeroize( ctx, sizeof( mbedtls_dhm_context ) ); 411 } 412 413 #if defined(MBEDTLS_ASN1_PARSE_C) 414 /* 415 * Parse DHM parameters 416 */ 417 int mbedtls_dhm_parse_dhm( mbedtls_dhm_context *dhm, const unsigned char *dhmin, 418 size_t dhminlen ) 419 { 420 int ret; 421 size_t len; 422 unsigned char *p, *end; 423 #if defined(MBEDTLS_PEM_PARSE_C) 424 mbedtls_pem_context pem; 425 426 mbedtls_pem_init( &pem ); 427 428 /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ 429 if( dhminlen == 0 || dhmin[dhminlen - 1] != '\0' ) 430 ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; 431 else 432 ret = mbedtls_pem_read_buffer( &pem, 433 "-----BEGIN DH PARAMETERS-----", 434 "-----END DH PARAMETERS-----", 435 dhmin, NULL, 0, &dhminlen ); 436 437 if( ret == 0 ) 438 { 439 /* 440 * Was PEM encoded 441 */ 442 dhminlen = pem.buflen; 443 } 444 else if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) 445 goto exit; 446 447 p = ( ret == 0 ) ? pem.buf : (unsigned char *) dhmin; 448 #else 449 p = (unsigned char *) dhmin; 450 #endif /* MBEDTLS_PEM_PARSE_C */ 451 end = p + dhminlen; 452 453 /* 454 * DHParams ::= SEQUENCE { 455 * prime INTEGER, -- P 456 * generator INTEGER, -- g 457 * privateValueLength INTEGER OPTIONAL 458 * } 459 */ 460 if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, 461 MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) 462 { 463 ret = MBEDTLS_ERR_DHM_INVALID_FORMAT + ret; 464 goto exit; 465 } 466 467 end = p + len; 468 469 if( ( ret = mbedtls_asn1_get_mpi( &p, end, &dhm->P ) ) != 0 || 470 ( ret = mbedtls_asn1_get_mpi( &p, end, &dhm->G ) ) != 0 ) 471 { 472 ret = MBEDTLS_ERR_DHM_INVALID_FORMAT + ret; 473 goto exit; 474 } 475 476 if( p != end ) 477 { 478 /* This might be the optional privateValueLength. 479 * If so, we can cleanly discard it */ 480 mbedtls_mpi rec; 481 mbedtls_mpi_init( &rec ); 482 ret = mbedtls_asn1_get_mpi( &p, end, &rec ); 483 mbedtls_mpi_free( &rec ); 484 if ( ret != 0 ) 485 { 486 ret = MBEDTLS_ERR_DHM_INVALID_FORMAT + ret; 487 goto exit; 488 } 489 if ( p != end ) 490 { 491 ret = MBEDTLS_ERR_DHM_INVALID_FORMAT + 492 MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; 493 goto exit; 494 } 495 } 496 497 ret = 0; 498 499 dhm->len = mbedtls_mpi_size( &dhm->P ); 500 501 exit: 502 #if defined(MBEDTLS_PEM_PARSE_C) 503 mbedtls_pem_free( &pem ); 504 #endif 505 if( ret != 0 ) 506 mbedtls_dhm_free( dhm ); 507 508 return( ret ); 509 } 510 511 #if defined(MBEDTLS_FS_IO) 512 /* 513 * Load all data from a file into a given buffer. 514 * 515 * The file is expected to contain either PEM or DER encoded data. 516 * A terminating null byte is always appended. It is included in the announced 517 * length only if the data looks like it is PEM encoded. 518 */ 519 static int load_file( const char *path, unsigned char **buf, size_t *n ) 520 { 521 FILE *f; 522 long size; 523 524 if( ( f = fopen( path, "rb" ) ) == NULL ) 525 return( MBEDTLS_ERR_DHM_FILE_IO_ERROR ); 526 527 fseek( f, 0, SEEK_END ); 528 if( ( size = ftell( f ) ) == -1 ) 529 { 530 fclose( f ); 531 return( MBEDTLS_ERR_DHM_FILE_IO_ERROR ); 532 } 533 fseek( f, 0, SEEK_SET ); 534 535 *n = (size_t) size; 536 537 if( *n + 1 == 0 || 538 ( *buf = mbedtls_calloc( 1, *n + 1 ) ) == NULL ) 539 { 540 fclose( f ); 541 return( MBEDTLS_ERR_DHM_ALLOC_FAILED ); 542 } 543 544 if( fread( *buf, 1, *n, f ) != *n ) 545 { 546 fclose( f ); 547 mbedtls_free( *buf ); 548 return( MBEDTLS_ERR_DHM_FILE_IO_ERROR ); 549 } 550 551 fclose( f ); 552 553 (*buf)[*n] = '\0'; 554 555 if( strstr( (const char *) *buf, "-----BEGIN " ) != NULL ) 556 ++*n; 557 558 return( 0 ); 559 } 560 561 /* 562 * Load and parse DHM parameters 563 */ 564 int mbedtls_dhm_parse_dhmfile( mbedtls_dhm_context *dhm, const char *path ) 565 { 566 int ret; 567 size_t n; 568 unsigned char *buf; 569 570 if( ( ret = load_file( path, &buf, &n ) ) != 0 ) 571 return( ret ); 572 573 ret = mbedtls_dhm_parse_dhm( dhm, buf, n ); 574 575 mbedtls_zeroize( buf, n ); 576 mbedtls_free( buf ); 577 578 return( ret ); 579 } 580 #endif /* MBEDTLS_FS_IO */ 581 #endif /* MBEDTLS_ASN1_PARSE_C */ 582 583 #if defined(MBEDTLS_SELF_TEST) 584 585 static const char mbedtls_test_dhm_params[] = 586 "-----BEGIN DH PARAMETERS-----\r\n" 587 "MIGHAoGBAJ419DBEOgmQTzo5qXl5fQcN9TN455wkOL7052HzxxRVMyhYmwQcgJvh\r\n" 588 "1sa18fyfR9OiVEMYglOpkqVoGLN7qd5aQNNi5W7/C+VBdHTBJcGZJyyP5B3qcz32\r\n" 589 "9mLJKudlVudV0Qxk5qUJaPZ/xupz0NyoVpviuiBOI1gNi8ovSXWzAgEC\r\n" 590 "-----END DH PARAMETERS-----\r\n"; 591 592 static const size_t mbedtls_test_dhm_params_len = sizeof( mbedtls_test_dhm_params ); 593 594 /* 595 * Checkup routine 596 */ 597 int mbedtls_dhm_self_test( int verbose ) 598 { 599 int ret; 600 mbedtls_dhm_context dhm; 601 602 mbedtls_dhm_init( &dhm ); 603 604 if( verbose != 0 ) 605 mbedtls_printf( " DHM parameter load: " ); 606 607 if( ( ret = mbedtls_dhm_parse_dhm( &dhm, 608 (const unsigned char *) mbedtls_test_dhm_params, 609 mbedtls_test_dhm_params_len ) ) != 0 ) 610 { 611 if( verbose != 0 ) 612 mbedtls_printf( "failed\n" ); 613 614 ret = 1; 615 goto exit; 616 } 617 618 if( verbose != 0 ) 619 mbedtls_printf( "passed\n\n" ); 620 621 exit: 622 mbedtls_dhm_free( &dhm ); 623 624 return( ret ); 625 } 626 627 #endif /* MBEDTLS_SELF_TEST */ 628 629 #endif /* MBEDTLS_DHM_C */ 630