Index: uspace/app/wifi_supplicant/wifi_supplicant.c
===================================================================
--- uspace/app/wifi_supplicant/wifi_supplicant.c	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/app/wifi_supplicant/wifi_supplicant.c	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -187,5 +187,5 @@
 	}
 	
-	ieee80211_connect(sess, ssid_start, password);
+	rc = ieee80211_connect(sess, ssid_start, password);
 	if(rc != EOK) {
 		if(rc == EREFUSED) {
Index: uspace/drv/nic/ar9271/ar9271.c
===================================================================
--- uspace/drv/nic/ar9271/ar9271.c	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/drv/nic/ar9271/ar9271.c	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -440,5 +440,5 @@
 		uint32_t key[5];
 		uint32_t key_type;
-		uint32_t reg_ptr;
+		uint32_t reg_ptr, mic_reg_ptr;
 		void *data_start;
 		
@@ -465,37 +465,34 @@
 		if(key_conf->flags & IEEE80211_KEY_FLAG_TYPE_PAIRWISE) {
 			reg_ptr = AR9271_KEY_TABLE_STA;
+			mic_reg_ptr = AR9271_KEY_TABLE_MIC_STA;
 		} else {
 			reg_ptr = AR9271_KEY_TABLE_GRP;
+			mic_reg_ptr = AR9271_KEY_TABLE_MIC_GRP;
 		}
 		
-		if(key_conf->suite == IEEE80211_SECURITY_SUITE_TKIP) {
-			// TODO
-		} else {
-			data_start = (void *) key_conf->data;
-			
-			key[0] = uint32_t_le2host(
-				*((uint32_t *) data_start));
-			key[1] = uint16_t_le2host(
-				*((uint16_t *) (data_start + 4)));
-			key[2] = uint32_t_le2host(
-				*((uint32_t *) (data_start + 6)));
-			key[3] = uint16_t_le2host(
-				*((uint16_t *) (data_start + 10)));
-			key[4] = uint32_t_le2host(
-				*((uint32_t *) (data_start + 12)));
-			
-			if(key_conf->suite == IEEE80211_SECURITY_SUITE_WEP40 ||
-			   key_conf->suite == IEEE80211_SECURITY_SUITE_WEP104) {
-				key[4] &= 0xFF;
-			}
-			
-			wmi_reg_write(ar9271->htc_device, reg_ptr + 0, key[0]);
-			wmi_reg_write(ar9271->htc_device, reg_ptr + 4, key[1]);
-			wmi_reg_write(ar9271->htc_device, reg_ptr + 8, key[2]);
-			wmi_reg_write(ar9271->htc_device, reg_ptr + 12, key[3]);
-			wmi_reg_write(ar9271->htc_device, reg_ptr + 16, key[4]);
-			wmi_reg_write(ar9271->htc_device, reg_ptr + 20, 
-				key_type);
+		data_start = (void *) key_conf->data;
+		
+		key[0] = uint32_t_le2host(
+			*((uint32_t *) data_start));
+		key[1] = uint16_t_le2host(
+			*((uint16_t *) (data_start + 4)));
+		key[2] = uint32_t_le2host(
+			*((uint32_t *) (data_start + 6)));
+		key[3] = uint16_t_le2host(
+			*((uint16_t *) (data_start + 10)));
+		key[4] = uint32_t_le2host(
+			*((uint32_t *) (data_start + 12)));
+
+		if(key_conf->suite == IEEE80211_SECURITY_SUITE_WEP40 ||
+		   key_conf->suite == IEEE80211_SECURITY_SUITE_WEP104) {
+			key[4] &= 0xFF;
 		}
+		
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 0, key[0]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 4, key[1]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 8, key[2]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 12, key[3]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 16, key[4]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 20, key_type);
 		
 		uint32_t macL, macH;
@@ -516,4 +513,45 @@
 		wmi_reg_write(ar9271->htc_device, reg_ptr + 24, macL);
 		wmi_reg_write(ar9271->htc_device, reg_ptr + 28, macH);
+		
+		/* Setup MIC keys for TKIP. */
+		if(key_conf->suite == IEEE80211_SECURITY_SUITE_TKIP) {
+			uint32_t mic[5];
+			uint8_t *gen_mic = 
+				data_start + IEEE80211_TKIP_RX_MIC_OFFSET;
+			uint8_t *tx_mic;
+			if(key_conf->flags & IEEE80211_KEY_FLAG_TYPE_GROUP) {
+				tx_mic = gen_mic;
+			} else {
+				tx_mic = data_start + 
+					IEEE80211_TKIP_TX_MIC_OFFSET;
+			}
+			
+			mic[0] = uint32_t_le2host(
+				*((uint32_t *) gen_mic));
+			mic[1] = uint16_t_le2host(
+				*((uint16_t *) (tx_mic + 2))) & 0xFFFF;
+			mic[2] = uint32_t_le2host(
+				*((uint32_t *) (gen_mic + 4)));
+			mic[3] = uint16_t_le2host(
+				*((uint16_t *) tx_mic)) & 0xFFFF;
+			mic[4] = uint32_t_le2host(
+				*((uint32_t *) (tx_mic + 4)));
+			
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 0, 
+				mic[0]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 4, 
+				mic[1]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 8, 
+				mic[2]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 12, 
+				mic[3]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 16, 
+				mic[4]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 20, 
+				AR9271_KEY_TABLE_TYPE_CLR);
+			
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 24, 0);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 28, 0);
+		}
 		
 		if(key_conf->flags & IEEE80211_KEY_FLAG_TYPE_GROUP)
