Update to new version 0.8.16 from BRCM

Sync with main tree commit b8349523e460493fa0b4de36c689595109e45e91
Author: Neeraj Kumar Garg <neerajkg@broadcom.com>
Date:   Tue Dec 27 23:21:45 2011 +0200
    P2P: Reject p2p_group_add if forced frequency is not acceptable

Change-Id: Icb4541a371b05c270e80440d7a7fdea7f33ff61e
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index 69aa16a..b221dd4 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -12,6 +12,7 @@
 CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
 CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
 #CFLAGS += -DALL_DH_GROUPS
+CFLAGS += -DCONFIG_SHA256
 
 LIB_OBJS= \
 	aes-cbc.o \
@@ -40,6 +41,7 @@
 	sha1-tlsprf.o \
 	sha1-tprf.o \
 	sha256.o \
+	sha256-tlsprf.o \
 	sha256-internal.o
 
 LIB_OBJS += crypto_internal.o
@@ -48,6 +50,7 @@
 LIB_OBJS += crypto_internal-rsa.o
 LIB_OBJS += tls_internal.o
 LIB_OBJS += fips_prf_internal.o
+LIB_OBJS += random.o
 
 
 libcrypto.a: $(LIB_OBJS)
diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c
index 2d32c03..a0fc45b 100644
--- a/src/crypto/aes-internal-dec.c
+++ b/src/crypto/aes-internal-dec.c
@@ -32,7 +32,7 @@
  *
  * @return	the number of rounds for the given cipher key size.
  */
-void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[])
+static void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[])
 {
 	int Nr = 10, i, j;
 	u32 temp;
diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c
index 2f19826..8726aa7 100644
--- a/src/crypto/aes-internal-enc.c
+++ b/src/crypto/aes-internal-enc.c
@@ -27,7 +27,7 @@
 #include "crypto.h"
 #include "aes_i.h"
 
-void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16])
+static void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16])
 {
 	u32 s0, s1, s2, s3, t0, t1, t2, t3;
 	const int Nr = 10;
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 587b5a9..6dca191 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -155,7 +155,8 @@
 
 enum crypto_hash_alg {
 	CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
-	CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1
+	CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
+	CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
 };
 
 struct crypto_hash;
diff --git a/src/crypto/crypto_internal-rsa.c b/src/crypto/crypto_internal-rsa.c
index 205042c..7f0a5cf 100644
--- a/src/crypto/crypto_internal-rsa.c
+++ b/src/crypto/crypto_internal-rsa.c
@@ -17,7 +17,6 @@
 #include "common.h"
 #include "crypto.h"
 #include "tls/rsa.h"
-#include "tls/bignum.h"
 #include "tls/pkcs1.h"
 #include "tls/pkcs8.h"
 
diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c
index 8fdba65..9362fe1 100644
--- a/src/crypto/crypto_internal.c
+++ b/src/crypto/crypto_internal.c
@@ -1,6 +1,6 @@
 /*
  * Crypto wrapper for internal crypto implementation
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -16,6 +16,7 @@
 
 #include "common.h"
 #include "crypto.h"
+#include "sha256_i.h"
 #include "sha1_i.h"
 #include "md5_i.h"
 
@@ -24,6 +25,9 @@
 	union {
 		struct MD5Context md5;
 		struct SHA1Context sha1;
+#ifdef CONFIG_SHA256
+		struct sha256_state sha256;
+#endif /* CONFIG_SHA256 */
 	} u;
 	u8 key[64];
 	size_t key_len;
@@ -35,7 +39,7 @@
 {
 	struct crypto_hash *ctx;
 	u8 k_pad[64];
-	u8 tk[20];
+	u8 tk[32];
 	size_t i;
 
 	ctx = os_zalloc(sizeof(*ctx));
@@ -51,6 +55,11 @@
 	case CRYPTO_HASH_ALG_SHA1:
 		SHA1Init(&ctx->u.sha1);
 		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_SHA256:
+		sha256_init(&ctx->u.sha256);
+		break;
+#endif /* CONFIG_SHA256 */
 	case CRYPTO_HASH_ALG_HMAC_MD5:
 		if (key_len > sizeof(k_pad)) {
 			MD5Init(&ctx->u.md5);
@@ -63,7 +72,8 @@
 		ctx->key_len = key_len;
 
 		os_memcpy(k_pad, key, key_len);
-		os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+		if (key_len < sizeof(k_pad))
+			os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
 		for (i = 0; i < sizeof(k_pad); i++)
 			k_pad[i] ^= 0x36;
 		MD5Init(&ctx->u.md5);
@@ -81,12 +91,34 @@
 		ctx->key_len = key_len;
 
 		os_memcpy(k_pad, key, key_len);
-		os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+		if (key_len < sizeof(k_pad))
+			os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
 		for (i = 0; i < sizeof(k_pad); i++)
 			k_pad[i] ^= 0x36;
 		SHA1Init(&ctx->u.sha1);
 		SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
 		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		if (key_len > sizeof(k_pad)) {
+			sha256_init(&ctx->u.sha256);
+			sha256_process(&ctx->u.sha256, key, key_len);
+			sha256_done(&ctx->u.sha256, tk);
+			key = tk;
+			key_len = 32;
+		}
+		os_memcpy(ctx->key, key, key_len);
+		ctx->key_len = key_len;
+
+		os_memcpy(k_pad, key, key_len);
+		if (key_len < sizeof(k_pad))
+			os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x36;
+		sha256_init(&ctx->u.sha256);
+		sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad));
+		break;
+#endif /* CONFIG_SHA256 */
 	default:
 		os_free(ctx);
 		return NULL;
@@ -110,6 +142,14 @@
 	case CRYPTO_HASH_ALG_HMAC_SHA1:
 		SHA1Update(&ctx->u.sha1, data, len);
 		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_SHA256:
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		sha256_process(&ctx->u.sha256, data, len);
+		break;
+#endif /* CONFIG_SHA256 */
+	default:
+		break;
 	}
 }
 
@@ -146,6 +186,17 @@
 		*len = 20;
 		SHA1Final(mac, &ctx->u.sha1);
 		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_SHA256:
