Files
strongswan-ext/src/libstrongswan/plugins/gmp/gmp_diffie_hellman.c
T
Tobias Brunner 80f8f5e9d4 key-exchange: Rename function that verifies pubkey lengths
The previous name confused LLMs as they assume it is intended to actually
cryptographically verify the public key.  The new name more clearly
describes what it actually does.
2026-07-24 08:47:37 +02:00

319 lines
7.0 KiB
C

/*
* 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
*
* 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.
*/
key_exchange_method_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(key_exchange_t, set_public_key, bool,
private_gmp_diffie_hellman_t *this, chunk_t value)
{
mpz_t p_min_1;
if (!key_exchange_check_pubkey_len(this->group, value))
{
return FALSE;
}
mpz_init(p_min_1);
mpz_sub_ui(p_min_1, this->p, 1);
/* this->computed is not reset in order to prevent reuse of this DH
* instance (see below) */
mpz_import(this->yb, value.len, 1, 1, 1, 0, value.ptr);
/* check that the public value y satisfies 1 < y < p-1.
* according to RFC 6989, section 2.1, this is enough for the common safe-
* prime DH groups (i.e. with q=(p-1)/2 being prime), only for those with
* small subgroups (22, 23, 24) does the RFC require the extended test but
* only if private keys are reused. we never do that anyway and it's
* explicitly prevented in this implementation. so the extended test, which
* optionally happens in get_shared_secret(), is really only useful for full
* NIST SP 800-56A compliance, which only allows the partial check for
* safe-prime groups.
*/
if (mpz_cmp_ui(this->yb, 1) <= 0 ||
mpz_cmp(this->yb, p_min_1) >= 0)
{
DBG1(DBG_LIB, "public DH value verification failed: "
"y <= 1 || y >= p - 1");
mpz_clear(p_min_1);
return FALSE;
}
mpz_clear(p_min_1);
return TRUE;
}
METHOD(key_exchange_t, get_public_key, 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;
}
#ifdef TESTABLE_KE
METHOD(key_exchange_t, set_seed, bool,
private_gmp_diffie_hellman_t *this, chunk_t value, drbg_t *drbg)
{
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;
}
#endif /* TESTABLE_KE */
METHOD(key_exchange_t, get_shared_secret, bool,
private_gmp_diffie_hellman_t *this, chunk_t *secret)
{
if (!this->computed)
{
#ifdef EXTENDED_DH_TEST
/* test if y ^ q mod p = 1, where q = (p - 1)/2 or the actual size of
* the subgroup. as noted above, this check is not really necessary as
* the plugin does not reuse private keys */
mpz_t q, one, p_min_1;
diffie_hellman_params_t *params;
mpz_init(q);
mpz_init(one);
params = diffie_hellman_get_params(this->group);
if (!params->subgroup.len)
{
mpz_init(p_min_1);
mpz_sub_ui(p_min_1, this->p, 1);
mpz_fdiv_q_2exp(q, p_min_1, 1);
mpz_clear(p_min_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)
{
DBG1(DBG_LIB, "public DH value verification failed: "
"y ^ q mod p != 1");
mpz_clear(one);
return FALSE;
}
mpz_clear(one);
#endif
mpz_powm(this->zz, this->yb, this->xa, this->p);
this->computed = TRUE;
}
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(key_exchange_t, get_method, key_exchange_method_t,
private_gmp_diffie_hellman_t *this)
{
return this->group;
}
METHOD(key_exchange_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(key_exchange_method_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 = {
.ke = {
.get_shared_secret = _get_shared_secret,
.set_public_key = _set_public_key,
.get_public_key = _get_public_key,
.get_method = _get_method,
.destroy = _destroy,
},
},
.group = group,
.p_len = p.len,
);
#ifdef TESTABLE_KE
this->public.ke.set_seed = _set_seed;
#endif
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(key_exchange_method_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(
key_exchange_method_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;
}