Patch for Enhancing crypto abstract API

Message ID F2A1327B-7A86-4AD5-BF7F-508B747752C7@wolfssl.com
State New
Headers show
Series
  • Patch for Enhancing crypto abstract API
Related show

Commit Message

Sean Parkinson Oct. 16, 2017, 5:45 a.m.
Please see below for my proposed patch.

The patch offers alternate implementations of some functions using the abstract cryptographic API.

This work was done in preparation for the changes to allow hostap to be compiled with the wolfSSL cryptography and TLS library.

Thanks,
Sean :-)
—
Sean Parkinson
sean@wolfssl.com
wolfSSL Inc


From 46e56e10434d861ec39a6b0f68eb043cffa797b2 Mon Sep 17 00:00:00 2001
From: Sean Parkinson <sparkinson@iprimus.com.au>
Date: Mon, 16 Oct 2017 15:00:31 +1000
Subject: [PATCH 1/4] Enhance crypto abstract API

Signed-off-by: Sean Parkinson <sean@wolfssl.com>
---
 src/crypto/crypto.h             |  59 +++++-
 src/crypto/dh_groups.c          |  70 +++++++
 src/eap_common/eap_eke_common.c |  70 +++++++
 src/eap_common/eap_pwd_common.c | 244 +++++++++++++++++++++
 src/eap_common/eap_pwd_common.h |  17 ++
 src/eap_peer/eap_pwd.c          | 421 +++++++++++++++++++++++++++++++++++++
 src/eap_server/eap_server_pwd.c | 454 ++++++++++++++++++++++++++++++++++++++++
 7 files changed, 1333 insertions(+), 2 deletions(-)

Patch

diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index a723201..a046c50 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -417,6 +417,13 @@  int __must_check crypto_public_key_decrypt_pkcs1(
 	struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
 	u8 *plain, size_t *plain_len);
 
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+                   u8 *pubkey);
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+                            const u8 *privkey, size_t privkey_len,
+                            const u8 *pubkey, size_t pubkey_len,
+                            u8 *secret, size_t *len);
+
 /**
  * crypto_global_init - Initialize crypto wrapper
  *
@@ -529,6 +536,14 @@  int crypto_bignum_to_bin(const struct crypto_bignum *a,
 			 u8 *buf, size_t buflen, size_t padlen);
 
 /**
+ * crypto_bignum_rand - Create random number in range of modulus.
+ * @r: Bignum; random value
+ * @m: Bignum; Modulus
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m);
+
+/**
  * crypto_bignum_add - c = a + b
  * @a: Bignum
  * @b: Bignum
@@ -610,6 +625,16 @@  int crypto_bignum_mulmod(const struct crypto_bignum *a,
 			 struct crypto_bignum *d);
 
 /**
+ * crypto_bugnum_rshift - r = a >> n
+ * @a: Bignum
+ * @n: Number of bits
+ * @r: Bignum; used to store thr result of a >> n
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bugnum_rshift(const struct crypto_bignum *a, int n,
+                         struct crypto_bignum *r);
+
+/**
  * crypto_bignum_cmp - Compare two bignums
  * @a: Bignum
  * @b: Bignum
@@ -640,6 +665,13 @@  int crypto_bignum_is_zero(const struct crypto_bignum *a);
 int crypto_bignum_is_one(const struct crypto_bignum *a);
 
 /**
+ * crypto_bignum_is_odd - Is the given bignum odd
+ * @a: Bignum
+ * Returns: 1 if @a is odd or 0 if not
+ */
+int crypto_bignum_is_odd(const struct crypto_bignum *a);
+
+/**
  * crypto_bignum_legendre - Compute the Legendre symbol (a/p)
  * @a: Bignum
  * @p: Bignum
@@ -671,6 +703,14 @@  struct crypto_ec * crypto_ec_init(int group);
 void crypto_ec_deinit(struct crypto_ec *e);
 
 /**
+ * crypto_ec_cofactor - Set the cofactor into the big number.
+ * @e: EC context from crypto_ec_init()
+ * @cofactor: Cofactor of curve.
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_cofactor(struct crypto_ec* e, struct crypto_bignum* cofactor);
+
+/**
  * crypto_ec_prime_len - Get length of the prime in octets
  * @e: EC context from crypto_ec_init()
  * Returns: Length of the prime defining the group
@@ -685,6 +725,13 @@  size_t crypto_ec_prime_len(struct crypto_ec *e);
 size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
 
 /**
+ * crypto_ec_order_len - Get length of the order in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the order defining the group
+ */
+size_t crypto_ec_order_len(struct crypto_ec *e);
+
+/**
  * crypto_ec_get_prime - Get prime defining an EC group
  * @e: EC context from crypto_ec_init()
  * Returns: Prime (bignum) defining the group
@@ -721,6 +768,14 @@  struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
 void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
 
 /**
+ * crypto_ec_point_x - Copies the x-ordinate point into big number
+ * @p: EC point data.
+ * @x; Big number copy of x-ordinate.
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_x(const struct crypto_ec_point *p, struct crypto_bignum *x);
+
+/**
  * crypto_ec_point_to_bin - Write EC point value as binary data
  * @e: EC context from crypto_ec_init()
  * @p: EC point data from crypto_ec_point_init()
@@ -749,7 +804,7 @@  struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
 						  const u8 *val);
 
 /**
- * crypto_bignum_add - c = a + b
+ * crypto_ec_point_add - c = a + b
  * @e: EC context from crypto_ec_init()
  * @a: Bignum
  * @b: Bignum
@@ -761,7 +816,7 @@  int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
 			struct crypto_ec_point *c);
 
 /**
- * crypto_bignum_mul - res = b * p
+ * crypto_ec_point_mul - res = b * p
  * @e: EC context from crypto_ec_init()
  * @p: EC point
  * @b: Bignum
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 7912361..be79887 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1190,6 +1190,43 @@  const struct dh_group * dh_groups_get(int id)
  * @priv: Pointer for returning Diffie-Hellman private key
  * Returns: Diffie-Hellman public value
  */