+		if (*len < 32) {
+			*len = 32;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 32;
+		sha256_done(&ctx->u.sha256, mac);
+		break;
+#endif /* CONFIG_SHA256 */
 	case CRYPTO_HASH_ALG_HMAC_MD5:
 		if (*len < 16) {
 			*len = 16;
@@ -186,6 +237,31 @@
 		SHA1Update(&ctx->u.sha1, mac, 20);
 		SHA1Final(mac, &ctx->u.sha1);
 		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		if (*len < 32) {
+			*len = 32;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 32;
+
+		sha256_done(&ctx->u.sha256, mac);
+
+		os_memcpy(k_pad, ctx->key, ctx->key_len);
+		os_memset(k_pad + ctx->key_len, 0,
+			  sizeof(k_pad) - ctx->key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x5c;
+		sha256_init(&ctx->u.sha256);
+		sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad));
+		sha256_process(&ctx->u.sha256, mac, 32);
+		sha256_done(&ctx->u.sha256, mac);
+		break;
+#endif /* CONFIG_SHA256 */
+	default:
+		os_free(ctx);
+		return -1;
 	}
 
 	os_free(ctx);
diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c
index 8c475bf..2a67d99 100644
--- a/src/crypto/dh_group5.c
+++ b/src/crypto/dh_group5.c
@@ -22,7 +22,7 @@
 void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
 {
 	*publ = dh_init(dh_groups_get(5), priv);
-	if (*publ == 0)
+	if (*publ == NULL)
 		return NULL;
 	return (void *) 1;
 }
diff --git a/src/crypto/fips_prf_internal.c b/src/crypto/fips_prf_internal.c
index a85cb14..1e0c453 100644
--- a/src/crypto/fips_prf_internal.c
+++ b/src/crypto/fips_prf_internal.c
@@ -28,13 +28,14 @@
 	u8 *xpos = x;
 	u32 carry;
 
-	if (seed_len > sizeof(xkey))
+	if (seed_len < sizeof(xkey))
+		os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+	else
 		seed_len = sizeof(xkey);
 
 	/* FIPS 186-2 + change notice 1 */
 
 	os_memcpy(xkey, seed, seed_len);
-	os_memset(xkey + seed_len, 0, 64 - seed_len);
 	t[0] = 0x67452301;
 	t[1] = 0xEFCDAB89;
 	t[2] = 0x98BADCFE;
diff --git a/src/crypto/md5-internal.c b/src/crypto/md5-internal.c
index f8692a9..137ad91 100644
--- a/src/crypto/md5-internal.c
+++ b/src/crypto/md5-internal.c
@@ -188,7 +188,7 @@
     MD5Transform(ctx->buf, (u32 *) ctx->in);
     byteReverse((unsigned char *) ctx->buf, 4);
     os_memcpy(digest, ctx->buf, 16);
-    os_memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+    os_memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
 }
 
 /* The four core functions - F1 is optimized somewhat */
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index dae15ab..c439ae9 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -19,6 +19,60 @@
 #include "ms_funcs.h"
 #include "crypto.h"
 
+/**
+ * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
+ * @utf8_string: UTF-8 string (IN)
+ * @utf8_string_len: Length of utf8_string (IN)
+ * @ucs2_buffer: UCS-2 buffer (OUT)
+ * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
+ * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
+ * Returns: 0 on success, -1 on failure
+ */
+static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
+                        u8 *ucs2_buffer, size_t ucs2_buffer_size,
+                        size_t *ucs2_string_size)
+{
+	size_t i, j;
+
+	for (i = 0, j = 0; i < utf8_string_len; i++) {
+		u8 c = utf8_string[i];
+		if (j >= ucs2_buffer_size) {
+			/* input too long */
+			return -1;
+		}
+		if (c <= 0x7F) {
+			WPA_PUT_LE16(ucs2_buffer + j, c);
+			j += 2;
+		} else if (i == utf8_string_len - 1 ||
+			   j >= ucs2_buffer_size - 1) {
+			/* incomplete surrogate */
+			return -1;
+		} else {
+			u8 c2 = utf8_string[++i];
+			if ((c & 0xE0) == 0xC0) {
+				/* two-byte encoding */
+				WPA_PUT_LE16(ucs2_buffer + j,
+					     ((c & 0x1F) << 6) | (c2 & 0x3F));
+				j += 2;
+			} else if (i == utf8_string_len ||
+				   j >= ucs2_buffer_size - 1) {
+				/* incomplete surrogate */
+				return -1;
+			} else {
+				/* three-byte encoding */
+				u8 c3 = utf8_string[++i];
+				WPA_PUT_LE16(ucs2_buffer + j,
+					     ((c & 0xF) << 12) |
+					     ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+			}
+		}
+	}
+
+	if (ucs2_string_size)
+		*ucs2_string_size = j / 2;
+	return 0;
+}
+
 
 /**
  * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
@@ -53,7 +107,7 @@
 
 /**
  * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @password_hash: 16-octet PasswordHash (OUT)
  * Returns: 0 on success, -1 on failure
@@ -62,18 +116,13 @@
 		      u8 *password_hash)
 {
 	u8 buf[512], *pos;
-	size_t i, len;
+	size_t len, max_len;
 
-	if (password_len > 256)
-		password_len = 256;
+	max_len = sizeof(buf);
+	if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
+		return -1;
 
-	/* Convert password into unicode */
-	for (i = 0; i < password_len; i++) {
-		buf[2 * i] = password[i];
-		buf[2 * i + 1] = 0;
-	}
-
-	len = password_len * 2;
+	len *= 2;
 	pos = buf;
 	return md4_vector(1, (const u8 **) &pos, &len, password_hash);
 }
@@ -117,7 +166,7 @@
  * @peer_challenge: 16-octet PeerChallenge (IN)
  * @username: 0-to-256-char UserName (IN)
  * @username_len: Length of username
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @response: 24-octet Response (OUT)
  * Returns: 0 on success, -1 on failure
