/* * 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" #ifndef NO_DH #include #include "wolfssl_diffie_hellman.h" #include "wolfssl_util.h" #include typedef struct private_wolfssl_diffie_hellman_t private_wolfssl_diffie_hellman_t; /** * Private data of an wolfssl_diffie_hellman_t object. */ struct private_wolfssl_diffie_hellman_t { /** * Public wolfssl_diffie_hellman_t interface. */ wolfssl_diffie_hellman_t public; /** * Diffie Hellman group number. */ diffie_hellman_group_t group; /** * Diffie Hellman object */ DhKey dh; /** * Length of public values */ int len; /** * Private key */ chunk_t priv; /** * Public key */ chunk_t pub; /** * Shared secret */ chunk_t shared_secret; }; METHOD(diffie_hellman_t, get_my_public_value, bool, private_wolfssl_diffie_hellman_t *this, chunk_t *value) { *value = chunk_copy_pad(chunk_alloc(this->len), this->pub, 0x00); return TRUE; } METHOD(diffie_hellman_t, get_shared_secret, bool, private_wolfssl_diffie_hellman_t *this, chunk_t *secret) { if (!this->shared_secret.len) { return FALSE; } *secret = chunk_copy_pad(chunk_alloc(this->len), this->shared_secret, 0x00); return TRUE; } METHOD(diffie_hellman_t, set_other_public_value, bool, private_wolfssl_diffie_hellman_t *this, chunk_t value) { word32 len; if (!diffie_hellman_verify_value(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) { DBG1(DBG_LIB, "DH shared secret computation failed"); chunk_free(&this->shared_secret); return FALSE; } this->shared_secret.len = len; return TRUE; } METHOD(diffie_hellman_t, set_private_value, bool, private_wolfssl_diffie_hellman_t *this, chunk_t value) { bool success = FALSE; chunk_t g; word32 len; chunk_clear(&this->priv); this->priv = chunk_clone(value); /* calculate public value - g^priv mod p */ if (wolfssl_mp2chunk(&this->dh.g, &g)) { len = this->pub.len; if (wc_DhAgree(&this->dh, this->pub.ptr, &len, this->priv.ptr, this->priv.len, g.ptr, g.len) == 0) { this->pub.len = len; success = TRUE; } } free(g.ptr); return success; } METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, private_wolfssl_diffie_hellman_t *this) { return this->group; } METHOD(diffie_hellman_t, destroy, void, private_wolfssl_diffie_hellman_t *this) { wc_FreeDhKey(&this->dh); chunk_clear(&this->pub); chunk_clear(&this->priv); chunk_clear(&this->shared_secret); free(this); } /** * Maximum private key length when generating key */ static int wolfssl_priv_key_size(int len) { if (len <= 128) { return 21; } if (len <= 256) { return 29; } if (len <= 384) { return 34; } if (len <= 512) { return 39; } if (len <= 640) { return 42; } if (len <= 768) { return 46; } if (len <= 896) { return 49; } if (len <= 1024) { return 52; } return len / 20; } /** * Generic internal constructor */ static wolfssl_diffie_hellman_t *create_generic(diffie_hellman_group_t group, chunk_t g, chunk_t p) { private_wolfssl_diffie_hellman_t *this; word32 privLen, pubLen; WC_RNG 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, .len = p.len, ); if (wc_InitDhKey(&this->dh) != 0) { free(this); return NULL; } if (wc_DhSetKey(&this->dh, p.ptr, p.len, g.ptr, g.len) != 0) { destroy(this); return NULL; } if (wc_InitRng(&rng) != 0) { destroy(this); return NULL; } this->priv = chunk_alloc(wolfssl_priv_key_size(this->len)); this->pub = chunk_alloc(this->len); privLen = this->priv.len; pubLen = this->pub.len; /* generate my public and private values */ if (wc_DhGenerateKeyPair(&this->dh, &rng, this->priv.ptr, &privLen, this->pub.ptr, &pubLen) != 0) { wc_FreeRng(&rng); destroy(this); return NULL; } this->pub.len = pubLen; this->priv.len = privLen; wc_FreeRng(&rng); return &this->public; } /* * Described in header */ wolfssl_diffie_hellman_t *wolfssl_diffie_hellman_create( diffie_hellman_group_t group, ...) { diffie_hellman_params_t *params; chunk_t g, p; if (group == MODP_CUSTOM) { VA_ARGS_GET(group, g, p); return create_generic(group, g, p); } params = diffie_hellman_get_params(group); if (!params) { return NULL; } /* wolfSSL doesn't support optimized exponent sizes according to RFC 3526 */ return create_generic(group, params->generator, params->prime); } #endif /* NO_DH */