+#ifdef CRYPTO_ABSTRACT_API
+struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
+{
+	struct wpabuf *pv;
+	size_t pv_len;
+
+	if (dh == NULL)
+		return NULL;
+
+	wpabuf_clear_free(*priv);
+	*priv = wpabuf_alloc(dh->prime_len);
+	if (*priv == NULL)
+		return NULL;
+
+	pv_len = dh->prime_len;
+	pv = wpabuf_alloc(pv_len);
+	if (pv == NULL) {
+		wpabuf_clear_free(*priv);
+		*priv = NULL;
+		return NULL;
+	}
+        if (crypto_dh_init(*dh->generator, dh->prime, dh->prime_len,
+                           wpabuf_mhead(*priv), wpabuf_mhead(pv)) < 0) {
+		wpabuf_clear_free(pv);
+		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+		wpabuf_clear_free(*priv);
+		*priv = NULL;
+		return NULL;
+	}
+        wpabuf_put(*priv, dh->prime_len);
+        wpabuf_put(pv, dh->prime_len);
+	wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
+	wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv);
+
+	return pv;
+}
+#else
 struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
 {
 	struct wpabuf *pv;
@@ -1238,6 +1275,7 @@  struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
 
 	return pv;
 }
+#endif
 
 
 /**
@@ -1247,6 +1285,37 @@  struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
  * @dh: Selected Diffie-Hellman group
  * Returns: Diffie-Hellman shared key
  */
+#ifdef CRYPTO_ABSTRACT_API
+struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
+				 const struct wpabuf *own_private,
+				 const struct dh_group *dh)
+{
+	struct wpabuf *shared;
+	size_t shared_len;
+
+	if (dh == NULL || peer_public == NULL || own_private == NULL)
+		return NULL;
+
+	shared_len = dh->prime_len;
+	shared = wpabuf_alloc(shared_len);
+	if (shared == NULL)
+		return NULL;
+        if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+                                    wpabuf_head(own_private),
+                                    wpabuf_len(own_private),
+                                    wpabuf_head(peer_public),
+                                    wpabuf_len(peer_public),
+                                    wpabuf_mhead(shared), &shared_len) < 0) {
+		wpabuf_clear_free(shared);
+		wpa_printf(MSG_INFO, "DH: crypto_dh_derive_secret failed");
+		return NULL;
+	}
+	wpabuf_put(shared, shared_len);
+	wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared);
+
+	return shared;
+}
+#else
 struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
 				 const struct wpabuf *own_private,
 				 const struct dh_group *dh)
@@ -1274,3 +1343,4 @@  struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
 
 	return shared;
 }
+#endif
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
index 6217468..1db5f31 100644
--- a/src/eap_common/eap_eke_common.c
+++ b/src/eap_common/eap_eke_common.c
@@ -155,7 +155,30 @@  static int eap_eke_auth_len(u8 prf)
 	return -1;
 }
 
+#ifdef CRYPTO_ABSTRACT_API
+int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub)
+{
+	int generator;
+	u8 gen;
+	const struct dh_group *dh;
 
+	generator = eap_eke_dh_generator(group);
+	dh = eap_eke_dh_group(group);
+	if (generator < 0 || generator > 255 || !dh)
+		return -1;
+	gen = generator;
+
+        if (crypto_dh_init(gen, dh->prime, dh->prime_len, ret_priv,
+                           ret_pub) < 0)
+            return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value",
+			ret_priv, dh->prime_len);
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value",
+		    ret_pub, dh->prime_len);
+
+	return 0;
+}
+#else
 int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub)
 {
 	int generator;
@@ -201,6 +224,7 @@  int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub)
 
 	return 0;
 }
+#endif
 
 
 static int eap_eke_prf(u8 prf, const u8 *key, size_t key_len, const u8 *data,
@@ -397,6 +421,51 @@  int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub,
 }
 
 
+#ifdef CRYPTO_ABSTRACT_API
+int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key,
+			  const u8 *dhpriv, const u8 *peer_dhcomp)
+{
+	u8 zeros[EAP_EKE_MAX_HASH_LEN];
+	u8 peer_pub[EAP_EKE_MAX_DH_LEN];
+	u8 modexp[EAP_EKE_MAX_DH_LEN];
+	size_t len;
+	const struct dh_group *dh;
+
+	dh = eap_eke_dh_group(sess->dhgroup);
+	if (sess->encr != EAP_EKE_ENCR_AES128_CBC || !dh)
+		return -1;
+
+	/* Decrypt peer DHComponent */
+	os_memcpy(peer_pub, peer_dhcomp + AES_BLOCK_SIZE, dh->prime_len);
+	if (aes_128_cbc_decrypt(key, peer_dhcomp, peer_pub, dh->prime_len) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt DHComponent");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted peer DH pubkey",
+			peer_pub, dh->prime_len);
+
+	/* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */
+	len = dh->prime_len;
+        if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+                                    dhpriv, dh->prime_len, peer_pub,
+                                    dh->prime_len, modexp, &len) < 0)
+		return -1;
+	if (len < dh->prime_len) {
+		size_t pad = dh->prime_len - len;
+		os_memmove(modexp + pad, modexp, len);
+		os_memset(modexp, 0, pad);
+	}
+
+	os_memset(zeros, 0, sess->auth_len);
+	if (eap_eke_prf(sess->prf, zeros, sess->auth_len, modexp, dh->prime_len,
+			NULL, 0, sess->shared_secret) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: SharedSecret",
+			sess->shared_secret, sess->auth_len);
+
+	return 0;
+}
+#else
 int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key,
 			  const u8 *dhpriv, const u8 *peer_dhcomp)
 {
@@ -439,6 +508,7 @@  int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key,
 
 	return 0;
 }
+#endif
 
 
 int eap_eke_derive_ke_ki(struct eap_eke_session *sess,
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 67f8f70..ded2ddc 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -81,6 +81,186 @@  static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label,
 }
 
 