Index: uspace/drv/nic/ar9271/ar9271.h
===================================================================
--- uspace/drv/nic/ar9271/ar9271.h	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/drv/nic/ar9271/ar9271.h	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -138,8 +138,11 @@
 	AR9271_KEY_TABLE_GRP = 0x8820,
 	AR9271_KEY_TABLE_STA = 0x8880,
+	AR9271_KEY_TABLE_MIC_GRP = 0x9020,
+	AR9271_KEY_TABLE_MIC_STA = 0x9080,
 	AR9271_KEY_TABLE_TYPE_WEP40 = 0x0,
 	AR9271_KEY_TABLE_TYPE_WEP104 = 0x1,
 	AR9271_KEY_TABLE_TYPE_TKIP = 0x4,
 	AR9271_KEY_TABLE_TYPE_CCMP = 0x6,
+	AR9271_KEY_TABLE_TYPE_CLR = 0x7,
 		
 	/* Physical layer registers */
Index: uspace/lib/crypto/crypto.c
===================================================================
--- uspace/lib/crypto/crypto.c	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/lib/crypto/crypto.c	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -252,5 +252,5 @@
 
 /**
- * Hash-based message authentication code using SHA-1 algorithm.
+ * Hash-based message authentication code.
  * 
  * @param key Cryptographic key sequence.
@@ -308,5 +308,5 @@
  * Password-Based Key Derivation Function 2 as defined in RFC 2898,
  * using HMAC-SHA1 with 4096 iterations and 32 bytes key result used
- * for WPA2.
+ * for WPA/WPA2.
  * 
  * @param pass Password sequence.
@@ -315,5 +315,4 @@
  * @param salt_size Salt sequence length.
  * @param hash Output parameter for result hash (32 byte value).
- * @param hash_sel Hash function selector.
  * 
  * @return EINVAL when pass or salt not specified, ENOMEM when pointer for 
@@ -321,5 +320,5 @@
  */
 int pbkdf2(uint8_t *pass, size_t pass_size, uint8_t *salt, size_t salt_size, 
-	uint8_t *hash, hash_func_t hash_sel)
+	uint8_t *hash)
 {
 	if(!pass || !salt)
@@ -331,8 +330,8 @@
 	uint8_t work_salt[salt_size + sizeof(uint32_t)];
 	memcpy(work_salt, salt, salt_size);
-	uint8_t work_hmac[hash_sel];
-	uint8_t temp_hmac[hash_sel];
-	uint8_t xor_hmac[hash_sel];
-	uint8_t temp_hash[hash_sel*2];
+	uint8_t work_hmac[HASH_SHA1];
+	uint8_t temp_hmac[HASH_SHA1];
+	uint8_t xor_hmac[HASH_SHA1];
+	uint8_t temp_hash[HASH_SHA1*2];
 	
 	for(size_t i = 0; i < 2; i++) {
@@ -340,15 +339,15 @@
 		memcpy(work_salt + salt_size, &big_i, sizeof(uint32_t));
 		hmac(pass, pass_size, work_salt, salt_size + sizeof(uint32_t),
-			work_hmac, hash_sel);
-		memcpy(xor_hmac, work_hmac, hash_sel);
+			work_hmac, HASH_SHA1);
+		memcpy(xor_hmac, work_hmac, HASH_SHA1);
 		for(size_t k = 1; k < 4096; k++) {
-			memcpy(temp_hmac, work_hmac, hash_sel);
-			hmac(pass, pass_size, temp_hmac, hash_sel, 
-				work_hmac, hash_sel);
-			for(size_t t = 0; t < hash_sel; t++) {
+			memcpy(temp_hmac, work_hmac, HASH_SHA1);
+			hmac(pass, pass_size, temp_hmac, HASH_SHA1, 
+				work_hmac, HASH_SHA1);
+			for(size_t t = 0; t < HASH_SHA1; t++) {
 				xor_hmac[t] ^= work_hmac[t];
 			}
 		}
-		memcpy(temp_hash + i*hash_sel, xor_hmac, hash_sel);
+		memcpy(temp_hash + i*HASH_SHA1, xor_hmac, HASH_SHA1);
 	}
 	
Index: uspace/lib/crypto/crypto.h
===================================================================
--- uspace/lib/crypto/crypto.h	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/lib/crypto/crypto.h	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -41,6 +41,6 @@
 } hash_func_t;
 