@@ -225,7 +274,7 @@
 
 /**
  * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @nt_response: 24-octet NT-Response (IN)
  * @peer_challenge: 16-octet PeerChallenge (IN)
@@ -254,7 +303,7 @@
 /**
  * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
  * @challenge: 8-octet Challenge (IN)
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @response: 24-octet Response (OUT)
  * Returns: 0 on success, -1 on failure
@@ -375,7 +424,7 @@
 
 /**
  * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @password_hash: 16-octet PasswordHash (IN)
  * @pw_block: 516-byte PwBlock (OUT)
@@ -385,18 +434,23 @@
 	const u8 *password, size_t password_len,
 	const u8 *password_hash, u8 *pw_block)
 {
-	size_t i, offset;
+	size_t ucs2_len, offset;
 	u8 *pos;
 
-	if (password_len > 256)
+	os_memset(pw_block, 0, PWBLOCK_LEN);
+
+	if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
 		return -1;
 
-	os_memset(pw_block, 0, PWBLOCK_LEN);
-	offset = (256 - password_len) * 2;
-	if (os_get_random(pw_block, offset) < 0)
+	if (ucs2_len > 256)
 		return -1;
-	for (i = 0; i < password_len; i++)
-		pw_block[offset + i * 2] = password[i];
+
+	offset = (256 - ucs2_len) * 2;
+	if (offset != 0) {
+		os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
+		if (os_get_random(pw_block, offset) < 0)
+			return -1;
+	}
 	/*
 	 * PasswordLength is 4 octets, but since the maximum password length is
 	 * 256, only first two (in little endian byte order) can be non-zero.
@@ -410,9 +464,9 @@
 
 /**
  * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
- * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
  * @new_password_len: Length of new_password
- * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
  * @old_password_len: Length of old_password
  * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
  * Returns: 0 on success, -1 on failure
@@ -450,9 +504,9 @@
 
 /**
  * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
- * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
  * @new_password_len: Length of new_password
- * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
  * @old_password_len: Length of old_password
  * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
  * Returns: 0 on success, -1 on failure
diff --git a/src/crypto/random.c b/src/crypto/random.c
index f545917..a54e197 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -354,23 +354,31 @@
 	char buf[RANDOM_ENTROPY_SIZE];
 	FILE *f;
 	u8 opr;
+	int fail = 0;
 
 	if (!random_entropy_file)
 		return;
 
-	random_get_bytes(buf, RANDOM_ENTROPY_SIZE);
+	if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0)
+		return;
 
 	f = fopen(random_entropy_file, "wb");
 	if (f == NULL) {
-		wpa_printf(MSG_ERROR, "random: Could not write %s",
-			   random_entropy_file);
+		wpa_printf(MSG_ERROR, "random: Could not open entropy file %s "
+			   "for writing", random_entropy_file);
 		return;
 	}
 
 	opr = own_pool_ready > 0xff ? 0xff : own_pool_ready;
-	fwrite(&opr, 1, 1, f);
-	fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f);
+	if (fwrite(&opr, 1, 1, f) != 1 ||
+	    fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1)
+		fail = 1;
 	fclose(f);
+	if (fail) {
+		wpa_printf(MSG_ERROR, "random: Could not write entropy data "
+			   "to %s", random_entropy_file);
+		return;
+	}
 
 	wpa_printf(MSG_DEBUG, "random: Updated entropy file %s "
 		   "(own_pool_ready=%u)",
diff --git a/src/crypto/sha1-pbkdf2.c b/src/crypto/sha1-pbkdf2.c
index 11323de..9dac977 100644
--- a/src/crypto/sha1-pbkdf2.c
+++ b/src/crypto/sha1-pbkdf2.c
@@ -16,8 +16,6 @@
 
 #include "common.h"
 #include "sha1.h"
-#include "md5.h"
-#include "crypto.h"
 
 static int pbkdf2_sha1_f(const char *passphrase, const char *ssid,
 			 size_t ssid_len, int iterations, unsigned int count,
diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c
index 2c8c029..f98fd65 100644
--- a/src/crypto/sha1-tlsprf.c
+++ b/src/crypto/sha1-tlsprf.c
@@ -17,11 +17,10 @@
 #include "common.h"
 #include "sha1.h"
 #include "md5.h"
-#include "crypto.h"
 
 
 /**
- * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
+ * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
  * @secret: Key for PRF
  * @secret_len: Length of the key in bytes
  * @label: A unique label for each purpose of the PRF
@@ -34,8 +33,8 @@
  * This function is used to derive new, cryptographically separate keys from a
  * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
  */
-int tls_prf(const u8 *secret, size_t secret_len, const char *label,
-	    const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
+		     const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
 {
 	size_t L_S1, L_S2, i;
 	const u8 *S1, *S2;
diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h
index c1a6233..f0c1a5f 100644
--- a/src/crypto/sha1.h
+++ b/src/crypto/sha1.h
@@ -25,9 +25,9 @@
 	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
 int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
 	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
-int __must_check tls_prf(const u8 *secret, size_t secret_len,
-			 const char *label, const u8 *seed, size_t seed_len,
-			 u8 *out, size_t outlen);
+int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len,
+				  const char *label, const u8 *seed,
+				  size_t seed_len, u8 *out, size_t outlen);
 int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
 		int iterations, u8 *buf, size_t buflen);
 #endif /* SHA1_H */
diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c
index b061373..ef5751d 100644
--- a/src/crypto/sha256-internal.c
+++ b/src/crypto/sha256-internal.c
@@ -1,6 +1,6 @@
 /*
  * SHA-256 hash implementation and interface functions
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -16,19 +16,9 @@
 
 #include "common.h"
 #include "sha256.h"
+#include "sha256_i.h"
 #include "crypto.h"
 
-struct sha256_state {
-	u64 length;
-	u32 state[8], curlen;
-	u8 buf[64];
-};
-
-static void sha256_init(struct sha256_state *md);
-static int sha256_process(struct sha256_state *md, const unsigned char *in,
-			  unsigned long inlen);
-static int sha256_done(struct sha256_state *md, unsigned char *out);
-
 
 /**
  * sha256_vector - SHA256 hash for data vector
@@ -137,7 +127,7 @@
 
 
 /* Initialize the hash state */