+#ifdef CRYPTO_ABSTRACT_API
+/*
+ * compute a "random" secret point on an elliptic curve based
+ * on the password and identities.
+ */
+int compute_password_element(EAP_PWD_group *grp, u16 num,
+			     const u8 *password, size_t password_len,
+			     const u8 *id_server, size_t id_server_len,
+			     const u8 *id_peer, size_t id_peer_len,
+			     const u8 *token)
+{
+	struct crypto_hash *hash;
+	unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
+	int is_odd, ret = 0;
+	size_t primebytelen, primebitlen;
+	struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+
+	grp->pwe = NULL;
+	if ((grp->group = crypto_ec_init(num)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
+		goto fail;
+	}
+
+	if (((cofactor = crypto_bignum_init()) == NULL) ||
+	    ((grp->pwe = crypto_ec_point_init(grp->group)) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
+		goto fail;
+	}
+
+	if (crypto_ec_cofactor(grp->group, cofactor) != 0) {
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
+			   "curve");
+		goto fail;
+	}
+	primebitlen = crypto_ec_prime_len_bits(grp->group);
+	primebytelen = crypto_ec_prime_len(grp->group);
+	if ((prfbuf = os_malloc(primebytelen)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
+			   "buffer");
+		goto fail;
+	}
+	os_memset(prfbuf, 0, primebytelen);
+	ctr = 0;
+	while (1) {
+		if (ctr > 30) {
+			wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
+				   "point on curve for group %d, something's "
+				   "fishy", num);
+			goto fail;
+		}
+		ctr++;
+
+		/*
+		 * compute counter-mode password value and stretch to prime
+		 *    pwd-seed = H(token | peer-id | server-id | password |
+		 *		   counter)
+		 */
+		hash = eap_pwd_h_init();
+		if (hash == NULL)
+			goto fail;
+		eap_pwd_h_update(hash, token, sizeof(u32));
+		eap_pwd_h_update(hash, id_peer, id_peer_len);
+		eap_pwd_h_update(hash, id_server, id_server_len);
+		eap_pwd_h_update(hash, password, password_len);
+		eap_pwd_h_update(hash, &ctr, sizeof(ctr));
+		eap_pwd_h_final(hash, pwe_digest);
+
+		if ((rnd = crypto_bignum_init_set(pwe_digest,
+						  SHA256_MAC_LEN)) == NULL) {
+			wpa_printf(MSG_INFO, "EAP-pwd: unable to create "
+				   "bignums");
+			goto fail;
+		}
+		if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
+				(u8 *) "EAP-pwd Hunting And Pecking",
+				os_strlen("EAP-pwd Hunting And Pecking"),
+				prfbuf, primebitlen) < 0)
+			goto fail;
+
+		if ((x_candidate = crypto_bignum_init_set(prfbuf,
+						       primebytelen)) == NULL) {
+			wpa_printf(MSG_INFO, "EAP-pwd: unable to create "
+				   "bignums");
+			goto fail;
+		}
+
+		/*
+		 * eap_pwd_kdf() returns a string of bits 0..primebitlen but
+		 * BN_bin2bn will treat that string of bits as a big endian
+		 * number. If the primebitlen is not an even multiple of 8
+		 * then excessive bits-- those _after_ primebitlen-- so now
+		 * we have to shift right the amount we masked off.
+		 */
+		if (primebitlen % 8) {
+			crypto_bugnum_rshift(x_candidate,
+					     (8 - (primebitlen % 8)),
+					     x_candidate);
+		}
+
+		if (crypto_bignum_cmp(x_candidate,
+				      crypto_ec_get_prime(grp->group)) >= 0) {
+                        crypto_bignum_deinit(x_candidate, 1);
+                        crypto_bignum_deinit(rnd, 1);
+			continue;
+                }
+
+		wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
+			    prfbuf, primebytelen);
+
+		/*
+		 * need to unambiguously identify the solution, if there is
+		 * one...
+		 */
+		is_odd = crypto_bignum_is_odd(rnd);
+
+		/*
+		 * solve the quadratic equation, if it's not solvable then we
+		 * don't have a point
+		 */
+		if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe,
+						  x_candidate, is_odd) != 0) {
+                        wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y");
+                        crypto_bignum_deinit(x_candidate, 1);
+                        crypto_bignum_deinit(rnd, 1);
+			continue;
+                }
+		/*
+		 * If there's a solution to the equation then the point must be
+		 * on the curve so why check again explicitly? OpenSSL code
+		 * says this is required by X9.62. We're not X9.62 but it can't
+		 * hurt just to be sure.
+		 */
+		if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) {
+			wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
+                        crypto_bignum_deinit(x_candidate, 1);
+                        crypto_bignum_deinit(rnd, 1);
+			continue;
+		}
+
+		if (!crypto_bignum_is_one(cofactor)) {
+			/* make sure the point is not in a small sub-group */
+			if (crypto_ec_point_mul(grp->group, grp->pwe,
+						cofactor, grp->pwe) != 0) {
+				wpa_printf(MSG_INFO, "EAP-pwd: cannot "
+					   "multiply generator by order");
+                                crypto_bignum_deinit(x_candidate, 1);
+                                crypto_bignum_deinit(rnd, 1);
+				continue;
+			}
+			if (crypto_ec_point_is_at_infinity(grp->group,
+							   grp->pwe)) {
+				wpa_printf(MSG_INFO, "EAP-pwd: point is at "
+					   "infinity");
+                                crypto_bignum_deinit(x_candidate, 1);
+                                crypto_bignum_deinit(rnd, 1);
+				continue;
+			}
+		}
+		/* if we got here then we have a new generator. */
+		break;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
+	grp->group_num = num;
+	if (0) {
+ fail:
+		crypto_ec_deinit(grp->group);
+		grp->group = NULL;
+		crypto_ec_point_deinit(grp->pwe, 1);
+		grp->pwe = NULL;
+		ret = 1;
+	}
+	/* cleanliness and order.... */
+	crypto_bignum_deinit(cofactor, 1);
+	crypto_bignum_deinit(x_candidate, 1);
+	crypto_bignum_deinit(rnd, 1);
+	os_free(prfbuf);
+
+	return ret;
+}
+#else
 /*
  * compute a "random" secret point on an elliptic curve based
  * on the password and identities.
@@ -97,6 +277,7 @@  int compute_password_element(EAP_PWD_group *grp, u16 num,
 	int nid, is_odd, ret = 0;
 	size_t primebytelen, primebitlen;
 
+
 	switch (num) { /* from IANA registry for IKE D-H groups */
         case 19:
 		nid = NID_X9_62_prime256v1;
@@ -302,8 +483,70 @@  int compute_password_element(EAP_PWD_group *grp, u16 num,
 
 	return ret;
 }