-extern int rc4(uint8_t *key, size_t key_size, uint8_t *input, 
-	size_t input_size, uint8_t *output);
+extern int rc4(uint8_t *key, size_t key_size, uint8_t *input, size_t input_size, 
+	size_t skip, uint8_t *output);
 extern int aes_encrypt(uint8_t *key, uint8_t *input, uint8_t *output);
 extern int aes_decrypt(uint8_t *key, uint8_t *input, uint8_t *output);
@@ -50,5 +50,5 @@
 	uint8_t *hash, hash_func_t hash_sel);
 extern int pbkdf2(uint8_t *pass, size_t pass_size, uint8_t *salt, 
-	size_t salt_size, uint8_t *hash, hash_func_t hash_sel);
+	size_t salt_size, uint8_t *hash);
 
 #endif
Index: uspace/lib/crypto/rc4.c
===================================================================
--- uspace/lib/crypto/rc4.c	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/lib/crypto/rc4.c	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -82,4 +82,5 @@
  * @param input Input data sequence to be processed.
  * @param input_size Size of input data sequence.
+ * @param skip Number of bytes to be skipped from the beginning of key stream.
  * @param output Result data sequence.
  * 
@@ -88,5 +89,5 @@
  */
 int rc4(uint8_t *key, size_t key_size, uint8_t *input, size_t input_size, 
-	uint8_t *output)
+	size_t skip, uint8_t *output)
 {
 	if(!key || !input)
@@ -100,6 +101,14 @@
 	create_sbox(key, key_size, sbox);
 	
+	/* Skip first x bytes. */
+	uint8_t i = 0, j = 0;
+	for(size_t k = 0; k < skip; k++) {
+		i = i+1;
+		j = j + sbox[i];
+		swap(i, j, sbox);
+	}
+	
 	/* Processing loop. */
-	uint8_t i = 0, j = 0, val;
+	uint8_t val;
 	for(size_t k = 0; k < input_size; k++) {
 		i = i+1;
Index: uspace/lib/ieee80211/include/ieee80211.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211.h	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/lib/ieee80211/include/ieee80211.h	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -82,4 +82,9 @@
 } ieee80211_key_flags_t;
 
+typedef enum {
+	IEEE80211_TKIP_TX_MIC_OFFSET = 16,
+	IEEE80211_TKIP_RX_MIC_OFFSET = 24
+} ieee80211_tkip_mic_offset_t;
+
 /** Key config structure. */
 typedef struct {
Index: uspace/lib/ieee80211/include/ieee80211_impl.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211_impl.h	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/lib/ieee80211/include/ieee80211_impl.h	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -53,5 +53,7 @@
 extern int ieee80211_scan_impl(ieee80211_dev_t *ieee80211_dev);
 extern int ieee80211_prf(uint8_t *key, uint8_t *data, uint8_t *hash, 
-	hash_func_t hash_sel);
+	size_t output_size);
+extern int ieee80211_rc4_key_unwrap(uint8_t *key, uint8_t *data, 
+	size_t data_size, uint8_t *output);
 extern int ieee80211_aes_key_unwrap(uint8_t *kek, uint8_t *data, 
 	size_t data_size, uint8_t *output);
Index: uspace/lib/ieee80211/include/ieee80211_private.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211_private.h	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/lib/ieee80211/include/ieee80211_private.h	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -52,5 +52,5 @@
 
 /* Timeout in us for waiting to finish 4-way handshake process. */
-#define HANDSHAKE_TIMEOUT 3000000
+#define HANDSHAKE_TIMEOUT 5000000
 
 /* Scanning period. */
@@ -86,7 +86,4 @@
 /* TK offset inside PTK. */
 #define TK_OFFSET 32
-
-/* Length of CCMP header we need to reserve. */
-#define IEEE80211_CCMP_HEADER_LENGTH 8
 
 /* 
@@ -95,4 +92,16 @@
  */
 #define PRF_CRYPT_DATA_LENGTH 2*32 + 2*ETH_ADDR
+
+/* Special room in header reserved for encryption. */
+typedef enum {
+	IEEE80211_TKIP_HEADER_LENGTH = 8,
+	IEEE80211_CCMP_HEADER_LENGTH = 8
+} ieee80211_encrypt_header_reserve_length_t;
+
+/* Special room in footer reserved for encryption. */
+typedef enum {
+	IEEE80211_TKIP_FOOTER_LENGTH = 4,
+	IEEE80211_CCMP_FOOTER_LENGTH = 8
+} ieee80211_encrypt_footer_reserve_length_t;
 
 /** IEEE 802.11 PTK key length. */
@@ -199,6 +208,6 @@
 	time_t last_beacon;
 	ieee80211_scan_result_t scan_result;
-	uint8_t rsn_copy[256];
-	size_t rsn_copy_len;
+	uint8_t auth_ie[256];
+	size_t auth_ie_len;
 } ieee80211_scan_result_link_t;
 
