Index: uspace/lib/c/include/ieee80211/ieee80211.h
===================================================================
--- uspace/lib/c/include/ieee80211/ieee80211.h	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/c/include/ieee80211/ieee80211.h	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -41,10 +41,41 @@
 #include <adt/list.h>
 #include <nic/nic.h>
+#include <time.h>
 
-/* Max length of scan results array. */
+/** Max length of scan results array. */
 #define IEEE80211_MAX_RESULTS_LENGTH 32
 
-/* Max SSID length including null character. */
+/** Max SSID length including null character. */
 #define IEEE80211_MAX_SSID_LENGTH 35
+
+/** WiFi security authentication method indicator. */
+typedef enum {
+	IEEE80211_SECURITY_OPEN,
+	IEEE80211_SECURITY_WEP,
+	IEEE80211_SECURITY_WPA,
+	IEEE80211_SECURITY_WPA2
+} ieee80211_security_type_t;
+
+/** WiFi security suite indicator. */
+typedef enum {
+	IEEE80211_SECURITY_SUITE_WEP40,
+	IEEE80211_SECURITY_SUITE_WEP104,
+	IEEE80211_SECURITY_SUITE_CCMP,
+	IEEE80211_SECURITY_SUITE_TKIP
+} ieee80211_security_suite_t;
+
+/** WiFi security authentication method indicator. */
+typedef enum {
+	IEEE80211_SECURITY_AUTH_PSK,
+	IEEE80211_SECURITY_AUTH_8021X
+} ieee80211_security_auth_t;
+
+/** Structure for indicating security network security settings. */
+typedef struct {
+	int type;
+	int group_alg;
+	int pair_alg;
+	int auth;
+} ieee80211_security_t;
 
 /** Structure with scan result info. */
@@ -52,7 +83,9 @@
 	nic_address_t bssid;
 	char ssid[IEEE80211_MAX_SSID_LENGTH];
+	uint8_t channel;
+	ieee80211_security_t security;
 } ieee80211_scan_result_t;
 
-/** List of scan results info. */
+/** Array of scan results info. */
 typedef struct {
 	uint8_t length;
Index: uspace/lib/crypto/Makefile
===================================================================
--- uspace/lib/crypto/Makefile	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
+++ uspace/lib/crypto/Makefile	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2015 Jan Kolarik
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBRARY = libcrypto
+
+SOURCES = \
+	crypto.c \
+	aes.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/crypto/aes.c
===================================================================
--- uspace/lib/crypto/aes.c	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
+++ uspace/lib/crypto/aes.c	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file aes.c
+ * 
+ * Implementation of AES algorithm.
+ * 
+ * Based on FIPS 197.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <mem.h>
+
+#include "crypto.h"
+
+/* Number of elements in rows/columns in AES arrays. */
+#define ELEMS 4
+
+/* Number of elements (words) in cipher. */
+#define CIPHER_ELEMS 4
+
+/* Length of AES block. */
+#define BLOCK_LEN 16
+
+/* Number of iterations in AES algorithm. */
+#define ROUNDS 10
+
+/* 
+ * Irreducible polynomial used in AES algorithm. 
+ * 
+ * NOTE: x^8 + x^4 + x^3 + x + 1.
+ */
+#define AES_IP 0x1B
+
+/* Precomputed values for AES sub_byte transformation. */
+static const uint8_t sub_byte_array[BLOCK_LEN][BLOCK_LEN] = {
+	{
+		0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 
+		0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+	},
+	{
+		0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 
+		0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+	},
+	{
+		0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+		0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+	},
+	{
+		0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 
+		0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+	},
+	{
+		0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 
+		0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+	},
+	{
+		0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 
+		0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+	},
+	{
+		0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 
+		0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+	},
+	{
+		0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 
+		0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+	},
+	{
+		0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 
+		0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+	},
+	{
+		0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 
+		0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+	},
+	{
+		0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 
+		0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+	},
+	{
+		0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 
+		0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+	},
+	{
+		0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 
+		0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+	},
+	{
+		0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 
+		0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+	},
+	{
+		0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 
+		0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+	},
+	{
+		0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 
+		0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+	}
+};
+
+/* Precomputed values for AES inv_sub_byte transformation. */
+static uint8_t inv_sub_byte_array[BLOCK_LEN][BLOCK_LEN] = {
+	{
+		0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 
+		0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+	},
+	{
+		0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 
+		0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+	},
+	{
+		0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 
+		0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+	},
+	{
+		0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 
+		0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+	},
+	{
+		0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 
+		0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+	},
+	{
+		0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 
+		0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+	},
+	{
+		0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 
+		0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+	},
+	{
+		0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 
+		0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+	},
+	{
+		0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 
+		0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+	},
+	{
+		0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 
+		0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+	},
+	{
+		0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 
+		0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+	},
+	{
+		0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 
+		0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+	},
+	{
+		0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 
+		0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+	},
+	{
+		0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 
+		0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+	},
+	{
+		0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 
+		0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+	},
+	{
+		0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 
+		0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+	}
+};
+
+/* Precomputed values of powers of 2 in GF(2^8) left shifted by 24b. */
+static const uint32_t r_con_array[] = {
+	0x01000000, 0x02000000,	0x04000000, 0x08000000, 
+	0x10000000, 0x20000000, 0x40000000, 0x80000000,
+	0x1B000000, 0x36000000
+};
+
+static uint8_t sub_byte(uint8_t byte, bool inv)
+{
+	uint8_t i = byte >> 4;
+	uint8_t j = byte & 0xF;
+	
+	if(!inv) {
+		return sub_byte_array[i][j];
+	} else {
+		return inv_sub_byte_array[i][j];
+	}
+}
+
+static void sub_bytes(uint8_t state[ELEMS][ELEMS], bool inv)
+{
+	uint8_t val;
+	
+	for(size_t i = 0; i < ELEMS; i++) {
+		for(size_t j = 0; j < ELEMS; j++) {
+			val = state[i][j];
+			state[i][j] = sub_byte(val, inv);
+		}
+	}
+}
+
+static void shift_rows(uint8_t state[ELEMS][ELEMS])
+{
+	uint8_t temp[ELEMS];
+	
+	for(size_t i = 1; i < ELEMS; i++) {
+		memcpy(temp, state[i], i);
+		memcpy(state[i], state[i] + i, ELEMS - i);
+		memcpy(state[i] + ELEMS - i, temp, i);
+	}
+}
+
+static void inv_shift_rows(uint8_t state[ELEMS][ELEMS])
+{
+	uint8_t temp[ELEMS];
+	
+	for(size_t i = 1; i < ELEMS; i++) {
+		memcpy(temp, state[i], ELEMS - i);
+		memcpy(state[i], state[i] + ELEMS - i, i);
+		memcpy(state[i] + i, temp, ELEMS - i);
+	}
+}
+
+static uint8_t galois_mult(uint8_t x, uint8_t y) {
+        uint8_t result = 0;
+        uint8_t F_bitH;
+	
+        for(size_t i = 0; i < 8; i++) {
+                if (y & 1) 
+			result ^= x;
+                F_bitH = (x & 0x80);
+                x <<= 1;
+                if (F_bitH) 
+                        x ^= AES_IP;
+                y >>= 1;
+        }
+	
+        return result;
+}
+
+static void mix_columns(uint8_t state[ELEMS][ELEMS])
+{
+	uint8_t orig_state[ELEMS][ELEMS];
+	memcpy(orig_state, state, BLOCK_LEN);
+	
+	for(size_t j = 0; j < ELEMS; j++) {
+		state[0][j] = 
+			galois_mult(0x2, orig_state[0][j]) ^
+			galois_mult(0x3, orig_state[1][j]) ^
+			orig_state[2][j] ^
+			orig_state[3][j];
+		state[1][j] = 
+			orig_state[0][j] ^
+			galois_mult(0x2, orig_state[1][j]) ^
+			galois_mult(0x3, orig_state[2][j]) ^
+			orig_state[3][j];
+		state[2][j] = 
+			orig_state[0][j] ^
+			orig_state[1][j] ^
+			galois_mult(0x2, orig_state[2][j]) ^
+			galois_mult(0x3, orig_state[3][j]);
+		state[3][j] = 
+			galois_mult(0x3, orig_state[0][j]) ^
+			orig_state[1][j] ^
+			orig_state[2][j] ^
+			galois_mult(0x2, orig_state[3][j]);
+	}
+}
+
+static void inv_mix_columns(uint8_t state[ELEMS][ELEMS])
+{
+	uint8_t orig_state[ELEMS][ELEMS];
+	memcpy(orig_state, state, BLOCK_LEN);
+	
+	for(size_t j = 0; j < ELEMS; j++) {
+		state[0][j] = 
+			galois_mult(0x0E, orig_state[0][j]) ^
+			galois_mult(0x0B, orig_state[1][j]) ^
+			galois_mult(0x0D, orig_state[2][j]) ^
+			galois_mult(0x09, orig_state[3][j]);
+		state[1][j] = 
+			galois_mult(0x09, orig_state[0][j]) ^
+			galois_mult(0x0E, orig_state[1][j]) ^
+			galois_mult(0x0B, orig_state[2][j]) ^
+			galois_mult(0x0D, orig_state[3][j]);
+		state[2][j] = 
+			galois_mult(0x0D, orig_state[0][j]) ^
+			galois_mult(0x09, orig_state[1][j]) ^
+			galois_mult(0x0E, orig_state[2][j]) ^
+			galois_mult(0x0B, orig_state[3][j]);
+		state[3][j] = 
+			galois_mult(0x0B, orig_state[0][j]) ^
+			galois_mult(0x0D, orig_state[1][j]) ^
+			galois_mult(0x09, orig_state[2][j]) ^
+			galois_mult(0x0E, orig_state[3][j]);
+	}
+}
+
+static void add_round_key(uint8_t state[ELEMS][ELEMS], uint32_t *round_key)
+{
+	uint8_t byte_round;
+	uint8_t shift;
+	uint32_t mask = 0xFF;
+	
+	for(size_t j = 0; j < ELEMS; j++) {
+		for(size_t i = 0; i < ELEMS; i++) {
+			shift = 24 - 8*i;
+			byte_round = (round_key[j] & (mask << shift)) >> shift;
+			state[i][j] = state[i][j] ^ byte_round;
+		}
+	}
+}
+
+static uint32_t sub_word(uint32_t word)
+{
+	uint32_t temp = word;
+	uint8_t *start = (uint8_t *) &temp;
+	
+	for(size_t i = 0; i < 4; i++) {
+		*(start+i) = sub_byte(*(start+i), false);
+	}
+	
+	return temp;
+}
+
+static uint32_t rot_word(uint32_t word)
+{
+	return (word << 8 | word >> 24);
+}
+
+static void key_expansion(uint8_t *key, uint32_t *key_exp)
+{
+	uint32_t temp;
+	
+	for(size_t i = 0; i < CIPHER_ELEMS; i++) {
+		key_exp[i] = 
+			(key[4*i] << 24) + 
+			(key[4*i+1] << 16) + 
+			(key[4*i+2] << 8) +
+			(key[4*i+3]);
+	}
+	
+	for(size_t i = CIPHER_ELEMS; i < ELEMS * (ROUNDS + 1); i++) {
+		temp = key_exp[i-1];
+		
+		if((i % CIPHER_ELEMS) == 0) {
+			temp = sub_word(rot_word(temp)) ^ 
+				r_con_array[i/CIPHER_ELEMS - 1];
+		}
+		
+		key_exp[i] = key_exp[i - CIPHER_ELEMS] ^ temp;
+	}
+}
+
+int aes_encrypt(uint8_t *key, uint8_t *input, uint8_t *output)
+{
+	if(!key || !input)
+		return EINVAL;
+	
+	if(!output)
+		return ENOMEM;
+	
+	/* Create key expansion. */
+	uint32_t key_exp[ELEMS * (ROUNDS+1)];
+	key_expansion(key, key_exp);
+	
+	/* Copy input into state array. */
+	uint8_t state[ELEMS][ELEMS];
+	for(size_t i = 0; i < ELEMS; i++) {
+		for(size_t j = 0; j < ELEMS; j++) {
+			state[i][j] = input[i + ELEMS*j];
+		}
+	}
+	
+	/* Processing loop. */
+	add_round_key(state, key_exp);
+	
+	for(size_t k = 1; k <= ROUNDS; k++) {
+		sub_bytes(state, false);
+		for(size_t i = 0; i < ELEMS; i++) {
+			for(size_t j = 0; j < ELEMS; j++) {
+				printf("STATE SUB %d %d : %x\n", i, j, state[i][j]);
+			}
+		}
+		shift_rows(state);
+		for(size_t i = 0; i < ELEMS; i++) {
+			for(size_t j = 0; j < ELEMS; j++) {
+				printf("STATE SHIFT %d %d : %x\n", i, j, state[i][j]);
+			}
+		}
+		if(k < ROUNDS)
+			mix_columns(state);
+		add_round_key(state, key_exp + k*ELEMS);
+	}
+	
+	/* Copy state array into output. */
+	for(size_t i = 0; i < ELEMS; i++) {
+		for(size_t j = 0; j < ELEMS; j++) {
+			output[i + j*ELEMS] = state[i][j];
+		}
+	}
+	
+	return EOK;
+}
+
+int aes_decrypt(uint8_t *key, uint8_t *input, uint8_t *output)
+{
+	if(!key || !input)
+		return EINVAL;
+	
+	if(!output)
+		return ENOMEM;
+	
+	/* Create key expansion. */
+	uint32_t key_exp[ELEMS * (ROUNDS+1)];
+	key_expansion(key, key_exp);
+	
+	/* Copy input into state array. */
+	uint8_t state[ELEMS][ELEMS];
+	for(size_t i = 0; i < ELEMS; i++) {
+		for(size_t j = 0; j < ELEMS; j++) {
+			state[i][j] = input[i + ELEMS*j];
+		}
+	}
+	
+	/* Processing loop. */
+	add_round_key(state, key_exp + ROUNDS*ELEMS);
+	
+	for(int k = ROUNDS - 1; k >= 0; k--) {
+		inv_shift_rows(state);
+		sub_bytes(state, true);
+		add_round_key(state, key_exp + k*ELEMS);
+		if(k > 0)
+			inv_mix_columns(state);
+	}
+	
+	/* Copy state array into output. */
+	for(size_t i = 0; i < ELEMS; i++) {
+		for(size_t j = 0; j < ELEMS; j++) {
+			output[i + j*ELEMS] = state[i][j];
+		}
+	}
+	
+	return EOK;
+}
Index: uspace/lib/crypto/crypto.c
===================================================================
--- uspace/lib/crypto/crypto.c	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
+++ uspace/lib/crypto/crypto.c	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file crypto.c
+ * 
+ * Cryptographic functions library.
+ */
+
+#include <unistd.h>
+#include <str.h>
+#include <macros.h>
+#include <errno.h>
+#include <byteorder.h>
+
+#include "crypto.h"
+
+typedef int (*HASH_FUNC)(uint8_t*, size_t, uint8_t*);
+
+#define ceil_uint32(val) (((val) - (uint32_t)(val)) > 0 ? \
+	(uint32_t)((val) + 1) : (uint32_t)(val))
+#define floor_uint32(val) (((val) - (uint32_t)(val)) < 0 ? \
+	(uint32_t)((val) - 1) : (uint32_t)(val))
+#define rotl_uint32(val, shift) (((val) << shift) | ((val) >> (32 - shift)))
+#define get_at(input, size, i) (i < size ? input[i] : 0)
+
+/**
+ * Setup hash function properties for use in crypto functions.
+ * 
+ * @param hash_sel Hash function selector.
+ * @param hash_func Output parameter where hash function pointer is stored.
+ * @param hash_length Output parameter for setup result hash length.
+ */
+static void config_hash_func(hash_func_t hash_sel, HASH_FUNC *hash_func,
+	size_t *hash_length)
+{
+	switch(hash_sel) {
+		case HASH_MD5:
+			if(hash_func) *hash_func = md5;
+			*hash_length = MD5_HASH_LENGTH;
+			break;
+		case HASH_SHA1:
+			if(hash_func) *hash_func = sha1;
+			*hash_length = SHA1_HASH_LENGTH;
+			break;
+	}
+}
+
+/**
+ * MD5 cryptographic hash function.
+ * 
+ * @param input Input sequence to be encrypted.
+ * @param input_size Size of input sequence.
+ * @param hash Output parameter for result hash (32 byte value).
+ * 
+ * @return EINVAL when input not specified, ENOMEM when pointer for output
+ * hash result is not allocated, otherwise EOK.
+ */
+int md5(uint8_t *input, size_t input_size, uint8_t *hash)
+{
+	if(!input)
+		return EINVAL;
+	
+	if(!hash)
+		return ENOMEM;
+	
+	// TODO
+	
+	return EOK;
+}
+
+/**
+ * SHA-1 cryptographic hash function.
+ * 
+ * @param input Input sequence to be encrypted.
+ * @param input_size Size of input sequence.
+ * @param hash Output parameter for result hash (20 byte value).
+ * 
+ * @return EINVAL when input not specified, ENOMEM when pointer for output
+ * hash result is not allocated, otherwise EOK.
+ */
+int sha1(uint8_t *input, size_t input_size, uint8_t *hash)
+{
+	if(!input)
+		return EINVAL;
+	
+	if(!hash)
+		return ENOMEM;
+	
+	uint32_t a, b, c, d, e, f, cf, temp;
+	uint32_t h[5] = {
+		0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
+	};
+	
+	uint8_t work_input[input_size + 1];
+	memcpy(work_input, input, input_size);
+	work_input[input_size] = 0x80;
+	
+	size_t blocks = ceil_uint32((((double)input_size + 1) / 4 + 2) / 16);
+	
+	uint32_t work_arr[blocks * 16 * sizeof(uint32_t)];
+	for(size_t i = 0; i < blocks; i++) {
+		for(size_t j = 0; j < 16; j++) {
+			work_arr[i*16 + j] = 
+			(get_at(work_input, input_size+1, i*64+j*4) << 24) |
+			(get_at(work_input, input_size+1, i*64+j*4+1) << 16) |
+			(get_at(work_input, input_size+1, i*64+j*4+2) << 8) |
+			get_at(work_input, input_size+1, i*64+j*4+3);
+		}
+	}
+	
+	work_arr[(blocks - 1) * 16 + 14] = (uint64_t)(input_size * 8) >> 32;
+	work_arr[(blocks - 1) * 16 + 15] = (input_size * 8) & 0xFFFFFFFF;
+
+	uint32_t sched_arr[80 * sizeof(uint32_t)];
+	for(size_t i = 0; i < blocks; i++) {
+		for(size_t k = 0; k < 16; k++) {
+			sched_arr[k] = work_arr[i*16 + k];
+		}
+		
+		for(size_t k = 16; k < 80; k++) {
+			sched_arr[k] = 
+				rotl_uint32(
+				sched_arr[k-3] ^
+				sched_arr[k-8] ^
+				sched_arr[k-14] ^
+				sched_arr[k-16], 
+				1);
+		}
+		
+		a = h[0]; b = h[1]; c = h[2]; d = h[3]; e = h[4];
+		
+		for(size_t k = 0; k < 80; k++) {
+			if(k < 20) {
+				f = (b & c) | (~b & d);
+				cf = 0x5A827999;
+			} else if(k >= 20 && k < 40) {
+				f = b ^ c ^ d;
+				cf = 0x6ED9EBA1;
+			} else if(k >= 40 && k < 60) {
+				f = (b & c) | (b & d) | (c & d);
+				cf = 0x8F1BBCDC;
+			} else {
+				f = b ^ c ^ d;
+				cf = 0xCA62C1D6;
+			}
+				
+			temp = (rotl_uint32(a, 5) + f + e + cf + sched_arr[k]) &
+				0xFFFFFFFF;
+			
+			e = d;
+			d = c;
+			c = rotl_uint32(b, 30);
+			b = a;
+			a = temp;
+		}
+		
+		h[0] = (h[0] + a) & 0xFFFFFFFF;
+		h[1] = (h[1] + b) & 0xFFFFFFFF;
+		h[2] = (h[2] + c) & 0xFFFFFFFF;
+		h[3] = (h[3] + d) & 0xFFFFFFFF;
+		h[4] = (h[4] + e) & 0xFFFFFFFF;
+	}
+	
+	for(size_t i = 0; i < 5; i++) {
+		h[i] = uint32_t_be2host(h[i]);
+		memcpy(hash + i*sizeof(uint32_t), &h[i], sizeof(uint32_t));
+	}
+	
+	return EOK;
+}
+
+/**
+ * Hash-based message authentication code using SHA-1 algorithm.
+ * 
+ * @param key Cryptographic key sequence.
+ * @param key_size Size of key sequence.
+ * @param msg Message sequence.
+ * @param msg_size Size of message sequence.
+ * @param hash Output parameter for result hash.
+ * @param hash_sel Hash function selector.
+ * 
+ * @return EINVAL when key or message not specified, ENOMEM when pointer for 
+ * output hash result is not allocated, otherwise EOK.
+ */
+int hmac(uint8_t *key, size_t key_size, uint8_t *msg, size_t msg_size, 
+	uint8_t *hash, hash_func_t hash_sel)
+{
+	if(!key || !msg)
+		return EINVAL;
+	
+	if(!hash)
+		return ENOMEM;
+	
+	size_t hash_length = 0;
+	HASH_FUNC hash_func = NULL;
+	config_hash_func(hash_sel, &hash_func, &hash_length);
+	
+	uint8_t work_key[HMAC_BLOCK_LENGTH];
+	uint8_t o_key_pad[HMAC_BLOCK_LENGTH];
+	uint8_t i_key_pad[HMAC_BLOCK_LENGTH];
+	uint8_t temp_hash[hash_length];
+	memset(work_key, 0, HMAC_BLOCK_LENGTH);
+	
+	if(key_size > HMAC_BLOCK_LENGTH) {
+		hash_func(key, key_size, work_key);
+	} else {
+		memcpy(work_key, key, key_size);
+	}
+	
+	for(size_t i = 0; i < HMAC_BLOCK_LENGTH; i++) {
+		o_key_pad[i] = work_key[i] ^ 0x5C;
+		i_key_pad[i] = work_key[i] ^ 0x36;
+	}
+	
+	uint8_t temp_work[HMAC_BLOCK_LENGTH + msg_size];
+	memcpy(temp_work, i_key_pad, HMAC_BLOCK_LENGTH);
+	memcpy(temp_work + HMAC_BLOCK_LENGTH, msg, msg_size);
+	
+	hash_func(temp_work, HMAC_BLOCK_LENGTH + msg_size, temp_hash);
+	
+	memcpy(temp_work, o_key_pad, HMAC_BLOCK_LENGTH);
+	memcpy(temp_work + HMAC_BLOCK_LENGTH, temp_hash, hash_length);
+	
+	hash_func(temp_work, HMAC_BLOCK_LENGTH + hash_length, hash);
+	
+	return EOK;
+}
+
+/**
+ * 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.
+ * 
+ * @param pass Password sequence.
+ * @param pass_size Password sequence length.
+ * @param salt Salt sequence to be used with password.
+ * @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 
+ * output hash result is not allocated, otherwise EOK.
+ */
+int pbkdf2(uint8_t *pass, size_t pass_size, uint8_t *salt, size_t salt_size, 
+	uint8_t *hash, hash_func_t hash_sel)
+{
+	if(!pass || !salt)
+		return EINVAL;
+	
+	if(!hash)
+		return ENOMEM;
+	
+	size_t hash_length = 0;
+	config_hash_func(hash_sel, NULL, &hash_length);
+
+	uint8_t work_salt[salt_size + sizeof(uint32_t)];
+	memcpy(work_salt, salt, salt_size);
+	uint8_t work_hmac[hash_length];
+	uint8_t temp_hmac[hash_length];
+	uint8_t xor_hmac[hash_length];
+	uint8_t temp_hash[hash_length*2];
+	
+	for(size_t i = 0; i < 2; i++) {
+		uint32_t big_i = host2uint32_t_be(i+1);
+		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_length);
+		for(size_t k = 1; k < 4096; k++) {
+			memcpy(temp_hmac, work_hmac, hash_length);
+			hmac(pass, pass_size, temp_hmac, hash_length, 
+				work_hmac, hash_sel);
+			for(size_t t = 0; t < hash_length; t++) {
+				xor_hmac[t] ^= work_hmac[t];
+			}
+		}
+		memcpy(temp_hash + i*hash_length, xor_hmac, hash_length);
+	}
+	
+	memcpy(hash, temp_hash, PBKDF2_KEY_LENGTH);
+	
+	return EOK;
+}
Index: uspace/lib/crypto/crypto.h
===================================================================
--- uspace/lib/crypto/crypto.h	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
+++ uspace/lib/crypto/crypto.h	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBCRYPTO_H
+#define LIBCRYPTO_H
+
+#include <sys/types.h>
+
+#define AES_CIPHER_LENGTH 16
+#define MD5_HASH_LENGTH 16
+#define SHA1_HASH_LENGTH 20
+#define HMAC_BLOCK_LENGTH 64
+#define PBKDF2_KEY_LENGTH 32
+
+/** Hash function selector. */
+typedef enum {
+	HASH_MD5,
+	HASH_SHA1
+} hash_func_t;
+
+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);
+extern int sha1(uint8_t *input, size_t input_size, uint8_t *hash);
+extern int md5(uint8_t *input, size_t input_size, uint8_t *hash);
+extern int hmac(uint8_t *key, size_t key_size, uint8_t *msg, size_t msg_size, 
+	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);
+
+#endif
Index: uspace/lib/drv/generic/remote_ieee80211.c
===================================================================
--- uspace/lib/drv/generic/remote_ieee80211.c	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/drv/generic/remote_ieee80211.c	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -47,5 +47,6 @@
 typedef enum {
 	IEEE80211_GET_SCAN_RESULTS,
-	IEEE80211_CONNECT
+	IEEE80211_CONNECT,
+	IEEE80211_DISCONNECT
 } ieee80211_funcs_t;
 