+#endif
+
+
+#ifdef CRYPTO_ABSTRACT_API
+int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
+		 const struct crypto_bignum *peer_scalar,
+		 const struct crypto_bignum *server_scalar,
+		 const u8 *confirm_peer, const u8 *confirm_server,
+		 const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
+{
+	struct crypto_hash *hash;
+	u8 mk[SHA256_MAC_LEN], *cruft;
+	u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
+	int prime_len, order_len;
+
+	prime_len = crypto_ec_prime_len(grp->group);
+	order_len = crypto_ec_order_len(grp->group);
+
+	if ((cruft = os_malloc(prime_len)) == NULL)
+		return -1;
+
+	/*
+	 * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
+	 *	scal_s)
+	 */
+	session_id[0] = EAP_TYPE_PWD;
+	hash = eap_pwd_h_init();
+	if (hash == NULL) {
+		os_free(cruft);
+		return -1;
+	}
+	eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
+	crypto_bignum_to_bin(peer_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
+	crypto_bignum_to_bin(server_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
+	eap_pwd_h_final(hash, &session_id[1]);
 
+	/* then compute MK = H(k | confirm-peer | confirm-server) */
+	hash = eap_pwd_h_init();
+	if (hash == NULL) {
+		os_free(cruft);
+		return -1;
+	}
+	crypto_bignum_to_bin(k, cruft, prime_len, prime_len);
+	eap_pwd_h_update(hash, cruft, prime_len);
+	os_free(cruft);
+	eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
+	eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
+	eap_pwd_h_final(hash, mk);
+
+	/* stretch the mk with the session-id to get MSK | EMSK */
+	if (eap_pwd_kdf(mk, SHA256_MAC_LEN,
+			session_id, SHA256_MAC_LEN + 1,
+			msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) {
+		return -1;
+	}
 
+	os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
+	os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
+
+	return 1;
+}
+#else
 int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
 		 const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
 		 const u8 *confirm_peer, const u8 *confirm_server,
@@ -365,3 +608,4 @@  int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
 
 	return 1;
 }
+#endif
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index a0d717e..f5c1413 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -9,9 +9,13 @@ 
 #ifndef EAP_PWD_COMMON_H
 #define EAP_PWD_COMMON_H
 
+#ifdef CRYPTO_ABSTRACT_API
+#include "crypto/crypto.h"
+#else
 #include <openssl/bn.h>
 #include <openssl/ec.h>
 #include <openssl/evp.h>
+#endif
 
 /*
  * definition of a finite cyclic group
@@ -19,10 +23,15 @@ 
  */
 typedef struct group_definition_ {
 	u16 group_num;
+#ifdef CRYPTO_ABSTRACT_API
+	struct crypto_ec *group;
+	struct crypto_ec_point *pwe;
+#else
 	EC_GROUP *group;
 	EC_POINT *pwe;
 	BIGNUM *order;
 	BIGNUM *prime;
+#endif
 } EAP_PWD_group;
 
 /*
@@ -61,10 +70,18 @@  int compute_password_element(EAP_PWD_group *grp, u16 num,
 			     const u8 *id_server, size_t id_server_len,
 			     const u8 *id_peer, size_t id_peer_len,
 			     const u8 *token);
+#ifdef CRYPTO_ABSTRACT_API
+int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
+		 const struct crypto_bignum *peer_scalar,
+		 const struct crypto_bignum  *server_scalar,
+		 const u8 *confirm_peer, const u8 *confirm_server,
+		 const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id);
+#else
 int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
 		 const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
 		 const u8 *confirm_peer, const u8 *confirm_server,
 		 const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id);
+#endif
 struct crypto_hash * eap_pwd_h_init(void);
 void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
 void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index ec277ac..724a554 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -13,6 +13,9 @@ 
 #include "crypto/ms_funcs.h"
 #include "eap_peer/eap_i.h"
 #include "eap_common/eap_pwd_common.h"
+#ifdef CRYPTO_ABSTRACT_API
+#include "crypto/crypto.h"
+#endif
 
 
 struct eap_pwd_data {
@@ -36,18 +39,29 @@  struct eap_pwd_data {
 	size_t out_frag_pos;
 	size_t mtu;
 
+#ifdef CRYPTO_ABSTRACT_API
+	struct crypto_bignum *k;
+	struct crypto_bignum *private_value;
+	struct crypto_bignum *server_scalar;
+	struct crypto_bignum *my_scalar;
+	struct crypto_ec_point *my_element;
+	struct crypto_ec_point *server_element;
+#else
 	BIGNUM *k;
 	BIGNUM *private_value;
 	BIGNUM *server_scalar;
 	BIGNUM *my_scalar;
 	EC_POINT *my_element;
 	EC_POINT *server_element;
+#endif
 
 	u8 msk[EAP_MSK_LEN];
 	u8 emsk[EAP_EMSK_LEN];
 	u8 session_id[1 + SHA256_MAC_LEN];
 
+#ifndef CRYPTO_ABSTRACT_API
 	BN_CTX *bnctx;
+#endif
 };
 
 
@@ -107,15 +121,19 @@  static void * eap_pwd_init(struct eap_sm *sm)
 		return NULL;
 	}
 
+#ifndef CRYPTO_ABSTRACT_API
 	if ((data->bnctx = BN_CTX_new()) == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
 		os_free(data);
 		return NULL;
 	}
+#endif
 
 	if ((data->id_peer = os_malloc(identity_len)) == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+#ifndef CRYPTO_ABSTRACT_API
 		BN_CTX_free(data->bnctx);
+#endif
 		os_free(data);
 		return NULL;
 	}
@@ -125,7 +143,9 @@  static void * eap_pwd_init(struct eap_sm *sm)
 
 	if ((data->password = os_malloc(password_len)) == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
+#ifndef CRYPTO_ABSTRACT_API
 		BN_CTX_free(data->bnctx);
+#endif
 		bin_clear_free(data->id_peer, data->id_peer_len);
 		os_free(data);
 		return NULL;
@@ -148,6 +168,30 @@  static void * eap_pwd_init(struct eap_sm *sm)
 }
 
 
+#ifdef CRYPTO_ABSTRACT_API
+static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_pwd_data *data = priv;
+
+	crypto_bignum_deinit(data->private_value, 1);
+	crypto_bignum_deinit(data->server_scalar, 1);
+	crypto_bignum_deinit(data->my_scalar, 1);
+	crypto_bignum_deinit(data->k, 1);
+	crypto_ec_point_deinit(data->my_element, 1);
+	crypto_ec_point_deinit(data->server_element, 1);
+	bin_clear_free(data->id_peer, data->id_peer_len);
+	bin_clear_free(data->id_server, data->id_server_len);
+	bin_clear_free(data->password, data->password_len);
+	if (data->grp) {
+		crypto_ec_deinit(data->grp->group);
+		crypto_ec_point_deinit(data->grp->pwe, 1);
+		os_free(data->grp);
+	}
+	wpabuf_free(data->inbuf);
+	wpabuf_free(data->outbuf);
+	bin_clear_free(data, sizeof(*data));
+}
+#else
 static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_pwd_data *data = priv;
@@ -173,6 +217,7 @@  static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
 	wpabuf_free(data->outbuf);
 	bin_clear_free(data, sizeof(*data));
 }
+#endif
 
 
 static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
@@ -330,8 +375,13 @@  eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 		return;
 	}
 
+#ifdef CRYPTO_ABSTRACT_API
+	wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
+		   (int)crypto_ec_prime_len_bits(data->grp->group));
+#else
 	wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
 		   BN_num_bits(data->grp->prime));
+#endif
 
 	data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
 				    data->id_peer_len);
@@ -350,6 +400,199 @@  eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 }
 
 
+#ifdef CRYPTO_ABSTRACT_API
+static void
+eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+				struct eap_method_ret *ret,
+				const struct wpabuf *reqData,
+				const u8 *payload, size_t payload_len)
+{
+	struct crypto_ec_point *K = NULL, *point = NULL;
+	struct crypto_bignum *mask = NULL, *x = NULL, *y = NULL,
+			     *cofactor = NULL;
+	u8 *ptr, *scalar = NULL, *element = NULL;
+	size_t prime_len, order_len;
+
+	if (data->state != PWD_Commit_Req) {
+		ret->ignore = TRUE;
+		goto fin;
+	}
+
+	prime_len = crypto_ec_prime_len(data->grp->group);
+	order_len = crypto_ec_order_len(data->grp->group);
+
+	if (payload_len != 2 * prime_len + order_len) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+			   (unsigned int) payload_len,
+			   (unsigned int) (2 * prime_len + order_len));
+		goto fin;
+	}
+
+	if (((data->private_value = crypto_bignum_init()) == NULL) ||
+	    ((data->my_element = crypto_ec_point_init(data->grp->group)) == NULL) ||
+	    ((cofactor = crypto_bignum_init()) == NULL) ||
+	    ((data->my_scalar = crypto_bignum_init()) == NULL) ||
+	    ((mask = crypto_bignum_init()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
+		goto fin;
+	}
+
+	if (crypto_ec_cofactor(data->grp->group, cofactor) != 0) {
+		wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
+			   "for curve");
+		goto fin;
+	}
+
+	if (crypto_bignum_rand(data->private_value,
+			       crypto_ec_get_order(data->grp->group)) != 0 ||
+	    crypto_bignum_rand(mask,
+			       crypto_ec_get_order(data->grp->group)) != 0 ||
+	    crypto_bignum_add(data->private_value, mask,
+			      data->my_scalar) != 0 ||
+	    crypto_bignum_mod(data->my_scalar,
+			      crypto_ec_get_order(data->grp->group),
+			      data->my_scalar) != 0) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd (peer): unable to get randomness");
+		goto fin;
+	}
+
+	if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
+				data->my_element) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
+			   "fail");
+		eap_pwd_state(data, FAILURE);
+		goto fin;
+	}
+
+	if (crypto_ec_point_invert(data->grp->group, data->my_element) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
+		goto fin;
+	}
+
+	/* process the request */
+	if (((data->k = crypto_bignum_init()) == NULL) ||
+	    ((K = crypto_ec_point_init(data->grp->group)) == NULL) ||
+	    ((point = crypto_ec_point_init(data->grp->group)) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
+			   "fail");
+		goto fin;
+	}
+
+	/* element, x then y, followed by scalar */
+	ptr = (u8 *) payload;
+	if ((data->server_element = crypto_ec_point_from_bin(data->grp->group,
+							     ptr)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
+		goto fin;
+	}
+	ptr += prime_len * 2;
+	if ((data->server_scalar = crypto_bignum_init_set(ptr,
+							  order_len)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
+		goto fin;
+	}
+
+	/* check to ensure server's element is not in a small sub-group */
+	if (!crypto_bignum_is_one(cofactor)) {
+		if (crypto_ec_point_mul(data->grp->group, data->server_element,
+					cofactor, point) != 0) {
+			wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
+				   "server element by order!\n");
+			goto fin;
+		}
+		if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
+			wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
+				   "is at infinity!\n");
+			goto fin;
+		}
+	}
+
+	/* compute the shared key, k */
+	if ((crypto_ec_point_mul(data->grp->group, data->grp->pwe,
+				 data->server_scalar, K) != 0) ||
+	    (crypto_ec_point_add(data->grp->group, K, data->server_element,
+				 K) != 0) ||
+	    (crypto_ec_point_mul(data->grp->group, K, data->private_value,
+				 K) != 0)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
+			   "fail");
+		goto fin;
+	}
+
+	/* ensure that the shared key isn't in a small sub-group */
+	if (!crypto_bignum_is_one(cofactor)) {
+		if (crypto_ec_point_mul(data->grp->group, K, cofactor,
+					K) != 0) {
+			wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
+				   "shared key point by order");
+			goto fin;
+		}
+	}
+
+	/*
+	 * This check is strictly speaking just for the case above where
+	 * co-factor > 1 but it was suggested that even though this is probably
+	 * never going to happen it is a simple and safe check "just to be
+	 * sure" so let's be safe.
+	 */
+	if (crypto_ec_point_is_at_infinity(data->grp->group, K)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
+			   "infinity!\n");
+		goto fin;
+	}
+
+	if (crypto_ec_point_x(K, data->k) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
+			   "shared secret from point");
+		goto fin;
+	}
+
+	if (((scalar = os_malloc(order_len)) == NULL) ||
+	    ((element = os_malloc(prime_len * 2)) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
+		goto fin;
+	}
+
+	/*
+	 * bignums occupy as little memory as possible so one that is
+	 * sufficiently smaller than the prime or order might need pre-pending
+	 * with zeros.
+	 */
+	os_memset(scalar, 0, order_len);
+	os_memset(element, 0, prime_len * 2);
+	crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+	/* now do the response */
+	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
+				   element + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
+		goto fin;
+	}
+
+	data->outbuf = wpabuf_alloc(order_len + 2 * prime_len);
+	if (data->outbuf == NULL)
+		goto fin;
+
+	/* we send the element as (x,y) follwed by the scalar */
+	wpabuf_put_data(data->outbuf, element, 2 * prime_len);
+	wpabuf_put_data(data->outbuf, scalar, order_len);
+
+fin:
+	os_free(scalar);
+	os_free(element);
+	crypto_bignum_deinit(x, 1);
+	crypto_bignum_deinit(y, 1);
+	crypto_bignum_deinit(mask, 1);
+	crypto_bignum_deinit(cofactor, 1);
+	crypto_ec_point_deinit(K, 1);
+	crypto_ec_point_deinit(point, 1);
+	if (data->outbuf == NULL)
+		eap_pwd_state(data, FAILURE);
+	else
+		eap_pwd_state(data, PWD_Confirm_Req);
+}
+#else
 static void
 eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 				struct eap_method_ret *ret,