Index: uspace/lib/ieee80211/src/ieee80211.c
===================================================================
--- uspace/lib/ieee80211/src/ieee80211.c	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/lib/ieee80211/src/ieee80211.c	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -504,5 +504,5 @@
 	while(true) {
 		ieee80211_dev->ops->scan(ieee80211_dev);
-		async_usleep(35000000);
+		async_usleep(SCAN_PERIOD_USEC);
 	}
 	
@@ -577,7 +577,12 @@
 	memset(add_data, 0, 8);
 	
+	// TODO: Distinguish used key (pair/group) by dest address ?
 	if(ieee80211_query_using_key(ieee80211_dev)) {
 		int sec_suite = auth_data->security.pair_alg;
 		switch(sec_suite) {
+			case IEEE80211_SECURITY_SUITE_TKIP:
+				add_size = IEEE80211_TKIP_HEADER_LENGTH;
+				// TODO: Add data?
+				break;
 			case IEEE80211_SECURITY_SUITE_CCMP:
 				add_size = IEEE80211_CCMP_HEADER_LENGTH;
@@ -978,6 +983,7 @@
 		payload_size;
 	
-	if(auth_data->security.type == IEEE80211_SECURITY_WPA2) {
-		buffer_size += auth_link->rsn_copy_len;
+	if(auth_data->security.type == IEEE80211_SECURITY_WPA || 
+		auth_data->security.type == IEEE80211_SECURITY_WPA2) {
+		buffer_size += auth_link->auth_ie_len;
 	}
 	
@@ -1017,6 +1023,7 @@
 	}
 	
-	if(auth_data->security.type == IEEE80211_SECURITY_WPA2) {
-		memcpy(it, auth_link->rsn_copy,	auth_link->rsn_copy_len);
+	if(auth_data->security.type == IEEE80211_SECURITY_WPA || 
+		auth_data->security.type == IEEE80211_SECURITY_WPA2) {
+		memcpy(it, auth_link->auth_ie,	auth_link->auth_ie_len);
 	}
 	
@@ -1037,4 +1044,6 @@
 /** 
  * IEEE 802.11 deauthentication implementation.
+ * 
+ * Note: Expecting locked results_mutex or scan_mutex.
  * 
  * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
@@ -1147,4 +1156,13 @@
 {
 	return (*seq << 24) + (*(seq+1) << 16) + (*(seq+2) << 8) + *(seq+3); 
+}
+
+static void copy_auth_ie(ieee80211_ie_header_t *ie_header,
+	ieee80211_scan_result_link_t *ap_data, void *it)
+{
+	ap_data->auth_ie_len = ie_header->length + 
+		sizeof(ieee80211_ie_header_t);
+	
+	memcpy(ap_data->auth_ie, it, ap_data->auth_ie_len);
 }
 
@@ -1170,9 +1188,5 @@
 				ieee80211_process_auth_info(ap_data, 
 					it + sizeof(ieee80211_ie_header_t));
-				ap_data->rsn_copy_len = ie_header->length +
-					sizeof(ieee80211_ie_header_t);
-				memcpy(ap_data->rsn_copy, 
-					it, 
-					ap_data->rsn_copy_len);
+				copy_auth_ie(ie_header, ap_data, it);
 				break;
 			case IEEE80211_VENDOR_IE:
@@ -1180,4 +1194,5 @@
 					sizeof(ieee80211_ie_header_t)) ==
 					WPA_OUI) {
+					/* Prefering WPA2. */
 					if(ap_data->scan_result.security.type ==
 						IEEE80211_SECURITY_WPA2) {
@@ -1190,4 +1205,5 @@
 						sizeof(ieee80211_ie_header_t) +
 						sizeof(uint32_t));
+					copy_auth_ie(ie_header, ap_data, it);
 				} else if(uint32_from_uint8_seq(it + 
 					sizeof(ieee80211_ie_header_t)) ==
@@ -1356,32 +1372,54 @@
 		(ieee80211_eapol_key_frame_t *) buffer;
 	
-	bool handshake_done = false;
-		
-	ieee80211_scan_result_link_t *auth_link =
+	ieee80211_scan_result_link_t *auth_link = 
 		ieee80211_dev->bssid_info.res_link;
 
 	ieee80211_scan_result_t *auth_data = &auth_link->scan_result;
+	
+	/* We don't support 802.1X authentication yet. */
+	if(auth_data->security.auth == IEEE80211_AUTH_AKM_8021X) {
+		return ENOTSUP;
+	}
+	
 	uint8_t *ptk = ieee80211_dev->bssid_info.ptk;
 	uint8_t *gtk = ieee80211_dev->bssid_info.gtk;
 
+	bool handshake_done = false;
+	
+	bool old_wpa = 
+		auth_data->security.type == IEEE80211_SECURITY_WPA;
+	
+	bool key_phase =
+		uint16_t_be2host(key_frame->key_info) & 
+		IEEE80211_EAPOL_KEY_KEYINFO_MIC;
+	
+	bool final_phase = 
+		uint16_t_be2host(key_frame->key_info) & 
+		IEEE80211_EAPOL_KEY_KEYINFO_SECURE;
+	
+	bool ccmp_used = 
+		auth_data->security.pair_alg == IEEE80211_SECURITY_SUITE_CCMP ||
+		auth_data->security.group_alg == IEEE80211_SECURITY_SUITE_CCMP;
+	
 	size_t ptk_key_length, gtk_key_length;
-	hash_func_t hash_sel;
-	switch(auth_data->security.pair_alg) {
-		case IEEE80211_SECURITY_SUITE_CCMP:
-			ptk_key_length = IEEE80211_PTK_CCMP_LENGTH;
-			hash_sel = HASH_SHA1;
-			break;
-		default:
-			return ENOTSUP;
-	}
-
-	switch(auth_data->security.group_alg) {
-		case IEEE80211_SECURITY_SUITE_CCMP:
-			gtk_key_length = IEEE80211_GTK_CCMP_LENGTH;
-			break;
-		default:
-			return ENOTSUP;
-	}
-
+	hash_func_t mic_hash;
+	if(ccmp_used) {
+		mic_hash = HASH_SHA1;
+	} else {
+		mic_hash = HASH_MD5;
+	}
+	
+	if(auth_data->security.pair_alg == IEEE80211_SECURITY_SUITE_CCMP) {
+		ptk_key_length = IEEE80211_PTK_CCMP_LENGTH;
+	} else {
+		ptk_key_length = IEEE80211_PTK_TKIP_LENGTH;
+	}
+	
+	if(auth_data->security.group_alg == IEEE80211_SECURITY_SUITE_CCMP) {
+		gtk_key_length = IEEE80211_GTK_CCMP_LENGTH;
+	} else {
+		gtk_key_length = IEEE80211_GTK_TKIP_LENGTH;
+	}
+	
 	size_t output_size = 
 		sizeof(eth_header_t) +
@@ -1389,6 +1427,6 @@
 
 	if(!(uint16_t_be2host(key_frame->key_info) & 
-		IEEE80211_EAPOL_KEY_KEYINFO_SECURE)) {
-		output_size += auth_link->rsn_copy_len;
+		IEEE80211_EAPOL_KEY_KEYINFO_MIC)) {
+		output_size += auth_link->auth_ie_len;
 	}
 
@@ -1415,7 +1453,6 @@
 
 	output_key_frame->proto_version = 0x1;
-	output_key_frame->key_length = 0;
 	output_key_frame->body_length =
-		host2uint16_t_be(output_size - sizeof(eth_header_t)-4);
+		host2uint16_t_be(output_size - sizeof(eth_header_t) - 4);
 	output_key_frame->key_info &= 
 		~host2uint16_t_be(
@@ -1423,10 +1460,5 @@
 		);
 
-	/* 
-	 * Check if it is last or first incoming message in 4-way 
-	 * handshake. 
-	 */
-	if(uint16_t_be2host(key_frame->key_info) & 
-		IEEE80211_EAPOL_KEY_KEYINFO_SECURE) {
+	if(key_phase) {
 		output_key_frame->key_info &= 
 			~host2uint16_t_be(
@@ -1438,5 +1470,4 @@
 			);
 		output_key_frame->key_data_length = 0;
-		output_key_frame->key_length = 0;
 		memset(output_key_frame->key_nonce, 0, 32);
 		memset(output_key_frame->key_mic, 0, 16);
@@ -1445,19 +1476,33 @@
 
 		/* Derive GTK and save it. */
-		uint16_t key_data_length = 
-			uint16_t_be2host(key_frame->key_data_length);
-		uint16_t decrypt_len = key_data_length - 8;
-		uint8_t key_data[decrypt_len];
-		uint8_t *data_ptr = (uint8_t *) (buffer + 
-			sizeof(ieee80211_eapol_key_frame_t));
-		if(ieee80211_aes_key_unwrap(ptk + KEK_OFFSET, data_ptr,
-			key_data_length, key_data) == EOK) {
+		if(final_phase) {
+			uint16_t key_data_length = 
+				uint16_t_be2host(key_frame->key_data_length);
+			uint8_t key_data[key_data_length];
+			uint8_t *data_ptr = (uint8_t *) (buffer + 
+				sizeof(ieee80211_eapol_key_frame_t));
+
+			int rc;
+			uint8_t work_key[32];
+		
+			if(ccmp_used) {
+				rc = ieee80211_aes_key_unwrap(ptk + KEK_OFFSET, 
+					data_ptr, key_data_length, key_data);
+			} else {
+				memcpy(work_key, key_frame->eapol_key_iv, 16);
+				memcpy(work_key + 16, ptk + KEK_OFFSET, 16);
+				rc = ieee80211_rc4_key_unwrap(work_key, 
+					data_ptr, key_data_length, key_data);
+			}
 			
-			uint8_t *key_ptr = ieee80211_process_ies(ieee80211_dev, 
-				NULL, key_data, decrypt_len);
-
-			if(key_ptr) {
-				memcpy(gtk, key_ptr, gtk_key_length);
-				handshake_done = true;
+			if(rc == EOK) {
+				uint8_t *key_ptr = old_wpa ? key_data :
+					ieee80211_process_ies(ieee80211_dev,
+					NULL, key_data, key_data_length);
+
+				if(key_ptr) {
+					memcpy(gtk, key_ptr, gtk_key_length);
+					handshake_done = true;
+				}
 			}
 		}
@@ -1468,9 +1513,9 @@
 			);
 		output_key_frame->key_data_length =
-			host2uint16_t_be(auth_link->rsn_copy_len);
+			host2uint16_t_be(auth_link->auth_ie_len);
 		memcpy((void *)output_key_frame + 
 			sizeof(ieee80211_eapol_key_frame_t),
-			auth_link->rsn_copy,
-			auth_link->rsn_copy_len);
+			auth_link->auth_ie,
+			auth_link->auth_ie_len);
 
 		/* Compute PMK. */
@@ -1479,5 +1524,5 @@
 			str_size(ieee80211_dev->bssid_info.password),
 			(uint8_t *) auth_data->ssid,
-			str_size(auth_data->ssid), pmk, hash_sel);
+			str_size(auth_data->ssid), pmk);
 
 		uint8_t *anonce = key_frame->key_nonce;
@@ -1493,5 +1538,4 @@
 
 		/* Derive PTK and save it. */
-		uint8_t prf_result[ptk_key_length];
 		uint8_t crypt_data[PRF_CRYPT_DATA_LENGTH];
 		memcpy(crypt_data, 
@@ -1507,12 +1551,11 @@
 			max_sequence(anonce, snonce, 32),
 			32);
-		ieee80211_prf(pmk, crypt_data, prf_result, hash_sel);
-		memcpy(ptk, prf_result, ptk_key_length);
+		ieee80211_prf(pmk, crypt_data, ptk, ptk_key_length);
 	}
 
 	/* Compute MIC of key frame data from KCK part of PTK. */
-	uint8_t mic[hash_sel];
+	uint8_t mic[mic_hash];
 	hmac(ptk, 16, (uint8_t *) output_key_frame, 
-		output_size - sizeof(eth_header_t), mic, hash_sel);
+		output_size - sizeof(eth_header_t), mic, mic_hash);
 
 	memcpy(output_key_frame->key_mic, mic, 16);
