1817466cbSJens Wiklander /* 2817466cbSJens Wiklander * Diffie-Hellman-Merkle key exchange 3817466cbSJens Wiklander * 47901324dSJerome Forissier * Copyright The Mbed TLS Contributors 57901324dSJerome Forissier * SPDX-License-Identifier: Apache-2.0 6817466cbSJens Wiklander * 7817466cbSJens Wiklander * Licensed under the Apache License, Version 2.0 (the "License"); you may 8817466cbSJens Wiklander * not use this file except in compliance with the License. 9817466cbSJens Wiklander * You may obtain a copy of the License at 10817466cbSJens Wiklander * 11817466cbSJens Wiklander * http://www.apache.org/licenses/LICENSE-2.0 12817466cbSJens Wiklander * 13817466cbSJens Wiklander * Unless required by applicable law or agreed to in writing, software 14817466cbSJens Wiklander * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15817466cbSJens Wiklander * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16817466cbSJens Wiklander * See the License for the specific language governing permissions and 17817466cbSJens Wiklander * limitations under the License. 18817466cbSJens Wiklander */ 19817466cbSJens Wiklander /* 20817466cbSJens Wiklander * The following sources were referenced in the design of this implementation 21817466cbSJens Wiklander * of the Diffie-Hellman-Merkle algorithm: 22817466cbSJens Wiklander * 23817466cbSJens Wiklander * [1] Handbook of Applied Cryptography - 1997, Chapter 12 24817466cbSJens Wiklander * Menezes, van Oorschot and Vanstone 25817466cbSJens Wiklander * 26817466cbSJens Wiklander */ 27817466cbSJens Wiklander 287901324dSJerome Forissier #include "common.h" 29817466cbSJens Wiklander 30817466cbSJens Wiklander #if defined(MBEDTLS_DHM_C) 31817466cbSJens Wiklander 32817466cbSJens Wiklander #include "mbedtls/dhm.h" 333d3b0591SJens Wiklander #include "mbedtls/platform_util.h" 3411fa71b9SJerome Forissier #include "mbedtls/error.h" 35817466cbSJens Wiklander 36817466cbSJens Wiklander #include <string.h> 37817466cbSJens Wiklander 38817466cbSJens Wiklander #if defined(MBEDTLS_PEM_PARSE_C) 39817466cbSJens Wiklander #include "mbedtls/pem.h" 40817466cbSJens Wiklander #endif 41817466cbSJens Wiklander 42817466cbSJens Wiklander #if defined(MBEDTLS_ASN1_PARSE_C) 43817466cbSJens Wiklander #include "mbedtls/asn1.h" 44817466cbSJens Wiklander #endif 45817466cbSJens Wiklander 46817466cbSJens Wiklander #include "mbedtls/platform.h" 47817466cbSJens Wiklander 483d3b0591SJens Wiklander #if !defined(MBEDTLS_DHM_ALT) 493d3b0591SJens Wiklander 50817466cbSJens Wiklander /* 51817466cbSJens Wiklander * helper to validate the mbedtls_mpi size and import it 52817466cbSJens Wiklander */ 53817466cbSJens Wiklander static int dhm_read_bignum(mbedtls_mpi *X, 54817466cbSJens Wiklander unsigned char **p, 55817466cbSJens Wiklander const unsigned char *end) 56817466cbSJens Wiklander { 57817466cbSJens Wiklander int ret, n; 58817466cbSJens Wiklander 59*32b31808SJens Wiklander if (end - *p < 2) { 60*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 61*32b31808SJens Wiklander } 62817466cbSJens Wiklander 63817466cbSJens Wiklander n = ((*p)[0] << 8) | (*p)[1]; 64817466cbSJens Wiklander (*p) += 2; 65817466cbSJens Wiklander 66*32b31808SJens Wiklander if ((int) (end - *p) < n) { 67*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 68*32b31808SJens Wiklander } 69817466cbSJens Wiklander 70*32b31808SJens Wiklander if ((ret = mbedtls_mpi_read_binary(X, *p, n)) != 0) { 71*32b31808SJens Wiklander return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_READ_PARAMS_FAILED, ret); 72*32b31808SJens Wiklander } 73817466cbSJens Wiklander 74817466cbSJens Wiklander (*p) += n; 75817466cbSJens Wiklander 76*32b31808SJens Wiklander return 0; 77817466cbSJens Wiklander } 78817466cbSJens Wiklander 79817466cbSJens Wiklander /* 80817466cbSJens Wiklander * Verify sanity of parameter with regards to P 81817466cbSJens Wiklander * 82817466cbSJens Wiklander * Parameter should be: 2 <= public_param <= P - 2 83817466cbSJens Wiklander * 843d3b0591SJens Wiklander * This means that we need to return an error if 853d3b0591SJens Wiklander * public_param < 2 or public_param > P-2 863d3b0591SJens Wiklander * 87817466cbSJens Wiklander * For more information on the attack, see: 88817466cbSJens Wiklander * http://www.cl.cam.ac.uk/~rja14/Papers/psandqs.pdf 89817466cbSJens Wiklander * http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2005-2643 90817466cbSJens Wiklander */ 91817466cbSJens Wiklander static int dhm_check_range(const mbedtls_mpi *param, const mbedtls_mpi *P) 92817466cbSJens Wiklander { 937901324dSJerome Forissier mbedtls_mpi U; 943d3b0591SJens Wiklander int ret = 0; 95817466cbSJens Wiklander 967901324dSJerome Forissier mbedtls_mpi_init(&U); 97817466cbSJens Wiklander 98817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&U, P, 2)); 99817466cbSJens Wiklander 1007901324dSJerome Forissier if (mbedtls_mpi_cmp_int(param, 2) < 0 || 101*32b31808SJens Wiklander mbedtls_mpi_cmp_mpi(param, &U) > 0) { 1023d3b0591SJens Wiklander ret = MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 103817466cbSJens Wiklander } 104817466cbSJens Wiklander 105817466cbSJens Wiklander cleanup: 1067901324dSJerome Forissier mbedtls_mpi_free(&U); 107*32b31808SJens Wiklander return ret; 108817466cbSJens Wiklander } 109817466cbSJens Wiklander 110817466cbSJens Wiklander void mbedtls_dhm_init(mbedtls_dhm_context *ctx) 111817466cbSJens Wiklander { 112817466cbSJens Wiklander memset(ctx, 0, sizeof(mbedtls_dhm_context)); 113817466cbSJens Wiklander } 114817466cbSJens Wiklander 115*32b31808SJens Wiklander size_t mbedtls_dhm_get_bitlen(const mbedtls_dhm_context *ctx) 116*32b31808SJens Wiklander { 117*32b31808SJens Wiklander return mbedtls_mpi_bitlen(&ctx->P); 118*32b31808SJens Wiklander } 119*32b31808SJens Wiklander 120*32b31808SJens Wiklander size_t mbedtls_dhm_get_len(const mbedtls_dhm_context *ctx) 121*32b31808SJens Wiklander { 122*32b31808SJens Wiklander return mbedtls_mpi_size(&ctx->P); 123*32b31808SJens Wiklander } 124*32b31808SJens Wiklander 125*32b31808SJens Wiklander int mbedtls_dhm_get_value(const mbedtls_dhm_context *ctx, 126*32b31808SJens Wiklander mbedtls_dhm_parameter param, 127*32b31808SJens Wiklander mbedtls_mpi *dest) 128*32b31808SJens Wiklander { 129*32b31808SJens Wiklander const mbedtls_mpi *src = NULL; 130*32b31808SJens Wiklander switch (param) { 131*32b31808SJens Wiklander case MBEDTLS_DHM_PARAM_P: 132*32b31808SJens Wiklander src = &ctx->P; 133*32b31808SJens Wiklander break; 134*32b31808SJens Wiklander case MBEDTLS_DHM_PARAM_G: 135*32b31808SJens Wiklander src = &ctx->G; 136*32b31808SJens Wiklander break; 137*32b31808SJens Wiklander case MBEDTLS_DHM_PARAM_X: 138*32b31808SJens Wiklander src = &ctx->X; 139*32b31808SJens Wiklander break; 140*32b31808SJens Wiklander case MBEDTLS_DHM_PARAM_GX: 141*32b31808SJens Wiklander src = &ctx->GX; 142*32b31808SJens Wiklander break; 143*32b31808SJens Wiklander case MBEDTLS_DHM_PARAM_GY: 144*32b31808SJens Wiklander src = &ctx->GY; 145*32b31808SJens Wiklander break; 146*32b31808SJens Wiklander case MBEDTLS_DHM_PARAM_K: 147*32b31808SJens Wiklander src = &ctx->K; 148*32b31808SJens Wiklander break; 149*32b31808SJens Wiklander default: 150*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 151*32b31808SJens Wiklander } 152*32b31808SJens Wiklander return mbedtls_mpi_copy(dest, src); 153*32b31808SJens Wiklander } 154*32b31808SJens Wiklander 155817466cbSJens Wiklander /* 156817466cbSJens Wiklander * Parse the ServerKeyExchange parameters 157817466cbSJens Wiklander */ 158817466cbSJens Wiklander int mbedtls_dhm_read_params(mbedtls_dhm_context *ctx, 159817466cbSJens Wiklander unsigned char **p, 160817466cbSJens Wiklander const unsigned char *end) 161817466cbSJens Wiklander { 16211fa71b9SJerome Forissier int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 163817466cbSJens Wiklander 164817466cbSJens Wiklander if ((ret = dhm_read_bignum(&ctx->P, p, end)) != 0 || 165817466cbSJens Wiklander (ret = dhm_read_bignum(&ctx->G, p, end)) != 0 || 166*32b31808SJens Wiklander (ret = dhm_read_bignum(&ctx->GY, p, end)) != 0) { 167*32b31808SJens Wiklander return ret; 168*32b31808SJens Wiklander } 169817466cbSJens Wiklander 170*32b31808SJens Wiklander if ((ret = dhm_check_range(&ctx->GY, &ctx->P)) != 0) { 171*32b31808SJens Wiklander return ret; 172*32b31808SJens Wiklander } 173817466cbSJens Wiklander 174*32b31808SJens Wiklander return 0; 175817466cbSJens Wiklander } 176817466cbSJens Wiklander 177817466cbSJens Wiklander /* 1787901324dSJerome Forissier * Pick a random R in the range [2, M-2] for blinding or key generation. 179817466cbSJens Wiklander */ 1807901324dSJerome Forissier static int dhm_random_below(mbedtls_mpi *R, const mbedtls_mpi *M, 1817901324dSJerome Forissier int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) 1827901324dSJerome Forissier { 1837901324dSJerome Forissier int ret; 1847901324dSJerome Forissier 1857901324dSJerome Forissier MBEDTLS_MPI_CHK(mbedtls_mpi_random(R, 3, M, f_rng, p_rng)); 1867901324dSJerome Forissier MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(R, R, 1)); 1877901324dSJerome Forissier 1887901324dSJerome Forissier cleanup: 189*32b31808SJens Wiklander return ret; 1907901324dSJerome Forissier } 1917901324dSJerome Forissier 1927901324dSJerome Forissier static int dhm_make_common(mbedtls_dhm_context *ctx, int x_size, 193817466cbSJens Wiklander int (*f_rng)(void *, unsigned char *, size_t), 194817466cbSJens Wiklander void *p_rng) 195817466cbSJens Wiklander { 1967901324dSJerome Forissier int ret = 0; 197817466cbSJens Wiklander 198*32b31808SJens Wiklander if (mbedtls_mpi_cmp_int(&ctx->P, 0) == 0) { 199*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 200817466cbSJens Wiklander } 201*32b31808SJens Wiklander if (x_size < 0) { 202*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 203*32b31808SJens Wiklander } 204*32b31808SJens Wiklander 205*32b31808SJens Wiklander if ((unsigned) x_size < mbedtls_mpi_size(&ctx->P)) { 206*32b31808SJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&ctx->X, x_size, f_rng, p_rng)); 207*32b31808SJens Wiklander } else { 2087901324dSJerome Forissier /* Generate X as large as possible ( <= P - 2 ) */ 2097901324dSJerome Forissier ret = dhm_random_below(&ctx->X, &ctx->P, f_rng, p_rng); 210*32b31808SJens Wiklander if (ret == MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { 211*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED; 212*32b31808SJens Wiklander } 213*32b31808SJens Wiklander if (ret != 0) { 214*32b31808SJens Wiklander return ret; 215*32b31808SJens Wiklander } 2167901324dSJerome Forissier } 217817466cbSJens Wiklander 218817466cbSJens Wiklander /* 219817466cbSJens Wiklander * Calculate GX = G^X mod P 220817466cbSJens Wiklander */ 221817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->GX, &ctx->G, &ctx->X, 222817466cbSJens Wiklander &ctx->P, &ctx->RP)); 223817466cbSJens Wiklander 224*32b31808SJens Wiklander if ((ret = dhm_check_range(&ctx->GX, &ctx->P)) != 0) { 225*32b31808SJens Wiklander return ret; 226*32b31808SJens Wiklander } 227817466cbSJens Wiklander 2287901324dSJerome Forissier cleanup: 229*32b31808SJens Wiklander return ret; 2307901324dSJerome Forissier } 2317901324dSJerome Forissier 232817466cbSJens Wiklander /* 2337901324dSJerome Forissier * Setup and write the ServerKeyExchange parameters 2347901324dSJerome Forissier */ 2357901324dSJerome Forissier int mbedtls_dhm_make_params(mbedtls_dhm_context *ctx, int x_size, 2367901324dSJerome Forissier unsigned char *output, size_t *olen, 2377901324dSJerome Forissier int (*f_rng)(void *, unsigned char *, size_t), 2387901324dSJerome Forissier void *p_rng) 2397901324dSJerome Forissier { 2407901324dSJerome Forissier int ret; 2417901324dSJerome Forissier size_t n1, n2, n3; 2427901324dSJerome Forissier unsigned char *p; 2437901324dSJerome Forissier 2447901324dSJerome Forissier ret = dhm_make_common(ctx, x_size, f_rng, p_rng); 245*32b31808SJens Wiklander if (ret != 0) { 2467901324dSJerome Forissier goto cleanup; 247*32b31808SJens Wiklander } 2487901324dSJerome Forissier 2497901324dSJerome Forissier /* 2507901324dSJerome Forissier * Export P, G, GX. RFC 5246 §4.4 states that "leading zero octets are 2517901324dSJerome Forissier * not required". We omit leading zeros for compactness. 252817466cbSJens Wiklander */ 253817466cbSJens Wiklander #define DHM_MPI_EXPORT(X, n) \ 2543d3b0591SJens Wiklander do { \ 2553d3b0591SJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary((X), \ 2563d3b0591SJens Wiklander p + 2, \ 2573d3b0591SJens Wiklander (n))); \ 258039e02dfSJerome Forissier *p++ = MBEDTLS_BYTE_1(n); \ 259039e02dfSJerome Forissier *p++ = MBEDTLS_BYTE_0(n); \ 2603d3b0591SJens Wiklander p += (n); \ 2613d3b0591SJens Wiklander } while (0) 262817466cbSJens Wiklander 263817466cbSJens Wiklander n1 = mbedtls_mpi_size(&ctx->P); 264817466cbSJens Wiklander n2 = mbedtls_mpi_size(&ctx->G); 265817466cbSJens Wiklander n3 = mbedtls_mpi_size(&ctx->GX); 266817466cbSJens Wiklander 267817466cbSJens Wiklander p = output; 268817466cbSJens Wiklander DHM_MPI_EXPORT(&ctx->P, n1); 269817466cbSJens Wiklander DHM_MPI_EXPORT(&ctx->G, n2); 270817466cbSJens Wiklander DHM_MPI_EXPORT(&ctx->GX, n3); 271817466cbSJens Wiklander 272817466cbSJens Wiklander *olen = p - output; 273817466cbSJens Wiklander 274817466cbSJens Wiklander cleanup: 275*32b31808SJens Wiklander if (ret != 0 && ret > -128) { 2767901324dSJerome Forissier ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED, ret); 277*32b31808SJens Wiklander } 278*32b31808SJens Wiklander return ret; 279817466cbSJens Wiklander } 280817466cbSJens Wiklander 281817466cbSJens Wiklander /* 2823d3b0591SJens Wiklander * Set prime modulus and generator 2833d3b0591SJens Wiklander */ 2843d3b0591SJens Wiklander int mbedtls_dhm_set_group(mbedtls_dhm_context *ctx, 2853d3b0591SJens Wiklander const mbedtls_mpi *P, 2863d3b0591SJens Wiklander const mbedtls_mpi *G) 2873d3b0591SJens Wiklander { 28811fa71b9SJerome Forissier int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 2893d3b0591SJens Wiklander 2903d3b0591SJens Wiklander if ((ret = mbedtls_mpi_copy(&ctx->P, P)) != 0 || 291*32b31808SJens Wiklander (ret = mbedtls_mpi_copy(&ctx->G, G)) != 0) { 292*32b31808SJens Wiklander return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_SET_GROUP_FAILED, ret); 2933d3b0591SJens Wiklander } 2943d3b0591SJens Wiklander 295*32b31808SJens Wiklander return 0; 2963d3b0591SJens Wiklander } 2973d3b0591SJens Wiklander 2983d3b0591SJens Wiklander /* 299817466cbSJens Wiklander * Import the peer's public value G^Y 300817466cbSJens Wiklander */ 301817466cbSJens Wiklander int mbedtls_dhm_read_public(mbedtls_dhm_context *ctx, 302817466cbSJens Wiklander const unsigned char *input, size_t ilen) 303817466cbSJens Wiklander { 30411fa71b9SJerome Forissier int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 305817466cbSJens Wiklander 306*32b31808SJens Wiklander if (ilen < 1 || ilen > mbedtls_dhm_get_len(ctx)) { 307*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 308*32b31808SJens Wiklander } 309817466cbSJens Wiklander 310*32b31808SJens Wiklander if ((ret = mbedtls_mpi_read_binary(&ctx->GY, input, ilen)) != 0) { 311*32b31808SJens Wiklander return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED, ret); 312*32b31808SJens Wiklander } 313817466cbSJens Wiklander 314*32b31808SJens Wiklander return 0; 315817466cbSJens Wiklander } 316817466cbSJens Wiklander 317817466cbSJens Wiklander /* 318817466cbSJens Wiklander * Create own private value X and export G^X 319817466cbSJens Wiklander */ 320817466cbSJens Wiklander int mbedtls_dhm_make_public(mbedtls_dhm_context *ctx, int x_size, 321817466cbSJens Wiklander unsigned char *output, size_t olen, 322817466cbSJens Wiklander int (*f_rng)(void *, unsigned char *, size_t), 323817466cbSJens Wiklander void *p_rng) 324817466cbSJens Wiklander { 3257901324dSJerome Forissier int ret; 326817466cbSJens Wiklander 327*32b31808SJens Wiklander if (olen < 1 || olen > mbedtls_dhm_get_len(ctx)) { 328*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 329*32b31808SJens Wiklander } 330817466cbSJens Wiklander 3317901324dSJerome Forissier ret = dhm_make_common(ctx, x_size, f_rng, p_rng); 332*32b31808SJens Wiklander if (ret == MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED) { 333*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED; 334*32b31808SJens Wiklander } 335*32b31808SJens Wiklander if (ret != 0) { 3367901324dSJerome Forissier goto cleanup; 337*32b31808SJens Wiklander } 338817466cbSJens Wiklander 339817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&ctx->GX, output, olen)); 340817466cbSJens Wiklander 341817466cbSJens Wiklander cleanup: 342*32b31808SJens Wiklander if (ret != 0 && ret > -128) { 3437901324dSJerome Forissier ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED, ret); 344*32b31808SJens Wiklander } 345*32b31808SJens Wiklander return ret; 346817466cbSJens Wiklander } 347817466cbSJens Wiklander 3487901324dSJerome Forissier 349817466cbSJens Wiklander /* 350817466cbSJens Wiklander * Use the blinding method and optimisation suggested in section 10 of: 351817466cbSJens Wiklander * KOCHER, Paul C. Timing attacks on implementations of Diffie-Hellman, RSA, 352817466cbSJens Wiklander * DSS, and other systems. In : Advances in Cryptology-CRYPTO'96. Springer 353817466cbSJens Wiklander * Berlin Heidelberg, 1996. p. 104-113. 354817466cbSJens Wiklander */ 355817466cbSJens Wiklander static int dhm_update_blinding(mbedtls_dhm_context *ctx, 356817466cbSJens Wiklander int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) 357817466cbSJens Wiklander { 3587901324dSJerome Forissier int ret; 3597901324dSJerome Forissier mbedtls_mpi R; 3607901324dSJerome Forissier 3617901324dSJerome Forissier mbedtls_mpi_init(&R); 362817466cbSJens Wiklander 363817466cbSJens Wiklander /* 364817466cbSJens Wiklander * Don't use any blinding the first time a particular X is used, 365817466cbSJens Wiklander * but remember it to use blinding next time. 366817466cbSJens Wiklander */ 367*32b31808SJens Wiklander if (mbedtls_mpi_cmp_mpi(&ctx->X, &ctx->pX) != 0) { 368817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&ctx->pX, &ctx->X)); 369817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&ctx->Vi, 1)); 370817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&ctx->Vf, 1)); 371817466cbSJens Wiklander 372*32b31808SJens Wiklander return 0; 373817466cbSJens Wiklander } 374817466cbSJens Wiklander 375817466cbSJens Wiklander /* 376817466cbSJens Wiklander * Ok, we need blinding. Can we re-use existing values? 377817466cbSJens Wiklander * If yes, just update them by squaring them. 378817466cbSJens Wiklander */ 379*32b31808SJens Wiklander if (mbedtls_mpi_cmp_int(&ctx->Vi, 1) != 0) { 380817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vi, &ctx->Vi, &ctx->Vi)); 381817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vi, &ctx->Vi, &ctx->P)); 382817466cbSJens Wiklander 383817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vf, &ctx->Vf, &ctx->Vf)); 384817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vf, &ctx->Vf, &ctx->P)); 385817466cbSJens Wiklander 386*32b31808SJens Wiklander return 0; 387817466cbSJens Wiklander } 388817466cbSJens Wiklander 389817466cbSJens Wiklander /* 390817466cbSJens Wiklander * We need to generate blinding values from scratch 391817466cbSJens Wiklander */ 392817466cbSJens Wiklander 3937901324dSJerome Forissier /* Vi = random( 2, P-2 ) */ 3947901324dSJerome Forissier MBEDTLS_MPI_CHK(dhm_random_below(&ctx->Vi, &ctx->P, f_rng, p_rng)); 395817466cbSJens Wiklander 3967901324dSJerome Forissier /* Vf = Vi^-X mod P 3977901324dSJerome Forissier * First compute Vi^-1 = R * (R Vi)^-1, (avoiding leaks from inv_mod), 3987901324dSJerome Forissier * then elevate to the Xth power. */ 3997901324dSJerome Forissier MBEDTLS_MPI_CHK(dhm_random_below(&R, &ctx->P, f_rng, p_rng)); 4007901324dSJerome Forissier MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vf, &ctx->Vi, &R)); 4017901324dSJerome Forissier MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vf, &ctx->Vf, &ctx->P)); 4027901324dSJerome Forissier MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&ctx->Vf, &ctx->Vf, &ctx->P)); 4037901324dSJerome Forissier MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vf, &ctx->Vf, &R)); 4047901324dSJerome Forissier MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vf, &ctx->Vf, &ctx->P)); 405817466cbSJens Wiklander 406817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->Vf, &ctx->Vf, &ctx->X, &ctx->P, &ctx->RP)); 407817466cbSJens Wiklander 408817466cbSJens Wiklander cleanup: 4097901324dSJerome Forissier mbedtls_mpi_free(&R); 4107901324dSJerome Forissier 411*32b31808SJens Wiklander return ret; 412817466cbSJens Wiklander } 413817466cbSJens Wiklander 414817466cbSJens Wiklander /* 415817466cbSJens Wiklander * Derive and export the shared secret (G^Y)^X mod P 416817466cbSJens Wiklander */ 417817466cbSJens Wiklander int mbedtls_dhm_calc_secret(mbedtls_dhm_context *ctx, 418817466cbSJens Wiklander unsigned char *output, size_t output_size, size_t *olen, 419817466cbSJens Wiklander int (*f_rng)(void *, unsigned char *, size_t), 420817466cbSJens Wiklander void *p_rng) 421817466cbSJens Wiklander { 42211fa71b9SJerome Forissier int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 423817466cbSJens Wiklander mbedtls_mpi GYb; 424817466cbSJens Wiklander 425*32b31808SJens Wiklander if (f_rng == NULL) { 426*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 427*32b31808SJens Wiklander } 428817466cbSJens Wiklander 429*32b31808SJens Wiklander if (output_size < mbedtls_dhm_get_len(ctx)) { 430*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; 431*32b31808SJens Wiklander } 432*32b31808SJens Wiklander 433*32b31808SJens Wiklander if ((ret = dhm_check_range(&ctx->GY, &ctx->P)) != 0) { 434*32b31808SJens Wiklander return ret; 435*32b31808SJens Wiklander } 436817466cbSJens Wiklander 437817466cbSJens Wiklander mbedtls_mpi_init(&GYb); 438817466cbSJens Wiklander 439817466cbSJens Wiklander /* Blind peer's value */ 440817466cbSJens Wiklander MBEDTLS_MPI_CHK(dhm_update_blinding(ctx, f_rng, p_rng)); 441817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&GYb, &ctx->GY, &ctx->Vi)); 442817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&GYb, &GYb, &ctx->P)); 443817466cbSJens Wiklander 444817466cbSJens Wiklander /* Do modular exponentiation */ 445817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->K, &GYb, &ctx->X, 446817466cbSJens Wiklander &ctx->P, &ctx->RP)); 447817466cbSJens Wiklander 448817466cbSJens Wiklander /* Unblind secret value */ 449817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->K, &ctx->K, &ctx->Vf)); 450817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->K, &ctx->K, &ctx->P)); 451817466cbSJens Wiklander 4527901324dSJerome Forissier /* Output the secret without any leading zero byte. This is mandatory 4537901324dSJerome Forissier * for TLS per RFC 5246 §8.1.2. */ 454817466cbSJens Wiklander *olen = mbedtls_mpi_size(&ctx->K); 455817466cbSJens Wiklander MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&ctx->K, output, *olen)); 456817466cbSJens Wiklander 457817466cbSJens Wiklander cleanup: 458817466cbSJens Wiklander mbedtls_mpi_free(&GYb); 459817466cbSJens Wiklander 460*32b31808SJens Wiklander if (ret != 0) { 461*32b31808SJens Wiklander return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_CALC_SECRET_FAILED, ret); 462*32b31808SJens Wiklander } 463817466cbSJens Wiklander 464*32b31808SJens Wiklander return 0; 465817466cbSJens Wiklander } 466817466cbSJens Wiklander 467817466cbSJens Wiklander /* 468817466cbSJens Wiklander * Free the components of a DHM key 469817466cbSJens Wiklander */ 470817466cbSJens Wiklander void mbedtls_dhm_free(mbedtls_dhm_context *ctx) 471817466cbSJens Wiklander { 472*32b31808SJens Wiklander if (ctx == NULL) { 4733d3b0591SJens Wiklander return; 474*32b31808SJens Wiklander } 4753d3b0591SJens Wiklander 4763d3b0591SJens Wiklander mbedtls_mpi_free(&ctx->pX); 4773d3b0591SJens Wiklander mbedtls_mpi_free(&ctx->Vf); 4783d3b0591SJens Wiklander mbedtls_mpi_free(&ctx->Vi); 4793d3b0591SJens Wiklander mbedtls_mpi_free(&ctx->RP); 4803d3b0591SJens Wiklander mbedtls_mpi_free(&ctx->K); 4813d3b0591SJens Wiklander mbedtls_mpi_free(&ctx->GY); 4823d3b0591SJens Wiklander mbedtls_mpi_free(&ctx->GX); 4833d3b0591SJens Wiklander mbedtls_mpi_free(&ctx->X); 4843d3b0591SJens Wiklander mbedtls_mpi_free(&ctx->G); 485817466cbSJens Wiklander mbedtls_mpi_free(&ctx->P); 486817466cbSJens Wiklander 4873d3b0591SJens Wiklander mbedtls_platform_zeroize(ctx, sizeof(mbedtls_dhm_context)); 488817466cbSJens Wiklander } 489817466cbSJens Wiklander 490817466cbSJens Wiklander #if defined(MBEDTLS_ASN1_PARSE_C) 491817466cbSJens Wiklander /* 492817466cbSJens Wiklander * Parse DHM parameters 493817466cbSJens Wiklander */ 494817466cbSJens Wiklander int mbedtls_dhm_parse_dhm(mbedtls_dhm_context *dhm, const unsigned char *dhmin, 495817466cbSJens Wiklander size_t dhminlen) 496817466cbSJens Wiklander { 49711fa71b9SJerome Forissier int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 498817466cbSJens Wiklander size_t len; 499817466cbSJens Wiklander unsigned char *p, *end; 500817466cbSJens Wiklander #if defined(MBEDTLS_PEM_PARSE_C) 501817466cbSJens Wiklander mbedtls_pem_context pem; 5023d3b0591SJens Wiklander #endif /* MBEDTLS_PEM_PARSE_C */ 503817466cbSJens Wiklander 5043d3b0591SJens Wiklander #if defined(MBEDTLS_PEM_PARSE_C) 505817466cbSJens Wiklander mbedtls_pem_init(&pem); 506817466cbSJens Wiklander 507817466cbSJens Wiklander /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ 508*32b31808SJens Wiklander if (dhminlen == 0 || dhmin[dhminlen - 1] != '\0') { 509817466cbSJens Wiklander ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; 510*32b31808SJens Wiklander } else { 511817466cbSJens Wiklander ret = mbedtls_pem_read_buffer(&pem, 512817466cbSJens Wiklander "-----BEGIN DH PARAMETERS-----", 513817466cbSJens Wiklander "-----END DH PARAMETERS-----", 514817466cbSJens Wiklander dhmin, NULL, 0, &dhminlen); 515*32b31808SJens Wiklander } 516817466cbSJens Wiklander 517*32b31808SJens Wiklander if (ret == 0) { 518817466cbSJens Wiklander /* 519817466cbSJens Wiklander * Was PEM encoded 520817466cbSJens Wiklander */ 521817466cbSJens Wiklander dhminlen = pem.buflen; 522*32b31808SJens Wiklander } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { 523817466cbSJens Wiklander goto exit; 524*32b31808SJens Wiklander } 525817466cbSJens Wiklander 526817466cbSJens Wiklander p = (ret == 0) ? pem.buf : (unsigned char *) dhmin; 527817466cbSJens Wiklander #else 528817466cbSJens Wiklander p = (unsigned char *) dhmin; 529817466cbSJens Wiklander #endif /* MBEDTLS_PEM_PARSE_C */ 530817466cbSJens Wiklander end = p + dhminlen; 531817466cbSJens Wiklander 532817466cbSJens Wiklander /* 533817466cbSJens Wiklander * DHParams ::= SEQUENCE { 534817466cbSJens Wiklander * prime INTEGER, -- P 535817466cbSJens Wiklander * generator INTEGER, -- g 536817466cbSJens Wiklander * privateValueLength INTEGER OPTIONAL 537817466cbSJens Wiklander * } 538817466cbSJens Wiklander */ 539817466cbSJens Wiklander if ((ret = mbedtls_asn1_get_tag(&p, end, &len, 540*32b31808SJens Wiklander MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { 5417901324dSJerome Forissier ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_INVALID_FORMAT, ret); 542817466cbSJens Wiklander goto exit; 543817466cbSJens Wiklander } 544817466cbSJens Wiklander 545817466cbSJens Wiklander end = p + len; 546817466cbSJens Wiklander 547817466cbSJens Wiklander if ((ret = mbedtls_asn1_get_mpi(&p, end, &dhm->P)) != 0 || 548*32b31808SJens Wiklander (ret = mbedtls_asn1_get_mpi(&p, end, &dhm->G)) != 0) { 5497901324dSJerome Forissier ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_INVALID_FORMAT, ret); 550817466cbSJens Wiklander goto exit; 551817466cbSJens Wiklander } 552817466cbSJens Wiklander 553*32b31808SJens Wiklander if (p != end) { 554817466cbSJens Wiklander /* This might be the optional privateValueLength. 555817466cbSJens Wiklander * If so, we can cleanly discard it */ 556817466cbSJens Wiklander mbedtls_mpi rec; 557817466cbSJens Wiklander mbedtls_mpi_init(&rec); 558817466cbSJens Wiklander ret = mbedtls_asn1_get_mpi(&p, end, &rec); 559817466cbSJens Wiklander mbedtls_mpi_free(&rec); 560*32b31808SJens Wiklander if (ret != 0) { 5617901324dSJerome Forissier ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_INVALID_FORMAT, ret); 562817466cbSJens Wiklander goto exit; 563817466cbSJens Wiklander } 564*32b31808SJens Wiklander if (p != end) { 5657901324dSJerome Forissier ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_INVALID_FORMAT, 5667901324dSJerome Forissier MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); 567817466cbSJens Wiklander goto exit; 568817466cbSJens Wiklander } 569817466cbSJens Wiklander } 570817466cbSJens Wiklander 571817466cbSJens Wiklander ret = 0; 572817466cbSJens Wiklander 573817466cbSJens Wiklander exit: 574817466cbSJens Wiklander #if defined(MBEDTLS_PEM_PARSE_C) 575817466cbSJens Wiklander mbedtls_pem_free(&pem); 576817466cbSJens Wiklander #endif 577*32b31808SJens Wiklander if (ret != 0) { 578817466cbSJens Wiklander mbedtls_dhm_free(dhm); 579*32b31808SJens Wiklander } 580817466cbSJens Wiklander 581*32b31808SJens Wiklander return ret; 582817466cbSJens Wiklander } 583817466cbSJens Wiklander 584817466cbSJens Wiklander #if defined(MBEDTLS_FS_IO) 585817466cbSJens Wiklander /* 586817466cbSJens Wiklander * Load all data from a file into a given buffer. 587817466cbSJens Wiklander * 588817466cbSJens Wiklander * The file is expected to contain either PEM or DER encoded data. 589817466cbSJens Wiklander * A terminating null byte is always appended. It is included in the announced 590817466cbSJens Wiklander * length only if the data looks like it is PEM encoded. 591817466cbSJens Wiklander */ 592817466cbSJens Wiklander static int load_file(const char *path, unsigned char **buf, size_t *n) 593817466cbSJens Wiklander { 594817466cbSJens Wiklander FILE *f; 595817466cbSJens Wiklander long size; 596817466cbSJens Wiklander 597*32b31808SJens Wiklander if ((f = fopen(path, "rb")) == NULL) { 598*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_FILE_IO_ERROR; 599*32b31808SJens Wiklander } 600*32b31808SJens Wiklander /* The data loaded here is public, so don't bother disabling buffering. */ 601817466cbSJens Wiklander 602817466cbSJens Wiklander fseek(f, 0, SEEK_END); 603*32b31808SJens Wiklander if ((size = ftell(f)) == -1) { 604817466cbSJens Wiklander fclose(f); 605*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_FILE_IO_ERROR; 606817466cbSJens Wiklander } 607817466cbSJens Wiklander fseek(f, 0, SEEK_SET); 608817466cbSJens Wiklander 609817466cbSJens Wiklander *n = (size_t) size; 610817466cbSJens Wiklander 611817466cbSJens Wiklander if (*n + 1 == 0 || 612*32b31808SJens Wiklander (*buf = mbedtls_calloc(1, *n + 1)) == NULL) { 613817466cbSJens Wiklander fclose(f); 614*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_ALLOC_FAILED; 615817466cbSJens Wiklander } 616817466cbSJens Wiklander 617*32b31808SJens Wiklander if (fread(*buf, 1, *n, f) != *n) { 618817466cbSJens Wiklander fclose(f); 6193d3b0591SJens Wiklander 6203d3b0591SJens Wiklander mbedtls_platform_zeroize(*buf, *n + 1); 621817466cbSJens Wiklander mbedtls_free(*buf); 6223d3b0591SJens Wiklander 623*32b31808SJens Wiklander return MBEDTLS_ERR_DHM_FILE_IO_ERROR; 624817466cbSJens Wiklander } 625817466cbSJens Wiklander 626817466cbSJens Wiklander fclose(f); 627817466cbSJens Wiklander 628817466cbSJens Wiklander (*buf)[*n] = '\0'; 629817466cbSJens Wiklander 630*32b31808SJens Wiklander if (strstr((const char *) *buf, "-----BEGIN ") != NULL) { 631817466cbSJens Wiklander ++*n; 632*32b31808SJens Wiklander } 633817466cbSJens Wiklander 634*32b31808SJens Wiklander return 0; 635817466cbSJens Wiklander } 636817466cbSJens Wiklander 637817466cbSJens Wiklander /* 638817466cbSJens Wiklander * Load and parse DHM parameters 639817466cbSJens Wiklander */ 640817466cbSJens Wiklander int mbedtls_dhm_parse_dhmfile(mbedtls_dhm_context *dhm, const char *path) 641817466cbSJens Wiklander { 64211fa71b9SJerome Forissier int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 643817466cbSJens Wiklander size_t n; 644817466cbSJens Wiklander unsigned char *buf; 645817466cbSJens Wiklander 646*32b31808SJens Wiklander if ((ret = load_file(path, &buf, &n)) != 0) { 647*32b31808SJens Wiklander return ret; 648*32b31808SJens Wiklander } 649817466cbSJens Wiklander 650817466cbSJens Wiklander ret = mbedtls_dhm_parse_dhm(dhm, buf, n); 651817466cbSJens Wiklander 6523d3b0591SJens Wiklander mbedtls_platform_zeroize(buf, n); 653817466cbSJens Wiklander mbedtls_free(buf); 654817466cbSJens Wiklander 655*32b31808SJens Wiklander return ret; 656817466cbSJens Wiklander } 657817466cbSJens Wiklander #endif /* MBEDTLS_FS_IO */ 658817466cbSJens Wiklander #endif /* MBEDTLS_ASN1_PARSE_C */ 6593d3b0591SJens Wiklander #endif /* MBEDTLS_DHM_ALT */ 660817466cbSJens Wiklander 661817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST) 662817466cbSJens Wiklander 6635b25c76aSJerome Forissier #if defined(MBEDTLS_PEM_PARSE_C) 664817466cbSJens Wiklander static const char mbedtls_test_dhm_params[] = 665817466cbSJens Wiklander "-----BEGIN DH PARAMETERS-----\r\n" 666817466cbSJens Wiklander "MIGHAoGBAJ419DBEOgmQTzo5qXl5fQcN9TN455wkOL7052HzxxRVMyhYmwQcgJvh\r\n" 667817466cbSJens Wiklander "1sa18fyfR9OiVEMYglOpkqVoGLN7qd5aQNNi5W7/C+VBdHTBJcGZJyyP5B3qcz32\r\n" 668817466cbSJens Wiklander "9mLJKudlVudV0Qxk5qUJaPZ/xupz0NyoVpviuiBOI1gNi8ovSXWzAgEC\r\n" 669817466cbSJens Wiklander "-----END DH PARAMETERS-----\r\n"; 6705b25c76aSJerome Forissier #else /* MBEDTLS_PEM_PARSE_C */ 6715b25c76aSJerome Forissier static const char mbedtls_test_dhm_params[] = { 6725b25c76aSJerome Forissier 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9e, 0x35, 0xf4, 0x30, 0x44, 6735b25c76aSJerome Forissier 0x3a, 0x09, 0x90, 0x4f, 0x3a, 0x39, 0xa9, 0x79, 0x79, 0x7d, 0x07, 0x0d, 6745b25c76aSJerome Forissier 0xf5, 0x33, 0x78, 0xe7, 0x9c, 0x24, 0x38, 0xbe, 0xf4, 0xe7, 0x61, 0xf3, 6755b25c76aSJerome Forissier 0xc7, 0x14, 0x55, 0x33, 0x28, 0x58, 0x9b, 0x04, 0x1c, 0x80, 0x9b, 0xe1, 6765b25c76aSJerome Forissier 0xd6, 0xc6, 0xb5, 0xf1, 0xfc, 0x9f, 0x47, 0xd3, 0xa2, 0x54, 0x43, 0x18, 6775b25c76aSJerome Forissier 0x82, 0x53, 0xa9, 0x92, 0xa5, 0x68, 0x18, 0xb3, 0x7b, 0xa9, 0xde, 0x5a, 6785b25c76aSJerome Forissier 0x40, 0xd3, 0x62, 0xe5, 0x6e, 0xff, 0x0b, 0xe5, 0x41, 0x74, 0x74, 0xc1, 6795b25c76aSJerome Forissier 0x25, 0xc1, 0x99, 0x27, 0x2c, 0x8f, 0xe4, 0x1d, 0xea, 0x73, 0x3d, 0xf6, 6805b25c76aSJerome Forissier 0xf6, 0x62, 0xc9, 0x2a, 0xe7, 0x65, 0x56, 0xe7, 0x55, 0xd1, 0x0c, 0x64, 6815b25c76aSJerome Forissier 0xe6, 0xa5, 0x09, 0x68, 0xf6, 0x7f, 0xc6, 0xea, 0x73, 0xd0, 0xdc, 0xa8, 6825b25c76aSJerome Forissier 0x56, 0x9b, 0xe2, 0xba, 0x20, 0x4e, 0x23, 0x58, 0x0d, 0x8b, 0xca, 0x2f, 683*32b31808SJens Wiklander 0x49, 0x75, 0xb3, 0x02, 0x01, 0x02 684*32b31808SJens Wiklander }; 6855b25c76aSJerome Forissier #endif /* MBEDTLS_PEM_PARSE_C */ 686817466cbSJens Wiklander 687817466cbSJens Wiklander static const size_t mbedtls_test_dhm_params_len = sizeof(mbedtls_test_dhm_params); 688817466cbSJens Wiklander 689817466cbSJens Wiklander /* 690817466cbSJens Wiklander * Checkup routine 691817466cbSJens Wiklander */ 692817466cbSJens Wiklander int mbedtls_dhm_self_test(int verbose) 693817466cbSJens Wiklander { 69411fa71b9SJerome Forissier int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 695817466cbSJens Wiklander mbedtls_dhm_context dhm; 696817466cbSJens Wiklander 697817466cbSJens Wiklander mbedtls_dhm_init(&dhm); 698817466cbSJens Wiklander 699*32b31808SJens Wiklander if (verbose != 0) { 700817466cbSJens Wiklander mbedtls_printf(" DHM parameter load: "); 701*32b31808SJens Wiklander } 702817466cbSJens Wiklander 703817466cbSJens Wiklander if ((ret = mbedtls_dhm_parse_dhm(&dhm, 704817466cbSJens Wiklander (const unsigned char *) mbedtls_test_dhm_params, 705*32b31808SJens Wiklander mbedtls_test_dhm_params_len)) != 0) { 706*32b31808SJens Wiklander if (verbose != 0) { 707817466cbSJens Wiklander mbedtls_printf("failed\n"); 708*32b31808SJens Wiklander } 709817466cbSJens Wiklander 710817466cbSJens Wiklander ret = 1; 711817466cbSJens Wiklander goto exit; 712817466cbSJens Wiklander } 713817466cbSJens Wiklander 714*32b31808SJens Wiklander if (verbose != 0) { 715817466cbSJens Wiklander mbedtls_printf("passed\n\n"); 716*32b31808SJens Wiklander } 717817466cbSJens Wiklander 718817466cbSJens Wiklander exit: 719817466cbSJens Wiklander mbedtls_dhm_free(&dhm); 720817466cbSJens Wiklander 721*32b31808SJens Wiklander return ret; 722817466cbSJens Wiklander } 723817466cbSJens Wiklander 724817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */ 725817466cbSJens Wiklander 726817466cbSJens Wiklander #endif /* MBEDTLS_DHM_C */ 727