@@ -561,8 +804,185 @@  fin:
 	else
 		eap_pwd_state(data, PWD_Confirm_Req);
 }
+#endif
+
+
+#ifdef CRYPTO_ABSTRACT_API
+static void
+eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+				 struct eap_method_ret *ret,
+				 const struct wpabuf *reqData,
+				 const u8 *payload, size_t payload_len)
+{
+	struct crypto_hash *hash;
+	u32 cs;
+	u16 grp;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
+	int prime_len = 0;
+	int order_len = 0;
+
+	if (data->state != PWD_Confirm_Req) {
+		ret->ignore = TRUE;
+		goto fin;
+	}
+
+	if (payload_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Confirm payload length %u "
+			   "(expected %u)", (unsigned int) payload_len,
+			   SHA256_MAC_LEN);
+		goto fin;
+	}
+
+        prime_len = crypto_ec_prime_len(data->grp->group);
+        order_len = crypto_ec_order_len(data->grp->group);
+
+	/*
+	 * first build up the ciphersuite which is group | random_function |
+	 *	prf
+	 */
+	grp = htons(data->group_num);
+	ptr = (u8 *) &cs;
+	os_memcpy(ptr, &grp, sizeof(u16));
+	ptr += sizeof(u16);
+	*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+	ptr += sizeof(u8);
+	*ptr = EAP_PWD_DEFAULT_PRF;
+
+	/* each component of the point will be at most as big as the prime */
+	if ((cruft = os_malloc(prime_len * 2)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation "
+			   "fail");
+		goto fin;
+	}
+
+	/*
+	 * server's commit is H(k | server_element | server_scalar |
+	 *			peer_element | peer_scalar | ciphersuite)
+	 */
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
+
+	/*
+	 * zero the memory each time because this is mod prime math and some
+	 * value may start with a few zeros and the previous one did not.
+	 */
+	crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+	eap_pwd_h_update(hash, cruft, prime_len);
+
+	/* server element: x, y */
+	if (crypto_ec_point_to_bin(data->grp->group, data->server_element,
+				   cruft, cruft + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	eap_pwd_h_update(hash, cruft, prime_len * 2);
+
+	/* server scalar */
+	crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
+
+	/* my element: x, y */
+	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+				   cruft + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
 
+	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
+	/* my scalar */
+	crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
+
+	/* the ciphersuite */
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
+
+	/* random function fin */
+	eap_pwd_h_final(hash, conf);
+
+	ptr = (u8 *) payload;
+	if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
+		goto fin;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified");
+
+	/*
+	 * compute confirm:
+	 *  H(k | peer_element | peer_scalar | server_element | server_scalar |
+	 *    ciphersuite)
+	 */
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
+
+	/* k */
+	crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+	eap_pwd_h_update(hash, cruft, prime_len);
+
+	/* my element */
+	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+				   cruft + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	eap_pwd_h_update(hash, cruft, prime_len * 2);
+
+	/* my scalar */
+	crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
+
+	/* server element: x, y */
+	if (crypto_ec_point_to_bin(data->grp->group, data->server_element,
+				   cruft, cruft + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	eap_pwd_h_update(hash, cruft, prime_len * 2);
+
+	/* server scalar */
+	crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
+
+	/* the ciphersuite */
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
+
+	/* all done */
+	eap_pwd_h_final(hash, conf);
+
+	if (compute_keys(data->grp, data->k, data->my_scalar,
+			 data->server_scalar, conf, ptr, &cs, data->msk,
+			 data->emsk, data->session_id) < 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
+			   "EMSK");
+		goto fin;
+	}
+
+	data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
+	if (data->outbuf == NULL)
+		goto fin;
+
+	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
+
+fin:
+	if (data->grp)
+		bin_clear_free(cruft, prime_len * 2);
+	if (data->outbuf == NULL) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		eap_pwd_state(data, FAILURE);
+	} else {
+		eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
+	}
+}
+#else
 static void
 eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 				 struct eap_method_ret *ret,
@@ -784,6 +1204,7 @@  fin:
 		eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
 	}
 }
