/* * Copyright (C) 1998-2002 D. Hugh Redelmeier. * Copyright (C) 1999, 2000, 2001 Henry Spencer. * Copyright (C) 2010 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include "gmp_diffie_hellman.h" #include #ifdef HAVE_MPZ_POWM_SEC # undef mpz_powm # define mpz_powm mpz_powm_sec #endif typedef struct private_gmp_diffie_hellman_t private_gmp_diffie_hellman_t; /** * Private data of an gmp_diffie_hellman_t object. */ struct private_gmp_diffie_hellman_t { /** * Public gmp_diffie_hellman_t interface. */ gmp_diffie_hellman_t public; /** * Diffie Hellman group number. */ diffie_hellman_group_t group; /* * Generator value. */ mpz_t g; /** * My private value. */ mpz_t xa; /** * My public value. */ mpz_t ya; /** * Other public value. */ mpz_t yb; /** * Shared secret. */ mpz_t zz; /** * Modulus. */ mpz_t p; /** * Modulus length. */ size_t p_len; /** * True if shared secret is computed and stored in my_public_value. */ bool computed; }; METHOD(diffie_hellman_t, set_other_public_value, bool, private_gmp_diffie_hellman_t *this, chunk_t value) { mpz_t p_min_1; if (!diffie_hellman_verify_value(this->group, value)) { return FALSE; } mpz_init(p_min_1); mpz_sub_ui(p_min_1, this->p, 1); mpz_import(this->yb, value.len, 1, 1, 1, 0, value.ptr); /* check public value: * 1. 0 or 1 is invalid as 0^a = 0 and 1^a = 1 * 2. a public value larger or equal the modulus is invalid */ if (mpz_cmp_ui(this->yb, 1) > 0 && mpz_cmp(this->yb, p_min_1) < 0) { #ifdef EXTENDED_DH_TEST /* 3. test if y ^ q mod p = 1, where q = (p - 1)/2. */ mpz_t q, one; diffie_hellman_params_t *params; mpz_init(q); mpz_init(one); params = diffie_hellman_get_params(this->group); if (!params->subgroup.len) { mpz_fdiv_q_2exp(q, p_min_1, 1); } else { mpz_import(q, params->subgroup.len, 1, 1, 1, 0, params->subgroup.ptr); } mpz_powm(one, this->yb, q, this->p); mpz_clear(q); if (mpz_cmp_ui(one, 1) == 0) { mpz_powm(this->zz, this->yb, this->xa, this->p); this->computed = TRUE; } else { DBG1(DBG_LIB, "public DH value verification failed:" " y ^ q mod p != 1"); } mpz_clear(one); #else mpz_powm(this->zz, this->yb, this->xa, this->p); this->computed = TRUE; #endif } else { DBG1(DBG_LIB, "public DH value verification failed:" " y < 2 || y > p - 1 "); } mpz_clear(p_min_1); return this->computed; } METHOD(diffie_hellman_t, get_my_public_value, bool, private_gmp_diffie_hellman_t *this,chunk_t *value) { value->len = this->p_len; value->ptr = mpz_export(NULL, NULL, 1, value->len, 1, 0, this->ya); if (value->ptr == NULL) { value->len = 0; } return TRUE; } METHOD(diffie_hellman_t, set_private_value, bool, private_gmp_diffie_hellman_t *this, chunk_t value) { mpz_import(this->xa, value.len, 1, 1, 1, 0, value.ptr); mpz_powm(this->ya, this->g, this->xa, this->p); this->computed = FALSE; return TRUE; } METHOD(diffie_hellman_t, get_shared_secret, bool, private_gmp_diffie_hellman_t *this, chunk_t *secret) { if (!this->computed) { return FALSE; } secret->len = this->p_len; secret->ptr = mpz_export(NULL, NULL, 1, secret->len, 1, 0, this->zz); if (secret->ptr == NULL) { return FALSE; } return TRUE; } METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, private_gmp_diffie_hellman_t *this) { return this->group; } METHOD(diffie_hellman_t, destroy, void, private_gmp_diffie_hellman_t *this) { mpz_clear(this->p); mpz_clear(this->xa); mpz_clear(this->ya); mpz_clear(this->yb); mpz_clear(this->zz); mpz_clear(this->g); free(this); } /** * Generic internal constructor */ static gmp_diffie_hellman_t *create_generic(diffie_hellman_group_t group, size_t exp_len, chunk_t g, chunk_t p) { private_gmp_diffie_hellman_t *this; chunk_t random; rng_t *rng; INIT(this, .public = { .dh = { .get_shared_secret = _get_shared_secret, .set_other_public_value = _set_other_public_value, .get_my_public_value = _get_my_public_value, .set_private_value = _set_private_value, .get_dh_group = _get_dh_group, .destroy = _destroy, }, }, .group = group, .p_len = p.len, ); mpz_init(this->p); mpz_init(this->yb); mpz_init(this->ya); mpz_init(this->xa); mpz_init(this->zz); mpz_init(this->g); mpz_import(this->g, g.len, 1, 1, 1, 0, g.ptr); mpz_import(this->p, p.len, 1, 1, 1, 0, p.ptr); rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); if (!rng) { DBG1(DBG_LIB, "no RNG found for quality %N", rng_quality_names, RNG_STRONG); destroy(this); return NULL; } if (!rng->allocate_bytes(rng, exp_len, &random)) { DBG1(DBG_LIB, "failed to allocate DH secret"); rng->destroy(rng); destroy(this); return NULL; } rng->destroy(rng); if (exp_len == this->p_len) { /* achieve bitsof(p)-1 by setting MSB to 0 */ *random.ptr &= 0x7F; } mpz_import(this->xa, random.len, 1, 1, 1, 0, random.ptr); chunk_clear(&random); DBG2(DBG_LIB, "size of DH secret exponent: %u bits", mpz_sizeinbase(this->xa, 2)); mpz_powm(this->ya, this->g, this->xa, this->p); return &this->public; } /* * Described in header */ gmp_diffie_hellman_t *gmp_diffie_hellman_create(diffie_hellman_group_t group) { diffie_hellman_params_t *params; params = diffie_hellman_get_params(group); if (!params) { return NULL; } return create_generic(group, params->exp_len, params->generator, params->prime); } /* * Described in header */ gmp_diffie_hellman_t *gmp_diffie_hellman_create_custom( diffie_hellman_group_t group, ...) { if (group == MODP_CUSTOM) { chunk_t g, p; VA_ARGS_GET(group, g, p); return create_generic(MODP_CUSTOM, p.len, g, p); } return NULL; }