@@ -59,5 +60,5 @@
  */
 int ieee80211_get_scan_results(async_sess_t *dev_sess, 
-	ieee80211_scan_results_t *results)
+	ieee80211_scan_results_t *results, bool now)
 {
 	assert(results);
@@ -65,6 +66,6 @@
 	async_exch_t *exch = async_exchange_begin(dev_sess);
 	
-	aid_t aid = async_send_1(exch, DEV_IFACE_ID(IEEE80211_DEV_IFACE),
-	    IEEE80211_GET_SCAN_RESULTS, NULL);
+	aid_t aid = async_send_2(exch, DEV_IFACE_ID(IEEE80211_DEV_IFACE),
+	    IEEE80211_GET_SCAN_RESULTS, now, NULL);
 	int rc = async_data_read_start(exch, results, sizeof(ieee80211_scan_results_t));
 	async_exchange_end(exch);
@@ -73,8 +74,8 @@
 	async_wait_for(aid, &res);
 	
-	if (rc != EOK)
+	if(res != EOK)
+		return (int) res;
+	else
 		return rc;
-	
-	return (int) res;
 }
 
@@ -82,5 +83,5 @@
  *
  * @param[in] dev_sess Device session.
- * @param[in] ssid Network SSID.
+ * @param[in] ssid_start Network SSID prefix.
  * @param[in] password Network password (pass empty string if not needed).
  *
@@ -88,7 +89,7 @@
  * negative error code otherwise.
  */
-int ieee80211_connect(async_sess_t *dev_sess, char *ssid, char *password)
-{
-	assert(ssid);
+int ieee80211_connect(async_sess_t *dev_sess, char *ssid_start, char *password)
+{
+	assert(ssid_start);
 	
 	sysarg_t rc_orig;
@@ -99,5 +100,6 @@
 	    IEEE80211_CONNECT, NULL);
 	
-	sysarg_t rc = async_data_write_start(exch, ssid, str_size(ssid) + 1);
+	sysarg_t rc = async_data_write_start(exch, ssid_start, 
+		str_size(ssid_start) + 1);
 	if (rc != EOK) {
 		async_exchange_end(exch);
@@ -132,4 +134,21 @@
 }
 
+/** Disconnect device from network.
+ *
+ * @param[in] dev_sess Device session.
+ *
+ * @return EOK If the operation was successfully completed,
+ * negative error code otherwise.
+ */
+int ieee80211_disconnect(async_sess_t *dev_sess)
+{
+	async_exch_t *exch = async_exchange_begin(dev_sess);
+	int rc = async_req_1_0(exch, DEV_IFACE_ID(IEEE80211_DEV_IFACE),
+	    IEEE80211_DISCONNECT);
+	async_exchange_end(exch);
+	
+	return rc;
+}
+
 static void remote_ieee80211_get_scan_results(ddf_fun_t *fun, void *iface,
     ipc_callid_t callid, ipc_call_t *call)
@@ -141,5 +160,7 @@
 	memset(&scan_results, 0, sizeof(ieee80211_scan_results_t));
 	
-	int rc = ieee80211_iface->get_scan_results(fun, &scan_results);
+	bool now = IPC_GET_ARG2(*call);
+	
+	int rc = ieee80211_iface->get_scan_results(fun, &scan_results, now);
 	if (rc == EOK) {
 		ipc_callid_t data_callid;
@@ -170,5 +191,5 @@
 	assert(ieee80211_iface->connect);
 	
-	char ssid[MAX_STRING_SIZE];
+	char ssid_start[MAX_STRING_SIZE];
 	char password[MAX_STRING_SIZE];
 	
@@ -187,5 +208,5 @@
 	}
 	
-	int rc = async_data_write_finalize(data_callid, ssid, len);
+	int rc = async_data_write_finalize(data_callid, ssid_start, len);
 	if (rc != EOK) {
 		async_answer_0(data_callid, EINVAL);
@@ -213,6 +234,15 @@
 	}
 	
-	rc = ieee80211_iface->connect(fun, ssid, password);
-	
+	rc = ieee80211_iface->connect(fun, ssid_start, password);
+	
+	async_answer_0(callid, rc);
+}
+
+static void remote_ieee80211_disconnect(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	ieee80211_iface_t *ieee80211_iface = (ieee80211_iface_t *) iface;
+	assert(ieee80211_iface->disconnect);
+	int rc = ieee80211_iface->disconnect(fun);
 	async_answer_0(callid, rc);
 }