+#endif
 
 
 static struct wpabuf *
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 68f0af9..4417800 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -11,6 +11,7 @@ 
 #include "common.h"
 #include "crypto/sha256.h"
 #include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
 #include "eap_server/eap_i.h"
 #include "eap_common/eap_pwd_common.h"
 
@@ -36,12 +37,21 @@  struct eap_pwd_data {
 	size_t out_frag_pos;
 	size_t mtu;
 
+#ifdef CRYPTO_ABSTRACT_API
+	struct crypto_bignum *k;
+	struct crypto_bignum *private_value;
+	struct crypto_bignum *peer_scalar;
+	struct crypto_bignum *my_scalar;
+	struct crypto_ec_point *my_element;
+	struct crypto_ec_point *peer_element;
+#else
 	BIGNUM *k;
 	BIGNUM *private_value;
 	BIGNUM *peer_scalar;
 	BIGNUM *my_scalar;
 	EC_POINT *my_element;
 	EC_POINT *peer_element;
+#endif
 
 	u8 my_confirm[SHA256_MAC_LEN];
 
@@ -49,7 +59,9 @@  struct eap_pwd_data {
 	u8 emsk[EAP_EMSK_LEN];
 	u8 session_id[1 + SHA256_MAC_LEN];
 
+#ifndef CRYPTO_ABSTRACT_API
 	BN_CTX *bnctx;
+#endif
 };
 
 
@@ -116,6 +128,7 @@  static void * eap_pwd_init(struct eap_sm *sm)
 	os_memcpy(data->password, sm->user->password, data->password_len);
 	data->password_hash = sm->user->password_hash;
 
+#ifndef CRYPTO_ABSTRACT_API
 	data->bnctx = BN_CTX_new();
 	if (data->bnctx == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
@@ -124,6 +137,7 @@  static void * eap_pwd_init(struct eap_sm *sm)
 		os_free(data);
 		return NULL;
 	}
+#endif
 
 	data->in_frag_pos = data->out_frag_pos = 0;
 	data->inbuf = data->outbuf = NULL;
@@ -134,6 +148,30 @@  static void * eap_pwd_init(struct eap_sm *sm)
 }
 
 
+#ifdef CRYPTO_ABSTRACT_API
+static void eap_pwd_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_pwd_data *data = priv;
+
+	crypto_bignum_deinit(data->private_value, 1);
+	crypto_bignum_deinit(data->peer_scalar, 1);
+	crypto_bignum_deinit(data->my_scalar, 1);
+	crypto_bignum_deinit(data->k, 1);
+	crypto_ec_point_deinit(data->my_element, 1);
+	crypto_ec_point_deinit(data->peer_element, 1);
+	bin_clear_free(data->id_peer, data->id_peer_len);
+	bin_clear_free(data->id_server, data->id_server_len);
+	bin_clear_free(data->password, data->password_len);
+	if (data->grp) {
+		crypto_ec_deinit(data->grp->group);
+		crypto_ec_point_deinit(data->grp->pwe, 1);
+		os_free(data->grp);
+	}
+	wpabuf_free(data->inbuf);
+	wpabuf_free(data->outbuf);
+	bin_clear_free(data, sizeof(*data));
+}
+#else
 static void eap_pwd_reset(struct eap_sm *sm, void *priv)
 {
 	struct eap_pwd_data *data = priv;
@@ -159,6 +197,7 @@  static void eap_pwd_reset(struct eap_sm *sm, void *priv)
 	wpabuf_free(data->outbuf);
 	bin_clear_free(data, sizeof(*data));
 }