-static void sha256_init(struct sha256_state *md)
+void sha256_init(struct sha256_state *md)
 {
 	md->curlen = 0;
 	md->length = 0;
@@ -158,32 +148,31 @@
    @param inlen  The length of the data (octets)
    @return CRYPT_OK if successful
 */
-static int sha256_process(struct sha256_state *md, const unsigned char *in,
-			  unsigned long inlen)
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+		   unsigned long inlen)
 {
 	unsigned long n;
-#define block_size 64
 
-	if (md->curlen > sizeof(md->buf))
+	if (md->curlen >= sizeof(md->buf))
 		return -1;
 
 	while (inlen > 0) {
-		if (md->curlen == 0 && inlen >= block_size) {
+		if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) {
 			if (sha256_compress(md, (unsigned char *) in) < 0)
 				return -1;
-			md->length += block_size * 8;
-			in += block_size;
-			inlen -= block_size;
+			md->length += SHA256_BLOCK_SIZE * 8;
+			in += SHA256_BLOCK_SIZE;
+			inlen -= SHA256_BLOCK_SIZE;
 		} else {
-			n = MIN(inlen, (block_size - md->curlen));
+			n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen));
 			os_memcpy(md->buf + md->curlen, in, n);
 			md->curlen += n;
 			in += n;
 			inlen -= n;
-			if (md->curlen == block_size) {
+			if (md->curlen == SHA256_BLOCK_SIZE) {
 				if (sha256_compress(md, md->buf) < 0)
 					return -1;
-				md->length += 8 * block_size;
+				md->length += 8 * SHA256_BLOCK_SIZE;
 				md->curlen = 0;
 			}
 		}
@@ -199,7 +188,7 @@
    @param out [out] The destination of the hash (32 bytes)
    @return CRYPT_OK if successful
 */
-static int sha256_done(struct sha256_state *md, unsigned char *out)
+int sha256_done(struct sha256_state *md, unsigned char *out)
 {
 	int i;
 
@@ -217,14 +206,14 @@
 	 * encoding like normal.
 	 */
 	if (md->curlen > 56) {
-		while (md->curlen < 64) {
+		while (md->curlen < SHA256_BLOCK_SIZE) {
 			md->buf[md->curlen++] = (unsigned char) 0;
 		}
 		sha256_compress(md, md->buf);
 		md->curlen = 0;
 	}
 
-	/* pad upto 56 bytes of zeroes */
+	/* pad up to 56 bytes of zeroes */
 	while (md->curlen < 56) {
 		md->buf[md->curlen++] = (unsigned char) 0;
 	}
diff --git a/src/crypto/sha256-tlsprf.c b/src/crypto/sha256-tlsprf.c
new file mode 100644
index 0000000..6763c96
--- /dev/null
+++ b/src/crypto/sha256-tlsprf.c
@@ -0,0 +1,72 @@
+/*
+ * TLS PRF P_SHA256
+ * Copyright (c) 2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+
+
+/**
+ * tls_prf_sha256 - Pseudo-Random Function for TLS v1.2 (P_SHA256, RFC 5246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
+		    const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	size_t clen;
+	u8 A[SHA256_MAC_LEN];
+	u8 P[SHA256_MAC_LEN];
+	size_t pos;
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = A;
+	len[0] = SHA256_MAC_LEN;
+	addr[1] = (unsigned char *) label;
+	len[1] = os_strlen(label);
+	addr[2] = seed;
+	len[2] = seed_len;
+
+	/*
+	 * RFC 5246, Chapter 5
+	 * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+	 * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+	 * PRF(secret, label, seed) = P_SHA256(secret, label + seed)
+	 */
+
+	hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A);
+
+	pos = 0;
+	while (pos < outlen) {
+		hmac_sha256_vector(secret, secret_len, 3, addr, len, P);
+		hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A);
+
+		clen = outlen - pos;
+		if (clen > SHA256_MAC_LEN)
+			clen = SHA256_MAC_LEN;
+		os_memcpy(out + pos, P, clen);
+		pos += clen;
+	}
+}
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index dc597f0..b1ce6af 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -1,6 +1,6 @@
 /*
  * SHA256 hash implementation and interface functions
- * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -23,5 +23,8 @@
 		 size_t data_len, u8 *mac);
 void sha256_prf(const u8 *key, size_t key_len, const char *label,
 	      const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void tls_prf_sha256(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
 
 #endif /* SHA256_H */
diff --git a/src/crypto/sha256_i.h b/src/crypto/sha256_i.h
new file mode 100644
index 0000000..20ae488
--- /dev/null
+++ b/src/crypto/sha256_i.h
@@ -0,0 +1,31 @@
+/*
+ * SHA-256 internal definitions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA256_I_H
+#define SHA256_I_H
+
+#define SHA256_BLOCK_SIZE 64
+
+struct sha256_state {
+	u64 length;
+	u32 state[8], curlen;
+	u8 buf[SHA256_BLOCK_SIZE];
+};
+
+void sha256_init(struct sha256_state *md);
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+		   unsigned long inlen);
+int sha256_done(struct sha256_state *md, unsigned char *out);
+
+#endif /* SHA256_I_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 0928b5b..d9d88cb 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -24,8 +24,6 @@
 	size_t client_random_len;
 	const u8 *server_random;
 	size_t server_random_len;
-	const u8 *inner_secret; /* TLS/IA inner secret */
-	size_t inner_secret_len;
 };
 
 enum tls_event {
@@ -72,6 +70,7 @@
 	const char *pkcs11_engine_path;
 	const char *pkcs11_module_path;
 	int fips_mode;
+	int cert_in_cb;
 
 	void (*event_cb)(void *ctx, enum tls_event ev,
 			 union tls_event_data *data);
@@ -114,7 +113,6 @@
  * specific for now)
  * @cert_id: the certificate's id when using engine
  * @ca_cert_id: the CA certificate's id when using engine
- * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1)
  * @flags: Parameter options (TLS_CONN_*)
  *
  * TLS connection parameters to be configured with tls_connection_set_params()
@@ -142,7 +140,6 @@
 	const char *dh_file;
 	const u8 *dh_blob;
 	size_t dh_blob_len;
-	int tls_ia;
 
 	/* OpenSSL specific variables */
 	int engine;
@@ -282,20 +279,6 @@
 					   int verify_peer);
 
 /**
- * tls_connection_set_ia - Set TLS/IA parameters
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @tls_ia: 1 = enable TLS/IA
- * Returns: 0 on success, -1 on failure
- *
- * This function is used to configure TLS/IA in server mode where
- * tls_connection_set_params() is not used.
- */
-int __must_check tls_connection_set_ia(void *tls_ctx,
-				       struct tls_connection *conn,
-				       int tls_ia);
-
-/**
  * tls_connection_get_keys - Get master key and random data from TLS connection
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
@@ -322,7 +305,7 @@
  * not exported from the TLS library, tls_connection_prf() is required so that
  * further keying material can be derived from the master secret. If not
  * implemented, the function will still need to be defined, but it can just
- * return -1. Example implementation of this function is in tls_prf() function
+ * return -1. Example implementation of this function is in tls_prf_sha1_md5()
  * when it is called with seed set to client_random|server_random (or
  * server_random|client_random).
  */
@@ -364,6 +347,12 @@
 					 const struct wpabuf *in_data,
 					 struct wpabuf **appl_data);
 
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+					  struct tls_connection *conn,
+					  const struct wpabuf *in_data,
+					  struct wpabuf **appl_data,
+					  int *more_data_needed);
+
 /**
  * tls_connection_server_handshake - Process TLS handshake (server side)
  * @tls_ctx: TLS context data from tls_init()
@@ -409,6 +398,11 @@
 				       struct tls_connection *conn,
 				       const struct wpabuf *in_data);
 
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+					struct tls_connection *conn,
+					const struct wpabuf *in_data,
+					int *more_data_needed);
+
 /**
  * tls_connection_resumed - Was session resumption used
  * @tls_ctx: TLS context data from tls_init()
@@ -514,7 +508,6 @@
 int tls_connection_get_keyblock_size(void *tls_ctx,
 				     struct tls_connection *conn);
 
-#define TLS_CAPABILITY_IA 0x0001 /* TLS Inner Application (TLS/IA) */
 /**
  * tls_capabilities - Get supported TLS capabilities
  * @tls_ctx: TLS context data from tls_init()
@@ -522,42 +515,6 @@
  */
 unsigned int tls_capabilities(void *tls_ctx);
 
-/**
- * tls_connection_ia_send_phase_finished - Send a TLS/IA PhaseFinished message
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished
- * Returns: Encrypted TLS/IA data, %NULL on failure
- *
- * This function is used to send the TLS/IA end phase message, e.g., when the
- * EAP server completes EAP-TTLSv1.
- */
-struct wpabuf * tls_connection_ia_send_phase_finished(
-	void *tls_ctx, struct tls_connection *conn, int final);
-
-/**
- * tls_connection_ia_final_phase_finished - Has final phase been completed
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * Returns: 1 if valid FinalPhaseFinished has been received, 0 if not, or -1
- * on failure
- */
-int __must_check tls_connection_ia_final_phase_finished(
-	void *tls_ctx, struct tls_connection *conn);
-
-/**
- * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @key: Session key material (session_key vectors with 2-octet length), or
- * %NULL if no session key was generating in the current phase
- * @key_len: Length of session key material
- * Returns: 0 on success, -1 on failure
- */
-int __must_check tls_connection_ia_permute_inner_secret(
-	void *tls_ctx, struct tls_connection *conn,
-	const u8 *key, size_t key_len);
-
 typedef int (*tls_session_ticket_cb)
 (void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
  const u8 *server_random, u8 *master_secret);
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index c3a7358..afa5268 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -1,6 +1,6 @@
 /*
  * SSL/TLS interface functions for GnuTLS
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -19,28 +19,12 @@
 #include <gnutls/pkcs12.h>
 #endif /* PKCS12_FUNCS */
 
-#ifdef CONFIG_GNUTLS_EXTRA
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
-#define GNUTLS_IA
-#include <gnutls/extra.h>
-#if LIBGNUTLS_VERSION_NUMBER == 0x010302
-/* This function is not included in the current gnutls/extra.h even though it
- * should be, so define it here as a workaround for the time being. */
-int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum);
-#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
-#endif /* CONFIG_GNUTLS_EXTRA */
-
 #include "common.h"
 #include "tls.h"
 
 
-#ifndef TLS_RANDOM_SIZE
-#define TLS_RANDOM_SIZE 32
-#endif
-#ifndef TLS_MASTER_SIZE
-#define TLS_MASTER_SIZE 48
-#endif
+#define WPA_TLS_RANDOM_SIZE 32
+#define WPA_TLS_MASTER_SIZE 48
 
 
 #if LIBGNUTLS_VERSION_NUMBER < 0x010302
@@ -77,9 +61,9 @@
 	gnutls_mac_algorithm_t write_mac_algorithm;
 	gnutls_compression_method_t write_compression_algorithm;
 	cipher_suite_st current_cipher_suite;
-	opaque master_secret[TLS_MASTER_SIZE];
-	opaque client_random[TLS_RANDOM_SIZE];
-	opaque server_random[TLS_RANDOM_SIZE];
+	opaque master_secret[WPA_TLS_MASTER_SIZE];
+	opaque client_random[WPA_TLS_RANDOM_SIZE];
+	opaque server_random[WPA_TLS_RANDOM_SIZE];
 	/* followed by stuff we are not interested in */
 } security_parameters_st;
 
@@ -118,21 +102,6 @@
 
 	int params_set;
 	gnutls_certificate_credentials_t xcred;