@@ -223,5 +253,6 @@
 static const remote_iface_func_ptr_t remote_ieee80211_iface_ops[] = {
 	[IEEE80211_GET_SCAN_RESULTS] = remote_ieee80211_get_scan_results,
-	[IEEE80211_CONNECT] = remote_ieee80211_connect
+	[IEEE80211_CONNECT] = remote_ieee80211_connect,
+	[IEEE80211_DISCONNECT] = remote_ieee80211_disconnect
 };
 
Index: uspace/lib/drv/include/ieee80211_iface.h
===================================================================
--- uspace/lib/drv/include/ieee80211_iface.h	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/drv/include/ieee80211_iface.h	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -39,6 +39,8 @@
 #include <async.h>
 
-extern int ieee80211_get_scan_results(async_sess_t *, ieee80211_scan_results_t *);
+extern int ieee80211_get_scan_results(async_sess_t *, 
+	ieee80211_scan_results_t *, bool);
 extern int ieee80211_connect(async_sess_t *, char *, char *);
+extern int ieee80211_disconnect(async_sess_t *);
 
 #endif
Index: uspace/lib/drv/include/ops/ieee80211.h
===================================================================
--- uspace/lib/drv/include/ops/ieee80211.h	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/drv/include/ops/ieee80211.h	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -47,11 +47,13 @@
 	 * @param fun IEEE 802.11 function.
 	 * @param results Structure where to put scan results.
+	 * @param now Whether to initiate scan immediately.
 	 * 
 	 * @return EOK if succeed, negative error code otherwise.
 	 */
-	int (*get_scan_results)(ddf_fun_t *, ieee80211_scan_results_t *);
+	int (*get_scan_results)(ddf_fun_t *, ieee80211_scan_results_t *, bool);
 	
 	/** 
 	 * Connect IEEE 802.11 device to specified network.
+	 *
 	 * @param fun IEEE 802.11 function.
 	 * @param ssid Network SSID.
@@ -61,4 +63,13 @@
 	 */
 	int (*connect)(ddf_fun_t *, char *, char *);
+	
+	/** 
+	 * Disconnect IEEE 802.11 device from network.
+	 *
+	 * @param fun IEEE 802.11 function.
+	 * 
+	 * @return EOK if succeed, negative error code otherwise.
+	 */
+	int (*disconnect)(ddf_fun_t *);
 } ieee80211_iface_t;
 
Index: uspace/lib/ieee80211/Makefile
===================================================================
--- uspace/lib/ieee80211/Makefile	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/ieee80211/Makefile	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -1,5 +1,4 @@
 #
-# Copyright (c) 2005 Martin Decky
-# Copyright (c) 2007 Jakub Jermar
+# Copyright (c) 2015 Jan Kolarik
 # All rights reserved.
 #
@@ -33,10 +32,12 @@
 LIBS = \
 	$(LIBDRV_PREFIX)/libdrv.a \
-	$(LIBNIC_PREFIX)/libnic.a 
+	$(LIBNIC_PREFIX)/libnic.a \
+	$(LIBCRYPTO_PREFIX)/libcrypto.a
 
 EXTRA_CFLAGS += \
 	-Iinclude \
 	-I$(LIBDRV_PREFIX)/include \
-	-I$(LIBNIC_PREFIX)/include
+	-I$(LIBNIC_PREFIX)/include \
+	-I$(LIBCRYPTO_PREFIX)
 
 SOURCES = \
Index: uspace/lib/ieee80211/include/ieee80211.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211.h	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/ieee80211/include/ieee80211.h	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -57,4 +57,15 @@
 #define IEEE80211_CHANNEL_GAP 5
 
+/* Max AMPDU factor. */
+#define IEEE80211_MAX_AMPDU_FACTOR 13
+
+/* Max passphrase length in WPA/WPA2 protocols. */
+#define IEEE80211_WPA_MAX_PASSWORD_LENGTH 64
+
+/** IEEE 802.11 b/g supported data rates in units of 500 kb/s. */
+static const uint8_t ieee80211bg_data_rates[] = {
+	2, 4, 11, 12, 18, 22, 24, 36, 48, 72, 96, 108
+};
+
 /** Device operating modes. */
 typedef enum {
@@ -65,4 +76,18 @@
 } ieee80211_operating_mode_t;
 
+/** Key flags. */
+typedef enum {
+	IEEE80211_KEY_FLAG_TYPE_PAIRWISE = 0x01,
+	IEEE80211_KEY_FLAG_TYPE_GROUP = 0x02
+} ieee80211_key_flags_t;
+
+/** Key config structure. */
+typedef struct {
+	uint8_t id;
+	uint8_t flags;
+	ieee80211_security_suite_t suite;
+	uint8_t data[32];
+} ieee80211_key_config_t;
+
 /** IEEE 802.11 callback functions. */
 typedef struct {
@@ -109,4 +134,25 @@
 	 */
 	int (*set_freq)(struct ieee80211_dev *, uint16_t);
+	
+	/**
+	 * Callback to inform device about BSSID change.
+	 * 
+	 * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+	 * 
+	 * @return EOK if succeed, negative error code otherwise.
+	 */
+	int (*bssid_change)(struct ieee80211_dev *);
+	
+	/**
+	 * Callback to setup encryption key in IEEE 802.11 device.
+	 * 
+	 * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+	 * @param key_conf Key config structure.
+	 * @param insert True to insert this key to device, false to remove it.
+	 * 
+	 * @return EOK if succeed, negative error code otherwise.
+	 */
+	int (*key_config)(struct ieee80211_dev *, 
+		ieee80211_key_config_t *key_conf, bool);
 } ieee80211_ops_t;
 
@@ -124,10 +170,21 @@
 	void *specific);
 extern ddf_dev_t *ieee80211_get_ddf_dev(ieee80211_dev_t* ieee80211_dev);
-extern ieee80211_operating_mode_t ieee80211_query_current_op_mode(ieee80211_dev_t *ieee80211_dev);
+extern ieee80211_operating_mode_t 
+	ieee80211_query_current_op_mode(ieee80211_dev_t *ieee80211_dev);
 extern uint16_t ieee80211_query_current_freq(ieee80211_dev_t *ieee80211_dev);
+extern void ieee80211_query_bssid(ieee80211_dev_t* ieee80211_dev, 
+	nic_address_t *bssid);
+extern bool ieee80211_is_connected(ieee80211_dev_t* ieee80211_dev);
 extern void ieee80211_report_current_op_mode(ieee80211_dev_t *ieee80211_dev,
 	ieee80211_operating_mode_t op_mode);
 extern void ieee80211_report_current_freq(ieee80211_dev_t *ieee80211_dev,
 	uint16_t freq);
+extern uint16_t ieee80211_get_aid(ieee80211_dev_t* ieee80211_dev);
+extern int ieee80211_get_security_suite(ieee80211_dev_t* ieee80211_dev);
+extern bool ieee80211_is_ready(ieee80211_dev_t* ieee80211_dev);
+extern void ieee80211_set_ready(ieee80211_dev_t* ieee80211_dev, bool ready);
+extern bool ieee80211_query_using_key(ieee80211_dev_t* ieee80211_dev);
+extern void ieee80211_setup_key_confirm(ieee80211_dev_t* ieee80211_dev, 
+	bool using_key);
 
 extern bool ieee80211_is_data_frame(uint16_t frame_ctrl);
@@ -135,4 +192,6 @@
 extern bool ieee80211_is_beacon_frame(uint16_t frame_ctrl);
 extern bool ieee80211_is_probe_response_frame(uint16_t frame_ctrl);
+extern bool ieee80211_is_auth_frame(uint16_t frame_ctrl);
+extern bool ieee80211_is_assoc_response_frame(uint16_t frame_ctrl);
 
 /* Worker functions. */
Index: uspace/lib/ieee80211/include/ieee80211_iface_impl.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211_iface_impl.h	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/ieee80211/include/ieee80211_iface_impl.h	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -45,6 +45,8 @@
 
 extern int ieee80211_get_scan_results_impl(ddf_fun_t *fun, 
-	ieee80211_scan_results_t *results);
-extern int ieee80211_connect_impl(ddf_fun_t *fun, char *ssid, char *password);
+	ieee80211_scan_results_t *results, bool now);
+extern int ieee80211_connect_impl(ddf_fun_t *fun, char *ssid_start, 
+	char *password);
+extern int ieee80211_disconnect_impl(ddf_fun_t *fun);
 
 #endif	/* LIB_IEEE80211_IFACE_IMPL_H */
Index: uspace/lib/ieee80211/include/ieee80211_impl.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211_impl.h	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/ieee80211/include/ieee80211_impl.h	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -43,8 +43,19 @@
 
 extern int ieee80211_start_impl(ieee80211_dev_t *ieee80211_dev);
-extern int ieee80211_tx_handler_impl(ieee80211_dev_t *ieee80211_dev, void *buffer, 
-	size_t buffer_size);
-extern int ieee80211_set_freq_impl(ieee80211_dev_t *ieee80211_dev, uint16_t freq);
+extern int ieee80211_tx_handler_impl(ieee80211_dev_t *ieee80211_dev, 
+	void *buffer, size_t buffer_size);
+extern int ieee80211_set_freq_impl(ieee80211_dev_t *ieee80211_dev, 
+	uint16_t freq);
+extern int ieee80211_bssid_change_impl(ieee80211_dev_t *ieee80211_dev);
+extern int ieee80211_key_config_impl(ieee80211_dev_t *ieee80211_dev,
+	ieee80211_key_config_t *key_conf, bool insert);
 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);
+extern int ieee80211_aes_key_unwrap(uint8_t *kek, uint8_t *data, 
+	size_t data_size, uint8_t *output);
+extern int rnd_sequence(uint8_t *sequence, size_t length);
+extern uint8_t *min_sequence(uint8_t *seq1, uint8_t *seq2, size_t size);
+extern uint8_t *max_sequence(uint8_t *seq1, uint8_t *seq2, size_t size);
 
 #endif	/* LIB_IEEE80211_IMPL_H */
Index: uspace/lib/ieee80211/include/ieee80211_private.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211_private.h	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/ieee80211/include/ieee80211_private.h	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -40,4 +40,5 @@
 #define LIBNET_IEEE80211_PRIVATE_H
 
+#include <fibril_synch.h>
 #include <byteorder.h>
 #include <ddf/driver.h>
@@ -46,4 +47,61 @@
 #include <ieee80211/ieee80211.h>
 #include "ieee80211.h"
+
+/* Timeout in us for waiting to authentication/association response. */
+#define AUTH_TIMEOUT 200000
+
+/* Timeout in us for waiting to finish 4-way handshake process. */
+#define HANDSHAKE_TIMEOUT 3000000
+
+/* Max period to rerun scan. */
+#define MAX_SCAN_SPAN_SEC 30
+
+/* Max time to keep scan result. */
+#define MAX_KEEP_SCAN_SPAN_SEC 120
+
+/* Security bit in capability info field. */
+#define CAP_SECURITY 0x10
+
+/* Protocol type used in EAPOL frames. */
+#define ETH_TYPE_PAE 0x888E
+
+/* WPA OUI used in vendor specific IE. */
+#define WPA_OUI 0x0050F201
+
+/* GTK OUI used in vendor specific IE. */
+#define GTK_OUI 0x000FAC01
+
+/* Max PTK key length. */
+#define MAX_PTK_LENGTH 64
+
+/* Max GTK key length. */
+#define MAX_GTK_LENGTH 64
+
+/* KEK offset inside PTK. */
+#define KEK_OFFSET 16
+
+/* TK offset inside PTK. */
+#define TK_OFFSET 32
+
+/* Length of CCMP header we need to reserve. */
+#define IEEE80211_CCMP_HEADER_LENGTH 8
+
+/* 
+ * Length of data to be encrypted by PRF function:
+ * NONCE + SNONCE (2 * 32) + DEST_MAC + SOURCE_MAC (2 * ETH_ADDR) 
+ */
+#define PRF_CRYPT_DATA_LENGTH 2*32 + 2*ETH_ADDR
+
+/** IEEE 802.11 PTK key length. */
+typedef enum {
+	IEEE80211_PTK_CCMP_LENGTH = 48,
+	IEEE80211_PTK_TKIP_LENGTH = 64
+} ieee80211_ptk_length_t;
+
+/** IEEE 802.11 GTK key length. */
+typedef enum {
+	IEEE80211_GTK_CCMP_LENGTH = 16,
+	IEEE80211_GTK_TKIP_LENGTH = 32
+} ieee80211_gtk_length_t;
 
 /** IEEE 802.11 frame types. */
@@ -55,5 +113,5 @@
 } ieee80211_frame_type_t;
 
-/** IEEE 802.11 frame subtypes. */
+/** IEEE 802.11 management frame subtypes. */
 typedef enum {
 	IEEE80211_MGMT_ASSOC_REQ_FRAME = 0x00,
@@ -64,8 +122,14 @@
 	IEEE80211_MGMT_PROBE_RESP_FRAME = 0x50,
 	IEEE80211_MGMT_BEACON_FRAME = 0x80,
-	IEEE80211_MGMT_DIASSOC_FRAME = 0xA0,
+	IEEE80211_MGMT_DISASSOC_FRAME = 0xA0,
 	IEEE80211_MGMT_AUTH_FRAME = 0xB0,
 	IEEE80211_MGMT_DEAUTH_FRAME = 0xC0,
-} ieee80211_frame_subtype_t;
+} ieee80211_frame_mgmt_subtype_t;
+
+/** IEEE 802.11 data frame subtypes. */
+typedef enum {
+	IEEE80211_DATA_DATA_FRAME = 0x0000,
+	IEEE80211_DATA_QOS_FRAME = 0x0080
+} ieee80211_frame_data_subtype_t;
 
 /** IEEE 802.11 frame control value masks. */
@@ -73,5 +137,38 @@
 	IEEE80211_FRAME_CTRL_FRAME_TYPE = 0x000C,
 	IEEE80211_FRAME_CTRL_FRAME_SUBTYPE = 0x00F0,
