/* * Copyright (C) 2019 Sean Parkinson, wolfSSL Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "wolfssl_common.h" #ifdef HAVE_ECC_DHE #include #include "wolfssl_ec_diffie_hellman.h" #include "wolfssl_util.h" #include typedef struct private_wolfssl_ec_diffie_hellman_t private_wolfssl_ec_diffie_hellman_t; /** * Private data of an wolfssl_ec_diffie_hellman_t object. */ struct private_wolfssl_ec_diffie_hellman_t { /** * Public wolfssl_ec_diffie_hellman_t interface. */ wolfssl_ec_diffie_hellman_t public; /** * Diffie Hellman group number. */ diffie_hellman_group_t group; /** * EC curve id for creating keys */ ecc_curve_id curve_id; /** * Size of an ordinate in bytes */ int keysize; /** * EC private (public) key */ ecc_key key; /** * Other public key */ ecc_point *pub_key; /** * Shared secret */ chunk_t shared_secret; /** * Random number generator */ WC_RNG rng; /** * True if shared secret is computed */ bool computed; }; /** * 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. */ static bool ecp2chunk(int keysize, ecc_point *point, chunk_t *chunk, bool x_coordinate_only) { mp_int *y = NULL; if (!x_coordinate_only) { keysize *= 2; y = point->y; } return wolfssl_mp_cat(keysize, point->x, y, chunk); } /** * Perform the elliptic curve scalar multiplication. */ static bool wolfssl_ecc_multiply(const ecc_set_type *ecc_set, mp_int *scalar, ecc_point* point, ecc_point* r) { int ret; mp_int a, prime; ret = mp_init(&a); if (ret != 0) { return FALSE; } ret = mp_init(&prime); if (ret != 0) { mp_free(&a); return FALSE; } ret = mp_read_radix(&a, ecc_set->Af, MP_RADIX_HEX); if (ret == 0) { ret = mp_read_radix(&prime, ecc_set->prime, MP_RADIX_HEX); } if (ret == 0) { /* Multiply the point by our secret */ ret = wc_ecc_mulmod(scalar, point, r, &a, &prime, 1); } mp_free(&prime); mp_free(&a); 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, chunk_t *shared_secret) { bool success = FALSE; ecc_point* secret; bool x_coordinate_only; if ((secret = wc_ecc_new_point()) == NULL) { return FALSE; } if (wolfssl_ecc_multiply(this->key.dp, &this->key.k, this->pub_key, secret)) { /* * The default setting ecp_x_coordinate_only = TRUE * applies the following errata for RFC 4753: * http://www.rfc-editor.org/errata_search.php?eid=9 */ x_coordinate_only = lib->settings->get_bool(lib->settings, "%s.ecp_x_coordinate_only", TRUE, lib->ns); success = ecp2chunk(this->keysize, secret, shared_secret, x_coordinate_only); } wc_ecc_del_point(secret); return success; } METHOD(diffie_hellman_t, set_other_public_value, bool, private_wolfssl_ec_diffie_hellman_t *this, chunk_t value) { if (!diffie_hellman_verify_value(this->group, value)) { return FALSE; } if (!chunk2ecp(value, this->pub_key)) { DBG1(DBG_LIB, "ECDH public value is malformed"); return FALSE; } chunk_clear(&this->shared_secret); if (!compute_shared_key(this, &this->shared_secret)) { DBG1(DBG_LIB, "ECDH shared secret computation failed"); return FALSE; } this->computed = TRUE; return TRUE; } METHOD(diffie_hellman_t, get_my_public_value, bool, private_wolfssl_ec_diffie_hellman_t *this,chunk_t *value) { return ecp2chunk(this->keysize, &this->key.pubkey, value, FALSE); } METHOD(diffie_hellman_t, set_private_value, bool, private_wolfssl_ec_diffie_hellman_t *this, chunk_t value) { bool success = FALSE; ecc_point *base; int ret; if ((base = wc_ecc_new_point()) == NULL) { return FALSE; } ret = mp_read_unsigned_bin(&this->key.k, value.ptr, value.len); /* Get base point */ if (ret == 0) { ret = mp_read_radix(base->x, this->key.dp->Gx, MP_RADIX_HEX); } if (ret == 0) { ret = mp_read_radix(base->y, this->key.dp->Gy, MP_RADIX_HEX); } if (ret == 0) { ret = mp_set(base->z, 1); } if (ret == 0) { /* Calculate public key */ success = wolfssl_ecc_multiply(this->key.dp, &this->key.k, base, &this->key.pubkey); } wc_ecc_del_point(base); return success; } METHOD(diffie_hellman_t, get_shared_secret, bool, private_wolfssl_ec_diffie_hellman_t *this, chunk_t *secret) { if (!this->computed) { return FALSE; } *secret = chunk_clone(this->shared_secret); return TRUE; } METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, private_wolfssl_ec_diffie_hellman_t *this) { return this->group; } METHOD(diffie_hellman_t, destroy, void, private_wolfssl_ec_diffie_hellman_t *this) { wc_FreeRng(&this->rng); wc_ecc_del_point(this->pub_key); wc_ecc_free(&this->key); chunk_clear(&this->shared_secret); free(this); } /* * Described in header. */ wolfssl_ec_diffie_hellman_t *wolfssl_ec_diffie_hellman_create(diffie_hellman_group_t group) { private_wolfssl_ec_diffie_hellman_t *this; 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, ); if (wc_ecc_init(&this->key) != 0) { DBG1(DBG_LIB, "key init failed, ecdh create failed"); free(this); return NULL; } if ((this->pub_key = wc_ecc_new_point()) == NULL) { wc_ecc_free(&this->key); DBG1(DBG_LIB, "pub_key init failed, ecdh create failed"); free(this); return NULL; } if (wc_InitRng(&this->rng) != 0) { wc_ecc_del_point(this->pub_key); wc_ecc_free(&this->key); DBG1(DBG_LIB, "RNG init failed, ecdh create failed"); free(this); return NULL; } switch (group) { case ECP_192_BIT: this->curve_id = ECC_SECP192R1; this->keysize = 192 / 8; break; case ECP_224_BIT: this->curve_id = ECC_SECP224R1; this->keysize = 224 / 8; break; case ECP_256_BIT: this->curve_id = ECC_SECP256R1; this->keysize = 256 / 8; break; case ECP_384_BIT: this->curve_id = ECC_SECP384R1; this->keysize = 384 / 8; break; case ECP_521_BIT: this->curve_id = ECC_SECP521R1; this->keysize = (521 + 7) / 8; break; #ifdef HAVE_BRAINPOOL case ECP_224_BP: this->curve_id = ECC_BRAINPOOLP224R1; this->keysize = 224 / 8; break; case ECP_256_BP: this->curve_id = ECC_BRAINPOOLP256R1; this->keysize = 256 / 8; break; case ECP_384_BP: this->curve_id = ECC_BRAINPOOLP384R1; this->keysize = 384 / 8; break; case ECP_512_BP: this->curve_id = ECC_BRAINPOOLP512R1; this->keysize = 512 / 8; break; #endif default: DBG1(DBG_LIB, "EC group not supported"); destroy(this); return NULL; } /* generate an EC private (public) key */ if (wc_ecc_make_key_ex(&this->rng, this->keysize, &this->key, this->curve_id) != 0) { DBG1(DBG_LIB, "Make key failed, wolfssl ECDH create failed"); destroy(this); return NULL; } return &this->public; } #endif /* HAVE_ECC_DHE */