-
-	int tls_ia;
-	int final_phase_finished;
-
-#ifdef GNUTLS_IA
-	gnutls_ia_server_credentials_t iacred_srv;
-	gnutls_ia_client_credentials_t iacred_cli;
-
-	/* Session keys generated in the current phase for inner secret
-	 * permutation before generating/verifying PhaseFinished. */
-	u8 *session_keys;
-	size_t session_keys_len;
-
-	u8 inner_secret[TLS_MASTER_SIZE];
-#endif /* GNUTLS_IA */
 };
 
 
@@ -285,8 +254,12 @@
 static int tls_gnutls_init_session(struct tls_global *global,
 				   struct tls_connection *conn)
 {
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
+	const char *err;
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
 	const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
 	const int protos[2] = { GNUTLS_TLS1, 0 };
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
 	int ret;
 
 	ret = gnutls_init(&conn->session,
@@ -301,6 +274,15 @@
 	if (ret < 0)
 		goto fail;
 
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
+	ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
+					 &err);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
+			   "'%s'", err);
+		goto fail;
+	}
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
 	ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
 	if (ret < 0)
 		goto fail;
@@ -308,6 +290,7 @@
 	ret = gnutls_protocol_set_priority(conn->session, protos);
 	if (ret < 0)
 		goto fail;
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
 
 	gnutls_transport_set_pull_function(conn->session, tls_pull_func);
 	gnutls_transport_set_push_function(conn->session, tls_push_func);
@@ -364,17 +347,6 @@
 	if (conn == NULL)
 		return;
 
-#ifdef GNUTLS_IA
-	if (conn->iacred_srv)
-		gnutls_ia_free_server_credentials(conn->iacred_srv);
-	if (conn->iacred_cli)
-		gnutls_ia_free_client_credentials(conn->iacred_cli);
-	if (conn->session_keys) {
-		os_memset(conn->session_keys, 0, conn->session_keys_len);
-		os_free(conn->session_keys);
-	}
-#endif /* GNUTLS_IA */
-
 	gnutls_certificate_free_credentials(conn->xcred);
 	gnutls_deinit(conn->session);
 	os_free(conn->pre_shared_secret);
@@ -407,14 +379,6 @@
 	wpabuf_free(conn->push_buf);
 	conn->push_buf = NULL;
 	conn->established = 0;
-	conn->final_phase_finished = 0;
-#ifdef GNUTLS_IA
-	if (conn->session_keys) {
-		os_memset(conn->session_keys, 0, conn->session_keys_len);
-		os_free(conn->session_keys);
-	}
-	conn->session_keys_len = 0;
-#endif /* GNUTLS_IA */
 
 	gnutls_deinit(conn->session);
 	if (tls_gnutls_init_session(global, conn)) {
@@ -597,11 +561,13 @@
 				conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
 		}
 
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
 		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
 			gnutls_certificate_set_verify_flags(
 				conn->xcred,
 				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
 		}
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
 	}
 
 	if (params->client_cert && params->private_key) {
@@ -646,7 +612,6 @@
 		}
 	}
 
-	conn->tls_ia = params->tls_ia;
 	conn->params_set = 1;
 
 	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
@@ -656,28 +621,6 @@
 			   gnutls_strerror(ret));
 	}
 
-#ifdef GNUTLS_IA
-	if (conn->iacred_cli)
-		gnutls_ia_free_client_credentials(conn->iacred_cli);
-
-	ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
-			   gnutls_strerror(ret));
-		return -1;
-	}
-
-	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
-				     conn->iacred_cli);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
-			   gnutls_strerror(ret));
-		gnutls_ia_free_client_credentials(conn->iacred_cli);
-		conn->iacred_cli = NULL;
-		return -1;
-	}
-#endif /* GNUTLS_IE */
-
 	return ret;
 }
 
@@ -729,11 +672,13 @@
 				GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
 		}
 
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
 		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
 			gnutls_certificate_set_verify_flags(
 				global->xcred,
 				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
 		}
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
 	}
 
 	if (params->client_cert && params->private_key) {
@@ -822,10 +767,11 @@
 
 	os_memset(keys, 0, sizeof(*keys));
 
+#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
 #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
 	sec = &conn->session->security_parameters;
 	keys->master_key = sec->master_secret;
-	keys->master_key_len = TLS_MASTER_SIZE;
+	keys->master_key_len = WPA_TLS_MASTER_SIZE;
 	keys->client_random = sec->client_random;
 	keys->server_random = sec->server_random;
 #else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
@@ -835,16 +781,12 @@
 		(u8 *) gnutls_session_get_server_random(conn->session);
 	/* No access to master_secret */
 #endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
 
-#ifdef GNUTLS_IA
-	gnutls_ia_extract_inner_secret(conn->session,
-				       (char *) conn->inner_secret);
-	keys->inner_secret = conn->inner_secret;
-	keys->inner_secret_len = TLS_MASTER_SIZE;
-#endif /* GNUTLS_IA */
-
-	keys->client_random_len = TLS_RANDOM_SIZE;
-	keys->server_random_len = TLS_RANDOM_SIZE;
+#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
+	keys->client_random_len = WPA_TLS_RANDOM_SIZE;
+	keys->server_random_len = WPA_TLS_RANDOM_SIZE;
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
 
 	return 0;
 }
@@ -883,11 +825,13 @@
 
 	if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
 		wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
+		*err = GNUTLS_A_INTERNAL_ERROR;
 		if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
 			wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
 				   "algorithm");
 			*err = GNUTLS_A_INSUFFICIENT_SECURITY;
 		}
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
 		if (status & GNUTLS_CERT_NOT_ACTIVATED) {
 			wpa_printf(MSG_INFO, "TLS: Certificate not yet "
 				   "activated");
@@ -897,6 +841,7 @@
 			wpa_printf(MSG_INFO, "TLS: Certificate expired");
 			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
 		}
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
 		return -1;
 	}
 
@@ -988,7 +933,7 @@
 				 wpabuf_size(ad));
 	wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
 	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
+		wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
 			   "(%s)", __func__, (int) res,
 			   gnutls_strerror(res));
 		wpabuf_free(ad);
@@ -1062,20 +1007,7 @@
 			goto out;
 		}
 