-} ieee80211_frame_control_mask_t;
+	IEEE80211_FRAME_CTRL_PROTECTED = 0x4000
+} ieee80211_frame_ctrl_mask_t;
+
+/** IEEE 802.11 frame control DS field values. */
+typedef enum {
+	IEEE80211_FRAME_CTRL_TODS = 0x0100,
+	IEEE80211_FRAME_CTRL_FROMDS = 0x0200
+} ieee80211_frame_ctrl_ds_t;
+
+/** IEEE 802.11 authentication cipher suites values. */
+typedef enum {
+	IEEE80211_AUTH_CIPHER_TKIP = 0x02,
+	IEEE80211_AUTH_CIPHER_CCMP = 0x04
+} ieee80211_auth_cipher_type_t;
+
+/** IEEE 802.11 AKM suites values. */
+typedef enum {
+	IEEE80211_AUTH_AKM_8021X = 0x01,
+	IEEE80211_AUTH_AKM_PSK = 0x02
+} ieee80211_auth_akm_type_t;
+
+typedef enum {
+	IEEE80211_EAPOL_START = 0x1,
+	IEEE80211_EAPOL_KEY = 0x3
+} ieee80211_eapol_frame_type_t;
+
+typedef enum {
+	IEEE80211_EAPOL_KEY_KEYINFO_KEYTYPE = 0x0008,
+	IEEE80211_EAPOL_KEY_KEYINFO_INSTALL = 0x0040,
+	IEEE80211_EAPOL_KEY_KEYINFO_ACK = 0x0080,
+	IEEE80211_EAPOL_KEY_KEYINFO_MIC = 0x0100,
+	IEEE80211_EAPOL_KEY_KEYINFO_SECURE = 0x0200,
+	IEEE80211_EAPOL_KEY_KEYINFO_ENCDATA = 0x1000
+} ieee80211_eapol_key_keyinfo_t;
 
 /** IEEE 802.11 information element types. */
@@ -80,6 +177,42 @@
 	IEEE80211_RATES_IE = 1,		/**< Supported data rates. */
 	IEEE80211_CHANNEL_IE = 3,	/**< Current channel number. */
-	IEEE80211_EXT_RATES_IE = 50	/**< Extended data rates. */
+	IEEE80211_CHALLENGE_IE = 16,	/**< Challenge text. */
+	IEEE80211_RSN_IE = 48,		/**< RSN. */
+	IEEE80211_EXT_RATES_IE = 50,	/**< Extended data rates. */
+	IEEE80211_VENDOR_IE = 221	/**< Vendor specific IE. */
 } ieee80211_ie_type_t;
+
+/** IEEE 802.11 authentication phases. */
+typedef enum {
+	IEEE80211_AUTH_DISCONNECTED,
+	IEEE80211_AUTH_AUTHENTICATED,
+	IEEE80211_AUTH_ASSOCIATED
+} ieee80211_auth_phase_t;
+
+/** Link with scan result info. */
+typedef struct {
+	link_t link;
+	time_t last_beacon;
+	ieee80211_scan_result_t scan_result;
+	uint8_t rsn_copy[256];
+	size_t rsn_copy_len;
+} ieee80211_scan_result_link_t;
+
+/** List of scan results info. */
+typedef struct {
+	list_t list;
+	time_t last_scan;
+	fibril_mutex_t scan_mutex;
+	size_t size;
+} ieee80211_scan_result_list_t;
+
+/** BSSID info. */
+typedef struct {
+	uint16_t aid;
+	char password[IEEE80211_WPA_MAX_PASSWORD_LENGTH];
+	uint8_t ptk[MAX_PTK_LENGTH];
+	uint8_t gtk[MAX_GTK_LENGTH];
+	ieee80211_scan_result_link_t *res_link;
+} ieee80211_bssid_info_t;
 
 /** IEEE 802.11 WiFi device structure. */
@@ -103,16 +236,45 @@
 	ieee80211_operating_mode_t current_op_mode;
 	
+	/** Info about BSSID we are connected to. */
+	ieee80211_bssid_info_t bssid_info;
+	
+	/** 
+	 * Flag indicating that data traffic is encrypted by HW key
+	 * that is set up in device. 
+	 */
+	bool using_hw_key;
+	
 	/** BSSIDs we listen to. */
 	nic_address_t bssid_mask;
 	
 	/** List of APs in neighborhood. */
-	ieee80211_scan_results_t ap_list;
-	
-	/* TODO: Probably to be removed later - nic.open function is now 
-	 * executed multiple times, have to find out reason and fix it. 
-	 */
+	ieee80211_scan_result_list_t ap_list;
+	
+	/** Current sequence number used in data frames. */
+	uint16_t sequence_number;
+	
+	/** Current authentication phase. */
+	ieee80211_auth_phase_t current_auth_phase;
+	
+	/** General purpose guard. */
+	fibril_mutex_t gen_mutex;
+	
+	/** General purpose condition variable. */
+	fibril_condvar_t gen_cond;
+	
+	/** Indicates whether device is fully initialized. */
+	bool ready;
+	
 	/** Indicates whether driver has already started. */
 	bool started;
 };
+
+/** IEEE 802.3 (ethernet) header. */
+typedef struct {
+	uint8_t dest_addr[ETH_ADDR];
+	uint8_t src_addr[ETH_ADDR];
+	uint16_t proto;			/**< Big Endian value! */
+} __attribute__((packed)) __attribute__ ((aligned(2))) 
+	eth_header_t;
 
 /** IEEE 802.11 management header structure. */
@@ -124,5 +286,6 @@
 	uint8_t bssid[ETH_ADDR];
 	uint16_t seq_ctrl;		/**< Little Endian value! */
-} __attribute__((packed)) __attribute__ ((aligned(2))) ieee80211_mgmt_header_t;
+} __attribute__((packed)) __attribute__ ((aligned(2))) 
+	ieee80211_mgmt_header_t;
 
 /** IEEE 802.11 data header structure. */
@@ -134,7 +297,6 @@
 	uint8_t address3[ETH_ADDR];
 	uint16_t seq_ctrl;		/**< Little Endian value! */
-	uint8_t address4[ETH_ADDR];
-	uint16_t qos_ctrl;		/**< Little Endian value! */
-} __attribute__((packed)) __attribute__ ((aligned(2))) ieee80211_data_header_t;
+} __attribute__((packed)) __attribute__ ((aligned(2))) 
+	ieee80211_data_header_t;
 
 /** IEEE 802.11 information element header. */
@@ -142,5 +304,6 @@
 	uint8_t element_id;
 	uint8_t length;
-} __attribute__((packed)) __attribute__ ((aligned(2))) ieee80211_ie_header_t;
+} __attribute__((packed)) __attribute__ ((aligned(2))) 
+	ieee80211_ie_header_t;
 
 /** IEEE 802.11 authentication frame body. */
@@ -149,15 +312,85 @@
 	uint16_t auth_trans_no;		/**< Little Endian value! */
 	uint16_t status;		/**< Little Endian value! */
-} __attribute__((packed)) __attribute__ ((aligned(2))) ieee80211_auth_body_t;
+} __attribute__((packed)) __attribute__ ((aligned(2))) 
+	ieee80211_auth_body_t;
+
+/** IEEE 802.11 deauthentication frame body. */
+typedef struct {
+	uint16_t reason;		/**< Little Endian value! */
+} __attribute__((packed)) __attribute__ ((aligned(2))) 
+	ieee80211_deauth_body_t;
+
+/** IEEE 802.11 association request frame body. */
+typedef struct {
+	uint16_t capability;		/**< Little Endian value! */
+	uint16_t listen_interval;	/**< Little Endian value! */
+} __attribute__((packed)) __attribute__ ((aligned(2))) 
+	ieee80211_assoc_req_body_t;
+
+/** IEEE 802.11 association response frame body. */
+typedef struct {
+	uint16_t capability;		/**< Little Endian value! */
+	uint16_t status;		/**< Little Endian value! */
+	uint16_t aid;			/**< Little Endian value! */
+} __attribute__((packed)) __attribute__ ((aligned(2))) 
+	ieee80211_assoc_resp_body_t;
 
 /** IEEE 802.11 beacon frame body start. */
 typedef struct {
 	uint8_t timestamp[8];
-	uint16_t beacon_interval;
-	uint16_t capability;
-} __attribute__((packed)) __attribute__ ((aligned(2))) ieee80211_beacon_start_t;
-
-extern int ieee80211_probe_request(ieee80211_dev_t *ieee80211_dev);
-extern int ieee80211_probe_auth(ieee80211_dev_t *ieee80211_dev);
+	uint16_t beacon_interval;	/**< Little Endian value! */
+	uint16_t capability;		/**< Little Endian value! */
+} __attribute__((packed)) __attribute__ ((aligned(2))) 
+	ieee80211_beacon_start_t;
+
+/** IEEE 802.11i EAPOL-Key frame format. */
+typedef struct {
+	uint8_t proto_version;
+	uint8_t packet_type;
+	uint16_t body_length;		/**< Big Endian value! */
+	uint8_t descriptor_type;
+	uint16_t key_info;		/**< Big Endian value! */
+	uint16_t key_length;		/**< Big Endian value! */
+	uint8_t key_replay_counter[8];
+	uint8_t key_nonce[32];
+	uint8_t eapol_key_iv[16];
+	uint8_t key_rsc[8];
+	uint8_t reserved[8];
+	uint8_t key_mic[16];
+	uint16_t key_data_length;	/**< Big Endian value! */
+} __attribute__((packed)) ieee80211_eapol_key_frame_t;
+
+#define ieee80211_scan_result_list_foreach(results, iter) \
+    list_foreach((results).list, link, ieee80211_scan_result_link_t, (iter))
+
+static inline void ieee80211_scan_result_list_init(
+	ieee80211_scan_result_list_t *results) 
+{
+	list_initialize(&results->list);
+	fibril_mutex_initialize(&results->scan_mutex);
+}
+
+static inline void ieee80211_scan_result_list_remove(
+	ieee80211_scan_result_list_t *results,
+	ieee80211_scan_result_link_t *result)
+{
+	list_remove(&result->link);
+	results->size--;
+}
+
+static inline void ieee80211_scan_result_list_append(
+	ieee80211_scan_result_list_t *results,
+	ieee80211_scan_result_link_t *result)
+{
+	list_append(&result->link, &results->list);
+	results->size++;
+}
+
+extern int ieee80211_probe_request(ieee80211_dev_t *ieee80211_dev, 
+	char *ssid);
+extern int ieee80211_authenticate(ieee80211_dev_t *ieee80211_dev);
+extern int ieee80211_associate(ieee80211_dev_t *ieee80211_dev, 
+	char *password);
+extern int ieee80211_deauthenticate(ieee80211_dev_t *ieee80211_dev);
 
 #endif /* LIBN_IEEE80211_H */
Index: uspace/lib/ieee80211/src/ieee80211.c
===================================================================
--- uspace/lib/ieee80211/src/ieee80211.c	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/ieee80211/src/ieee80211.c	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -36,4 +36,7 @@
  */
 
+#include <stdio.h>
+#include <crypto.h>
+#include <str.h>
 #include <macros.h>
 #include <errno.h>
@@ -45,4 +48,12 @@
 #include <ops/ieee80211.h>
 
+#define IEEE80211_DATA_RATES_SIZE 8
+#define IEEE80211_EXT_DATA_RATES_SIZE 4
+
+/** Frame encapsulation used in IEEE 802.11. */
+static const uint8_t rfc1042_header[] = { 
+	0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00 
+};
+
 /** Broadcast MAC address used to spread probe request through channel. */
 static const uint8_t ieee80211_broadcast_mac_addr[] = {
@@ -50,18 +61,11 @@
 };
 
-/** IEEE 802.11 b/g supported data rates in units of 500 kb/s. */
-static const uint8_t ieee80211bg_data_rates[] = {
-	2, 4, 11, 12, 18, 22, 24, 36
-};
-
-/** IEEE 802.11 b/g extended supported data rates in units of 500 kb/s.
- * 
- *  These are defined separately, because probe request message can
- *  only handle up to 8 data rates in supported rates IE. 
- */
-static const uint8_t ieee80211bg_ext_data_rates[] = {
-	48, 72, 96, 108
-};
-
+/**
+ * Check data frame.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if it is data frame, otherwise false.
+ */
 inline bool ieee80211_is_data_frame(uint16_t frame_ctrl)
 {
@@ -72,4 +76,11 @@
 }
 
+/**
+ * Check management frame.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if it is management frame, otherwise false.
+ */
 inline bool ieee80211_is_mgmt_frame(uint16_t frame_ctrl)
 {
@@ -80,4 +91,11 @@
 }
 
+/**
+ * Check management beacon frame.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if it is beacon frame, otherwise false.
+ */
 inline bool ieee80211_is_beacon_frame(uint16_t frame_ctrl)
 {
@@ -88,4 +106,11 @@
 }
 
+/**
+ * Check management probe response frame.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if it is probe resp frame, otherwise false.
+ */
 inline bool ieee80211_is_probe_response_frame(uint16_t frame_ctrl)
 {
@@ -97,4 +122,119 @@
 
 /**
+ * Check management authentication frame.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if it is auth frame, otherwise false.
+ */
+inline bool ieee80211_is_auth_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FRAME_SUBTYPE)
+		== IEEE80211_MGMT_AUTH_FRAME;
+}
+
+/**
+ * Check management association response frame.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if it is assoc resp frame, otherwise false.
+ */
+inline bool ieee80211_is_assoc_response_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FRAME_SUBTYPE)
+		== IEEE80211_MGMT_ASSOC_RESP_FRAME;
+}
+
+/**
+ * Check data frame "to distribution system" direction.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if it is TODS frame, otherwise false.
+ */
+static inline bool ieee80211_is_tods_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_TODS);
+}
+
+/**
+ * Check data frame "from distribution system" direction.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if it is FROMDS frame, otherwise false.
+ */
+static inline bool ieee80211_is_fromds_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FROMDS);
+}
+
+/**
+ * Check if it is data frame containing payload data.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if it has payload data, otherwise false.
+ */
+static inline bool ieee80211_has_data_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & (IEEE80211_FRAME_CTRL_FRAME_TYPE | 0x40))
+		== IEEE80211_DATA_FRAME;
+}
+
+/**
+ * Check if it is encrypted frame.
+ * 
+ * @param frame_ctrl Frame control field in little endian (!).
+ * 
+ * @return True if the frame is encrypted, otherwise false.
+ */
+static inline bool ieee80211_is_encrypted_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_PROTECTED) != 0;
+}
+
+/**
+ * Check if PAE packet is EAPOL-Key frame.
+ * 
+ * @param key_frame Pointer to start of EAPOL frame.
+ * 
+ * @return True if it is EAPOL-Key frame, otherwise false.
+ */
+static inline bool ieee80211_is_eapol_key_frame(ieee80211_eapol_key_frame_t 
+	*key_frame)
+{
+	return (key_frame->packet_type == IEEE80211_EAPOL_KEY);
+}
+
+
+/**
+ * Generate packet sequence number.
+ * 
+ * @param ieee80211_dev IEEE 802.11 device.
+ * 
+ * @return True if it has payload data, otherwise false.
+ */
+static uint16_t ieee80211_get_sequence_number(ieee80211_dev_t *ieee80211_dev)
+{
+	uint16_t ret_val = ieee80211_dev->sequence_number;
+	ieee80211_dev->sequence_number += (1 << 4);
+	return ret_val;
+}
+
+/**
  * Get driver-specific structure for IEEE 802.11 device.
  * 
@@ -139,5 +279,6 @@
  * @return Current IEEE 802.11 operating mode.
  */
