wolfssl: Move shared secret calculation to get_shared_secret()

The ECDH implementation gets a bit simpler since we removed the
ecp_x_coordinate_only option a while ago.

Also added calls to verify public keys.
This commit is contained in:
Tobias Brunner 2021-07-23 17:29:41 +02:00 committed by Andreas Steffen
parent 6ae51d35d0
commit 73439707b6
3 changed files with 146 additions and 149 deletions

View File

@ -68,6 +68,11 @@ struct private_wolfssl_diffie_hellman_t {
*/
chunk_t pub;
/**
* Public key provided by peer
*/
chunk_t other;
/**
* Shared secret
*/
@ -84,9 +89,19 @@ METHOD(key_exchange_t, get_public_key, bool,
METHOD(key_exchange_t, get_shared_secret, bool,
private_wolfssl_diffie_hellman_t *this, chunk_t *secret)
{
word32 len;
if (!this->shared_secret.len)
{
return FALSE;
this->shared_secret = chunk_alloc(this->len);
if (wc_DhAgree(&this->dh, this->shared_secret.ptr, &len, this->priv.ptr,
this->priv.len, this->other.ptr, this->other.len) != 0)
{
DBG1(DBG_LIB, "DH shared secret computation failed");
chunk_free(&this->shared_secret);
return FALSE;
}
this->shared_secret.len = len;
}
*secret = chunk_copy_pad(chunk_alloc(this->len), this->shared_secret, 0x00);
return TRUE;
@ -95,23 +110,17 @@ METHOD(key_exchange_t, get_shared_secret, bool,
METHOD(key_exchange_t, set_public_key, bool,
private_wolfssl_diffie_hellman_t *this, chunk_t value)
{
word32 len;
if (!key_exchange_verify_pubkey(this->group, value))
{
return FALSE;
}
chunk_clear(&this->shared_secret);
this->shared_secret = chunk_alloc(this->len);
if (wc_DhAgree(&this->dh, this->shared_secret.ptr, &len, this->priv.ptr,
this->priv.len, value.ptr, value.len) != 0)
if (wc_DhCheckPubKey(&this->dh, value.ptr, value.len) != 0)
{
DBG1(DBG_LIB, "DH shared secret computation failed");
chunk_free(&this->shared_secret);
DBG1(DBG_LIB, "DH public key value invalid");
return FALSE;
}
this->shared_secret.len = len;
chunk_clear(&this->other);
this->other = chunk_clone(value);
return TRUE;
}
@ -153,6 +162,7 @@ METHOD(key_exchange_t, destroy, void,
wc_FreeDhKey(&this->dh);
chunk_clear(&this->pub);
chunk_clear(&this->priv);
chunk_clear(&this->other);
chunk_clear(&this->shared_secret);
free(this);
}

View File

@ -62,29 +62,17 @@ struct private_wolfssl_ec_diffie_hellman_t {
*/
ecc_key key;
/**
* Public key provided by peer
*/
ecc_key pubkey;
/**
* Shared secret
*/
chunk_t shared_secret;
};
/**
* Convert a chunk to an ecc_point (which must already exist). The x and y
* coordinates of the point have to be concatenated in the chunk.
*/
static bool chunk2ecp(chunk_t chunk, ecc_point *point)
{
if (!wolfssl_mp_split(chunk, point->x, point->y))
{
return FALSE;
}
if (mp_set(point->z, 1) != 0)
{
return FALSE;
}
return TRUE;
}
/**
* Convert an ec_point to a chunk by concatenating the x and y coordinates of
* the point. This function allocates memory for the chunk.
@ -138,69 +126,30 @@ static bool wolfssl_ecc_multiply(const ecc_set_type *ecc_set, mp_int *scalar,
return ret == 0;
}
/**
* Compute the shared secret.
*
* We cannot use the function wc_ecc_shared_secret() because that returns only
* the x coordinate of the shared secret point (which is defined, for instance,
* in 'NIST SP 800-56A').
* However, we need both coordinates as RFC 4753 says: "The Diffie-Hellman
* public value is obtained by concatenating the x and y values. The format
* of the Diffie-Hellman shared secret value is the same as that of the
* Diffie-Hellman public value."
*/
static bool compute_shared_key(private_wolfssl_ec_diffie_hellman_t *this,
ecc_point *pub_key, chunk_t *shared_secret)
{
ecc_point* secret;
bool success = FALSE;
if ((secret = wc_ecc_new_point()) == NULL)
{
return FALSE;
}
if (wolfssl_ecc_multiply(this->key.dp, &this->key.k, pub_key, secret))
{
success = ecp2chunk(this->keysize, secret, shared_secret, TRUE);
}
wc_ecc_del_point(secret);
return success;
}
METHOD(key_exchange_t, set_public_key, bool,
private_wolfssl_ec_diffie_hellman_t *this, chunk_t value)
{
ecc_point *pub_key;
chunk_t uncomp;
if (!key_exchange_verify_pubkey(this->group, value))
{
return FALSE;
}
if ((pub_key = wc_ecc_new_point()) == NULL)
{
return FALSE;
}
if (!chunk2ecp(value, pub_key))
/* prepend 0x04 to indicate uncompressed point format */
uncomp = chunk_cata("cc", chunk_from_chars(0x04), value);
if (wc_ecc_import_x963_ex(uncomp.ptr, uncomp.len, &this->pubkey,
this->curve_id) != 0)
{
DBG1(DBG_LIB, "ECDH public value is malformed");
wc_ecc_del_point(pub_key);
return FALSE;
}
chunk_clear(&this->shared_secret);
if (!compute_shared_key(this, pub_key, &this->shared_secret))
if (wc_ecc_check_key(&this->pubkey) != 0)
{
DBG1(DBG_LIB, "ECDH shared secret computation failed");
chunk_clear(&this->shared_secret);
wc_ecc_del_point(pub_key);
DBG1(DBG_LIB, "ECDH public value is invalid");
return FALSE;
}
wc_ecc_del_point(pub_key);
return TRUE;
}
@ -248,10 +197,51 @@ METHOD(key_exchange_t, set_seed, bool,
return success;
}
/**
* Derive the shared secret
*/
static bool compute_shared_key(private_wolfssl_ec_diffie_hellman_t *this)
{
word32 len;
#ifdef ECC_TIMING_RESISTANT
WC_RNG rng;
if (wc_InitRng(&rng) != 0)
{
return FALSE;
}
if (wc_ecc_set_rng(&this->key, &rng) != 0)
{
wc_FreeRng(&rng);
return FALSE;
}
#endif
this->shared_secret = chunk_alloc(this->keysize);
len = this->shared_secret.len;
if (wc_ecc_shared_secret(&this->key, &this->pubkey, this->shared_secret.ptr,
&len) != 0)
{
DBG1(DBG_LIB, "ECDH shared secret computation failed");
chunk_clear(&this->shared_secret);
#ifdef ECC_TIMING_RESISTANT
wc_FreeRng(&rng);
#endif
return FALSE;
}
this->shared_secret.len = len;
#ifdef ECC_TIMING_RESISTANT
wc_FreeRng(&rng);
#endif
return TRUE;
}
METHOD(key_exchange_t, get_shared_secret, bool,
private_wolfssl_ec_diffie_hellman_t *this, chunk_t *secret)
{
if (!this->shared_secret.len)
if (!this->shared_secret.len &&
!compute_shared_key(this))
{
return FALSE;
}
@ -269,6 +259,7 @@ METHOD(key_exchange_t, destroy, void,
private_wolfssl_ec_diffie_hellman_t *this)
{
wc_ecc_free(&this->key);
wc_ecc_free(&this->pubkey);
chunk_clear(&this->shared_secret);
free(this);
}
@ -295,10 +286,10 @@ wolfssl_ec_diffie_hellman_t *wolfssl_ec_diffie_hellman_create(key_exchange_metho
.group = group,
);
if (wc_ecc_init(&this->key) != 0)
if (wc_ecc_init(&this->key) != 0 || wc_ecc_init(&this->pubkey) != 0)
{
DBG1(DBG_LIB, "key init failed, ecdh create failed");
free(this);
destroy(this);
return NULL;
}

View File

@ -69,6 +69,18 @@ struct private_diffie_hellman_t {
#endif
} key;
/**
* Public key provided by peer
*/
union {
#ifdef HAVE_CURVE25519
curve25519_key key25519;
#endif
#ifdef HAVE_CURVE448
curve448_key key448;
#endif
} pub;
/**
* Shared secret
*/
@ -77,47 +89,42 @@ struct private_diffie_hellman_t {
#ifdef HAVE_CURVE25519
METHOD(key_exchange_t, get_shared_secret_25519, bool,
private_diffie_hellman_t *this, chunk_t *secret)
{
word32 len = CURVE25519_KEYSIZE;
if (!this->shared_secret.len)
{
this->shared_secret = chunk_alloc(len);
if (wc_curve25519_shared_secret_ex(&this->key.key25519, &this->pub.key25519,
this->shared_secret.ptr, &len, EC25519_LITTLE_ENDIAN) != 0)
{
DBG1(DBG_LIB, "%N shared secret computation failed",
key_exchange_method_names, this->group);
chunk_clear(&this->shared_secret);
return FALSE;
}
}
*secret = chunk_clone(this->shared_secret);
return TRUE;
}
METHOD(key_exchange_t, set_public_key_25519, bool,
private_diffie_hellman_t *this, chunk_t value)
{
word32 len = CURVE25519_KEYSIZE;
curve25519_key pub;
int ret;
if (!key_exchange_verify_pubkey(this->group, value))
{
return FALSE;
}
ret = wc_curve25519_init(&pub);
if (ret != 0)
{
DBG1(DBG_LIB, "%N public key initialization failed",
key_exchange_method_names, this->group);
return FALSE;
}
ret = wc_curve25519_import_public_ex(value.ptr, value.len, &pub,
EC25519_LITTLE_ENDIAN);
if (ret != 0)
if (wc_curve25519_import_public_ex(value.ptr, value.len, &this->pub.key25519,
EC25519_LITTLE_ENDIAN) != 0)
{
DBG1(DBG_LIB, "%N public value is malformed",
key_exchange_method_names, this->group);
return FALSE;
}
chunk_clear(&this->shared_secret);
this->shared_secret = chunk_alloc(len);
if (wc_curve25519_shared_secret_ex(&this->key.key25519, &pub,
this->shared_secret.ptr, &len, EC25519_LITTLE_ENDIAN) != 0)
{
DBG1(DBG_LIB, "%N shared secret computation failed",
key_exchange_method_names, this->group);
chunk_clear(&this->shared_secret);
wc_curve25519_free(&pub);
return FALSE;
}
wc_curve25519_free(&pub);
return TRUE;
}
@ -169,47 +176,42 @@ METHOD(key_exchange_t, set_seed_25519, bool,
#ifdef HAVE_CURVE448
METHOD(key_exchange_t, get_shared_secret_448, bool,
private_diffie_hellman_t *this, chunk_t *secret)
{
word32 len = CURVE448_KEY_SIZE;
if (!this->shared_secret.len)
{
this->shared_secret = chunk_alloc(len);
if (wc_curve448_shared_secret_ex(&this->key.key448, &this->pub.key448,
this->shared_secret.ptr, &len, EC448_LITTLE_ENDIAN) != 0)
{
DBG1(DBG_LIB, "%N shared secret computation failed",
key_exchange_method_names, this->group);
chunk_clear(&this->shared_secret);
return FALSE;
}
}
*secret = chunk_clone(this->shared_secret);
return TRUE;
}
METHOD(key_exchange_t, set_public_key_448, bool,
private_diffie_hellman_t *this, chunk_t value)
{
word32 len = CURVE448_KEY_SIZE;
curve448_key pub;
int ret;
if (!key_exchange_verify_pubkey(this->group, value))
{
return FALSE;
}
ret = wc_curve448_init(&pub);
if (ret != 0)
{
DBG1(DBG_LIB, "%N public key initialization failed",
key_exchange_method_names, this->group);
return FALSE;
}
ret = wc_curve448_import_public_ex(value.ptr, value.len, &pub,
EC448_LITTLE_ENDIAN);
if (ret != 0)
if (wc_curve448_import_public_ex(value.ptr, value.len, &this->pub.key448,
EC448_LITTLE_ENDIAN) != 0)
{
DBG1(DBG_LIB, "%N public value is malformed",
key_exchange_method_names, this->group);
return FALSE;
}
chunk_clear(&this->shared_secret);
this->shared_secret = chunk_alloc(len);
if (wc_curve448_shared_secret_ex(&this->key.key448, &pub,
this->shared_secret.ptr, &len, EC448_LITTLE_ENDIAN) != 0)
{
DBG1(DBG_LIB, "%N shared secret computation failed",
key_exchange_method_names, this->group);
chunk_clear(&this->shared_secret);
wc_curve448_free(&pub);
return FALSE;
}
wc_curve448_free(&pub);
return TRUE;
}
@ -259,17 +261,6 @@ METHOD(key_exchange_t, set_seed_448, bool,
#endif /* HAVE_CURVE448 */
METHOD(key_exchange_t, get_shared_secret, bool,
private_diffie_hellman_t *this, chunk_t *secret)
{
if (!this->shared_secret.len)
{
return FALSE;
}
*secret = chunk_clone(this->shared_secret);
return TRUE;
}
METHOD(key_exchange_t, get_method, key_exchange_method_t,
private_diffie_hellman_t *this)
{
@ -283,12 +274,14 @@ METHOD(key_exchange_t, destroy, void,
{
#ifdef HAVE_CURVE25519
wc_curve25519_free(&this->key.key25519);
wc_curve25519_free(&this->pub.key25519);
#endif
}
else if (this->group == CURVE_448)
{
#ifdef HAVE_CURVE448
wc_curve448_free(&this->key.key448);
wc_curve448_free(&this->pub.key448);
#endif
}
chunk_clear(&this->shared_secret);
@ -306,7 +299,6 @@ key_exchange_t *wolfssl_x_diffie_hellman_create(key_exchange_method_t group)
INIT(this,
.public = {
.get_shared_secret = _get_shared_secret,
.get_method = _get_method,
.destroy = _destroy,
},
@ -323,14 +315,16 @@ key_exchange_t *wolfssl_x_diffie_hellman_create(key_exchange_method_t group)
if (group == CURVE_25519)
{
#ifdef HAVE_CURVE25519
this->public.get_shared_secret = _get_shared_secret_25519;
this->public.set_public_key = _set_public_key_25519;
this->public.get_public_key = _get_public_key_25519;
this->public.set_seed = _set_seed_25519;
if (wc_curve25519_init(&this->key.key25519) != 0)
if (wc_curve25519_init(&this->key.key25519) != 0 ||
wc_curve25519_init(&this->pub.key25519) != 0)
{
DBG1(DBG_LIB, "initializing key failed");
free(this);
destroy(this);
return NULL;
}
ret = wc_curve25519_make_key(&rng, CURVE25519_KEYSIZE,
@ -340,14 +334,16 @@ key_exchange_t *wolfssl_x_diffie_hellman_create(key_exchange_method_t group)
else if (group == CURVE_448)
{
#ifdef HAVE_CURVE448
this->public.get_shared_secret = _get_shared_secret_448;
this->public.set_public_key = _set_public_key_448;
this->public.get_public_key = _get_public_key_448;
this->public.set_seed = _set_seed_448;
if (wc_curve448_init(&this->key.key448) != 0)
if (wc_curve448_init(&this->key.key448) != 0 ||
wc_curve448_init(&this->pub.key448) != 0)
{
DBG1(DBG_LIB, "initializing key failed");
free(this);
destroy(this);
return NULL;
}
ret = wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &this->key.key448);