-#ifdef CONFIG_GNUTLS_EXTRA
-		if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) {
-			wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation");
-			conn->failed++;
-			return NULL;
-		}
-#endif /* CONFIG_GNUTLS_EXTRA */
-
-		if (conn->tls_ia)
-			wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake");
-		else {
-			wpa_printf(MSG_DEBUG, "TLS: Handshake completed "
-				   "successfully");
-		}
+		wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
 		conn->established = 1;
 		if (conn->push_buf == NULL) {
 			/* Need to return something to get final TLS ACK. */
@@ -1122,12 +1054,6 @@
 	ssize_t res;
 	struct wpabuf *buf;
 
-#ifdef GNUTLS_IA
-	if (conn->tls_ia)
-		res = gnutls_ia_send(conn->session, wpabuf_head(in_data),
-				     wpabuf_len(in_data));
-	else
-#endif /* GNUTLS_IA */
 	res = gnutls_record_send(conn->session, wpabuf_head(in_data),
 				 wpabuf_len(in_data));
 	if (res < 0) {
@@ -1170,65 +1096,6 @@
 	if (out == NULL)
 		return NULL;
 
-#ifdef GNUTLS_IA
-	if (conn->tls_ia) {
-		res = gnutls_ia_recv(conn->session, wpabuf_mhead(out),
-				     wpabuf_size(out));
-		if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
-		    res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) {
-			int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED;
-			wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished",
-				   __func__, final ? "Final" : "Intermediate");
-
-			res = gnutls_ia_permute_inner_secret(
-				conn->session, conn->session_keys_len,
-				(char *) conn->session_keys);
-			if (conn->session_keys) {
-				os_memset(conn->session_keys, 0,
-					  conn->session_keys_len);
-				os_free(conn->session_keys);
-			}
-			conn->session_keys = NULL;
-			conn->session_keys_len = 0;
-			if (res) {
-				wpa_printf(MSG_DEBUG, "%s: Failed to permute "
-					   "inner secret: %s",
-					   __func__, gnutls_strerror(res));
-				wpabuf_free(out);
-				return NULL;
-			}
-
-			res = gnutls_ia_verify_endphase(conn->session,
-							wpabuf_head(out));
-			if (res == 0) {
-				wpa_printf(MSG_DEBUG, "%s: Correct endphase "
-					   "checksum", __func__);
-			} else {
-				wpa_printf(MSG_INFO, "%s: Endphase "
-					   "verification failed: %s",
-					   __func__, gnutls_strerror(res));
-				wpabuf_free(out);
-				return NULL;
-			}
-
-			if (final)
-				conn->final_phase_finished = 1;
-
-			return out;
-		}
-
-		if (res < 0) {
-			wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
-				   "(%s)", __func__, (int) res,
-				   gnutls_strerror(res));
-			wpabuf_free(out);
-			return NULL;
-		}
-		wpabuf_put(out, res);
-		return out;
-	}
-#endif /* GNUTLS_IA */
-
 	res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
 				 wpabuf_size(out));
 	if (res < 0) {
@@ -1319,133 +1186,7 @@
 
 unsigned int tls_capabilities(void *tls_ctx)
 {
-	unsigned int capa = 0;
-
-#ifdef GNUTLS_IA
-	capa |= TLS_CAPABILITY_IA;
-#endif /* GNUTLS_IA */
-
-	return capa;
-}
-
-
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-			  int tls_ia)
-{
-#ifdef GNUTLS_IA
-	int ret;
-
-	if (conn == NULL)
-		return -1;
-
-	conn->tls_ia = tls_ia;
-	if (!tls_ia)
-		return 0;
-
-	ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
-			   gnutls_strerror(ret));
-		return -1;
-	}
-
-	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
-				     conn->iacred_srv);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
-			   gnutls_strerror(ret));
-		gnutls_ia_free_server_credentials(conn->iacred_srv);
-		conn->iacred_srv = NULL;
-		return -1;
-	}
-
 	return 0;
-#else /* GNUTLS_IA */
-	return -1;
-#endif /* GNUTLS_IA */
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
-	void *tls_ctx, struct tls_connection *conn, int final)
-{
-#ifdef GNUTLS_IA
-	int ret;
-	struct wpabuf *buf;
-
-	if (conn == NULL || conn->session == NULL || !conn->tls_ia)
-		return NULL;
-
-	ret = gnutls_ia_permute_inner_secret(conn->session,
-					     conn->session_keys_len,
-					     (char *) conn->session_keys);
-	if (conn->session_keys) {
-		os_memset(conn->session_keys, 0, conn->session_keys_len);
-		os_free(conn->session_keys);
-	}
-	conn->session_keys = NULL;
-	conn->session_keys_len = 0;
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s",
-			   __func__, gnutls_strerror(ret));
-		return NULL;
-	}
-
-	ret = gnutls_ia_endphase_send(conn->session, final);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s",
-			   __func__, gnutls_strerror(ret));
-		return NULL;
-	}
-
-	buf = conn->push_buf;
-	conn->push_buf = NULL;
-	return buf;
-#else /* GNUTLS_IA */
-	return NULL;
-#endif /* GNUTLS_IA */
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-					   struct tls_connection *conn)
-{
-	if (conn == NULL)
-		return -1;
-
-	return conn->final_phase_finished;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-					   struct tls_connection *conn,
-					   const u8 *key, size_t key_len)
-{
-#ifdef GNUTLS_IA
-	if (conn == NULL || !conn->tls_ia)
-		return -1;
-
-	if (conn->session_keys) {
-		os_memset(conn->session_keys, 0, conn->session_keys_len);
-		os_free(conn->session_keys);
-	}
-	conn->session_keys_len = 0;
-
-	if (key) {
-		conn->session_keys = os_malloc(key_len);
-		if (conn->session_keys == NULL)
-			return -1;
-		os_memcpy(conn->session_keys, key, key_len);
-		conn->session_keys_len = key_len;
-	} else {
-		conn->session_keys = NULL;
-		conn->session_keys_len = 0;
-	}
-
-	return 0;
-#else /* GNUTLS_IA */
-	return -1;
-#endif /* GNUTLS_IA */
 }
 
 
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index cc165f6..f5e31d9 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -290,13 +290,6 @@
 }
 
 
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-			  int tls_ia)
-{
-	return -1;
-}
-
-
 int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
 			    struct tls_keys *keys)
 {
@@ -339,6 +332,17 @@
 					 const struct wpabuf *in_data,
 					 struct wpabuf **appl_data)
 {
+	return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
+					 NULL);
+}
+
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+					  struct tls_connection *conn,
+					  const struct wpabuf *in_data,
+					  struct wpabuf **appl_data,
+					  int *need_more_data)
+{
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
 	u8 *res, *ad;
 	size_t res_len, ad_len;
@@ -351,7 +355,7 @@
 	res = tlsv1_client_handshake(conn->client,
 				     in_data ? wpabuf_head(in_data) : NULL,
 				     in_data ? wpabuf_len(in_data) : 0,
-				     &res_len, &ad, &ad_len);
+				     &res_len, &ad, &ad_len, need_more_data);
 	if (res == NULL)
 		return NULL;
 	out = wpabuf_alloc_ext_data(res, res_len);