-ieee80211_operating_mode_t ieee80211_query_current_op_mode(ieee80211_dev_t* ieee80211_dev)
+ieee80211_operating_mode_t ieee80211_query_current_op_mode(ieee80211_dev_t* 
+	ieee80211_dev)
 {
 	return ieee80211_dev->current_op_mode;
@@ -154,4 +295,60 @@
 {
 	return ieee80211_dev->current_freq;
+}
+
+/**
+ * Query BSSID the device is connected to.
+ * 
+ * @param ieee80211_dev IEEE 802.11 device.
+ * @param bssid Pointer to structure where should be stored BSSID.
+ */
+void ieee80211_query_bssid(ieee80211_dev_t* ieee80211_dev, 
+	nic_address_t *bssid)
+{
+	if(bssid) {
+		ieee80211_scan_result_t *auth_data =
+			&ieee80211_dev->bssid_info.res_link->scan_result;
+		
+		memcpy(bssid, (void *)&auth_data->bssid, sizeof(nic_address_t));
+	}
+}
+
+/**
+ * Get AID of network we are connected to.
+ * 
+ * @param ieee80211_dev IEEE 802.11 device.
+ * 
+ * @return AID.
+ */
+uint16_t ieee80211_get_aid(ieee80211_dev_t* ieee80211_dev)
+{
+	return ieee80211_dev->bssid_info.aid;
+}
+
+/**
+ * Get security suite used for HW encryption. 
+ * 
+ * @param ieee80211_dev IEEE 802.11 device.
+ * 
+ * @return Security suite indicator.
+ */
+int ieee80211_get_security_suite(ieee80211_dev_t* ieee80211_dev)
+{
+	ieee80211_scan_result_link_t *auth_link = 
+		ieee80211_dev->bssid_info.res_link;
+	
+	return auth_link->scan_result.security.pair_alg;
+}
+
+/**
+ * Check if IEEE 802.11 device is connected to network.
+ * 
+ * @param ieee80211_dev IEEE 802.11 device.
+ * 
+ * @return True if device is connected to network, otherwise false.
+ */
+bool ieee80211_is_connected(ieee80211_dev_t* ieee80211_dev)
+{
+	return ieee80211_dev->current_auth_phase == IEEE80211_AUTH_ASSOCIATED;
 }
 
@@ -178,4 +375,44 @@
 {
 	ieee80211_dev->current_freq = freq;
+}
+
+/**
+ * Check if IEEE 802.11 device is ready (fully initialized).
+ * 
+ * @param ieee80211_dev IEEE 802.11 device.
+ * 
+ * @return True if device is ready to work, otherwise false.
+ */
+bool ieee80211_is_ready(ieee80211_dev_t* ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	bool ready_state = ieee80211_dev->ready;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return ready_state;
+}
+
+/**
+ * Set IEEE 802.11 device to ready state.
+ * 
+ * @param ieee80211_dev IEEE 802.11 device.
+ * @param ready Ready state to be set.
+ */
+void ieee80211_set_ready(ieee80211_dev_t* ieee80211_dev, bool ready)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_dev->ready = ready;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+}
+
+extern bool ieee80211_query_using_key(ieee80211_dev_t* ieee80211_dev)
+{
+	return ieee80211_dev->using_hw_key;
+}
+
+void ieee80211_setup_key_confirm(ieee80211_dev_t* ieee80211_dev, 
+	bool using_key)
+{
+	ieee80211_dev->using_hw_key = using_key;
 }
 
@@ -209,6 +446,10 @@
 }
 
-/** 
+/**
  * Send frame handler.
+ * 
+ * @param nic Pointer to NIC device.
+ * @param data Data buffer.
+ * @param size Data buffer size.
  */
 static void ieee80211_send_frame(nic_t *nic, void *data, size_t size)
@@ -217,5 +458,76 @@
 		nic_get_specific(nic);
 	
-	ieee80211_dev->ops->tx_handler(ieee80211_dev, data, size);
+	if(!ieee80211_is_connected(ieee80211_dev)) {
+		return;
+	}
+	
+	ieee80211_scan_result_t *auth_data =
+			&ieee80211_dev->bssid_info.res_link->scan_result;
+	
+	/* We drop part of IEEE 802.3 ethernet header. */
+	size_t drop_bytes = sizeof(eth_header_t) - 2;
+	
+	size_t complete_size = (size - drop_bytes) + 
+		sizeof(ieee80211_data_header_t) +
+		ARRAY_SIZE(rfc1042_header);
+	
+	/* Init crypto data. */
+	size_t add_size = 0;
+	uint16_t crypto = 0;
+	uint8_t add_data[8];
+	memset(add_data, 0, 8);
+	
+	if(ieee80211_dev->using_hw_key) {
+		int sec_suite = auth_data->security.pair_alg;
+		switch(sec_suite) {
+			case IEEE80211_SECURITY_SUITE_CCMP:
+				add_size = IEEE80211_CCMP_HEADER_LENGTH;
+				add_data[3] = 0x20;
+				break;
+			default:
+				break;
+		}
+
+		crypto = uint16_t_le2host(IEEE80211_FRAME_CTRL_PROTECTED);
+	}
+	
+	complete_size += add_size;
+	
+	void *complete_buffer = malloc(complete_size);
+	memset(complete_buffer, 0, complete_size);
+	
+	if(add_size) {
+		memcpy(complete_buffer + sizeof(ieee80211_data_header_t),
+			add_data, add_size);
+	}
+	
+	memcpy(complete_buffer + sizeof(ieee80211_data_header_t) + add_size,
+		rfc1042_header,
+		ARRAY_SIZE(rfc1042_header));
+	
+	memcpy(complete_buffer + 
+		sizeof(ieee80211_data_header_t) +
+		ARRAY_SIZE(rfc1042_header) + add_size, 
+		data + drop_bytes, size - drop_bytes);
+	
+	ieee80211_data_header_t *data_header =
+		(ieee80211_data_header_t *) complete_buffer;
+	data_header->frame_ctrl = 
+		uint16_t_le2host(IEEE80211_DATA_FRAME) |
+		uint16_t_le2host(IEEE80211_DATA_DATA_FRAME) |
+		uint16_t_le2host(IEEE80211_FRAME_CTRL_TODS) |
+		crypto;
+	data_header->seq_ctrl = ieee80211_get_sequence_number(ieee80211_dev);
+	
+	/* BSSID, SA, DA. */
+	memcpy(data_header->address1, auth_data->bssid.address,	ETH_ADDR);
+	memcpy(data_header->address2, data + ETH_ADDR, ETH_ADDR);
+	memcpy(data_header->address3, data, ETH_ADDR);
+	
+	ieee80211_dev->ops->tx_handler(ieee80211_dev, 
+		complete_buffer, 
+		complete_size);
+	
+	free(complete_buffer);
 }
 
@@ -246,4 +558,11 @@
 			ieee80211_ops->set_freq = ieee80211_set_freq_impl;
 		
+		if(!ieee80211_ops->bssid_change)
+			ieee80211_ops->bssid_change = 
+				ieee80211_bssid_change_impl;
+		
+		if(!ieee80211_ops->key_config)
+			ieee80211_ops->key_config = ieee80211_key_config_impl;
+		
 		if(!ieee80211_ops->scan)
 			ieee80211_ops->scan = ieee80211_scan_impl;
@@ -257,11 +576,16 @@
 		if(nic_dev_ops)
 			if (!nic_dev_ops->interfaces[IEEE80211_DEV_IFACE])
-				nic_dev_ops->interfaces[IEEE80211_DEV_IFACE] = ieee80211_iface;
+				nic_dev_ops->interfaces[IEEE80211_DEV_IFACE] = 
+					ieee80211_iface;
 		
 		if(!ieee80211_iface->get_scan_results)
-			ieee80211_iface->get_scan_results = ieee80211_get_scan_results_impl;
+			ieee80211_iface->get_scan_results = 
+				ieee80211_get_scan_results_impl;
 		
 		if(!ieee80211_iface->connect)
 			ieee80211_iface->connect = ieee80211_connect_impl;
+		
+		if(!ieee80211_iface->disconnect)
+			ieee80211_iface->disconnect = ieee80211_disconnect_impl;
 	} else {
 		return EINVAL;
@@ -304,7 +628,15 @@
 	ieee80211_dev->ddf_dev = ddf_dev;
 	ieee80211_dev->started = false;
+	ieee80211_dev->ready = false;
+	ieee80211_dev->using_hw_key = false;
 	ieee80211_dev->current_op_mode = IEEE80211_OPMODE_STATION;
+	ieee80211_dev->current_auth_phase = IEEE80211_AUTH_DISCONNECTED;
 	memcpy(ieee80211_dev->bssid_mask.address, ieee80211_broadcast_mac_addr, 
 		ETH_ADDR);
+	
+	ieee80211_scan_result_list_init(&ieee80211_dev->ap_list);
+	
+	fibril_mutex_initialize(&ieee80211_dev->gen_mutex);
+	fibril_condvar_initialize(&ieee80211_dev->gen_cond);
 	
 	/* Bind NIC to device */
@@ -373,4 +705,11 @@
 }
 
+/**
+ * Convert frequency value to channel number.
+ * 
+ * @param freq IEEE 802.11 operating frequency.
+ * 
+ * @return Operating channel number.
+ */
 static uint8_t ieee80211_freq_to_channel(uint16_t freq)
 {
@@ -378,12 +717,28 @@
 }
 
+static void ieee80211_prepare_ie_header(void **ie_header,
+	uint8_t id, uint8_t length, void *data)
+{
+	ieee80211_ie_header_t *header =
+		(ieee80211_ie_header_t *) *ie_header;
+	
+	header->element_id = id;
+	header->length = length;
+	
+	memcpy(*ie_header + sizeof(ieee80211_ie_header_t), data, length);
+	
+	*ie_header = (void *) ((void *) header + 
+		sizeof(ieee80211_ie_header_t) + length);
+}
+
 /**
  * Probe request implementation.
  * 
  * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param ssid Probing SSID or NULL if broadcast.
  * 
  * @return EOK if succeed, negative error code otherwise.
  */
