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++) {