@@ -462,23 +466,23 @@
 				       struct tls_connection *conn,
 				       const struct wpabuf *in_data)
 {
+	return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
+}
+
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+					struct tls_connection *conn,
+					const struct wpabuf *in_data,
+					int *need_more_data)
+{
+	if (need_more_data)
+		*need_more_data = 0;
+
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
 	if (conn->client) {
-		struct wpabuf *buf;
-		int res;
-		buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
-		if (buf == NULL)
-			return NULL;
-		res = tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
-					   wpabuf_len(in_data),
-					   wpabuf_mhead(buf),
-					   wpabuf_size(buf));
-		if (res < 0) {
-			wpabuf_free(buf);
-			return NULL;
-		}
-		wpabuf_put(buf, res);
-		return buf;
+		return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
+					    wpabuf_len(in_data),
+					    need_more_data);
 	}
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
@@ -611,28 +615,6 @@
 }
 
 
-struct wpabuf * tls_connection_ia_send_phase_finished(
-	void *tls_ctx, struct tls_connection *conn, int final)
-{
-	return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-					   struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-					   struct tls_connection *conn,
-					   const u8 *key, size_t key_len)
-{
-	return -1;
-}
-
-
 int tls_connection_set_session_ticket_cb(void *tls_ctx,
 					 struct tls_connection *conn,
 					 tls_session_ticket_cb cb,
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index 0c836bb..927edf5 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -84,13 +84,6 @@
 }
 
 
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-			  int tls_ia)
-{
-	return -1;
-}
-
-
 int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
 			    struct tls_keys *keys)
 {
@@ -205,25 +198,3 @@
 {
 	return 0;
 }
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
-	void *tls_ctx, struct tls_connection *conn, int final)
-{
-	return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-					   struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-					   struct tls_connection *conn,
-					   const u8 *key, size_t key_len)
-{
-	return -1;
-}
diff --git a/src/crypto/tls_nss.c b/src/crypto/tls_nss.c
index ad834b6..09a1e73 100644
--- a/src/crypto/tls_nss.c
+++ b/src/crypto/tls_nss.c
@@ -419,13 +419,6 @@
 }
 
 
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-			  int tls_ia)
-{
-	return -1;
-}
-
-
 int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
 			    struct tls_keys *keys)
 {
@@ -649,28 +642,6 @@
 }
 
 
-struct wpabuf * tls_connection_ia_send_phase_finished(
-	void *tls_ctx, struct tls_connection *conn, int final)
-{
-	return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-					   struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-					   struct tls_connection *conn,
-					   const u8 *key, size_t key_len)
-{
-	return -1;
-}
-
-
 int tls_connection_set_session_ticket_cb(void *tls_ctx,
 					 struct tls_connection *conn,
 					 tls_session_ticket_cb cb,
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 14ff87e..8374096 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -59,6 +59,7 @@
 	void (*event_cb)(void *ctx, enum tls_event ev,
 			 union tls_event_data *data);
 	void *cb_ctx;
+	int cert_in_cb;
 };
 
 static struct tls_global *tls_global = NULL;
@@ -694,6 +695,7 @@
 		if (conf) {
 			tls_global->event_cb = conf->event_cb;
 			tls_global->cb_ctx = conf->cb_ctx;
+			tls_global->cert_in_cb = conf->cert_in_cb;
 		}
 
 #ifdef CONFIG_FIPS
@@ -1144,7 +1146,7 @@
 		return;
 
 	os_memset(&ev, 0, sizeof(ev));
-	if (conn->cert_probe) {
+	if (conn->cert_probe || tls_global->cert_in_cb) {
 		cert = get_x509_cert(err_cert);
 		ev.peer_cert.cert = cert;
 	}
@@ -1661,6 +1663,7 @@
 
 	if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
 					 SSL_FILETYPE_ASN1) != 1 &&
+	    SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 &&
 	    SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
 					 SSL_FILETYPE_PEM) != 1) {
 		tls_show_errors(MSG_INFO, __func__,
@@ -2809,35 +2812,6 @@
 }
 
 
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-			  int tls_ia)
-{
-	return -1;
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
-	void *tls_ctx, struct tls_connection *conn, int final)
-{
-	return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-					   struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-					   struct tls_connection *conn,
-					   const u8 *key, size_t key_len)
-{
-	return -1;
-}
-
-
 #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
 /* Pre-shared secred requires a patch to openssl, so this function is
  * commented out unless explicitly needed for EAP-FAST in order to be able to
diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c
index 4a94e99..a33d24e 100644
--- a/src/crypto/tls_schannel.c
+++ b/src/crypto/tls_schannel.c
@@ -736,32 +736,3 @@
 {
 	return 0;
 }
-
-
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-			  int tls_ia)
-{
-	return -1;
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
-	void *tls_ctx, struct tls_connection *conn, int final);
-{
-	return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-					   struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-					   struct tls_connection *conn,
-					   const u8 *key, size_t key_len)
-{
-	return -1;
-}