-int ieee80211_probe_request(ieee80211_dev_t *ieee80211_dev)
+int ieee80211_probe_request(ieee80211_dev_t *ieee80211_dev, char *ssid)
 {
 	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
@@ -391,13 +746,18 @@
 	nic_query_address(nic, &nic_address);
 	
-	size_t data_rates_size = ARRAY_SIZE(ieee80211bg_data_rates);
-	size_t ext_data_rates_size = ARRAY_SIZE(ieee80211bg_ext_data_rates);
-	
-	/* 3 headers - (rates, ext rates, current channel) and their data
-	 * lengths + pad. 
+	size_t ssid_data_size = (ssid != NULL) ? str_size(ssid) : 0;
+	size_t channel_data_size = 1;
+	
+	uint8_t channel = 
+		ieee80211_freq_to_channel(ieee80211_dev->current_freq);
+	
+	/* 4 headers - (ssid, rates, ext rates, current channel) and their data
+	 * lengths. 
 	 */
 	size_t payload_size = 
-		sizeof(ieee80211_ie_header_t) * 3 +
-		data_rates_size + ext_data_rates_size + sizeof(uint8_t) + 2;
+		sizeof(ieee80211_ie_header_t) * 4 +
+		ssid_data_size + 
+		IEEE80211_DATA_RATES_SIZE + IEEE80211_EXT_DATA_RATES_SIZE + 
+		channel_data_size;
 	
 	size_t buffer_size = sizeof(ieee80211_mgmt_header_t) + payload_size;
@@ -415,31 +775,19 @@
 	memcpy(mgmt_header->src_addr, nic_address.address, ETH_ADDR);
 	memcpy(mgmt_header->bssid, ieee80211_broadcast_mac_addr, ETH_ADDR);
-	
-	/* Jump to payload -> header + padding. */
-	ieee80211_ie_header_t *rates_ie_header = (ieee80211_ie_header_t *) 
-		((void *)buffer + sizeof(ieee80211_mgmt_header_t) + 2);
-	rates_ie_header->element_id = IEEE80211_RATES_IE;
-	rates_ie_header->length = data_rates_size;
-	memcpy(rates_ie_header + sizeof(ieee80211_ie_header_t), 
-		ieee80211bg_data_rates, 
-		data_rates_size);
-	
-	ieee80211_ie_header_t *ext_rates_ie_header = (ieee80211_ie_header_t *) 
-		((void *)rates_ie_header + sizeof(ieee80211_ie_header_t) + 
-		data_rates_size);
-	ext_rates_ie_header->element_id = IEEE80211_EXT_RATES_IE;
-	ext_rates_ie_header->length = ext_data_rates_size;
-	memcpy(ext_rates_ie_header + sizeof(ieee80211_ie_header_t), 
-		ieee80211bg_ext_data_rates, 
-		ext_data_rates_size);
-	
-	ieee80211_ie_header_t *chan_ie_header = (ieee80211_ie_header_t *) 
-		((void *)ext_rates_ie_header + sizeof(ieee80211_ie_header_t) + 
-		ext_data_rates_size);
-	chan_ie_header->element_id = IEEE80211_CHANNEL_IE;
-	chan_ie_header->length = 1;
-	uint8_t *it = (uint8_t *) ((void *)chan_ie_header + 
-		sizeof(ieee80211_ie_header_t));
-	*it = ieee80211_freq_to_channel(ieee80211_dev->current_freq);
+	mgmt_header->seq_ctrl = 
+		host2uint16_t_le(ieee80211_get_sequence_number(ieee80211_dev));
+	
+	/* Jump to payload. */
+	void *it = (void *) buffer + sizeof(ieee80211_mgmt_header_t);
+	ieee80211_prepare_ie_header(&it, IEEE80211_SSID_IE, ssid_data_size, 
+		(void *) ssid);
+	ieee80211_prepare_ie_header(&it, IEEE80211_RATES_IE, 
+		IEEE80211_DATA_RATES_SIZE, 
+		(void *) &ieee80211bg_data_rates);
+	ieee80211_prepare_ie_header(&it, IEEE80211_EXT_RATES_IE, 
+		IEEE80211_EXT_DATA_RATES_SIZE, 
+		(void *) &ieee80211bg_data_rates[IEEE80211_DATA_RATES_SIZE]);
+	ieee80211_prepare_ie_header(&it, IEEE80211_CHANNEL_IE, 
+		channel_data_size, (void *) &channel);
 	
 	ieee80211_dev->ops->tx_handler(ieee80211_dev, buffer, buffer_size);
@@ -451,5 +799,5 @@
 
 /**
- * Probe authentication implementation.
+ * IEEE 802.11 authentication implementation.
  * 
  * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
@@ -457,14 +805,16 @@
  * @return EOK if succeed, negative error code otherwise.
  */
-int ieee80211_probe_auth(ieee80211_dev_t *ieee80211_dev)
-{
-	uint8_t test_bssid[] = {0x14, 0xF6, 0x5A, 0xAF, 0x5E, 0xB7};
-	
+int ieee80211_authenticate(ieee80211_dev_t *ieee80211_dev)
+{
 	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
 	nic_address_t nic_address;
 	nic_query_address(nic, &nic_address);
 	
+	ieee80211_scan_result_t *auth_data =
+		&ieee80211_dev->bssid_info.res_link->scan_result;
+	
 	size_t buffer_size = sizeof(ieee80211_mgmt_header_t) + 
 		sizeof(ieee80211_auth_body_t);
+	
 	void *buffer = malloc(buffer_size);
 	memset(buffer, 0, buffer_size);
@@ -477,7 +827,7 @@
 		IEEE80211_MGMT_AUTH_FRAME
 		);
-	memcpy(mgmt_header->dest_addr, test_bssid, ETH_ADDR);
+	memcpy(mgmt_header->dest_addr, auth_data->bssid.address, ETH_ADDR);
 	memcpy(mgmt_header->src_addr, nic_address.address, ETH_ADDR);
-	memcpy(mgmt_header->bssid, test_bssid, ETH_ADDR);
+	memcpy(mgmt_header->bssid, auth_data->bssid.address, ETH_ADDR);
 	
 	ieee80211_auth_body_t *auth_body =
@@ -485,5 +835,5 @@
 		(buffer + sizeof(ieee80211_mgmt_header_t));
 	auth_body->auth_alg = host2uint16_t_le(0);
-	auth_body->auth_trans_no = host2uint16_t_le(0);
+	auth_body->auth_trans_no = host2uint16_t_le(1);
 	
 	ieee80211_dev->ops->tx_handler(ieee80211_dev, buffer, buffer_size);
@@ -494,33 +844,683 @@
 }
 
+/** 
+ * IEEE 802.11 association implementation.
+ * 
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param password Passphrase to be used in encrypted communication or NULL
+ * for open networks.
+ * 
+ * @return EOK if succeed, negative error code otherwise.
+ */
+int ieee80211_associate(ieee80211_dev_t *ieee80211_dev, char *password) 
+{
+	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+	nic_address_t nic_address;
+	nic_query_address(nic, &nic_address);
+	
+	ieee80211_scan_result_link_t *auth_link =
+		ieee80211_dev->bssid_info.res_link;
+	
+	ieee80211_scan_result_t *auth_data = &auth_link->scan_result;
+	
+	size_t ssid_data_size = str_size(auth_data->ssid);
+	
+	size_t payload_size = 
+		sizeof(ieee80211_ie_header_t) * 3 +
+		ssid_data_size + 
+		IEEE80211_DATA_RATES_SIZE + 
+		IEEE80211_EXT_DATA_RATES_SIZE;
+	
+	size_t buffer_size = 
+		sizeof(ieee80211_mgmt_header_t) + 
+		sizeof(ieee80211_assoc_req_body_t) +
+		payload_size;
+	
+	if(auth_data->security.type == IEEE80211_SECURITY_WPA2) {
+		buffer_size += auth_link->rsn_copy_len;
+	}
+	
+	void *buffer = malloc(buffer_size);
+	memset(buffer, 0, buffer_size);
+	
+	ieee80211_mgmt_header_t *mgmt_header = 
+		(ieee80211_mgmt_header_t *) buffer;
+	
+	mgmt_header->frame_ctrl = host2uint16_t_le(
+		IEEE80211_MGMT_FRAME | 
+		IEEE80211_MGMT_ASSOC_REQ_FRAME
+		);
+	memcpy(mgmt_header->dest_addr, auth_data->bssid.address, ETH_ADDR);
+	memcpy(mgmt_header->src_addr, nic_address.address, ETH_ADDR);
+	memcpy(mgmt_header->bssid, auth_data->bssid.address, ETH_ADDR);
+	
+	ieee80211_assoc_req_body_t *assoc_body =
+		(ieee80211_assoc_req_body_t *) 
+		(buffer + sizeof(ieee80211_mgmt_header_t));
+	assoc_body->listen_interval = host2uint16_t_le(1);
+	
+	/* Jump to payload. */
+	void *it = buffer + sizeof(ieee80211_mgmt_header_t) +
+		sizeof(ieee80211_assoc_req_body_t);
+	ieee80211_prepare_ie_header(&it, IEEE80211_SSID_IE, 
+		ssid_data_size, (void *) auth_data->ssid);
+	ieee80211_prepare_ie_header(&it, IEEE80211_RATES_IE, 
+		IEEE80211_DATA_RATES_SIZE, 
+		(void *) &ieee80211bg_data_rates);
+	ieee80211_prepare_ie_header(&it, IEEE80211_EXT_RATES_IE, 
+		IEEE80211_EXT_DATA_RATES_SIZE, 
+		(void *) &ieee80211bg_data_rates[IEEE80211_DATA_RATES_SIZE]);
+	
+	if(auth_data->security.type != IEEE80211_SECURITY_OPEN) {
+		assoc_body->capability |= host2uint16_t_le(CAP_SECURITY);
+	}
+	
+	if(auth_data->security.type == IEEE80211_SECURITY_WPA2) {
+		memcpy(it, auth_link->rsn_copy,	auth_link->rsn_copy_len);
+	}
+	
+	ieee80211_dev->ops->tx_handler(ieee80211_dev, buffer, buffer_size);
+	
+	/* 
+	 * Save password and SSID to be used in eventual authentication 
+	 * handshake. 
+	 */
+	memcpy(ieee80211_dev->bssid_info.password, password, 
+		str_size(password));
+	
+	free(buffer);
+	
+	return EOK;
+}
+
+/** 
+ * IEEE 802.11 deauthentication implementation.
+ * 
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * 
+ * @return EOK if succeed, negative error code otherwise.
+ */
+int ieee80211_deauthenticate(ieee80211_dev_t *ieee80211_dev)
+{
+	ieee80211_scan_result_t *auth_data =
+		&ieee80211_dev->bssid_info.res_link->scan_result;
+	
+	ieee80211_dev->current_auth_phase = IEEE80211_AUTH_DISCONNECTED;
+	ieee80211_dev->bssid_info.aid = (uint16_t) -1;
+	memcpy(auth_data->bssid.address, ieee80211_broadcast_mac_addr, 
+		ETH_ADDR);
+	
+	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+	nic_address_t nic_address;
+	nic_query_address(nic, &nic_address);
+	
+	size_t buffer_size = sizeof(ieee80211_mgmt_header_t) + 
+		sizeof(ieee80211_deauth_body_t);
+	void *buffer = malloc(buffer_size);
+	memset(buffer, 0, buffer_size);
+	
+	ieee80211_mgmt_header_t *mgmt_header = 
+		(ieee80211_mgmt_header_t *) buffer;
+	
+	mgmt_header->frame_ctrl = host2uint16_t_le(
+		IEEE80211_MGMT_FRAME | 
+		IEEE80211_MGMT_DEAUTH_FRAME
+		);
+	memcpy(mgmt_header->dest_addr, auth_data->bssid.address, ETH_ADDR);
+	memcpy(mgmt_header->src_addr, nic_address.address, ETH_ADDR);
+	memcpy(mgmt_header->bssid, auth_data->bssid.address, ETH_ADDR);
+	
+	ieee80211_dev->ops->tx_handler(ieee80211_dev, buffer, buffer_size);
+	
+	free(buffer);
+	
+	ieee80211_dev->ops->bssid_change(ieee80211_dev);
+	
+	return EOK;
+}
+
+static void ieee80211_process_auth_info(ieee80211_scan_result_link_t *ap_data,
+	void *buffer)
+{
+	uint8_t *it = (uint8_t *) buffer;
+	
+	uint16_t *version = (uint16_t *) it;
+	if(uint16_t_le2host(*version) != 0x1) {
+		ap_data->scan_result.security.type = -1;
+		return;
+	}
+	
+	it += sizeof(uint16_t);
+	
+	uint32_t group_cipher = *(it+3);
+	switch(group_cipher) {
+		case IEEE80211_AUTH_CIPHER_TKIP:
+			ap_data->scan_result.security.group_alg =
+				IEEE80211_SECURITY_SUITE_TKIP;
+			break;
+		case IEEE80211_AUTH_CIPHER_CCMP:
+			ap_data->scan_result.security.group_alg =
+				IEEE80211_SECURITY_SUITE_CCMP;
+			break;
+		default:
+			ap_data->scan_result.security.group_alg = -1;
+	}
+	
+	it += 4*sizeof(uint8_t);
+	
+	uint16_t *pairwise_count = (uint16_t *) it;
+	uint32_t pairwise_cipher = *(it+sizeof(uint16_t)+3);
+	switch(pairwise_cipher) {
+		case IEEE80211_AUTH_CIPHER_TKIP:
+			ap_data->scan_result.security.pair_alg =
+				IEEE80211_SECURITY_SUITE_TKIP;
+			break;
+		case IEEE80211_AUTH_CIPHER_CCMP:
+			ap_data->scan_result.security.pair_alg =
+				IEEE80211_SECURITY_SUITE_CCMP;
+			break;
+		default:
+			ap_data->scan_result.security.pair_alg = -1;
+	}
+	
+	it += 2*sizeof(uint16_t) + 
+		uint16_t_le2host(*pairwise_count)*sizeof(uint32_t);
+	
+	uint32_t auth_suite = *(it+3);
+	switch(auth_suite) {
+		case IEEE80211_AUTH_AKM_PSK:
+			ap_data->scan_result.security.auth =
+				IEEE80211_SECURITY_AUTH_PSK;
+			break;
+		case IEEE80211_AUTH_AKM_8021X:
+			ap_data->scan_result.security.auth =
+				IEEE80211_SECURITY_AUTH_8021X;
+			break;
+		default:
+			ap_data->scan_result.security.auth = -1;
+	}
+}
+
+static uint32_t uint32_from_uint8_seq(uint8_t *seq)
+{
+	return (*seq << 24) + (*(seq+1) << 16) + (*(seq+2) << 8) + *(seq+3); 
+}
+
+static uint8_t *ieee80211_process_ies(ieee80211_dev_t *ieee80211_dev,
+	ieee80211_scan_result_link_t *ap_data, void *buffer, size_t buffer_size)
+{
+	void *it = buffer;
+	while((it + sizeof(ieee80211_ie_header_t)) < buffer + buffer_size) {
+		ieee80211_ie_header_t *ie_header = 
+			(ieee80211_ie_header_t *) it;
+		uint8_t *channel;
+		switch(ie_header->element_id) {
+			case IEEE80211_CHANNEL_IE:
+				channel = (uint8_t *) 
+					(it + sizeof(ieee80211_ie_header_t));
+				ap_data->scan_result.channel = *channel;
+				break;
+			case IEEE80211_RSN_IE:
+				if(!ap_data)
+					break;
+				ap_data->scan_result.security.type =
+					IEEE80211_SECURITY_WPA2;
+				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);
+				break;
+			case IEEE80211_VENDOR_IE:
+				if(uint32_from_uint8_seq(it + 
+					sizeof(ieee80211_ie_header_t)) ==
+					WPA_OUI) {
+					if(ap_data->scan_result.security.type ==
+						IEEE80211_SECURITY_WPA2) {
+						break;
+					}
+					ap_data->scan_result.security.type =
+						IEEE80211_SECURITY_WPA;
+					ieee80211_process_auth_info(ap_data,
+						it + 
+						sizeof(ieee80211_ie_header_t) +
+						sizeof(uint32_t));
+				} else if(uint32_from_uint8_seq(it + 
+					sizeof(ieee80211_ie_header_t)) ==
+					GTK_OUI) {
+					return it + 
+						sizeof(ieee80211_ie_header_t) +
+						sizeof(uint32_t) + 2;
+				}
+		}
+		it += sizeof(ieee80211_ie_header_t) + ie_header->length;
+	}
+	
+	return NULL;
+}
+
 /**
  * Process probe response and store results.
  * 
  * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param mgmt_header Pointer to start of management frame header.
  * 
  * @return EOK if succeed, negative error code otherwise.
  */
 static int ieee80211_process_probe_response(ieee80211_dev_t *ieee80211_dev, 
+	ieee80211_mgmt_header_t *mgmt_header, size_t buffer_size)
+{
+	ieee80211_beacon_start_t *beacon_body = (ieee80211_beacon_start_t *) 
+		((void *)mgmt_header + sizeof(ieee80211_mgmt_header_t));
+	
+	ieee80211_ie_header_t *ssid_ie_header = (ieee80211_ie_header_t *) 
+		((void *)beacon_body + sizeof(ieee80211_beacon_start_t));
+	
+	/* Not empty SSID. */
+	if(ssid_ie_header->length > 0) {
+		fibril_mutex_t *scan_mutex = &ieee80211_dev->ap_list.scan_mutex;
+		
+		fibril_mutex_lock(scan_mutex);
+		
+		ieee80211_scan_result_list_t *result_list =
+			&ieee80211_dev->ap_list;
+		
+		uint8_t *ssid_start = (uint8_t *) ((void *)ssid_ie_header + 
+			sizeof(ieee80211_ie_header_t));
+		char ssid[IEEE80211_MAX_SSID_LENGTH];
+		memcpy(ssid, ssid_start, ssid_ie_header->length);
+		ssid[ssid_ie_header->length] = '\0';
+		
+		/* Check whether SSID is already in results. */
+		ieee80211_scan_result_list_foreach(*result_list, result) {
+			if(!str_cmp(ssid, result->scan_result.ssid)) {
+				result->last_beacon = time(NULL);
+				fibril_mutex_unlock(scan_mutex);
+				return EOK;
+			}
+		}
+		
+		/* Results are full. */
+		if(result_list->size == IEEE80211_MAX_RESULTS_LENGTH - 1) {
+			fibril_mutex_unlock(scan_mutex);
+			return EOK;
+		}
+		
+		ieee80211_scan_result_link_t *ap_data = 
+			malloc(sizeof(ieee80211_scan_result_link_t));
+		memset(ap_data, 0, sizeof(ieee80211_scan_result_link_t));
+		link_initialize(&ap_data->link);
+		
+		memcpy(ap_data->scan_result.bssid.address, 
+			mgmt_header->bssid, ETH_ADDR);
+		memcpy(ap_data->scan_result.ssid, ssid, 
+			ssid_ie_header->length + 1);
+		
+		if(uint16_t_le2host(beacon_body->capability) & CAP_SECURITY) {
+			ap_data->scan_result.security.type = 
+				IEEE80211_SECURITY_WEP;
+		} else {
+			ap_data->scan_result.security.type = 
+				IEEE80211_SECURITY_OPEN;
+			ap_data->scan_result.security.auth = -1;
+			ap_data->scan_result.security.pair_alg = -1;
+			ap_data->scan_result.security.group_alg = -1;
+		}
+		
+		void *rest_ies_start = ssid_start + ssid_ie_header->length;
+		size_t rest_buffer_size = 
+			buffer_size - 
+			sizeof(ieee80211_mgmt_header_t) -
+			sizeof(ieee80211_beacon_start_t) -
+			sizeof(ieee80211_ie_header_t) -
+			ssid_ie_header->length;
+		
+		ieee80211_process_ies(ieee80211_dev, ap_data, rest_ies_start, 
+			rest_buffer_size);
+		
+		ap_data->last_beacon = time(NULL);
+		
+		ieee80211_scan_result_list_append(result_list, ap_data);
+		
+		fibril_mutex_unlock(scan_mutex);
+	}
+	
+	return EOK;
+}
+
+/**
+ * Process authentication response.
+ * 
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param mgmt_header Pointer to start of management frame header.
+ * 
+ * @return EOK if succeed, negative error code otherwise.
+ */
+static int ieee80211_process_auth_response(ieee80211_dev_t *ieee80211_dev,
 	ieee80211_mgmt_header_t *mgmt_header)
 {
-	ieee80211_ie_header_t *ssid_ie_header = (ieee80211_ie_header_t *) 
-		((void *)mgmt_header + sizeof(ieee80211_mgmt_header_t) + 
-		sizeof(ieee80211_beacon_start_t));
-	
-	if(ssid_ie_header->length > 0) {
-		uint8_t *results_length = &ieee80211_dev->ap_list.length;
-		
-		ieee80211_scan_result_t *ap_data = 
-			&ieee80211_dev->ap_list.results[(*results_length)++];
-		
-		memset(ap_data, 0, sizeof(ieee80211_scan_result_t));
-		
-		uint8_t *ssid_start = (uint8_t *) 
-			((void *)ssid_ie_header + 
-			sizeof(ieee80211_ie_header_t));
-
-		memcpy(ap_data->bssid.address, mgmt_header->bssid, ETH_ADDR);
-		memcpy(ap_data->ssid, ssid_start, ssid_ie_header->length);
-		ap_data->ssid[ssid_ie_header->length] = '\0';
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	
+	ieee80211_dev->current_auth_phase = IEEE80211_AUTH_AUTHENTICATED;
+	
+	fibril_condvar_signal(&ieee80211_dev->gen_cond);
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return EOK;
+}
+
+/**
+ * Process association response.
+ * 
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param mgmt_header Pointer to start of management frame header.
+ * 
+ * @return EOK if succeed, negative error code otherwise.
+ */
+static int ieee80211_process_assoc_response(ieee80211_dev_t *ieee80211_dev,
+	ieee80211_mgmt_header_t *mgmt_header)
+{
+	ieee80211_scan_result_t *auth_data =
+		&ieee80211_dev->bssid_info.res_link->scan_result;
+	
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	
+	ieee80211_assoc_resp_body_t *assoc_resp =
+		(ieee80211_assoc_resp_body_t *) ((void *)mgmt_header +
+		sizeof(ieee80211_mgmt_header_t));
+	
+	ieee80211_dev->bssid_info.aid = uint16_t_le2host(assoc_resp->aid);
+	memcpy(auth_data->bssid.address, mgmt_header->bssid, ETH_ADDR);
+	
+	ieee80211_dev->current_auth_phase = IEEE80211_AUTH_ASSOCIATED;
+	
+	ieee80211_dev->ops->bssid_change(ieee80211_dev);
+	
+	fibril_condvar_signal(&ieee80211_dev->gen_cond);
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return EOK;
+}
+
+static int ieee80211_process_4way_handshake(ieee80211_dev_t *ieee80211_dev,
+	void *buffer, size_t buffer_size)
+{
+	ieee80211_eapol_key_frame_t *key_frame = 
+		(ieee80211_eapol_key_frame_t *) buffer;
+	
+	bool handshake_done = false;
+		
+	ieee80211_scan_result_link_t *auth_link =
+		ieee80211_dev->bssid_info.res_link;
+
+	ieee80211_scan_result_t *auth_data = &auth_link->scan_result;
+	uint8_t *ptk = ieee80211_dev->bssid_info.ptk;
+	uint8_t *gtk = ieee80211_dev->bssid_info.gtk;
+
+	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;
+	}
+
+	size_t output_size = 
+		sizeof(eth_header_t) +
+		sizeof(ieee80211_eapol_key_frame_t);
+
+	if(!(uint16_t_be2host(key_frame->key_info) & 
+		IEEE80211_EAPOL_KEY_KEYINFO_SECURE)) {
+		output_size += auth_link->rsn_copy_len;
+	}
+
+	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+	nic_address_t nic_address;
+	nic_query_address(nic, &nic_address);
+
+	void *output_buffer = malloc(output_size);
+	memset(output_buffer, 0, output_size);
+
+	/* Setup ethernet header. */
+	eth_header_t *eth_header = (eth_header_t *) output_buffer;
+	memcpy(eth_header->dest_addr, auth_data->bssid.address,	ETH_ADDR);
+	memcpy(eth_header->src_addr, nic_address.address, ETH_ADDR);
+	eth_header->proto = host2uint16_t_be(ETH_TYPE_PAE);
+
+	ieee80211_eapol_key_frame_t *output_key_frame =
+		(ieee80211_eapol_key_frame_t *) 
+		(output_buffer + sizeof(eth_header_t));
+
+	/* Copy content of incoming EAPOL-Key frame. */
+	memcpy((void *) output_key_frame, buffer, 
+		sizeof(ieee80211_eapol_key_frame_t));
+
+	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);
+	output_key_frame->key_info &= 
+		~host2uint16_t_be(
+			IEEE80211_EAPOL_KEY_KEYINFO_ACK
+		);
+
+	/* 
+	 * 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) {
+		output_key_frame->key_info &= 
+			~host2uint16_t_be(
+				IEEE80211_EAPOL_KEY_KEYINFO_ENCDATA
+			);
+		output_key_frame->key_info &= 
+			~host2uint16_t_be(
+				IEEE80211_EAPOL_KEY_KEYINFO_INSTALL
+			);
+		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);
+		memset(output_key_frame->key_rsc, 0, 8);
+		memset(output_key_frame->eapol_key_iv, 0, 16);
+
+		/* 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) {
+			
+			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;
+			}
+		}
+	} else {
+		output_key_frame->key_info |= 
+			host2uint16_t_be(
+				IEEE80211_EAPOL_KEY_KEYINFO_MIC
+			);
+		output_key_frame->key_data_length =
+			host2uint16_t_be(auth_link->rsn_copy_len);
+		memcpy((void *)output_key_frame + 
+			sizeof(ieee80211_eapol_key_frame_t),
+			auth_link->rsn_copy,
+			auth_link->rsn_copy_len);
+
+		/* Compute PMK. */
+		uint8_t pmk[PBKDF2_KEY_LENGTH];
+		pbkdf2((uint8_t *) ieee80211_dev->bssid_info.password,
+			str_size(ieee80211_dev->bssid_info.password),
+			(uint8_t *) auth_data->ssid,
+			str_size(auth_data->ssid), pmk, hash_sel);
+
+		uint8_t *anonce = key_frame->key_nonce;
+
+		/* Generate SNONCE. */
+		uint8_t snonce[32];
+		rnd_sequence(snonce, 32);
+
+		memcpy(output_key_frame->key_nonce, snonce, 32);
+
+		uint8_t *dest_addr = eth_header->dest_addr;
+		uint8_t *src_addr = eth_header->src_addr;
+
+		/* Derive PTK and save it. */
+		uint8_t prf_result[ptk_key_length];
+		uint8_t crypt_data[PRF_CRYPT_DATA_LENGTH];
+		memcpy(crypt_data, 
+			min_sequence(dest_addr, src_addr, ETH_ADDR), 
+			ETH_ADDR);
+		memcpy(crypt_data + ETH_ADDR, 
+			max_sequence(dest_addr, src_addr, ETH_ADDR), 
+			ETH_ADDR);
+		memcpy(crypt_data + 2*ETH_ADDR, 
+			min_sequence(anonce, snonce, 32), 
+			32);
+		memcpy(crypt_data + 2*ETH_ADDR + 32, 
+			max_sequence(anonce, snonce, 32),
+			32);
+		ieee80211_prf(pmk, crypt_data, prf_result, hash_sel);
+		memcpy(ptk, prf_result, ptk_key_length);
+	}
+
+	/* Compute MIC of key frame data from KCK part of PTK. */
+	uint8_t mic[SHA1_HASH_LENGTH];
+	hmac(ptk, 16, (uint8_t *) output_key_frame, 
+		output_size - sizeof(eth_header_t), mic, hash_sel);
+
+	memcpy(output_key_frame->key_mic, mic, 16);
+
+	ieee80211_send_frame(nic, output_buffer, output_size);
+
+	free(output_buffer);
+
+	if(handshake_done) {
+		/* Insert keys into device. */
+		
+		/* Pairwise key. */
+		ieee80211_key_config_t key_config;
+		key_config.suite = auth_data->security.pair_alg;
+		key_config.flags =
+			IEEE80211_KEY_FLAG_TYPE_PAIRWISE;
+		memcpy(key_config.data, 
+			ptk + TK_OFFSET,
+			ptk_key_length - TK_OFFSET);
+
+		ieee80211_dev->ops->key_config(ieee80211_dev,
+			&key_config, true);
+		
+		/* Group key. */
+		key_config.suite = auth_data->security.group_alg;
+		key_config.flags =
+			IEEE80211_KEY_FLAG_TYPE_GROUP;
+		memcpy(key_config.data, gtk, gtk_key_length);
+
+		ieee80211_dev->ops->key_config(ieee80211_dev,
+			&key_config, true);
+
+		/* Signal successful handshake completion. */
+		fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+		fibril_condvar_signal(&ieee80211_dev->gen_cond);
+		fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	}
+	
+	return EOK;
+}
+
+static int ieee80211_process_eapol_frame(ieee80211_dev_t *ieee80211_dev,
+	void *buffer, size_t buffer_size)
+{
+	ieee80211_eapol_key_frame_t *key_frame = 
+		(ieee80211_eapol_key_frame_t *) buffer;
+	if(ieee80211_is_eapol_key_frame(key_frame)) {
+		return ieee80211_process_4way_handshake(ieee80211_dev, buffer,
+			buffer_size);
+	}
+	
+	return EOK;
+}
+
+/**
+ * Process data frame.
+ * 
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param buffer Data buffer starting with IEEE 802.11 data header.
+ * @param buffer_size Size of buffer.
+ * 
+ * @return EOK if succeed, negative error code otherwise.
+ */
+static int ieee80211_process_data(ieee80211_dev_t *ieee80211_dev, 
+	void *buffer, size_t buffer_size)
+{
+	ieee80211_data_header_t *data_header = 
+		(ieee80211_data_header_t *) buffer;
+	
+	if(ieee80211_has_data_frame(data_header->frame_ctrl)) {
+		nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+		size_t strip_length = sizeof(ieee80211_data_header_t) + 
+			ARRAY_SIZE(rfc1042_header);
+		
+		/* TODO: Probably different by used security alg. */
+		if(ieee80211_is_encrypted_frame(data_header->frame_ctrl)) {
+			strip_length += 8;
+		}
+		
+		/* Process 4-way authentication handshake. */
+		uint16_t *proto = (uint16_t *) (buffer + strip_length);
+		if(uint16_t_be2host(*proto) == ETH_TYPE_PAE) {
+			return ieee80211_process_eapol_frame(ieee80211_dev,
+				buffer + strip_length + sizeof(uint16_t),
+				buffer_size - strip_length - sizeof(uint16_t));
+		}
+		
+		/* Note: ETH protocol ID is already there, so we don't create 
+		 * whole ETH header. */
+		size_t frame_size = 
+			buffer_size - strip_length + sizeof(eth_header_t)-2;
+		nic_frame_t *frame = nic_alloc_frame(nic, frame_size);
+
+		if(frame == NULL) {
+			return ENOMEM;
+		}
+
+		uint8_t *src_addr = 
+			ieee80211_is_fromds_frame(data_header->frame_ctrl) ?
+				data_header->address3 : data_header->address2;
+		uint8_t *dest_addr = 
+			ieee80211_is_tods_frame(data_header->frame_ctrl) ?
+				data_header->address3 : data_header->address1;
+
+		eth_header_t *eth_header =
+			(eth_header_t *) frame->data;
+		memcpy(eth_header->src_addr, src_addr, ETH_ADDR);
+		memcpy(eth_header->dest_addr, dest_addr, ETH_ADDR);
+
+		memcpy(frame->data + sizeof(eth_header_t)-2, 
+			buffer + strip_length, 
+			buffer_size - strip_length);
+
+		nic_received_frame(nic, frame);
 	}
 	
@@ -548,19 +1548,19 @@
 			ieee80211_is_beacon_frame(mgmt_header->frame_ctrl)) {
 			return ieee80211_process_probe_response(ieee80211_dev,
+				mgmt_header, buffer_size);
+		}
+		
+		if(ieee80211_is_auth_frame(mgmt_header->frame_ctrl)) {
+			return ieee80211_process_auth_response(ieee80211_dev,
 				mgmt_header);
 		}
