]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
OpenSSL: Add wrapper functions for ECDH
authorJouni Malinen <j@w1.fi>
Sat, 11 Mar 2017 16:54:33 +0000 (18:54 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 11 Mar 2017 20:08:48 +0000 (22:08 +0200)
These allow ECDH to be used with compressed public key encoding (only
x-coordinate). This is needed for FILS PFS and OWE.

Signed-off-by: Jouni Malinen <j@w1.fi>
src/crypto/crypto.h
src/crypto/crypto_openssl.c

index eb3c33bbee1a6def12685b9e0b3403e7c264ad86..a723201e1198cb3782bec5c87223f109f94446c5 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Wrapper functions for crypto libraries
- * Copyright (c) 2004-2013, 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.
@@ -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 */
index 0492468812239ffb33211aa36648d63495283847..d535c7c9ca5a26e80175dce8482506af662d1cb2 100644 (file)
@@ -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, &params) != 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 */