+#endif
 
 
 static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data,
@@ -195,6 +234,93 @@  static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data,
 }
 
 
+#ifdef CRYPTO_ABSTRACT_API
+static void eap_pwd_build_commit_req(struct eap_sm *sm,
+				     struct eap_pwd_data *data, u8 id)
+{
+	struct crypto_bignum *mask = NULL;
+	u8 *scalar = NULL, *element = NULL;
+	int prime_len, order_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
+	/*
+	 * if we're fragmenting then we already have an commit request, just
+	 * return
+	 */
+	if (data->out_frag_pos)
+		return;
+
+	prime_len = crypto_ec_prime_len(data->grp->group);
+	order_len = crypto_ec_order_len(data->grp->group);
+
+	if (((data->private_value = crypto_bignum_init()) == NULL) ||
+	    ((data->my_element = crypto_ec_point_init(data->grp->group)) == NULL) ||
+	    ((data->my_scalar = crypto_bignum_init()) == NULL) ||
+	    ((mask = crypto_bignum_init()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation "
+			   "fail");
+		goto fin;
+	}
+
+	if (crypto_bignum_rand(data->private_value,
+			       crypto_ec_get_order(data->grp->group)) != 0 ||
+	    crypto_bignum_rand(mask,
+			       crypto_ec_get_order(data->grp->group)) != 0 ||
+	    crypto_bignum_add(data->private_value, mask,
+			      data->my_scalar) != 0 ||
+	    crypto_bignum_mod(data->my_scalar,
+			      crypto_ec_get_order(data->grp->group),
+			      data->my_scalar) != 0) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd (server): unable to get randomness");
+		goto fin;
+	}
+
+	if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
+				data->my_element) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation "
+			   "fail");
+		eap_pwd_state(data, FAILURE);
+		goto fin;
+	}
+
+	if (crypto_ec_point_invert(data->grp->group, data->my_element) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
+			   "fail");
+		goto fin;
+	}
+	crypto_bignum_deinit(mask, 1);
+
+	if (((scalar = os_malloc(order_len)) == NULL) ||
+	    ((element = os_malloc(prime_len * 2)) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
+		goto fin;
+	}
+
+	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
+				   element + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
+			   "fail");
+		goto fin;
+	}
+
+	crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+
+	data->outbuf = wpabuf_alloc(2 * prime_len + order_len);
+	if (data->outbuf == NULL)
+		goto fin;
+
+	/* We send the element as (x,y) followed by the scalar */
+	wpabuf_put_data(data->outbuf, element, 2 * prime_len);
+	wpabuf_put_data(data->outbuf, scalar, order_len);
+
+fin:
+	os_free(scalar);
+	os_free(element);
+	if (data->outbuf == NULL)
+		eap_pwd_state(data, FAILURE);
+}
+#else
 static void eap_pwd_build_commit_req(struct eap_sm *sm,
 				     struct eap_pwd_data *data, u8 id)
 {
@@ -300,8 +426,107 @@  fin:
 	if (data->outbuf == NULL)
 		eap_pwd_state(data, FAILURE);
 }
+#endif
+
+
+#ifdef CRYPTO_ABSTRACT_API
+static void eap_pwd_build_confirm_req(struct eap_sm *sm,
+				      struct eap_pwd_data *data, u8 id)
+{
+	struct crypto_hash *hash;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
+	u16 grp;
+	int prime_len, order_len;
 
+	wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
+	/*
+	 * if we're fragmenting then we already have an confirm request, just
+	 * return
+	 */
+	if (data->out_frag_pos)
+		return;
+
+	prime_len = crypto_ec_prime_len(data->grp->group);
+	order_len = crypto_ec_order_len(data->grp->group);
+
+	/* Each component of the cruft will be at most as big as the prime */
+	if ((cruft = os_malloc(prime_len * 2)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
+			   "fail");
+		goto fin;
+	}
+
+	/*
+	 * commit is H(k | server_element | server_scalar | peer_element |
+	 *	       peer_scalar | ciphersuite)
+	 */
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
+
+	/*
+	 * Zero the memory each time because this is mod prime math and some
+	 * value may start with a few zeros and the previous one did not.
+	 *
+	 * First is k
+	 */
+	crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+	eap_pwd_h_update(hash, cruft, prime_len);
+
+	/* server element: x, y */
+	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+				   cruft + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	eap_pwd_h_update(hash, cruft, prime_len * 2);
+
+	/* server scalar */
+	crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
+
+	/* peer element: x, y */
+	if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft,
+				   cruft + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	eap_pwd_h_update(hash, cruft, prime_len * 2);
+
+	/* peer scalar */
+	crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
+
+	/* ciphersuite */
+	grp = htons(data->group_num);
+	os_memset(cruft, 0, prime_len);
+	ptr = cruft;
+	os_memcpy(ptr, &grp, sizeof(u16));
+	ptr += sizeof(u16);
+	*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+	ptr += sizeof(u8);
+	*ptr = EAP_PWD_DEFAULT_PRF;
+	ptr += sizeof(u8);
+	eap_pwd_h_update(hash, cruft, ptr - cruft);
 
+	/* all done with the random function */
+	eap_pwd_h_final(hash, conf);
+	os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN);
+
+	data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
+	if (data->outbuf == NULL)
+		goto fin;
+
+	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
+
+fin:
+	bin_clear_free(cruft, prime_len * 2);
+	if (data->outbuf == NULL)
+		eap_pwd_state(data, FAILURE);
+}
+#else
 static void eap_pwd_build_confirm_req(struct eap_sm *sm,
 				      struct eap_pwd_data *data, u8 id)
 {
@@ -425,6 +650,7 @@  fin:
 	if (data->outbuf == NULL)
 		eap_pwd_state(data, FAILURE);
 }
+#endif
 
 
 static struct wpabuf *
@@ -648,13 +874,139 @@  static void eap_pwd_process_id_resp(struct eap_sm *sm,
 			   "PWE");
 		return;
 	}
+#ifdef CRYPTO_ABSTRACT_API
+	wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
+		   (int)crypto_ec_prime_len_bits(data->grp->group));
+#else
 	wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
 		   BN_num_bits(data->grp->prime));