-		// TODO
+		
+		if(ieee80211_is_assoc_response_frame(mgmt_header->frame_ctrl)) {
+			return ieee80211_process_assoc_response(ieee80211_dev,
+				mgmt_header);
+		}
 	} else if(ieee80211_is_data_frame(frame_ctrl)) {
-		nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
-		size_t frame_size = buffer_size - sizeof(ieee80211_data_header_t);
-		nic_frame_t *frame = nic_alloc_frame(nic, frame_size); 
-		if (frame != NULL) {
-			memcpy(frame->data, 
-				buffer + sizeof(ieee80211_data_header_t), 
-				frame_size);
-			nic_received_frame(nic, frame);
-		}
-	} else {
-		// TODO
+		return ieee80211_process_data(ieee80211_dev, buffer, 
+			buffer_size);
 	}
 	
Index: uspace/lib/ieee80211/src/ieee80211_iface_impl.c
===================================================================
--- uspace/lib/ieee80211/src/ieee80211_iface_impl.c	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/ieee80211/src/ieee80211_iface_impl.c	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -27,5 +27,5 @@
  */
 
-#include <stdio.h>
+#include <str.h>
 #include <errno.h>
 
@@ -42,31 +42,181 @@
  */
 
+/**
+ * Implementation of fetching scan results.
+ * 
+ * @param fun Device function.
+ * @param results Structure where should be stored scan results.
+ * 
+ * @return EOK. 
+ */
 int ieee80211_get_scan_results_impl(ddf_fun_t *fun, 
-	ieee80211_scan_results_t *results)
+	ieee80211_scan_results_t *results, bool now)
 {
 	nic_t *nic_data = nic_get_from_ddf_fun(fun);
 	ieee80211_dev_t *ieee80211_dev = nic_get_specific(nic_data);
 	
+	if(!ieee80211_is_ready(ieee80211_dev))
+		return EREFUSED;
+	
+	fibril_mutex_lock(&ieee80211_dev->ap_list.scan_mutex);
+	time_t scan_span = time(NULL) - ieee80211_dev->ap_list.last_scan;
+	fibril_mutex_unlock(&ieee80211_dev->ap_list.scan_mutex);
+	
+	if(now || scan_span > MAX_SCAN_SPAN_SEC) {
+		ieee80211_dev->ops->scan(ieee80211_dev);
+	}
+	
+	fibril_mutex_lock(&ieee80211_dev->ap_list.scan_mutex);
 	if(results) {
-		memcpy(results, &ieee80211_dev->ap_list, 
-			sizeof(ieee80211_scan_results_t));
-	}
+		ieee80211_scan_result_list_t *result_list =
+			&ieee80211_dev->ap_list;
+		
+		int i = 0;
+		ieee80211_scan_result_list_foreach(*result_list, result) {
+			memcpy(&results->results[i], 
+				&result->scan_result, 
+				sizeof(ieee80211_scan_result_t));
+			i++;
+		}
+		
+		results->length = i;
+	}
+	fibril_mutex_unlock(&ieee80211_dev->ap_list.scan_mutex);
 	
 	return EOK;
 }
 
