|
@@ -1,6 +1,6 @@
|
|
|
|
|
|
* Wrapper functions for OpenSSL libcrypto
|
|
|
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
|
|
|
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
|
|
|
*
|
|
|
* 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
|