+#endif
 
 	eap_pwd_state(data, PWD_Commit_Req);
 }
 
 
+#ifdef CRYPTO_ABSTRACT_API
+static void
+eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
+			    const u8 *payload, size_t payload_len)
+{
+	u8 *ptr;
+	struct crypto_bignum *cofactor = NULL;
+	struct crypto_ec_point *K = NULL, *point = NULL;
+	int res = 0;
+	size_t prime_len, order_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
+
+	prime_len = crypto_ec_prime_len(data->grp->group);
+	order_len = crypto_ec_order_len(data->grp->group);
+
+	if (payload_len != 2 * prime_len + order_len) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+			   (unsigned int) payload_len,
+			   (unsigned int) (2 * prime_len + order_len));
+		goto fin;
+	}
+
+	if (((data->k = crypto_bignum_init()) == NULL) ||
+	    ((cofactor = crypto_bignum_init()) == NULL) ||
+	    ((point = crypto_ec_point_init(data->grp->group)) == NULL) ||
+	    ((K = crypto_ec_point_init(data->grp->group)) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
+			   "fail");
+		goto fin;
+	}
+
+	if (crypto_ec_cofactor(data->grp->group, cofactor) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
+			   "cofactor for curve");
+		goto fin;
+	}
+
+	/* element, x then y, followed by scalar */
+	ptr = (u8 *) payload;
+	if ((data->peer_element = crypto_ec_point_from_bin(data->grp->group,
+							   ptr)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
+			   "fail");
+		goto fin;
+	}
+	ptr += prime_len * 2;
+	if ((data->peer_scalar = crypto_bignum_init_set(ptr,
+							order_len)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
+			   "fail");
+		goto fin;
+	}
+
+	/* check to ensure peer's element is not in a small sub-group */
+	if (!crypto_bignum_is_one(cofactor)) {
+		if (crypto_ec_point_mul(data->grp->group, data->peer_element,
+					cofactor, point) != 0) {
+			wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
+				   "multiply peer element by order");
+			goto fin;
+		}
+		if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
+			wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
+				   "is at infinity!\n");
+			goto fin;
+		}
+	}
+
+	/* compute the shared key, k */
+	if ((crypto_ec_point_mul(data->grp->group, data->grp->pwe,
+				 data->peer_scalar, K) != 0) ||
+	    (crypto_ec_point_add(data->grp->group, K, data->peer_element,
+				 K) != 0) ||
+	    (crypto_ec_point_mul(data->grp->group, K, data->private_value,
+				 K) != 0)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
+			   "fail");
+		goto fin;
+	}
+
+	/* ensure that the shared key isn't in a small sub-group */
+	if (!crypto_bignum_is_one(cofactor)) {
+		if (crypto_ec_point_mul(data->grp->group, K, cofactor,
+					K) != 0) {
+			wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
+				   "multiply shared key point by order!\n");
+			goto fin;
+		}
+	}
+
+	/*
+	 * This check is strictly speaking just for the case above where
+	 * co-factor > 1 but it was suggested that even though this is probably
+	 * never going to happen it is a simple and safe check "just to be
+	 * sure" so let's be safe.
+	 */
+	if (crypto_ec_point_is_at_infinity(data->grp->group, K)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
+			   "at infinity");
+		goto fin;
+	}
+	if (crypto_ec_point_x(K, data->k)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
+			   "shared secret from secret point");
+		goto fin;
+	}
+	res = 1;
+
+fin:
+	crypto_ec_point_deinit(K, 1);
+	crypto_ec_point_deinit(point, 1);
+	crypto_bignum_deinit(cofactor, 1);
+
+	if (res)
+		eap_pwd_state(data, PWD_Confirm_Req);
+	else
+		eap_pwd_state(data, FAILURE);
+}
+#else
 static void
 eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
 			    const u8 *payload, size_t payload_len)
@@ -780,8 +1132,109 @@  fin:
 	else
 		eap_pwd_state(data, FAILURE);
 }
+#endif
+
+
+#ifdef CRYPTO_ABSTRACT_API
+static void
+eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
+			     const u8 *payload, size_t payload_len)
+{
+	struct crypto_hash *hash;
+	u32 cs;
+	u16 grp;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
+	int prime_len, order_len;
+
+	prime_len = crypto_ec_prime_len(data->grp->group);
+	order_len = crypto_ec_order_len(data->grp->group);
+
+	if (payload_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Confirm payload length %u "
+			   "(expected %u)", (unsigned int) payload_len,
+			   SHA256_MAC_LEN);
+		goto fin;
+	}
+
+	/* build up the ciphersuite: group | random_function | prf */
+	grp = htons(data->group_num);
+	ptr = (u8 *) &cs;
+	os_memcpy(ptr, &grp, sizeof(u16));
+	ptr += sizeof(u16);
+	*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+	ptr += sizeof(u8);
+	*ptr = EAP_PWD_DEFAULT_PRF;
+
+	/* each component of the cruft will be at most as big as the prime */
+	if ((cruft = os_malloc(prime_len * 2)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail");
+		goto fin;
+	}
+
+	/*
+	 * commit is H(k | peer_element | peer_scalar | server_element |
+	 *	       server_scalar | ciphersuite)
+	 */
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
+
+	/* k */
+	crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+	eap_pwd_h_update(hash, cruft, prime_len);
+
+	/* peer element: x, y */
+	if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft,
+				   cruft + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	eap_pwd_h_update(hash, cruft, prime_len * 2);
+
+	/* peer scalar */
+	crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
+
+	/* server element: x, y */
+	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+				   cruft + prime_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
+	/* server scalar */
+	crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+	eap_pwd_h_update(hash, cruft, order_len);
 
+	/* ciphersuite */
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
+
+	/* all done */
+	eap_pwd_h_final(hash, conf);
+
+	ptr = (u8 *) payload;
+	if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
+			   "verify");
+		goto fin;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified");
+	if (compute_keys(data->grp, data->k, data->peer_scalar, data->my_scalar,
+			 conf, data->my_confirm, &cs, data->msk, data->emsk,
+			 data->session_id) < 0)
+		eap_pwd_state(data, FAILURE);
+	else
+		eap_pwd_state(data, SUCCESS);
+
+fin:
+	bin_clear_free(cruft, prime_len);
+}
+#else
 static void
 eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
 			     const u8 *payload, size_t payload_len)
@@ -907,6 +1360,7 @@  fin:
 	BN_clear_free(x);
 	BN_clear_free(y);
 }
+#endif
 
 
 static void eap_pwd_process(struct eap_sm *sm, void *priv,