-int ieee80211_connect_impl(ddf_fun_t *fun, char *ssid, char *password)
-{
-	assert(ssid);
+static uint16_t ieee80211_channel_to_freq(uint8_t channel)
+{
+	return IEEE80211_CHANNEL_GAP * (channel - 1) + IEEE80211_FIRST_FREQ;
+}
+
+/**
+ * Working procedure of connect function.
+ * 
+ * @param ieee80211_dev Pointer to IEEE 802.11 device.
+ * @param auth_data Selected AP data we want to connect to.
+ * 
+ * @return EOK if everything OK, ETIMEOUT when timeout during authenticating.
+ */
+static int ieee80211_connect_proc(ieee80211_dev_t *ieee80211_dev,
+	ieee80211_scan_result_link_t *auth_data, char *password)
+{
+	int rc;
+	
+	ieee80211_dev->bssid_info.res_link = auth_data;
+	
+	/* Set channel. */
+	rc = ieee80211_dev->ops->set_freq(ieee80211_dev, 
+		ieee80211_channel_to_freq(auth_data->scan_result.channel));
+	if(rc != EOK)
+		return rc;
+	
+	/* Try to authenticate. */
+	ieee80211_authenticate(ieee80211_dev);
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	rc = fibril_condvar_wait_timeout(&ieee80211_dev->gen_cond,
+			&ieee80211_dev->gen_mutex,
+			AUTH_TIMEOUT);
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	if(rc != EOK)
+		return rc;
+	if(ieee80211_dev->current_auth_phase != IEEE80211_AUTH_AUTHENTICATED)
+		return EINVAL;
+	
+	/* Try to associate. */
+	ieee80211_associate(ieee80211_dev, password);
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	rc = fibril_condvar_wait_timeout(&ieee80211_dev->gen_cond,
+			&ieee80211_dev->gen_mutex,
+			AUTH_TIMEOUT);
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	if(rc != EOK)
+		return rc;
+	if(ieee80211_dev->current_auth_phase != IEEE80211_AUTH_ASSOCIATED)
+		return EINVAL;
+	
+	/* On open network, we are finished. */
+	if(auth_data->scan_result.security.type == IEEE80211_SECURITY_OPEN)
+		return EOK;
+	
+	/* Otherwise wait for 4-way handshake to complete. */
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	rc = fibril_condvar_wait_timeout(&ieee80211_dev->gen_cond,
+			&ieee80211_dev->gen_mutex,
+			HANDSHAKE_TIMEOUT);
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	if(rc != EOK)
+		return rc;
+	if(ieee80211_dev->current_auth_phase != IEEE80211_AUTH_ASSOCIATED)
+		return EINVAL;
+	
+	return EOK;
+}
+
+/**
+ * Implementation of connecting to specified SSID.
+ * 
+ * @param fun Device function.
+ * @param ssid_start SSID prefix of access point we want to connect to.
+ * 
+ * @return EOK if everything OK, ETIMEOUT when timeout during authenticating,
+ * EINVAL when SSID not in scan results list, EPERM when incorrect password
+ * passed.
+ */
+int ieee80211_connect_impl(ddf_fun_t *fun, char *ssid_start, char *password)
+{
+	assert(ssid_start);
 	assert(password);
 	
-	/*
 	nic_t *nic_data = nic_get_from_ddf_fun(fun);
 	ieee80211_dev_t *ieee80211_dev = nic_get_specific(nic_data);
-	 */
-	
-	// TODO
-	
-	return EOK;
+	
+	if(!ieee80211_is_ready(ieee80211_dev))
+		return EREFUSED;
+	
+	if(ieee80211_is_connected(ieee80211_dev)) {
+		int rc = ieee80211_dev->iface->disconnect(fun);
+		if(rc != EOK)
+			return rc;
+	}
+	
+	fibril_mutex_lock(&ieee80211_dev->ap_list.scan_mutex);
+
+	ieee80211_scan_result_list_foreach(ieee80211_dev->ap_list, result) {
+		if(!str_lcmp(ssid_start, 
+			result->scan_result.ssid, 
+			str_size(ssid_start))) {
+			fibril_mutex_unlock(&ieee80211_dev->ap_list.scan_mutex);
+			return ieee80211_connect_proc(ieee80211_dev, result,
+				password);
+		}
+	}
+	
+	fibril_mutex_unlock(&ieee80211_dev->ap_list.scan_mutex);
+	
+	return EINVAL;
+}
+
+/**
+ * Implementation of disconnecting device from network.
+ * 
+ * @param fun Device function.
+ * 
+ * @return EOK if everything OK, EINVAL when not connected to any network.
+ */
+int ieee80211_disconnect_impl(ddf_fun_t *fun)
+{
+	nic_t *nic_data = nic_get_from_ddf_fun(fun);
+	ieee80211_dev_t *ieee80211_dev = nic_get_specific(nic_data);
+	
+	if(!ieee80211_is_ready(ieee80211_dev))
+		return EREFUSED;
+	
+	if(!ieee80211_is_connected(ieee80211_dev)) {
+		return EINVAL;
+	} else {
+		return ieee80211_deauthenticate(ieee80211_dev);
+	}
 }
 
Index: uspace/lib/ieee80211/src/ieee80211_impl.c
===================================================================
--- uspace/lib/ieee80211/src/ieee80211_impl.c	(revision 59fa7ab26f7b4336f753dcd52b89d6cac0dfca97)
+++ uspace/lib/ieee80211/src/ieee80211_impl.c	(revision 1dcc0b9aaf9f79f759ef62cc27bccd9e6b7c8d80)
@@ -36,4 +36,7 @@
  */
 
+#include <stdio.h>
+#include <crypto.h>
+#include <stdlib.h>
 #include <errno.h>
 
@@ -81,8 +84,33 @@
 
 /**
+ * Default implementation of IEEE802.11 BSSID change function.
+ * 
+ * @param ieee80211_dev Structure of IEEE802.11 device.
+ * 
+ * @return EOK. 
+ */
+int ieee80211_bssid_change_impl(ieee80211_dev_t *ieee80211_dev)
+{
+	return EOK;
+}
+
+/**
+ * Default implementation of IEEE802.11 key config function.
+ * 
+ * @param ieee80211_dev Structure of IEEE802.11 device.
+ * 
+ * @return EOK. 
+ */
+int ieee80211_key_config_impl(ieee80211_dev_t *ieee80211_dev,
+	ieee80211_key_config_t *key_conf, bool insert)
+{
+	return EOK;
+}
+
+/**
  * Default implementation of IEEE802.11 scan function.
  * 
  * @param ieee80211_dev Structure of IEEE802.11 device.
- * @param results Pointer to structure where should be scan results stored.
+ * @param clear Whether to clear current scan results.
  * 
  * @return EOK if succeed, negative error code otherwise. 
@@ -90,4 +118,26 @@
 int ieee80211_scan_impl(ieee80211_dev_t *ieee80211_dev)
 {
+	if(ieee80211_is_connected(ieee80211_dev))
+		return EOK;
+	
+	fibril_mutex_lock(&ieee80211_dev->ap_list.scan_mutex);
+	
+	/* Remove old entries we don't receive beacons from. */
+	ieee80211_scan_result_list_t *result_list = 
+		&ieee80211_dev->ap_list;
+	list_foreach_safe(result_list->list, cur_link, next_link) {
+		ieee80211_scan_result_link_t *cur_result = 
+			list_get_instance(cur_link,
+			ieee80211_scan_result_link_t, 
+			link);
+		if((time(NULL) - cur_result->last_beacon) > 
+			MAX_KEEP_SCAN_SPAN_SEC) {
+			ieee80211_scan_result_list_remove(result_list, 
+				cur_result);
+		}
+	}
+	
+	fibril_mutex_unlock(&ieee80211_dev->ap_list.scan_mutex);
+	
 	uint16_t orig_freq = ieee80211_dev->current_freq;
 	
@@ -96,5 +146,5 @@
 		freq += IEEE80211_CHANNEL_GAP) {
 		ieee80211_dev->ops->set_freq(ieee80211_dev, freq);
-		ieee80211_probe_request(ieee80211_dev);
+		ieee80211_probe_request(ieee80211_dev, NULL);
 		
 		/* Wait for probe responses. */
@@ -104,5 +154,156 @@
 	ieee80211_dev->ops->set_freq(ieee80211_dev, orig_freq);
 	
-	return EOK;
+	fibril_mutex_lock(&ieee80211_dev->ap_list.scan_mutex);
+	time(&ieee80211_dev->ap_list.last_scan);
+	fibril_mutex_unlock(&ieee80211_dev->ap_list.scan_mutex);
+	
+	return EOK;
+}
+
+/**
+ * Pseudorandom function used for IEEE 802.11 pairwise key computation.
+ * 
+ * @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.
+ * 
+ * @return EINVAL when key or data not specified, ENOMEM when pointer for 
+ * output hash result is not allocated, otherwise EOK. 
+ */
+int ieee80211_prf(uint8_t *key, uint8_t *data, uint8_t *hash, 
+	hash_func_t hash_sel)
+{
+	if(!key || !data)
+		return EINVAL;
+	
+	if(!hash)
+		return ENOMEM;
+	
+	size_t hash_length, result_length;
+	switch(hash_sel) {
+		case HASH_MD5:
+			hash_length = MD5_HASH_LENGTH;
+			result_length = IEEE80211_PTK_TKIP_LENGTH;
+			break;
+		case HASH_SHA1:
+			hash_length = SHA1_HASH_LENGTH;
+			result_length = IEEE80211_PTK_CCMP_LENGTH;
+			break;
+		default:
+			hash_length = 0;
+			result_length = 0;
+	}
+	
+	size_t iters = ((result_length * 8) + 159) / 160;
+	
+	const char *a = "Pairwise key expansion";
+	uint8_t result[hash_length*iters];
+	uint8_t temp[hash_length];
+	size_t data_size = PRF_CRYPT_DATA_LENGTH + str_size(a) + 2;
+	uint8_t work_arr[data_size];
+	memset(work_arr, 0, data_size);
+	
+	memcpy(work_arr, a, str_size(a));
+	memcpy(work_arr + str_size(a) + 1, data, PRF_CRYPT_DATA_LENGTH);
+
+	for(uint8_t i = 0; i < iters; i++) {
+		memcpy(work_arr + data_size - 1, &i, 1);
+		hmac(key, PBKDF2_KEY_LENGTH, work_arr, data_size, temp, 
+			hash_sel);
+		memcpy(result + i*hash_length, temp, hash_length);
+	}
+	
+	memcpy(hash, result, result_length);
+	
+	return EOK;
+}
+
+int ieee80211_aes_key_unwrap(uint8_t *kek, uint8_t *data, size_t data_size,
+	uint8_t *output)
+{
+	if(!kek || !data)
+		return EINVAL;
+	
+	if(!output)
+		return ENOMEM;
+
+	uint32_t n = data_size/8 - 1;
+	uint8_t work_data[n*8];
+	uint8_t work_input[AES_CIPHER_LENGTH];
+	uint8_t work_output[AES_CIPHER_LENGTH];
+	uint8_t *work_block;
+	uint8_t a[8];
+	memcpy(a, data, 8);
+	uint64_t mask = 0xFF;
+	uint8_t shift, shb;
+	
+	memcpy(work_data, data + 8, n*8);
+	for(int j = 5; j >=0; j--) {
+		for(int i = n; i > 0; i--) {
+			for(size_t k = 0; k < 8; k++) {
+				shift = 56 - 8*k;
+				shb = ((n*j+i) & (mask << shift)) >> shift;
+				a[k] ^= shb;
+			}
+			work_block = work_data + (i-1)*8;
+			memcpy(work_input, a, 8);
+			memcpy(work_input + 8, work_block, 8);
+			aes_decrypt(kek, work_input, work_output);
+			memcpy(a, work_output, 8);
+			memcpy(work_data + (i-1)*8, work_output + 8, 8);
+		}
+	}
+	
+	size_t it;
+	for(it = 0; it < 8; it++) {
+		if(a[it] != 0xA6)
+			break;
+	}
+	
+	if(it == 8) {
+		memcpy(output, work_data, n*8);
+		return EOK;
+	} else {
+		return EINVAL;
+	}
+}
+
+int rnd_sequence(uint8_t *sequence, size_t length)
+{
+	if(!sequence)
+		return ENOMEM;
+	
+	for(size_t i = 0; i < length; i++) {
+		sequence[i] = (uint8_t) rand();
+	}
+	
+	return EOK;
+}
+
+uint8_t *min_sequence(uint8_t *seq1, uint8_t *seq2, size_t size)
+{
+	if(!seq1 || !seq2)
+		return NULL;
+	
+	for(size_t i = 0; i < size; i++) {
+		if(seq1[i] < seq2[i]) {
+			return seq1;
+		} else if(seq1[i] > seq2[i]) {
+			return seq2;
+		}
+	}
+	
+	return seq1;
+}
+
+uint8_t *max_sequence(uint8_t *seq1, uint8_t *seq2, size_t size)
+{
+	uint8_t *min = min_sequence(seq1, seq2, size);
+	if(min == seq1) {
+		return seq2;
+	} else {
+		return seq1;
+	}
 }
 
