| /* |
| * 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 <http://www.fsf.org/copyleft/gpl.txt>. |
| * |
| * 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 <gmp.h> |
| |
| #include "gmp_diffie_hellman.h" |
| |
| #include <utils/debug.h> |
| |
| #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; |
| } |