xref: /reactos/dll/3rdparty/mbedtls/dhm.c (revision c2c66aff)
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