diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index eb3c33bbe..a723201e1 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,6 +1,6 @@ /* * Wrapper functions for crypto libraries - * Copyright (c) 2004-2013, Jouni Malinen + * Copyright (c) 2004-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -832,4 +832,12 @@ int crypto_ec_point_cmp(const struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b); +struct crypto_ecdh; + +struct crypto_ecdh * crypto_ecdh_init(int group); +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y); +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len); +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh); + #endif /* CRYPTO_H */ diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 049246881..d535c7c9c 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -1,6 +1,6 @@ /* * Wrapper functions for OpenSSL libcrypto - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1363,6 +1363,7 @@ fail: struct crypto_ec { EC_GROUP *group; + int nid; BN_CTX *bnctx; BIGNUM *prime; BIGNUM *order; @@ -1420,6 +1421,7 @@ struct crypto_ec * crypto_ec_init(int group) if (e == NULL) return NULL; + e->nid = nid; e->bnctx = BN_CTX_new(); e->group = EC_GROUP_new_by_curve_name(nid); e->prime = BN_new(); @@ -1660,4 +1662,239 @@ int crypto_ec_point_cmp(const struct crypto_ec *e, (const EC_POINT *) b, e->bnctx); } + +struct crypto_ecdh { + struct crypto_ec *ec; + EVP_PKEY *pkey; +}; + +struct crypto_ecdh * crypto_ecdh_init(int group) +{ + struct crypto_ecdh *ecdh; + EVP_PKEY *params = NULL; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY_CTX *kctx = NULL; + + ecdh = os_zalloc(sizeof(*ecdh)); + if (!ecdh) + goto fail; + + ecdh->ec = crypto_ec_init(group); + if (!ecdh->ec) + goto fail; + + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (!pctx) + goto fail; + + if (EVP_PKEY_paramgen_init(pctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_paramgen_init failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ecdh->ec->nid) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_CTX_set_ec_paramgen_curve_nid failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (EVP_PKEY_paramgen(pctx, ¶ms) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_paramgen failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + kctx = EVP_PKEY_CTX_new(params, NULL); + if (!kctx) + goto fail; + + if (EVP_PKEY_keygen_init(kctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_keygen_init failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + +done: + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_CTX_free(kctx); + + return ecdh; +fail: + crypto_ecdh_deinit(ecdh); + ecdh = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y) +{ + struct wpabuf *buf = NULL; + EC_KEY *eckey; + const EC_POINT *pubkey; + BIGNUM *x, *y = NULL; + int len = BN_num_bytes(ecdh->ec->prime); + int res; + + eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey); + if (!eckey) + return NULL; + + pubkey = EC_KEY_get0_public_key(eckey); + if (!pubkey) + return NULL; + + x = BN_new(); + if (inc_y) { + y = BN_new(); + if (!y) + goto fail; + } + buf = wpabuf_alloc(inc_y ? 2 * len : len); + if (!x || !buf) + goto fail; + + if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey, + x, y, ecdh->ec->bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + res = crypto_bignum_to_bin((struct crypto_bignum *) x, + wpabuf_put(buf, len), len, len); + if (res < 0) + goto fail; + + if (inc_y) { + res = crypto_bignum_to_bin((struct crypto_bignum *) y, + wpabuf_put(buf, len), len, len); + if (res < 0) + goto fail; + } + +done: + BN_clear_free(x); + BN_clear_free(y); + EC_KEY_free(eckey); + + return buf; +fail: + wpabuf_free(buf); + buf = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len) +{ + BIGNUM *x, *y = NULL; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *peerkey = NULL; + struct wpabuf *secret = NULL; + size_t secret_len; + EC_POINT *pub; + EC_KEY *eckey = NULL; + + x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL); + pub = EC_POINT_new(ecdh->ec->group); + if (!x || !pub) + goto fail; + + if (inc_y) { + y = BN_bin2bn(key + len / 2, len / 2, NULL); + if (!y) + goto fail; + if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub, + x, y, + ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + } else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group, + pub, x, 0, + ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: ECDH peer public key is not on curve"); + goto fail; + } + + eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid); + if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_KEY_set_public_key failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + peerkey = EVP_PKEY_new(); + if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1) + goto fail; + + ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL); + if (!ctx || EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_derive(1) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + secret = wpabuf_alloc(secret_len); + if (!secret) + goto fail; + if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len), + &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_derive(2) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + +done: + BN_free(x); + BN_free(y); + EC_KEY_free(eckey); + EC_POINT_free(pub); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(peerkey); + return secret; +fail: + wpabuf_free(secret); + secret = NULL; + goto done; +} + + +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh) +{ + if (ecdh) { + crypto_ec_deinit(ecdh->ec); + EVP_PKEY_free(ecdh->pkey); + os_free(ecdh); + } +} + #endif /* CONFIG_ECC */