@@ -1522,7 +1565,8 @@
 	free(output_buffer);
 
-	if(handshake_done) {
-		/* Insert Pairwise key. */
-		ieee80211_key_config_t key_config;
+	ieee80211_key_config_t key_config;
+	
+	/* Insert Pairwise key. */
+	if((key_phase && old_wpa) || (final_phase && !old_wpa)) {
 		key_config.suite = auth_data->security.pair_alg;
 		key_config.flags =
@@ -1534,6 +1578,8 @@
 		ieee80211_dev->ops->key_config(ieee80211_dev,
 			&key_config, true);
-		
-		/* Insert Group key. */
+	}
+	
+	/* Insert Group key. */
+	if(final_phase) {
 		key_config.suite = auth_data->security.group_alg;
 		key_config.flags =
@@ -1543,6 +1589,8 @@
 		ieee80211_dev->ops->key_config(ieee80211_dev,
 			&key_config, true);
-
-		/* Signal successful handshake completion. */
+	}
+
+	/* Signal successful handshake completion. */
+	if(handshake_done) {
 		fibril_mutex_lock(&ieee80211_dev->gen_mutex);
 		fibril_condvar_signal(&ieee80211_dev->gen_cond);
@@ -1586,5 +1634,7 @@
 			ARRAY_SIZE(rfc1042_header);
 		
-		/* TODO: Probably different by used security alg. */
+		/* TODO: Different by used security alg. */
+		/* TODO: Trim frame by used security alg. */
+		// TODO: Distinguish used key (pair/group) by dest address ?
 		if(ieee80211_is_encrypted_frame(data_header->frame_ctrl)) {
 			strip_length += 8;
Index: uspace/lib/ieee80211/src/ieee80211_impl.c
===================================================================
--- uspace/lib/ieee80211/src/ieee80211_impl.c	(revision 053fc2bcb6fbea6b3e6edf10ca784c77402816d4)
+++ uspace/lib/ieee80211/src/ieee80211_impl.c	(revision a931b7b2faca447e0708fd5fae0f85116e4c788d)
@@ -165,10 +165,11 @@
 
 /**
- * Pseudorandom function used for IEEE 802.11 pairwise key computation.
+ * Pseudorandom function used for IEEE 802.11 pairwise key computation
+ * using SHA1 hash algorithm.
  * 
  * @param key Key with PBKDF2 encrypted passphrase.
  * @param data Concatenated sequence of both mac addresses and nonces.
- * @param hash Output parameter for result hash (48 byte value).
- * @param hash_sel Hash function selector.
+ * @param hash Output parameter for result hash.
+ * @param output_size Length of output sequence to be generated.
  * 
  * @return EINVAL when key or data not specified, ENOMEM when pointer for 
@@ -176,5 +177,5 @@
  */
 int ieee80211_prf(uint8_t *key, uint8_t *data, uint8_t *hash, 
-	hash_func_t hash_sel)
+	size_t output_size)
 {
 	if(!key || !data)
@@ -184,11 +185,9 @@
 		return ENOMEM;
 	
-	size_t result_length = (hash_sel == HASH_MD5) ? 
-		IEEE80211_PTK_TKIP_LENGTH : IEEE80211_PTK_CCMP_LENGTH;
-	size_t iters = ((result_length * 8) + 159) / 160;
+	size_t iters = ((output_size * 8) + 159) / 160;
 	
 	const char *a = "Pairwise key expansion";
-	uint8_t result[hash_sel*iters];
-	uint8_t temp[hash_sel];
+	uint8_t result[HASH_SHA1*iters];
+	uint8_t temp[HASH_SHA1];
 	size_t data_size = PRF_CRYPT_DATA_LENGTH + str_size(a) + 2;
 	uint8_t work_arr[data_size];
@@ -201,11 +200,17 @@
 		memcpy(work_arr + data_size - 1, &i, 1);
 		hmac(key, PBKDF2_KEY_LENGTH, work_arr, data_size, temp, 
-			hash_sel);
-		memcpy(result + i*hash_sel, temp, hash_sel);
-	}
-	
-	memcpy(hash, result, result_length);
-	
-	return EOK;
+			HASH_SHA1);
+		memcpy(result + i*HASH_SHA1, temp, HASH_SHA1);
+	}
+	
+	memcpy(hash, result, output_size);
+	
+	return EOK;
+}
+
+int ieee80211_rc4_key_unwrap(uint8_t *key, uint8_t *data, size_t data_size,
+	uint8_t *output)
+{
+	return rc4(key, 32, data, data_size, 256, output);
 }
 
@@ -230,5 +235,5 @@
 	
 	memcpy(work_data, data + 8, n*8);
-	for(int j = 5; j >=0; j--) {
+	for(int j = 5; j >= 0; j--) {
 		for(int i = n; i > 0; i--) {
 			for(size_t k = 0; k < 8; k++) {
