wpa_supplicant: Update to 07-Sep-2012 TOT

commit 44256451130c4766e4a019162de17d0734444ee9
Author: Arik Nemtsov <arik@wizery.com>
Date:   Fri Sep 7 00:22:40 2012 +0300

    AP: Configure basic rates from iface and not conf

Skipped patches:
20ed5e40ba95440a1946cf2dffad3047fb620582
cf8baca6a5719f4f3257631e03317affee015417
a297201df15656dbb0f37e90f3410d9e8102c6fd
620c783753bddd37988269314862dc7e4a62f700

Change-Id: I857aa80af6d1a21b61f7c03a085e7dfc6066d61a
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 76aff77..97e1238 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -129,6 +129,8 @@
 	i = 0;
 	while (basic_rates[i] >= 0)
 		i++;
+	if (i)
+		i++; /* -1 termination */
 	os_free(iface->basic_rates);
 	iface->basic_rates = os_malloc(i * sizeof(int));
 	if (iface->basic_rates)
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 3203d4f..49d8175 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -278,28 +278,6 @@
 }
 
 
-static void wpa_group_set_key_len(struct wpa_group *group, int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_CCMP:
-		group->GTK_len = 16;
-		break;
-	case WPA_CIPHER_GCMP:
-		group->GTK_len = 16;
-		break;
-	case WPA_CIPHER_TKIP:
-		group->GTK_len = 32;
-		break;
-	case WPA_CIPHER_WEP104:
-		group->GTK_len = 13;
-		break;
-	case WPA_CIPHER_WEP40:
-		group->GTK_len = 5;
-		break;
-	}
-}
-
-
 static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
 					  struct wpa_group *group)
 {
@@ -341,8 +319,7 @@
 
 	group->GTKAuthenticator = TRUE;
 	group->vlan_id = vlan_id;
-
-	wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+	group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
 
 	if (random_pool_ready() != 1) {
 		wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
@@ -517,7 +494,7 @@
 	 * configuration.
 	 */
 	group = wpa_auth->group;
-	wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+	group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
 	group->GInit = TRUE;
 	wpa_group_sm_step(wpa_auth, group);
 	group->GInit = FALSE;
@@ -1291,23 +1268,7 @@
 	WPA_PUT_BE16(key->key_info, key_info);
 
 	alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
-	switch (alg) {
-	case WPA_CIPHER_CCMP:
-		WPA_PUT_BE16(key->key_length, 16);
-		break;
-	case WPA_CIPHER_GCMP:
-		WPA_PUT_BE16(key->key_length, 16);
-		break;
-	case WPA_CIPHER_TKIP:
-		WPA_PUT_BE16(key->key_length, 32);
-		break;
-	case WPA_CIPHER_WEP40:
-		WPA_PUT_BE16(key->key_length, 5);
-		break;
-	case WPA_CIPHER_WEP104:
-		WPA_PUT_BE16(key->key_length, 13);
-		break;
-	}
+	WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
 	if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
 		WPA_PUT_BE16(key->key_length, 0);
 
@@ -1540,24 +1501,6 @@
 }
 
 
-static enum wpa_alg wpa_alg_enum(int alg)
-{
-	switch (alg) {
-	case WPA_CIPHER_CCMP:
-		return WPA_ALG_CCMP;
-	case WPA_CIPHER_GCMP:
-		return WPA_ALG_GCMP;
-	case WPA_CIPHER_TKIP:
-		return WPA_ALG_TKIP;
-	case WPA_CIPHER_WEP104:
-	case WPA_CIPHER_WEP40:
-		return WPA_ALG_WEP;
-	default:
-		return WPA_ALG_NONE;
-	}
-}
-
-
 SM_STATE(WPA_PTK, INITIALIZE)
 {
 	SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
@@ -2097,18 +2040,8 @@
 	SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
 	sm->EAPOLKeyReceived = FALSE;
 	if (sm->Pair) {
-		enum wpa_alg alg;
-		int klen;
-		if (sm->pairwise == WPA_CIPHER_TKIP) {
-			alg = WPA_ALG_TKIP;
-			klen = 32;
-		} else if (sm->pairwise == WPA_CIPHER_GCMP) {
-			alg = WPA_ALG_GCMP;
-			klen = 16;
-		} else {
-			alg = WPA_ALG_CCMP;
-			klen = 16;
-		}
+		enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
+		int klen = wpa_cipher_key_len(sm->pairwise);
 		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
 				     sm->PTK.tk1, klen)) {
 			wpa_sta_disconnect(sm->wpa_auth, sm->addr);
@@ -2657,7 +2590,7 @@
 	int ret = 0;
 
 	if (wpa_auth_set_key(wpa_auth, group->vlan_id,
-			     wpa_alg_enum(wpa_auth->conf.wpa_group),
+			     wpa_cipher_to_alg(wpa_auth->conf.wpa_group),
 			     broadcast_ether_addr, group->GN,
 			     group->GTK[group->GN - 1], group->GTK_len) < 0)
 		ret = -1;
@@ -2797,25 +2730,6 @@
 }
 
 
-static int wpa_cipher_bits(int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_CCMP:
-		return 128;
-	case WPA_CIPHER_GCMP:
-		return 128;
-	case WPA_CIPHER_TKIP:
-		return 256;
-	case WPA_CIPHER_WEP104:
-		return 104;
-	case WPA_CIPHER_WEP40:
-		return 40;
-	default:
-		return 0;
-	}
-}
-
-
 #define RSN_SUITE "%02x-%02x-%02x-%d"
 #define RSN_SUITE_ARG(s) \
 ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
@@ -2878,7 +2792,7 @@
 		!!wpa_auth->conf.wpa_strict_rekey,
 		dot11RSNAConfigGroupUpdateCount,
 		dot11RSNAConfigPairwiseUpdateCount,
-		wpa_cipher_bits(wpa_auth->conf.wpa_group),
+		wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
 		dot11RSNAConfigPMKLifetime,
 		dot11RSNAConfigPMKReauthThreshold,
 		dot11RSNAConfigSATimeout,
@@ -2921,31 +2835,10 @@
 
 	/* dot11RSNAStatsEntry */
 
-	if (sm->wpa == WPA_VERSION_WPA) {
-		if (sm->pairwise == WPA_CIPHER_CCMP)
-			pairwise = WPA_CIPHER_SUITE_CCMP;
-		else if (sm->pairwise == WPA_CIPHER_TKIP)
-			pairwise = WPA_CIPHER_SUITE_TKIP;
-		else if (sm->pairwise == WPA_CIPHER_WEP104)
-			pairwise = WPA_CIPHER_SUITE_WEP104;
-		else if (sm->pairwise == WPA_CIPHER_WEP40)
-			pairwise = WPA_CIPHER_SUITE_WEP40;
-		else if (sm->pairwise == WPA_CIPHER_NONE)
-			pairwise = WPA_CIPHER_SUITE_NONE;
-	} else if (sm->wpa == WPA_VERSION_WPA2) {
-		if (sm->pairwise == WPA_CIPHER_CCMP)
-			pairwise = RSN_CIPHER_SUITE_CCMP;
-		else if (sm->pairwise == WPA_CIPHER_GCMP)
-			pairwise = RSN_CIPHER_SUITE_GCMP;
-		else if (sm->pairwise == WPA_CIPHER_TKIP)
-			pairwise = RSN_CIPHER_SUITE_TKIP;
-		else if (sm->pairwise == WPA_CIPHER_WEP104)
-			pairwise = RSN_CIPHER_SUITE_WEP104;
-		else if (sm->pairwise == WPA_CIPHER_WEP40)
-			pairwise = RSN_CIPHER_SUITE_WEP40;
-		else if (sm->pairwise == WPA_CIPHER_NONE)
-			pairwise = RSN_CIPHER_SUITE_NONE;
-	} else
+	pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ?
+				       WPA_PROTO_RSN : WPA_PROTO_WPA,
+				       sm->pairwise);
+	if (pairwise == 0)
 		return 0;
 
 	ret = os_snprintf(
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 9f7cdae..48bf79b 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -754,16 +754,9 @@
 	int klen;
 
 	/* MLME-SETKEYS.request(PTK) */
-	if (sm->pairwise == WPA_CIPHER_TKIP) {
-		alg = WPA_ALG_TKIP;
-		klen = 32;
-	} else if (sm->pairwise == WPA_CIPHER_CCMP) {
-		alg = WPA_ALG_CCMP;
-		klen = 16;
-	} else if (sm->pairwise == WPA_CIPHER_GCMP) {
-		alg = WPA_ALG_GCMP;
-		klen = 16;
-	} else {
+	alg = wpa_cipher_to_alg(sm->pairwise);
+	klen = wpa_cipher_key_len(sm->pairwise);
+	if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
 		wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
 			   "PTK configuration", sm->pairwise);
 		return;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index b88b80a..1786230 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -29,6 +29,7 @@
 	struct wpa_ie_hdr *hdr;
 	int num_suites;
 	u8 *pos, *count;
+	u32 suite;
 
 	hdr = (struct wpa_ie_hdr *) buf;
 	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
@@ -36,46 +37,25 @@
 	WPA_PUT_LE16(hdr->version, WPA_VERSION);
 	pos = (u8 *) (hdr + 1);
 
-	if (conf->wpa_group == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
-	} else if (conf->wpa_group == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
-	} else if (conf->wpa_group == WPA_CIPHER_WEP104) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
-	} else if (conf->wpa_group == WPA_CIPHER_WEP40) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group);
+	if (suite == 0) {
 		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
 			   conf->wpa_group);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += WPA_SELECTOR_LEN;
 
-	num_suites = 0;
 	count = pos;
 	pos += 2;
 
-	if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
-		pos += WPA_SELECTOR_LEN;
-		num_suites++;
-	}
-	if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
-		pos += WPA_SELECTOR_LEN;
-		num_suites++;
-	}
-	if (conf->wpa_pairwise & WPA_CIPHER_NONE) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
-		pos += WPA_SELECTOR_LEN;
-		num_suites++;
-	}
-
+	num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise);
 	if (num_suites == 0) {
 		wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
 			   conf->wpa_pairwise);
 		return -1;
 	}
+	pos += num_suites * WPA_SELECTOR_LEN;
 	WPA_PUT_LE16(count, num_suites);
 
 	num_suites = 0;
@@ -112,30 +92,23 @@
 		     const u8 *pmkid)
 {
 	struct rsn_ie_hdr *hdr;
-	int num_suites;
+	int num_suites, res;
 	u8 *pos, *count;
 	u16 capab;
+	u32 suite;
 
 	hdr = (struct rsn_ie_hdr *) buf;
 	hdr->elem_id = WLAN_EID_RSN;
 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
 	pos = (u8 *) (hdr + 1);
 
-	if (conf->wpa_group == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	} else if (conf->wpa_group == WPA_CIPHER_GCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
-	} else if (conf->wpa_group == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	} else if (conf->wpa_group == WPA_CIPHER_WEP104) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
-	} else if (conf->wpa_group == WPA_CIPHER_WEP40) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+	if (suite == 0) {
 		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
 			   conf->wpa_group);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += RSN_SELECTOR_LEN;
 
 	num_suites = 0;
@@ -150,26 +123,9 @@
 	}
 #endif /* CONFIG_RSN_TESTING */
 
-	if (conf->rsn_pairwise & WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-		pos += RSN_SELECTOR_LEN;
-		num_suites++;
-	}
-	if (conf->rsn_pairwise & WPA_CIPHER_GCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
-		pos += RSN_SELECTOR_LEN;
-		num_suites++;
-	}
-	if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-		pos += RSN_SELECTOR_LEN;
-		num_suites++;
-	}
-	if (conf->rsn_pairwise & WPA_CIPHER_NONE) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
-		pos += RSN_SELECTOR_LEN;
-		num_suites++;
-	}
+	res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
+	num_suites += res;
+	pos += res * RSN_SELECTOR_LEN;
 
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing) {
@@ -457,34 +413,16 @@
 			selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
 
-		selector = RSN_CIPHER_SUITE_CCMP;
-		if (data.pairwise_cipher & WPA_CIPHER_CCMP)
+		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+					       data.pairwise_cipher);
+		if (!selector)
 			selector = RSN_CIPHER_SUITE_CCMP;
-		else if (data.pairwise_cipher & WPA_CIPHER_GCMP)
-			selector = RSN_CIPHER_SUITE_GCMP;
-		else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
-			selector = RSN_CIPHER_SUITE_TKIP;
-		else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
-			selector = RSN_CIPHER_SUITE_WEP104;
-		else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
-			selector = RSN_CIPHER_SUITE_WEP40;
-		else if (data.pairwise_cipher & WPA_CIPHER_NONE)
-			selector = RSN_CIPHER_SUITE_NONE;
 		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
 
-		selector = RSN_CIPHER_SUITE_CCMP;
-		if (data.group_cipher & WPA_CIPHER_CCMP)
+		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+					       data.group_cipher);
+		if (!selector)
 			selector = RSN_CIPHER_SUITE_CCMP;
-		else if (data.group_cipher & WPA_CIPHER_GCMP)
-			selector = RSN_CIPHER_SUITE_GCMP;
-		else if (data.group_cipher & WPA_CIPHER_TKIP)
-			selector = RSN_CIPHER_SUITE_TKIP;
-		else if (data.group_cipher & WPA_CIPHER_WEP104)
-			selector = RSN_CIPHER_SUITE_WEP104;
-		else if (data.group_cipher & WPA_CIPHER_WEP40)
-			selector = RSN_CIPHER_SUITE_WEP40;
-		else if (data.group_cipher & WPA_CIPHER_NONE)
-			selector = RSN_CIPHER_SUITE_NONE;
 		wpa_auth->dot11RSNAGroupCipherSelected = selector;
 	} else {
 		res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
@@ -496,30 +434,16 @@
 			selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
 
-		selector = WPA_CIPHER_SUITE_TKIP;
-		if (data.pairwise_cipher & WPA_CIPHER_CCMP)
-			selector = WPA_CIPHER_SUITE_CCMP;
-		else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
-			selector = WPA_CIPHER_SUITE_TKIP;
-		else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
-			selector = WPA_CIPHER_SUITE_WEP104;
-		else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
-			selector = WPA_CIPHER_SUITE_WEP40;
-		else if (data.pairwise_cipher & WPA_CIPHER_NONE)
-			selector = WPA_CIPHER_SUITE_NONE;
+		selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+					       data.pairwise_cipher);
+		if (!selector)
+			selector = RSN_CIPHER_SUITE_TKIP;
 		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
 
-		selector = WPA_CIPHER_SUITE_TKIP;
-		if (data.group_cipher & WPA_CIPHER_CCMP)
-			selector = WPA_CIPHER_SUITE_CCMP;
-		else if (data.group_cipher & WPA_CIPHER_TKIP)
+		selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+					       data.group_cipher);
+		if (!selector)
 			selector = WPA_CIPHER_SUITE_TKIP;
-		else if (data.group_cipher & WPA_CIPHER_WEP104)
-			selector = WPA_CIPHER_SUITE_WEP104;
-		else if (data.group_cipher & WPA_CIPHER_WEP40)
-			selector = WPA_CIPHER_SUITE_WEP40;
-		else if (data.group_cipher & WPA_CIPHER_NONE)
-			selector = WPA_CIPHER_SUITE_NONE;
 		wpa_auth->dot11RSNAGroupCipherSelected = selector;
 	}
 	if (res) {
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 36febb3..36c308a 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1073,3 +1073,138 @@
 	return added;
 }
 #endif /* CONFIG_IEEE80211R */
+
+
+int wpa_cipher_key_len(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP:
+	case WPA_CIPHER_GCMP:
+		return 16;
+	case WPA_CIPHER_TKIP:
+		return 32;
+	case WPA_CIPHER_WEP104:
+		return 13;
+	case WPA_CIPHER_WEP40:
+		return 5;
+	}
+
+	return 0;
+}
+
+
+int wpa_cipher_rsc_len(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP:
+	case WPA_CIPHER_GCMP:
+	case WPA_CIPHER_TKIP:
+		return 6;
+	case WPA_CIPHER_WEP104:
+	case WPA_CIPHER_WEP40:
+		return 0;
+	}
+
+	return 0;
+}
+
+
+int wpa_cipher_to_alg(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP:
+		return WPA_ALG_CCMP;
+	case WPA_CIPHER_GCMP:
+		return WPA_ALG_GCMP;
+	case WPA_CIPHER_TKIP:
+		return WPA_ALG_TKIP;
+	case WPA_CIPHER_WEP104:
+	case WPA_CIPHER_WEP40:
+		return WPA_ALG_WEP;
+	}
+	return WPA_ALG_NONE;
+}
+
+
+int wpa_cipher_valid_pairwise(int cipher)
+{
+	return cipher == WPA_CIPHER_CCMP ||
+		cipher == WPA_CIPHER_GCMP ||
+		cipher == WPA_CIPHER_TKIP;
+}
+
+
+u32 wpa_cipher_to_suite(int proto, int cipher)
+{
+	if (cipher & WPA_CIPHER_CCMP)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
+	if (cipher & WPA_CIPHER_GCMP)
+		return RSN_CIPHER_SUITE_GCMP;
+	if (cipher & WPA_CIPHER_TKIP)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
+	if (cipher & WPA_CIPHER_WEP104)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
+	if (cipher & WPA_CIPHER_WEP40)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
+	if (cipher & WPA_CIPHER_NONE)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+	return 0;
+}
+
+
+int rsn_cipher_put_suites(u8 *pos, int ciphers)
+{
+	int num_suites = 0;
+
+	if (ciphers & WPA_CIPHER_CCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_GCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_NONE) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+
+	return num_suites;
+}
+
+
+int wpa_cipher_put_suites(u8 *pos, int ciphers)
+{
+	int num_suites = 0;
+
+	if (ciphers & WPA_CIPHER_CCMP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_NONE) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+
+	return num_suites;
+}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index c871ae1..603166b 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -379,4 +379,12 @@
 
 int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
 
+int wpa_cipher_key_len(int cipher);
+int wpa_cipher_rsc_len(int cipher);
+int wpa_cipher_to_alg(int cipher);
+int wpa_cipher_valid_pairwise(int cipher);
+u32 wpa_cipher_to_suite(int proto, int cipher);
+int rsn_cipher_put_suites(u8 *pos, int ciphers);
+int wpa_cipher_put_suites(u8 *pos, int ciphers);
+
 #endif /* WPA_COMMON_H */
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index fefae8c..5f2e675 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -324,8 +324,7 @@
 	}
 #endif /* CONFIG_IEEE80211W */
 
-	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
-		   __func__, params->rsn_preauth);
+	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v);
 	if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
 		printf("Unable to set RSN capabilities to 0x%x\n", v);
 		return -1;
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index ba973a5..2ed74b8 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -2321,3 +2321,16 @@
 	sm->ext_pw_buf = NULL;
 	sm->ext_pw = ext;
 }
+
+
+/**
+ * eap_set_anon_id - Set or add anonymous identity
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+ * @len: Length of anonymous identity in octets
+ */
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len)
+{
+	if (sm->eapol_cb->set_anon_id)
+		sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len);
+}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index cf58608..8bccef1 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machine functions (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -235,6 +235,14 @@
 	 */
 	void (*notify_status)(void *ctx, const char *status,
 			      const char *parameter);
+
+	/**
+	 * set_anon_id - Set or add anonymous identity
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+	 * @len: Length of anonymous identity in octets
+	 */
+	void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
 };
 
 /**
@@ -308,6 +316,7 @@
 
 struct ext_password_data;
 void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext);
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
 
 #endif /* IEEE8021X_EAPOL */
 
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 1cec4d8..59861cb 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -90,6 +90,7 @@
 {
 	struct eap_aka_data *data;
 	const char *phase1 = eap_get_config_phase1(sm);
+	struct eap_peer_config *config = eap_get_config(sm);
 
 	data = os_zalloc(sizeof(*data));
 	if (data == NULL)
@@ -102,6 +103,15 @@
 
 	data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
 
+	if (config && config->anonymous_identity) {
+		data->pseudonym = os_malloc(config->anonymous_identity_len);
+		if (data->pseudonym) {
+			os_memcpy(data->pseudonym, config->anonymous_identity,
+				  config->anonymous_identity_len);
+			data->pseudonym_len = config->anonymous_identity_len;
+		}
+	}
+
 	return data;
 }
 
@@ -227,13 +237,15 @@
 #define CLEAR_REAUTH_ID	0x02
 #define CLEAR_EAP_ID	0x04
 
-static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
+static void eap_aka_clear_identities(struct eap_sm *sm,
+				     struct eap_aka_data *data, int id)
 {
 	if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym");
 		os_free(data->pseudonym);
 		data->pseudonym = NULL;
 		data->pseudonym_len = 0;
+		eap_set_anon_id(sm, NULL, 0);
 	}
 	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
@@ -288,6 +300,7 @@
 				  realm, realm_len);
 		}
 		data->pseudonym_len = attr->next_pseudonym_len + realm_len;
+		eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
 	}
 
 	if (attr->next_reauth_id) {
@@ -425,6 +438,8 @@
 	data->num_id_req = 0;
 	data->num_notification = 0;
 
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)",
+		   err);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 			       EAP_AKA_SUBTYPE_CLIENT_ERROR);
 	eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
@@ -486,16 +501,16 @@
 		   data->pseudonym) {
 		identity = data->pseudonym;
 		identity_len = data->pseudonym_len;
-		eap_aka_clear_identities(data, CLEAR_REAUTH_ID);
+		eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
 	} else if (id_req != NO_ID_REQ) {
 		identity = eap_get_config_identity(sm, &identity_len);
 		if (identity) {
-			eap_aka_clear_identities(data, CLEAR_PSEUDONYM |
+			eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM |
 						 CLEAR_REAUTH_ID);
 		}
 	}
 	if (id_req != NO_ID_REQ)
-		eap_aka_clear_identities(data, CLEAR_EAP_ID);
+		eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
 
 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
@@ -898,7 +913,7 @@
 	 * other words, if no new identities are received, full
 	 * authentication will be used on next reauthentication (using
 	 * pseudonym identity or permanent identity). */
-	eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 
 	if (attr->encr_data) {
 		u8 *decrypted;
@@ -1126,7 +1141,7 @@
 					   data->nonce_s, data->mk,
 					   data->msk, data->emsk);
 	}
-	eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 	eap_aka_learn_ids(sm, data, &eattr);
 
 	if (data->result_ind && attr->result_ind)
@@ -1142,7 +1157,8 @@
 	if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
 			   "fast reauths performed - force fullauth");
-		eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+		eap_aka_clear_identities(sm, data,
+					 CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 	}
 	os_free(decrypted);
 	return eap_aka_response_reauth(data, id, 0, data->nonce_s);
@@ -1260,7 +1276,7 @@
 static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_aka_data *data = priv;
-	eap_aka_clear_identities(data, CLEAR_EAP_ID);
+	eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
 	data->prev_id = -1;
 	wpabuf_free(data->id_msgs);
 	data->id_msgs = NULL;
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index a08543e..ed90919 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -35,6 +35,9 @@
 	 *
 	 * If not set, the identity field will be used for both unencrypted and
 	 * protected fields.
+	 *
+	 * This field can also be used with EAP-SIM/AKA/AKA' to store the
+	 * pseudonym identity.
 	 */
 	u8 *anonymous_identity;
 
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index fb4ae82..c936a44 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-SIM (RFC 4186)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -117,6 +117,15 @@
 			NULL;
 	}
 
+	if (config && config->anonymous_identity) {
+		data->pseudonym = os_malloc(config->anonymous_identity_len);
+		if (data->pseudonym) {
+			os_memcpy(data->pseudonym, config->anonymous_identity,
+				  config->anonymous_identity_len);
+			data->pseudonym_len = config->anonymous_identity_len;
+		}
+	}
+
 	eap_sim_state(data, CONTINUE);
 
 	return data;
@@ -258,13 +267,15 @@
 #define CLEAR_REAUTH_ID	0x02
 #define CLEAR_EAP_ID	0x04
 
-static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
+static void eap_sim_clear_identities(struct eap_sm *sm,
+				     struct eap_sim_data *data, int id)
 {
 	if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym");
 		os_free(data->pseudonym);
 		data->pseudonym = NULL;
 		data->pseudonym_len = 0;
+		eap_set_anon_id(sm, NULL, 0);
 	}
 	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id");
@@ -319,6 +330,7 @@
 				  realm, realm_len);
 		}
 		data->pseudonym_len = attr->next_pseudonym_len + realm_len;
+		eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
 	}
 
 	if (attr->next_reauth_id) {
@@ -352,6 +364,8 @@
 	data->num_id_req = 0;
 	data->num_notification = 0;
 
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)",
+		   err);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
 			       EAP_SIM_SUBTYPE_CLIENT_ERROR);
 	eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
@@ -376,16 +390,16 @@
 		   data->pseudonym) {
 		identity = data->pseudonym;
 		identity_len = data->pseudonym_len;
-		eap_sim_clear_identities(data, CLEAR_REAUTH_ID);
+		eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
 	} else if (id_req != NO_ID_REQ) {
 		identity = eap_get_config_identity(sm, &identity_len);
 		if (identity) {
-			eap_sim_clear_identities(data, CLEAR_PSEUDONYM |
+			eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM |
 						 CLEAR_REAUTH_ID);
 		}
 	}
 	if (id_req != NO_ID_REQ)
-		eap_sim_clear_identities(data, CLEAR_EAP_ID);
+		eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
 
 	wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
@@ -432,7 +446,8 @@
 
 
 static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
-					       u8 id, int counter_too_small)
+					       u8 id, int counter_too_small,
+					       const u8 *nonce_s)
 {
 	struct eap_sim_msg *msg;
 	unsigned int counter;
@@ -467,7 +482,7 @@
 	}
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, data->nonce_s,
+	return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
 				  EAP_SIM_NONCE_S_LEN);
 }
 
@@ -667,7 +682,7 @@
 	 * other words, if no new reauth identity is received, full
 	 * authentication will be used on next reauthentication (using
 	 * pseudonym identity or permanent identity). */
-	eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 
 	if (attr->encr_data) {
 		u8 *decrypted;
@@ -863,7 +878,7 @@
 		data->reauth_id = NULL;
 		data->reauth_id_len = 0;
 		os_free(decrypted);
-		return eap_sim_response_reauth(data, id, 1);
+		return eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
 	}
 	data->counter = eattr.counter;
 
@@ -875,7 +890,7 @@
 				   data->reauth_id, data->reauth_id_len,
 				   data->nonce_s, data->mk, data->msk,
 				   data->emsk);
-	eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 	eap_sim_learn_ids(sm, data, &eattr);
 
 	if (data->result_ind && attr->result_ind)
@@ -891,10 +906,11 @@
 	if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of "
 			   "fast reauths performed - force fullauth");
-		eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+		eap_sim_clear_identities(sm, data,
+					 CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 	}
 	os_free(decrypted);
-	return eap_sim_response_reauth(data, id, 0);
+	return eap_sim_response_reauth(data, id, 0, data->nonce_s);
 }
 
 
@@ -1002,7 +1018,7 @@
 static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_sim_data *data = priv;
-	eap_sim_clear_identities(data, CLEAR_EAP_ID);
+	eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
 	data->use_result_ind = 0;
 }
 
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index dfb0ff5..f92704a 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -151,7 +151,7 @@
 	int user_eap_method_index;
 	int init_phase2;
 	void *ssl_ctx;
-	void *eap_sim_db_priv;
+	struct eap_sim_db_data *eap_sim_db_priv;
 	Boolean backend_auth;
 	Boolean update_user;
 	int eap_server;
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 9cd5509..a965cac 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -49,12 +49,12 @@
 	u8 *network_name;
 	size_t network_name_len;
 	u16 kdf;
+	int identity_round;
+	char permanent[20]; /* Permanent username */
 };
 
 
-static void eap_aka_determine_identity(struct eap_sm *sm,
-				       struct eap_aka_data *data,
-				       int before_identity, int after_reauth);
+static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data);
 
 
 static const char * eap_aka_state_txt(int state)
@@ -87,6 +87,96 @@
 }
 
 
+static int eap_aka_check_identity_reauth(struct eap_sm *sm,
+					 struct eap_aka_data *data,
+					 const char *username)
+{
+	if (data->eap_method == EAP_TYPE_AKA_PRIME &&
+	    username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX)
+		return 0;
+	if (data->eap_method == EAP_TYPE_AKA &&
+	    username[0] != EAP_AKA_REAUTH_ID_PREFIX)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username);
+	data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv,
+						   username);
+	if (data->reauth == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - "
+			   "request full auth identity");
+		/* Remain in IDENTITY state for another round */
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication");
+	os_strlcpy(data->permanent, data->reauth->permanent,
+		   sizeof(data->permanent));
+	data->counter = data->reauth->counter;
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		os_memcpy(data->k_encr, data->reauth->k_encr,
+			  EAP_SIM_K_ENCR_LEN);
+		os_memcpy(data->k_aut, data->reauth->k_aut,
+			  EAP_AKA_PRIME_K_AUT_LEN);
+		os_memcpy(data->k_re, data->reauth->k_re,
+			  EAP_AKA_PRIME_K_RE_LEN);
+	} else {
+		os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
+	}
+
+	eap_aka_state(data, REAUTH);
+	return 1;
+}
+
+
+static void eap_aka_check_identity(struct eap_sm *sm,
+				   struct eap_aka_data *data)
+{
+	char *username;
+
+	/* Check if we already know the identity from EAP-Response/Identity */
+
+	username = sim_get_username(sm->identity, sm->identity_len);
+	if (username == NULL)
+		return;
+
+	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
+		os_free(username);
+		/*
+		 * Since re-auth username was recognized, skip AKA/Identity
+		 * exchange.
+		 */
+		return;
+	}
+
+	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
+	    (data->eap_method == EAP_TYPE_AKA &&
+	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
+		const char *permanent;
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
+			   username);
+		permanent = eap_sim_db_get_permanent(
+			sm->eap_sim_db_priv, username);
+		if (permanent == NULL) {
+			os_free(username);
+			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
+				   "identity - request permanent identity");
+			/* Remain in IDENTITY state for another round */
+			return;
+		}
+		os_strlcpy(data->permanent, permanent,
+			   sizeof(data->permanent));
+		/*
+		 * Since pseudonym username was recognized, skip AKA/Identity
+		 * exchange.
+		 */
+		eap_aka_fullauth(sm, data);
+	}
+
+	os_free(username);
+}
+
+
 static void * eap_aka_init(struct eap_sm *sm)
 {
 	struct eap_aka_data *data;
@@ -103,8 +193,8 @@
 	data->eap_method = EAP_TYPE_AKA;
 
 	data->state = IDENTITY;
-	eap_aka_determine_identity(sm, data, 1, 0);
 	data->pending_id = -1;
+	eap_aka_check_identity(sm, data);
 
 	return data;
 }
@@ -136,8 +226,8 @@
 	data->network_name_len = os_strlen(network_name);
 
 	data->state = IDENTITY;
-	eap_aka_determine_identity(sm, data, 1, 0);
 	data->pending_id = -1;
+	eap_aka_check_identity(sm, data);
 
 	return data;
 }
@@ -264,21 +354,8 @@
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity");
 	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
 			       EAP_AKA_SUBTYPE_IDENTITY);
-	if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
-				      sm->identity_len)) {
-		if (sm->identity_len > 0 &&
-		    (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
-		     sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) {
-			/* Reauth id may have expired - try fullauth */
-			wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
-			eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0,
-					NULL, 0);
-		} else {
-			wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
-			eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0,
-					NULL, 0);
-		}
-	} else {
+	data->identity_round++;
+	if (data->identity_round == 1) {
 		/*
 		 * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is
 		 * ignored and the AKA/Identity is used to request the
@@ -286,6 +363,18 @@
 		 */
 		wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
 		eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+	} else if (data->identity_round > 3) {
+		/* Cannot use more than three rounds of Identity messages */
+		return NULL;
+	} else if (sm->identity && sm->identity_len > 0 &&
+		   (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
+		    sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) {
+		/* Reauth id may have expired - try fullauth */
+		wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
+	} else {
+		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
 	}
 	buf = eap_sim_msg_finish(msg, NULL, NULL, 0);
 	if (eap_aka_add_id_msg(data, buf) < 0) {
@@ -622,93 +711,72 @@
 
 
 static void eap_aka_determine_identity(struct eap_sm *sm,
-				       struct eap_aka_data *data,
-				       int before_identity, int after_reauth)
+				       struct eap_aka_data *data)
 {
-	const u8 *identity;
-	size_t identity_len;
-	int res;
-
-	identity = NULL;
-	identity_len = 0;
-
-	if (after_reauth && data->reauth) {
-		identity = data->reauth->identity;
-		identity_len = data->reauth->identity_len;
-	} else if (sm->identity && sm->identity_len > 0 &&
-		   (sm->identity[0] == EAP_AKA_PERMANENT_PREFIX ||
-		    sm->identity[0] == EAP_AKA_PRIME_PERMANENT_PREFIX)) {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	} else {
-		identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
-						    sm->identity,
-						    sm->identity_len,
-						    &identity_len);
-		if (identity == NULL) {
-			data->reauth = eap_sim_db_get_reauth_entry(
-				sm->eap_sim_db_priv, sm->identity,
-				sm->identity_len);
-			if (data->reauth &&
-			    data->reauth->aka_prime !=
-			    (data->eap_method == EAP_TYPE_AKA_PRIME)) {
-				wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data "
-					   "was for different AKA version");
-				data->reauth = NULL;
-			}
-			if (data->reauth) {
-				wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast "
-					   "re-authentication");
-				identity = data->reauth->identity;
-				identity_len = data->reauth->identity_len;
-				data->counter = data->reauth->counter;
-				if (data->eap_method == EAP_TYPE_AKA_PRIME) {
-					os_memcpy(data->k_encr,
-						  data->reauth->k_encr,
-						  EAP_SIM_K_ENCR_LEN);
-					os_memcpy(data->k_aut,
-						  data->reauth->k_aut,
-						  EAP_AKA_PRIME_K_AUT_LEN);
-					os_memcpy(data->k_re,
-						  data->reauth->k_re,
-						  EAP_AKA_PRIME_K_RE_LEN);
-				} else {
-					os_memcpy(data->mk, data->reauth->mk,
-						  EAP_SIM_MK_LEN);
-				}
-			}
-		}
-	}
-
-	if (identity == NULL ||
-	    eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
-				      sm->identity_len) < 0) {
-		if (before_identity) {
-			wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name "
-				   "not known - send AKA-Identity request");
-			eap_aka_state(data, IDENTITY);
-			return;
-		} else {
-			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the "
-				   "permanent user name is known; try to use "
-				   "it");
-			/* eap_sim_db_get_aka_auth() will report failure, if
-			 * this identity is not known. */
-		}
-	}
+	char *username;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity",
-			  identity, identity_len);
+			  sm->identity, sm->identity_len);
 
-	if (!after_reauth && data->reauth) {
-		eap_aka_state(data, REAUTH);
+	username = sim_get_username(sm->identity, sm->identity_len);
+	if (username == NULL) {
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
 		return;
 	}
 
-	res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity,
-				      identity_len, data->rand, data->autn,
-				      data->ik, data->ck, data->res,
-				      &data->res_len, sm);
+	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
+		os_free(username);
+		return;
+	}
+
+	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
+	    (data->eap_method == EAP_TYPE_AKA &&
+	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
+		const char *permanent;
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
+			   username);
+		permanent = eap_sim_db_get_permanent(
+			sm->eap_sim_db_priv, username);
+		os_free(username);
+		if (permanent == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
+				   "identity - request permanent identity");
+			/* Remain in IDENTITY state for another round */
+			return;
+		}
+		os_strlcpy(data->permanent, permanent,
+			   sizeof(data->permanent));
+	} else if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+		    username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+		   (data->eap_method == EAP_TYPE_AKA &&
+		    username[0] == EAP_AKA_PERMANENT_PREFIX)) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'",
+			   username);
+		os_strlcpy(data->permanent, username, sizeof(data->permanent));
+		os_free(username);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'",
+			   username);
+		os_free(username);
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	eap_aka_fullauth(sm, data);
+}
+
+
+static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data)
+{
+	size_t identity_len;
+	int res;
+
+	res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent,
+				      data->rand, data->autn, data->ik,
+				      data->ck, data->res, &data->res_len, sm);
 	if (res == EAP_SIM_DB_PENDING) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
 			   "not yet available - pending request");
@@ -772,6 +840,8 @@
 				     struct wpabuf *respData,
 				     struct eap_sim_attrs *attr)
 {
+	u8 *new_identity;
+
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity");
 
 	if (attr->mac || attr->iv || attr->encr_data) {
@@ -782,17 +852,30 @@
 		return;
 	}
 
-	if (attr->identity) {
-		os_free(sm->identity);
-		sm->identity = os_malloc(attr->identity_len);
-		if (sm->identity) {
-			os_memcpy(sm->identity, attr->identity,
-				  attr->identity_len);
-			sm->identity_len = attr->identity_len;
-		}
+	/*
+	 * We always request identity with AKA/Identity, so the peer is
+	 * required to have replied with one.
+	 */
+	if (!attr->identity || attr->identity_len == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any "
+			   "identity");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
 	}
 
-	eap_aka_determine_identity(sm, data, 0, 0);
+	new_identity = os_malloc(attr->identity_len);
+	if (new_identity == NULL) {
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+	os_free(sm->identity);
+	sm->identity = new_identity;
+	os_memcpy(sm->identity, attr->identity, attr->identity_len);
+	sm->identity_len = attr->identity_len;
+
+	eap_aka_determine_identity(sm, data);
 	if (eap_get_id(respData) == data->pending_id) {
 		data->pending_id = -1;
 		eap_aka_add_id_msg(data, respData);
@@ -817,9 +900,6 @@
 				      struct wpabuf *respData,
 				      struct eap_sim_attrs *attr)
 {
-	const u8 *identity;
-	size_t identity_len;
-
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
 
 #ifdef EAP_SERVER_AKA_PRIME
@@ -892,16 +972,8 @@
 	} else
 		eap_aka_state(data, SUCCESS);
 
-	identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
-					    sm->identity_len, &identity_len);
-	if (identity == NULL) {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	}
-
 	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-					 identity_len,
+		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
 					 data->next_pseudonym);
 		data->next_pseudonym = NULL;
 	}
@@ -909,16 +981,15 @@
 		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 #ifdef EAP_SERVER_AKA_PRIME
 			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
-						    identity,
-						    identity_len,
+						    data->permanent,
 						    data->next_reauth_id,
 						    data->counter + 1,
 						    data->k_encr, data->k_aut,
 						    data->k_re);
 #endif /* EAP_SERVER_AKA_PRIME */
 		} else {
-			eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
-					      identity_len,
+			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+					      data->permanent,
 					      data->next_reauth_id,
 					      data->counter + 1,
 					      data->mk);
@@ -947,9 +1018,8 @@
 	 * maintaining a local flag stating whether this AUTS has already been
 	 * reported. */
 	if (!data->auts_reported &&
-	    eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity,
-				     sm->identity_len, attr->auts,
-				     data->rand)) {
+	    eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent,
+				     attr->auts, data->rand)) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed");
 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
 		eap_aka_state(data, NOTIFICATION);
@@ -957,8 +1027,7 @@
 	}
 	data->auts_reported = 1;
 
-	/* Try again after resynchronization */
-	eap_aka_determine_identity(sm, data, 0, 0);
+	/* Remain in CHALLENGE state to re-try after resynchronization */
 }
 
 
@@ -969,8 +1038,6 @@
 {
 	struct eap_sim_attrs eattr;
 	u8 *decrypted = NULL;
-	const u8 *identity, *id2;
-	size_t identity_len, id2_len;
 
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication");
 
@@ -1013,7 +1080,7 @@
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
 			   "included AT_COUNTER_TOO_SMALL - starting full "
 			   "authentication");
-		eap_aka_determine_identity(sm, data, 0, 1);
+		eap_aka_fullauth(sm, data);
 		return;
 	}
 
@@ -1024,35 +1091,19 @@
 	} else
 		eap_aka_state(data, SUCCESS);
 
-	if (data->reauth) {
-		identity = data->reauth->identity;
-		identity_len = data->reauth->identity_len;
-	} else {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	}
-
-	id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
-				       identity_len, &id2_len);
-	if (id2) {
-		identity = id2;
-		identity_len = id2_len;
-	}
-
 	if (data->next_reauth_id) {
 		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 #ifdef EAP_SERVER_AKA_PRIME
 			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
-						    identity,
-						    identity_len,
+						    data->permanent,
 						    data->next_reauth_id,
 						    data->counter + 1,
 						    data->k_encr, data->k_aut,
 						    data->k_re);
 #endif /* EAP_SERVER_AKA_PRIME */
 		} else {
-			eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
-					      identity_len,
+			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+					      data->permanent,
 					      data->next_reauth_id,
 					      data->counter + 1,
 					      data->mk);
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 6658d9c..f83c3cb 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-SIM (RFC 4186)
- * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -36,6 +36,8 @@
 	struct eap_sim_reauth *reauth;
 	u16 notification;
 	int use_result_ind;
+	int start_round;
+	char permanent[20]; /* Permanent username */
 };
 
 
@@ -105,26 +107,32 @@
 	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
 	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
 			       EAP_SIM_SUBTYPE_START);
-	if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
-				      sm->identity_len)) {
-		if (sm->identity_len > 0 &&
-		    sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) {
-			/* Reauth id may have expired - try fullauth */
-			wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
-			eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0,
-					NULL, 0);
-		} else {
-			wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
-			eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0,
-					NULL, 0);
-		}
-	} else {
+	data->start_round++;
+	if (data->start_round == 1) {
 		/*
 		 * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
 		 * ignored and the SIM/Start is used to request the identity.
 		 */
 		wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
 		eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+	} else if (data->start_round > 3) {
+		/* Cannot use more than three rounds of Start messages */
+		return NULL;
+	} else if (data->start_round == 0) {
+		/*
+		 * This is a special case that is used to recover from
+		 * AT_COUNTER_TOO_SMALL during re-authentication. Since we
+		 * already know the identity of the peer, there is no need to
+		 * request any identity in this case.
+		 */
+	} else if (sm->identity && sm->identity_len > 0 &&
+		   sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) {
+		/* Reauth id may have expired - try fullauth */
+		wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
+	} else {
+		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
 	}
 	wpa_printf(MSG_DEBUG, "   AT_VERSION_LIST");
 	ver[0] = 0;
@@ -337,18 +345,22 @@
 static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
 			     struct wpabuf *respData)
 {
-	struct eap_sim_data *data = priv;
 	const u8 *pos;
 	size_t len;
-	u8 subtype;
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
 	if (pos == NULL || len < 3) {
 		wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
 		return TRUE;
 	}
-	subtype = *pos;
 
+	return FALSE;
+}
+
+
+static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data,
+					  u8 subtype)
+{
 	if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
 		return FALSE;
 
@@ -402,85 +414,113 @@
 				  struct wpabuf *respData,
 				  struct eap_sim_attrs *attr)
 {
-	const u8 *identity;
 	size_t identity_len;
 	u8 ver_list[2];
+	u8 *new_identity;
+	char *username;
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
 
-	if (attr->identity) {
-		os_free(sm->identity);
-		sm->identity = os_malloc(attr->identity_len);
-		if (sm->identity) {
-			os_memcpy(sm->identity, attr->identity,
-				  attr->identity_len);
-			sm->identity_len = attr->identity_len;
-		}
+	if (data->start_round == 0) {
+		/*
+		 * Special case for AT_COUNTER_TOO_SMALL recovery - no identity
+		 * was requested since we already know it.
+		 */
+		goto skip_id_update;
 	}
 
-	identity = NULL;
-	identity_len = 0;
-
-	if (sm->identity && sm->identity_len > 0 &&
-	    sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	} else {
-		identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
-						    sm->identity,
-						    sm->identity_len,
-						    &identity_len);
-		if (identity == NULL) {
-			data->reauth = eap_sim_db_get_reauth_entry(
-				sm->eap_sim_db_priv, sm->identity,
-				sm->identity_len);
-			if (data->reauth) {
-				wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast "
-					   "re-authentication");
-				identity = data->reauth->identity;
-				identity_len = data->reauth->identity_len;
-				data->counter = data->reauth->counter;
-				os_memcpy(data->mk, data->reauth->mk,
-					  EAP_SIM_MK_LEN);
-			}
-		}
+	/*
+	 * We always request identity in SIM/Start, so the peer is required to
+	 * have replied with one.
+	 */
+	if (!attr->identity || attr->identity_len == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any "
+			   "identity");
+		goto failed;
 	}
 
-	if (identity == NULL) {
-		wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
-			   " user name");
-		eap_sim_state(data, FAILURE);
-		return;
-	}
+	new_identity = os_malloc(attr->identity_len);
+	if (new_identity == NULL)
+		goto failed;
+	os_free(sm->identity);
+	sm->identity = new_identity;
+	os_memcpy(sm->identity, attr->identity, attr->identity_len);
+	sm->identity_len = attr->identity_len;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
-			  identity, identity_len);
+			  sm->identity, sm->identity_len);
+	username = sim_get_username(sm->identity, sm->identity_len);
+	if (username == NULL)
+		goto failed;
 
-	if (data->reauth) {
+	if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'",
+			   username);
+		data->reauth = eap_sim_db_get_reauth_entry(
+			sm->eap_sim_db_priv, username);
+		os_free(username);
+		if (data->reauth == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth "
+				   "identity - request full auth identity");
+			/* Remain in START state for another round */
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication");
+		os_strlcpy(data->permanent, data->reauth->permanent,
+			   sizeof(data->permanent));
+		data->counter = data->reauth->counter;
+		os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
 		eap_sim_state(data, REAUTH);
 		return;
 	}
 
+	if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) {
+		const char *permanent;
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'",
+			   username);
+		permanent = eap_sim_db_get_permanent(
+			sm->eap_sim_db_priv, username);
+		os_free(username);
+		if (permanent == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym "
+				   "identity - request permanent identity");
+			/* Remain in START state for another round */
+			return;
+		}
+		os_strlcpy(data->permanent, permanent,
+			   sizeof(data->permanent));
+	} else if (username[0] == EAP_SIM_PERMANENT_PREFIX) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'",
+			   username);
+		os_strlcpy(data->permanent, username, sizeof(data->permanent));
+		os_free(username);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'",
+			   username);
+		os_free(username);
+		goto failed;
+	}
+
+skip_id_update:
+	/* Full authentication */
+
 	if (attr->nonce_mt == NULL || attr->selected_version < 0) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
 			   "required attributes");
-		eap_sim_state(data, FAILURE);
-		return;
+		goto failed;
 	}
 
 	if (!eap_sim_supported_ver(data, attr->selected_version)) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
 			   "version %d", attr->selected_version);
-		eap_sim_state(data, FAILURE);
-		return;
+		goto failed;
 	}
 
 	data->counter = 0; /* reset re-auth counter since this is full auth */
 	data->reauth = NULL;
 
 	data->num_chal = eap_sim_db_get_gsm_triplets(
-		sm->eap_sim_db_priv, identity, identity_len,
-		EAP_SIM_MAX_CHAL,
+		sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL,
 		(u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
 	if (data->num_chal == EAP_SIM_DB_PENDING) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
@@ -491,8 +531,7 @@
 	if (data->num_chal < 2) {
 		wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
 			   "authentication triplets for the peer");
-		eap_sim_state(data, FAILURE);
-		return;
+		goto failed;
 	}
 
 	identity_len = sm->identity_len;
@@ -513,6 +552,11 @@
 			    data->emsk);
 
 	eap_sim_state(data, CHALLENGE);
+	return;
+
+failed:
+	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+	eap_sim_state(data, NOTIFICATION);
 }
 
 
@@ -521,16 +565,14 @@
 				      struct wpabuf *respData,
 				      struct eap_sim_attrs *attr)
 {
-	const u8 *identity;
-	size_t identity_len;
-
 	if (attr->mac == NULL ||
 	    eap_sim_verify_mac(data->k_aut, respData, attr->mac,
 			       (u8 *) data->sres,
 			       data->num_chal * EAP_SIM_SRES_LEN)) {
 		wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
 			   "did not include valid AT_MAC");
-		eap_sim_state(data, FAILURE);
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_sim_state(data, NOTIFICATION);
 		return;
 	}
 
@@ -543,22 +585,13 @@
 	} else
 		eap_sim_state(data, SUCCESS);
 
-	identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
-					    sm->identity_len, &identity_len);
-	if (identity == NULL) {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	}
-
 	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-					 identity_len,
+		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
 					 data->next_pseudonym);
 		data->next_pseudonym = NULL;
 	}
 	if (data->next_reauth_id) {
-		eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
-				      identity_len,
+		eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
 				      data->next_reauth_id, data->counter + 1,
 				      data->mk);
 		data->next_reauth_id = NULL;
@@ -573,8 +606,6 @@
 {
 	struct eap_sim_attrs eattr;
 	u8 *decrypted = NULL;
-	const u8 *identity, *id2;
-	size_t identity_len, id2_len;
 
 	if (attr->mac == NULL ||
 	    eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
@@ -610,6 +641,16 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
 		   "the correct AT_MAC");
+
+	if (eattr.counter_too_small) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
+			   "included AT_COUNTER_TOO_SMALL - starting full "
+			   "authentication");
+		data->start_round = -1;
+		eap_sim_state(data, START);
+		return;
+	}
+
 	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
 		data->use_result_ind = 1;
 		data->notification = EAP_SIM_SUCCESS;
@@ -617,24 +658,9 @@
 	} else
 		eap_sim_state(data, SUCCESS);
 
-	if (data->reauth) {
-		identity = data->reauth->identity;
-		identity_len = data->reauth->identity_len;
-	} else {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	}
-
-	id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
-				       identity_len, &id2_len);
-	if (id2) {
-		identity = id2;
-		identity_len = id2_len;
-	}
-
 	if (data->next_reauth_id) {
-		eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
-				      identity_len, data->next_reauth_id,
+		eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
+				      data->next_reauth_id,
 				      data->counter + 1, data->mk);
 		data->next_reauth_id = NULL;
 	} else {
@@ -645,7 +671,8 @@
 	return;
 
 fail:
-	eap_sim_state(data, FAILURE);
+	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+	eap_sim_state(data, NOTIFICATION);
 	eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
 	data->reauth = NULL;
 	os_free(decrypted);
@@ -696,8 +723,24 @@
 	subtype = *pos;
 	pos += 3;
 
+	if (eap_sim_unexpected_subtype(data, subtype)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected "
+			   "EAP-SIM Subtype in EAP Response");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_sim_state(data, NOTIFICATION);
+		return;
+	}
+
 	if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
+		if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR &&
+		    (data->state == START || data->state == CHALLENGE ||
+		     data->state == REAUTH)) {
+			data->notification =
+				EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+			eap_sim_state(data, NOTIFICATION);
+			return;
+		}
 		eap_sim_state(data, FAILURE);
 		return;
 	}
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
index 68fb1f0..257013e 100644
--- a/src/eap_server/eap_sim_db.c
+++ b/src/eap_server/eap_sim_db.c
@@ -17,6 +17,9 @@
 
 #include "includes.h"
 #include <sys/un.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
 
 #include "common.h"
 #include "crypto/random.h"
@@ -26,15 +29,13 @@
 
 struct eap_sim_pseudonym {
 	struct eap_sim_pseudonym *next;
-	u8 *identity;
-	size_t identity_len;
-	char *pseudonym;
+	char *permanent; /* permanent username */
+	char *pseudonym; /* pseudonym username */
 };
 
 struct eap_sim_db_pending {
 	struct eap_sim_db_pending *next;
-	u8 imsi[20];
-	size_t imsi_len;
+	char imsi[20];
 	enum { PENDING, SUCCESS, FAILURE } state;
 	void *cb_session_ctx;
 	struct os_time timestamp;
@@ -66,19 +67,316 @@
 	struct eap_sim_pseudonym *pseudonyms;
 	struct eap_sim_reauth *reauths;
 	struct eap_sim_db_pending *pending;
+#ifdef CONFIG_SQLITE
+	sqlite3 *sqlite_db;
+	char db_tmp_identity[100];
+	char db_tmp_pseudonym_str[100];
+	struct eap_sim_pseudonym db_tmp_pseudonym;
+	struct eap_sim_reauth db_tmp_reauth;
+#endif /* CONFIG_SQLITE */
 };
 
 
+#ifdef CONFIG_SQLITE
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+	char cmd[128];
+	os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+	return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_pseudonym(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE pseudonyms("
+		"  permanent CHAR(21) PRIMARY KEY,"
+		"  pseudonym CHAR(21) NOT NULL"
+		");";
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
+		   "pseudonym information");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int db_table_create_reauth(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE reauth("
+		"  permanent CHAR(21) PRIMARY KEY,"
+		"  reauth_id CHAR(21) NOT NULL,"
+		"  counter INTEGER,"
+		"  mk CHAR(40),"
+		"  k_encr CHAR(32),"
+		"  k_aut CHAR(64),"
+		"  k_re CHAR(64)"
+		");";
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
+		   "reauth information");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static sqlite3 * db_open(const char *db_file)
+{
+	sqlite3 *db;
+
+	if (sqlite3_open(db_file, &db)) {
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database "
+			   "%s: %s", db_file, sqlite3_errmsg(db));
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	if (!db_table_exists(db, "pseudonyms") &&
+	    db_table_create_pseudonym(db) < 0) {
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	if (!db_table_exists(db, "reauth") &&
+	    db_table_create_reauth(db) < 0) {
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	return db;
+}
+
+
+static int valid_db_string(const char *str)
+{
+	const char *pos = str;
+	while (*pos) {
+		if ((*pos < '0' || *pos > '9') &&
+		    (*pos < 'a' || *pos > 'f'))
+			return 0;
+		pos++;
+	}
+	return 1;
+}
+
+
+static int db_add_pseudonym(struct eap_sim_db_data *data,
+			    const char *permanent, char *pseudonym)
+{
+	char cmd[128];
+	char *err = NULL;
+
+	if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) {
+		os_free(pseudonym);
+		return -1;
+	}
+
+	os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms "
+		    "(permanent, pseudonym) VALUES ('%s', '%s');",
+		    permanent, pseudonym);
+	os_free(pseudonym);
+	if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
+	{
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct eap_sim_db_data *data = ctx;
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
+			os_strlcpy(data->db_tmp_identity, argv[i],
+				   sizeof(data->db_tmp_identity));
+		}
+	}
+
+	return 0;
+}
+
+
+static char *
+db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym)
+{
+	char cmd[128];
+
+	if (!valid_db_string(pseudonym))
+		return NULL;
+	os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity));
+	os_snprintf(cmd, sizeof(cmd),
+		    "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';",
+		    pseudonym);
+	if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) !=
+	    SQLITE_OK)
+		return NULL;
+	if (data->db_tmp_identity[0] == '\0')
+		return NULL;
+	return data->db_tmp_identity;
+}
+
+
+static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+			 char *reauth_id, u16 counter, const u8 *mk,
+			 const u8 *k_encr, const u8 *k_aut, const u8 *k_re)
+{
+	char cmd[2000], *pos, *end;
+	char *err = NULL;
+
+	if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) {
+		os_free(reauth_id);
+		return -1;
+	}
+
+	pos = cmd;
+	end = pos + sizeof(cmd);
+	pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth "
+			   "(permanent, reauth_id, counter%s%s%s%s) "
+			   "VALUES ('%s', '%s', %u",
+			   mk ? ", mk" : "",
+			   k_encr ? ", k_encr" : "",
+			   k_aut ? ", k_aut" : "",
+			   k_re ? ", k_re" : "",
+			   permanent, reauth_id, counter);
+	os_free(reauth_id);
+
+	if (mk) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	if (k_encr) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, k_encr,
+					EAP_SIM_K_ENCR_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	if (k_aut) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, k_aut,
+					EAP_AKA_PRIME_K_AUT_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	if (k_re) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, k_re,
+					EAP_AKA_PRIME_K_RE_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	os_snprintf(pos, end - pos, ");");
+
+	if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
+	{
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct eap_sim_db_data *data = ctx;
+	int i;
+	struct eap_sim_reauth *reauth = &data->db_tmp_reauth;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
+			os_strlcpy(data->db_tmp_identity, argv[i],
+				   sizeof(data->db_tmp_identity));
+			reauth->permanent = data->db_tmp_identity;
+		} else if (os_strcmp(col[i], "counter") == 0 && argv[i]) {
+			reauth->counter = atoi(argv[i]);
+		} else if (os_strcmp(col[i], "mk") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk));
+		} else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->k_encr,
+				   sizeof(reauth->k_encr));
+		} else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->k_aut,
+				   sizeof(reauth->k_aut));
+		} else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->k_re,
+				   sizeof(reauth->k_re));
+		}
+	}
+
+	return 0;
+}
+
+
+static struct eap_sim_reauth *
+db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id)
+{
+	char cmd[256];
+
+	if (!valid_db_string(reauth_id))
+		return NULL;
+	os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth));
+	os_strlcpy(data->db_tmp_pseudonym_str, reauth_id,
+		   sizeof(data->db_tmp_pseudonym_str));
+	data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str;
+	os_snprintf(cmd, sizeof(cmd),
+		    "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id);
+	if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) !=
+	    SQLITE_OK)
+		return NULL;
+	if (data->db_tmp_reauth.permanent == NULL)
+		return NULL;
+	return &data->db_tmp_reauth;
+}
+
+
+static void db_remove_reauth(struct eap_sim_db_data *data,
+			     struct eap_sim_reauth *reauth)
+{
+	char cmd[256];
+
+	if (!valid_db_string(reauth->permanent))
+		return;
+	os_snprintf(cmd, sizeof(cmd),
+		    "DELETE FROM reauth WHERE permanent='%s';",
+		    reauth->permanent);
+	sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL);
+}
+
+#endif /* CONFIG_SQLITE */
+
+
 static struct eap_sim_db_pending *
-eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
-		       size_t imsi_len, int aka)
+eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka)
 {
 	struct eap_sim_db_pending *entry, *prev = NULL;
 
 	entry = data->pending;
 	while (entry) {
-		if (entry->aka == aka && entry->imsi_len == imsi_len &&
-		    os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
+		if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) {
 			if (prev)
 				prev->next = entry->next;
 			else
@@ -113,7 +411,7 @@
 	 * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
 	 */
 
-	entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
+	entry = eap_sim_db_get_pending(data, imsi, 0);
 	if (entry == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
 			   "received message found");
@@ -191,7 +489,7 @@
 	 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
 	 */
 
-	entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
+	entry = eap_sim_db_get_pending(data, imsi, 1);
 	if (entry == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
 			   "received message found");
@@ -390,11 +688,13 @@
  * @ctx: Context pointer for get_complete_cb
  * Returns: Pointer to a private data structure or %NULL on failure
  */
-void * eap_sim_db_init(const char *config,
-		       void (*get_complete_cb)(void *ctx, void *session_ctx),
-		       void *ctx)
+struct eap_sim_db_data *
+eap_sim_db_init(const char *config,
+		void (*get_complete_cb)(void *ctx, void *session_ctx),
+		void *ctx)
 {
 	struct eap_sim_db_data *data;
+	char *pos;
 
 	data = os_zalloc(sizeof(*data));
 	if (data == NULL)
@@ -406,6 +706,16 @@
 	data->fname = os_strdup(config);
 	if (data->fname == NULL)
 		goto fail;
+	pos = os_strstr(data->fname, " db=");
+	if (pos) {
+		*pos = '\0';
+#ifdef CONFIG_SQLITE
+		pos += 4;
+		data->sqlite_db = db_open(pos);
+		if (data->sqlite_db == NULL)
+			goto fail;
+#endif /* CONFIG_SQLITE */
+	}
 
 	if (os_strncmp(data->fname, "unix:", 5) == 0) {
 		if (eap_sim_db_open_socket(data)) {
@@ -427,7 +737,7 @@
 
 static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
 {
-	os_free(p->identity);
+	os_free(p->permanent);
 	os_free(p->pseudonym);
 	os_free(p);
 }
@@ -435,7 +745,7 @@
 
 static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
 {
-	os_free(r->identity);
+	os_free(r->permanent);
 	os_free(r->reauth_id);
 	os_free(r);
 }
@@ -452,6 +762,13 @@
 	struct eap_sim_reauth *r, *prevr;
 	struct eap_sim_db_pending *pending, *prev_pending;
 
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db) {
+		sqlite3_close(data->sqlite_db);
+		data->sqlite_db = NULL;
+	}
+#endif /* CONFIG_SQLITE */
+
 	eap_sim_db_close_socket(data);
 	os_free(data->fname);
 
@@ -518,9 +835,8 @@
 
 /**
  * eap_sim_db_get_gsm_triplets - Get GSM triplets
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username (prefix | IMSI)
  * @max_chal: Maximum number of triplets
  * @_rand: Buffer for RAND values
  * @kc: Buffer for Kc values
@@ -532,9 +848,6 @@
  * callback function registered with eap_sim_db_init() will be called once the
  * results become available.
  *
- * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
- * ASCII format.
- *
  * When using an external server for GSM triplets, this function can always
  * start a request and return EAP_SIM_DB_PENDING immediately if authentication
  * triplets are not available. Once the triplets are received, callback
@@ -543,39 +856,28 @@
  * function will then be called again and the newly received triplets will then
  * be given to the caller.
  */
-int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
-				size_t identity_len, int max_chal,
+int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
+				const char *username, int max_chal,
 				u8 *_rand, u8 *kc, u8 *sres,
 				void *cb_session_ctx)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_db_pending *entry;
 	int len, ret;
-	size_t i;
 	char msg[40];
+	const char *imsi;
+	size_t imsi_len;
 
-	if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
+	if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX ||
+	    username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+			   username);
 		return EAP_SIM_DB_FAILURE;
 	}
-	identity++;
-	identity_len--;
-	for (i = 0; i < identity_len; i++) {
-		if (identity[i] == '@') {
-			identity_len = i;
-			break;
-		}
-	}
-	if (identity_len + 1 > sizeof(entry->imsi)) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
-		return EAP_SIM_DB_FAILURE;
-	}
-	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
-			  identity, identity_len);
+	imsi = username + 1;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'",
+		   imsi);
 
-	entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
+	entry = eap_sim_db_get_pending(data, imsi, 0);
 	if (entry) {
 		int num_chal;
 		if (entry->state == FAILURE) {
@@ -610,18 +912,19 @@
 			return EAP_SIM_DB_FAILURE;
 	}
 
+	imsi_len = os_strlen(imsi);
 	len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
-	if (len < 0 || len + identity_len >= sizeof(msg))
+	if (len < 0 || len + imsi_len >= sizeof(msg))
 		return EAP_SIM_DB_FAILURE;
-	os_memcpy(msg + len, identity, identity_len);
-	len += identity_len;
+	os_memcpy(msg + len, imsi, imsi_len);
+	len += imsi_len;
 	ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
 	if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
 		return EAP_SIM_DB_FAILURE;
 	len += ret;
 
-	wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
-		    "data for IMSI", identity, identity_len);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
+		   "data for IMSI '%s'", imsi);
 	if (eap_sim_db_send(data, msg, len) < 0)
 		return EAP_SIM_DB_FAILURE;
 
@@ -630,8 +933,7 @@
 		return EAP_SIM_DB_FAILURE;
 
 	os_get_time(&entry->timestamp);
-	os_memcpy(entry->imsi, identity, identity_len);
-	entry->imsi_len = identity_len;
+	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
 	entry->cb_session_ctx = cb_session_ctx;
 	entry->state = PENDING;
 	eap_sim_db_add_pending(data, entry);
@@ -641,196 +943,6 @@
 }
 
 
-static struct eap_sim_pseudonym *
-eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
-			 size_t identity_len)
-{
-	char *pseudonym;
-	size_t len;
-	struct eap_sim_pseudonym *p;
-
-	if (identity_len == 0 ||
-	    (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
-	     identity[0] != EAP_AKA_PSEUDONYM_PREFIX &&
-	     identity[0] != EAP_AKA_PRIME_PSEUDONYM_PREFIX))
-		return NULL;
-
-	/* Remove possible realm from identity */
-	len = 0;
-	while (len < identity_len) {
-		if (identity[len] == '@')
-			break;
-		len++;
-	}
-
-	pseudonym = os_malloc(len + 1);
-	if (pseudonym == NULL)
-		return NULL;
-	os_memcpy(pseudonym, identity, len);
-	pseudonym[len] = '\0';
-
-	p = data->pseudonyms;
-	while (p) {
-		if (os_strcmp(p->pseudonym, pseudonym) == 0)
-			break;
-		p = p->next;
-	}
-
-	os_free(pseudonym);
-
-	return p;
-}
-
-
-static struct eap_sim_pseudonym *
-eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
-			    size_t identity_len)
-{
-	struct eap_sim_pseudonym *p;
-
-	if (identity_len == 0 ||
-	    (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
-	     identity[0] != EAP_AKA_PERMANENT_PREFIX &&
-	     identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX))
-		return NULL;
-
-	p = data->pseudonyms;
-	while (p) {
-		if (identity_len == p->identity_len &&
-		    os_memcmp(p->identity, identity, identity_len) == 0)
-			break;
-		p = p->next;
-	}
-
-	return p;
-}
-
-
-static struct eap_sim_reauth *
-eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
-		      size_t identity_len)
-{
-	char *reauth_id;
-	size_t len;
-	struct eap_sim_reauth *r;
-
-	if (identity_len == 0 ||
-	    (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
-	     identity[0] != EAP_AKA_REAUTH_ID_PREFIX &&
-	     identity[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX))
-		return NULL;
-
-	/* Remove possible realm from identity */
-	len = 0;
-	while (len < identity_len) {
-		if (identity[len] == '@')
-			break;
-		len++;
-	}
-
-	reauth_id = os_malloc(len + 1);
-	if (reauth_id == NULL)
-		return NULL;
-	os_memcpy(reauth_id, identity, len);
-	reauth_id[len] = '\0';
-
-	r = data->reauths;
-	while (r) {
-		if (os_strcmp(r->reauth_id, reauth_id) == 0)
-			break;
-		r = r->next;
-	}
-
-	os_free(reauth_id);
-
-	return r;
-}
-
-
-static struct eap_sim_reauth *
-eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
-			 size_t identity_len)
-{
-	struct eap_sim_pseudonym *p;
-	struct eap_sim_reauth *r;
-
-	if (identity_len == 0)
-		return NULL;
-
-	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
-	if (p == NULL)
-		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
-	if (p) {
-		identity = p->identity;
-		identity_len = p->identity_len;
-	}
-
-	r = data->reauths;
-	while (r) {
-		if (identity_len == r->identity_len &&
-		    os_memcmp(r->identity, identity, identity_len) == 0)
-			break;
-		r = r->next;
-	}
-
-	return r;
-}
-
-
-/**
- * eap_sim_db_identity_known - Verify whether the given identity is known
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes 
- * Returns: 0 if the user is found or -1 on failure
- *
- * In most cases, the user name is ['0','1','6'] | IMSI, i.e., 1 followed by
- * the IMSI in ASCII format for EAP-SIM, ['2','3','7'] | pseudonym, or
- * ['4','5','7'] | reauth_id.
- */
-int eap_sim_db_identity_known(void *priv, const u8 *identity,
-			      size_t identity_len)
-{
-	struct eap_sim_db_data *data = priv;
-
-	if (identity == NULL || identity_len < 2)
-		return -1;
-
-	if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
-	    identity[0] == EAP_AKA_PSEUDONYM_PREFIX ||
-	    identity[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) {
-		struct eap_sim_pseudonym *p =
-			eap_sim_db_get_pseudonym(data, identity, identity_len);
-		return p ? 0 : -1;
-	}
-
-	if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
-	    identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
-	    identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) {
-		struct eap_sim_reauth *r =
-			eap_sim_db_get_reauth(data, identity, identity_len);
-		return r ? 0 : -1;
-	}
-
-	if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
-	    identity[0] != EAP_AKA_PERMANENT_PREFIX &&
-	    identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) {
-		/* Unknown identity prefix */
-		return -1;
-	}
-
-	/* TODO: Should consider asking HLR/AuC gateway whether this permanent
-	 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
-	 * In case of EAP-AKA, this would reduce number of needed round-trips.
-	 * Ideally, this would be done with one wait, i.e., just request
-	 * authentication data and store it for the next use. This would then
-	 * need to use similar pending-request functionality as the normal
-	 * request for authentication data at later phase.
-	 */
-	return -1;
-}
-
-
 static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
 {
 	char *id, *pos, *end;
@@ -853,7 +965,7 @@
 
 /**
  * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
- * @priv: Private data pointer from eap_sim_db_init()
+ * @data: Private data pointer from eap_sim_db_init()
  * @method: EAP method (SIM/AKA/AKA')
  * Returns: Next pseudonym (allocated string) or %NULL on failure
  *
@@ -862,9 +974,9 @@
  * with eap_sim_db_add_pseudonym() once the authentication has been completed
  * successfully. Caller is responsible for freeing the returned buffer.
  */
-char * eap_sim_db_get_next_pseudonym(void *priv, enum eap_sim_db_method method)
+char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method)
 {
-	struct eap_sim_db_data *data = priv;
 	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
 
 	switch (method) {
@@ -885,7 +997,7 @@
 
 /**
  * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
- * @priv: Private data pointer from eap_sim_db_init()
+ * @data: Private data pointer from eap_sim_db_init()
  * @method: EAP method (SIM/AKA/AKA')
  * Returns: Next reauth_id (allocated string) or %NULL on failure
  *
@@ -895,9 +1007,9 @@
  * has been completed successfully. Caller is responsible for freeing the
  * returned buffer.
  */
-char * eap_sim_db_get_next_reauth_id(void *priv, enum eap_sim_db_method method)
+char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method)
 {
-	struct eap_sim_db_data *data = priv;
 	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
 
 	switch (method) {
@@ -918,9 +1030,8 @@
 
 /**
  * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
- * @identity_len: Length of identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @permanent: Permanent username
  * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
  * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
  * free it.
@@ -929,20 +1040,22 @@
  * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
  * responsible of freeing pseudonym buffer once it is not needed anymore.
  */
-int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
-			     size_t identity_len, char *pseudonym)
+int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
+			     const char *permanent, char *pseudonym)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_pseudonym *p;
-	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
-			  identity, identity_len);
-	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent "
+		   "username '%s'", pseudonym, permanent);
 
 	/* TODO: could store last two pseudonyms */
-	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
-	if (p == NULL)
-		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
-
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_add_pseudonym(data, permanent, pseudonym);
+#endif /* CONFIG_SQLITE */
+	for (p = data->pseudonyms; p; p = p->next) {
+		if (os_strcmp(permanent, p->permanent) == 0)
+			break;
+	}
 	if (p) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
 			   "pseudonym: %s", p->pseudonym);
@@ -958,14 +1071,12 @@
 	}
 
 	p->next = data->pseudonyms;
-	p->identity = os_malloc(identity_len);
-	if (p->identity == NULL) {
+	p->permanent = os_strdup(permanent);
+	if (p->permanent == NULL) {
 		os_free(p);
 		os_free(pseudonym);
 		return -1;
 	}
-	os_memcpy(p->identity, identity, identity_len);
-	p->identity_len = identity_len;
 	p->pseudonym = pseudonym;
 	data->pseudonyms = p;
 
@@ -975,18 +1086,16 @@
 
 
 static struct eap_sim_reauth *
-eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
-			   size_t identity_len, char *reauth_id, u16 counter)
+eap_sim_db_add_reauth_data(struct eap_sim_db_data *data,
+			   const char *permanent,
+			   char *reauth_id, u16 counter)
 {
 	struct eap_sim_reauth *r;
 
-	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
-			  identity, identity_len);
-	wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
-
-	r = eap_sim_db_get_reauth(data, identity, identity_len);
-	if (r == NULL)
-		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+	for (r = data->reauths; r; r = r->next) {
+		if (os_strcmp(r->permanent, permanent) == 0)
+			break;
+	}
 
 	if (r) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
@@ -1001,14 +1110,12 @@
 		}
 
 		r->next = data->reauths;
-		r->identity = os_malloc(identity_len);
-		if (r->identity == NULL) {
+		r->permanent = os_strdup(permanent);
+		if (r->permanent == NULL) {
 			os_free(r);
 			os_free(reauth_id);
 			return NULL;
 		}
-		os_memcpy(r->identity, identity, identity_len);
-		r->identity_len = identity_len;
 		r->reauth_id = reauth_id;
 		data->reauths = r;
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
@@ -1023,7 +1130,7 @@
 /**
  * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
  * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @permanent: Permanent username
  * @identity_len: Length of identity
  * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
  * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
@@ -1036,20 +1143,24 @@
  * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
  * anymore.
  */
-int eap_sim_db_add_reauth(void *priv, const u8 *identity,
-			  size_t identity_len, char *reauth_id, u16 counter,
-			  const u8 *mk)
+int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+			  char *reauth_id, u16 counter, const u8 *mk)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_reauth *r;
 
-	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
-				       counter);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
+		   "identity '%s'", reauth_id, permanent);
+
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_add_reauth(data, permanent, reauth_id, counter, mk,
+				     NULL, NULL, NULL);
+#endif /* CONFIG_SQLITE */
+	r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
 	if (r == NULL)
 		return -1;
 
 	os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
-	r->aka_prime = 0;
 
 	return 0;
 }
@@ -1058,9 +1169,8 @@
 #ifdef EAP_SERVER_AKA_PRIME
 /**
  * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
- * @identity_len: Length of identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @permanent: Permanent username
  * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
  * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
  * free it.
@@ -1074,20 +1184,25 @@
  * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
  * anymore.
  */
-int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
-				size_t identity_len, char *reauth_id,
-				u16 counter, const u8 *k_encr, const u8 *k_aut,
-				const u8 *k_re)
+int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
+				const char *permanent, char *reauth_id,
+				u16 counter, const u8 *k_encr,
+				const u8 *k_aut, const u8 *k_re)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_reauth *r;
 
-	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
-				       counter);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
+		   "identity '%s'", reauth_id, permanent);
+
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_add_reauth(data, permanent, reauth_id, counter, NULL,
+				     k_encr, k_aut, k_re);
+#endif /* CONFIG_SQLITE */
+	r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
 	if (r == NULL)
 		return -1;
 
-	r->aka_prime = 1;
 	os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
 	os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
 	os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
@@ -1099,66 +1214,75 @@
 
 /**
  * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
- * @identity_len: Length of identity
- * @len: Buffer for length of the returned permanent identity
- * Returns: Pointer to the permanent identity, or %NULL if not found
+ * @data: Private data pointer from eap_sim_db_init()
+ * @pseudonym: Pseudonym username
+ * Returns: Pointer to permanent username or %NULL if not found
  */
-const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
-				    size_t identity_len, size_t *len)
+const char *
+eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_pseudonym *p;
 
-	if (identity == NULL)
-		return NULL;
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_get_pseudonym(data, pseudonym);
+#endif /* CONFIG_SQLITE */
 
-	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
-	if (p == NULL)
-		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
-	if (p == NULL)
-		return NULL;
+	p = data->pseudonyms;
+	while (p) {
+		if (os_strcmp(p->pseudonym, pseudonym) == 0)
+			return p->permanent;
+		p = p->next;
+	}
 
-	*len = p->identity_len;
-	return p->identity;
+	return NULL;
 }
 
 
 /**
  * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity, pseudonym, or
- * reauth_id)
- * @identity_len: Length of identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @reauth_id: Fast re-authentication username
  * Returns: Pointer to the re-auth entry, or %NULL if not found
  */
 struct eap_sim_reauth *
-eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
-			    size_t identity_len)
+eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data,
+			    const char *reauth_id)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_reauth *r;
 
-	if (identity == NULL)
-		return NULL;
-	r = eap_sim_db_get_reauth(data, identity, identity_len);
-	if (r == NULL)
-		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_get_reauth(data, reauth_id);
+#endif /* CONFIG_SQLITE */
+
+	r = data->reauths;
+	while (r) {
+		if (os_strcmp(r->reauth_id, reauth_id) == 0)
+			break;
+		r = r->next;
+	}
+
 	return r;
 }
 
 
 /**
  * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
- * @priv: Private data pointer from eap_sim_db_init()
+ * @data: Private data pointer from eap_sim_db_init()
  * @reauth: Pointer to re-authentication entry from
  * eap_sim_db_get_reauth_entry()
  */
-void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
+void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
+			      struct eap_sim_reauth *reauth)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_reauth *r, *prev = NULL;
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db) {
+		db_remove_reauth(data, reauth);
+		return;
+	}
+#endif /* CONFIG_SQLITE */
 	r = data->reauths;
 	while (r) {
 		if (r == reauth) {
@@ -1177,9 +1301,8 @@
 
 /**
  * eap_sim_db_get_aka_auth - Get AKA authentication values
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username (prefix | IMSI)
  * @_rand: Buffer for RAND value
  * @autn: Buffer for AUTN value
  * @ik: Buffer for IK value
@@ -1192,9 +1315,6 @@
  * case, the callback function registered with eap_sim_db_init() will be
  * called once the results become available.
  *
- * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
- * ASCII format for EAP-AKA and '6' | IMSI for EAP-AKA'.
- *
  * When using an external server for AKA authentication, this function can
  * always start a request and return EAP_SIM_DB_PENDING immediately if
  * authentication triplets are not available. Once the authentication data are
@@ -1203,41 +1323,29 @@
  * eap_sim_db_get_aka_auth() function will then be called again and the newly
  * received triplets will then be given to the caller.
  */
-int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
-			    size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
-			    u8 *ck, u8 *res, size_t *res_len,
-			    void *cb_session_ctx)
+int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
+			    u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
+			    u8 *res, size_t *res_len, void *cb_session_ctx)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_db_pending *entry;
 	int len;
-	size_t i;
 	char msg[40];
+	const char *imsi;
+	size_t imsi_len;
 
-	if (identity_len < 2 || identity == NULL ||
-	    (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
-	     identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
+	if (username == NULL ||
+	    (username[0] != EAP_AKA_PERMANENT_PREFIX &&
+	     username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+	    username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+			   username);
 		return EAP_SIM_DB_FAILURE;
 	}
-	identity++;
-	identity_len--;
-	for (i = 0; i < identity_len; i++) {
-		if (identity[i] == '@') {
-			identity_len = i;
-			break;
-		}
-	}
-	if (identity_len + 1 > sizeof(entry->imsi)) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
-		return EAP_SIM_DB_FAILURE;
-	}
-	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
-			  identity, identity_len);
+	imsi = username + 1;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
+		   imsi);
 
-	entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
+	entry = eap_sim_db_get_pending(data, imsi, 1);
 	if (entry) {
 		if (entry->state == FAILURE) {
 			os_free(entry);
@@ -1268,14 +1376,15 @@
 			return EAP_SIM_DB_FAILURE;
 	}
 
+	imsi_len = os_strlen(imsi);
 	len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
-	if (len < 0 || len + identity_len >= sizeof(msg))
+	if (len < 0 || len + imsi_len >= sizeof(msg))
 		return EAP_SIM_DB_FAILURE;
-	os_memcpy(msg + len, identity, identity_len);
-	len += identity_len;
+	os_memcpy(msg + len, imsi, imsi_len);
+	len += imsi_len;
 
-	wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
-		    "data for IMSI", identity, identity_len);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
+		    "data for IMSI '%s'", imsi);
 	if (eap_sim_db_send(data, msg, len) < 0)
 		return EAP_SIM_DB_FAILURE;
 
@@ -1285,8 +1394,7 @@
 
 	os_get_time(&entry->timestamp);
 	entry->aka = 1;
-	os_memcpy(entry->imsi, identity, identity_len);
-	entry->imsi_len = identity_len;
+	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
 	entry->cb_session_ctx = cb_session_ctx;
 	entry->state = PENDING;
 	eap_sim_db_add_pending(data, entry);
@@ -1298,9 +1406,8 @@
 
 /**
  * eap_sim_db_resynchronize - Resynchronize AKA AUTN
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username
  * @auts: AUTS value from the peer
  * @_rand: RAND value used in the rejected message
  * Returns: 0 on success, -1 on failure
@@ -1311,43 +1418,35 @@
  * eap_sim_db_get_aka_auth() will be called again to to fetch updated
  * RAND/AUTN values for the next challenge.
  */
-int eap_sim_db_resynchronize(void *priv, const u8 *identity,
-			     size_t identity_len, const u8 *auts,
-			     const u8 *_rand)
+int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
+			     const char *username,
+			     const u8 *auts, const u8 *_rand)
 {
-	struct eap_sim_db_data *data = priv;
-	size_t i;
+	const char *imsi;
+	size_t imsi_len;
 
-	if (identity_len < 2 || identity == NULL ||
-	    (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
-	     identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
+	if (username == NULL ||
+	    (username[0] != EAP_AKA_PERMANENT_PREFIX &&
+	     username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+	    username[1] == '\0' || os_strlen(username) > 20) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+			   username);
 		return -1;
 	}
-	identity++;
-	identity_len--;
-	for (i = 0; i < identity_len; i++) {
-		if (identity[i] == '@') {
-			identity_len = i;
-			break;
-		}
-	}
-	if (identity_len > 20) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
-		return -1;
-	}
+	imsi = username + 1;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
+		   imsi);
 
 	if (data->sock >= 0) {
 		char msg[100];
 		int len, ret;
 
+		imsi_len = os_strlen(imsi);
 		len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
-		if (len < 0 || len + identity_len >= sizeof(msg))
+		if (len < 0 || len + imsi_len >= sizeof(msg))
 			return -1;
-		os_memcpy(msg + len, identity, identity_len);
-		len += identity_len;
+		os_memcpy(msg + len, imsi, imsi_len);
+		len += imsi_len;
 
 		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
 		if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
@@ -1361,11 +1460,42 @@
 		len += ret;
 		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
 					_rand, EAP_AKA_RAND_LEN);
-		wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
-			    "IMSI", identity, identity_len);
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
+			   "IMSI '%s'", imsi);
 		if (eap_sim_db_send(data, msg, len) < 0)
 			return -1;
 	}
 
 	return 0;
 }
+
+
+/**
+ * sim_get_username - Extract username from SIM identity
+ * @identity: Identity
+ * @identity_len: Identity length
+ * Returns: Allocated buffer with the username part of the identity
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char * sim_get_username(const u8 *identity, size_t identity_len)
+{
+	char *username;
+	size_t pos;
+
+	if (identity == NULL)
+		return NULL;
+
+	for (pos = 0; pos < identity_len; pos++) {
+		if (identity[pos] == '@' || identity[pos] == '\0')
+			break;
+	}
+
+	username = os_malloc(pos + 1);
+	if (username == NULL)
+		return NULL;
+	os_memcpy(username, identity, pos);
+	username[pos] = '\0';
+
+	return username;
+}
diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h
index 1f6375a..53a1a7c 100644
--- a/src/eap_server/eap_sim_db.h
+++ b/src/eap_server/eap_sim_db.h
@@ -28,50 +28,47 @@
 	EAP_SIM_DB_AKA_PRIME
 };
 
-void * eap_sim_db_init(const char *config,
-		       void (*get_complete_cb)(void *ctx, void *session_ctx),
-		       void *ctx);
+struct eap_sim_db_data;
+
+struct eap_sim_db_data *
+eap_sim_db_init(const char *config,
+		void (*get_complete_cb)(void *ctx, void *session_ctx),
+		void *ctx);
 
 void eap_sim_db_deinit(void *priv);
 
-int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
-				size_t identity_len, int max_chal,
+int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
+				const char *username, int max_chal,
 				u8 *_rand, u8 *kc, u8 *sres,
 				void *cb_session_ctx);
 
 #define EAP_SIM_DB_FAILURE -1
 #define EAP_SIM_DB_PENDING -2
 
-int eap_sim_db_identity_known(void *priv, const u8 *identity,
-			      size_t identity_len);
-
-char * eap_sim_db_get_next_pseudonym(void *priv,
+char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
 				     enum eap_sim_db_method method);
 
-char * eap_sim_db_get_next_reauth_id(void *priv,
+char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
 				     enum eap_sim_db_method method);
 
-int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
-			     size_t identity_len, char *pseudonym);
+int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
+			     const char *permanent, char *pseudonym);
 
-int eap_sim_db_add_reauth(void *priv, const u8 *identity,
-			  size_t identity_len, char *reauth_id, u16 counter,
-			  const u8 *mk);
-int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
-				size_t identity_len, char *reauth_id,
-				u16 counter, const u8 *k_encr, const u8 *k_aut,
-				const u8 *k_re);
+int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+			  char *reauth_id, u16 counter, const u8 *mk);
+int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
+				const char *permanent,
+				char *reauth_id, u16 counter, const u8 *k_encr,
+				const u8 *k_aut, const u8 *k_re);
 
-const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
-				    size_t identity_len, size_t *len);
+const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data,
+				      const char *pseudonym);
 
 struct eap_sim_reauth {
 	struct eap_sim_reauth *next;
-	u8 *identity;
-	size_t identity_len;
-	char *reauth_id;
+	char *permanent; /* Permanent username */
+	char *reauth_id; /* Fast re-authentication username */
 	u16 counter;
-	int aka_prime;
 	u8 mk[EAP_SIM_MK_LEN];
 	u8 k_encr[EAP_SIM_K_ENCR_LEN];
 	u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
@@ -79,18 +76,20 @@
 };
 
 struct eap_sim_reauth *
-eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
-			    size_t identity_len);
+eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data,
+			    const char *reauth_id);
 
-void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth);
+void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
+			      struct eap_sim_reauth *reauth);
 
-int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
-			    size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
-			    u8 *ck, u8 *res, size_t *res_len,
-			    void *cb_session_ctx);
+int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
+			    u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
+			    u8 *res, size_t *res_len, void *cb_session_ctx);
 
-int eap_sim_db_resynchronize(void *priv, const u8 *identity,
-			     size_t identity_len, const u8 *auts,
+int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
+			     const char *username, const u8 *auts,
 			     const u8 *_rand);
 
+char * sim_get_username(const u8 *identity, size_t identity_len);
+
 #endif /* EAP_SIM_DB_H */
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index e3bfa38..851cf49 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -1825,6 +1825,15 @@
 }
 
 
+static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->set_anon_id)
+		sm->ctx->set_anon_id(sm->ctx->ctx, id, len);
+}
+
+
 static struct eapol_callbacks eapol_cb =
 {
 	eapol_sm_get_config,
@@ -1838,7 +1847,8 @@
 	eapol_sm_notify_pending,
 	eapol_sm_eap_param_needed,
 	eapol_sm_notify_cert,
-	eapol_sm_notify_status
+	eapol_sm_notify_status,
+	eapol_sm_set_anon_id
 };
 
 
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index d2a4b94..c4b87da 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -239,6 +239,14 @@
 	 */
 	void (*status_cb)(void *ctx, const char *status,
 			  const char *parameter);
+
+	/**
+	 * set_anon_id - Set or add anonymous identity
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @id: Anonymous identity (e.g., EAP-SIM pseudonym)
+	 * @len: Length of anonymous identity in octets
+	 */
+	void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
 };
 
 
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index df24c64..d26654b 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -30,6 +30,9 @@
 		for (i = 0; i < p2p->num_groups; i++) {
 			struct p2p_group *g = p2p->groups[i];
 			struct wpabuf *ie;
+			if (os_memcmp(p2p_group_get_interface_addr(g),
+				      p2p->inv_bssid, ETH_ALEN) != 0)
+				continue;
 			ie = p2p_group_get_wfd_ie(g);
 			if (ie) {
 				wfd_ie = ie;
@@ -101,8 +104,8 @@
 		for (i = 0; i < p2p->num_groups; i++) {
 			struct p2p_group *g = p2p->groups[i];
 			struct wpabuf *ie;
-			if (!p2p_group_is_group_id_match(g, group_bssid,
-							 ETH_ALEN))
+			if (os_memcmp(p2p_group_get_interface_addr(g),
+				      group_bssid, ETH_ALEN) != 0)
 				continue;
 			ie = p2p_group_get_wfd_ie(g);
 			if (ie) {
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index 5e4872e..f2bac34 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -270,12 +270,7 @@
 	/* Include only the selected cipher in pairwise cipher suite */
 	WPA_PUT_LE16(pos, 1);
 	pos += 2;
-	if (cipher == WPA_CIPHER_CCMP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	else if (cipher == WPA_CIPHER_GCMP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
-	else if (cipher == WPA_CIPHER_TKIP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher));
 	pos += RSN_SELECTOR_LEN;
 
 	hdr->len = (pos - peerkey->rsnie_p) - 2;
@@ -1063,22 +1058,8 @@
 	count_pos = pos;
 	pos += 2;
 
-	count = 0;
-	if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-		pos += RSN_SELECTOR_LEN;
-		count++;
-	}
-	if (sm->allowed_pairwise_cipher & WPA_CIPHER_GCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
-		pos += RSN_SELECTOR_LEN;
-		count++;
-	}
-	if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-		pos += RSN_SELECTOR_LEN;
-		count++;
-	}
+	count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher);
+	pos += count * RSN_SELECTOR_LEN;
 	WPA_PUT_LE16(count_pos, count);
 
 	hdr->len = (pos - peerkey->rsnie_i) - 2;
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index bcd5951..5cf32df 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -520,33 +520,23 @@
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 		"WPA: Installing PTK to the driver");
 
-	switch (sm->pairwise_cipher) {
-	case WPA_CIPHER_CCMP:
-		alg = WPA_ALG_CCMP;
-		keylen = 16;
-		rsclen = 6;
-		break;
-	case WPA_CIPHER_GCMP:
-		alg = WPA_ALG_GCMP;
-		keylen = 16;
-		rsclen = 6;
-		break;
-	case WPA_CIPHER_TKIP:
-		alg = WPA_ALG_TKIP;
-		keylen = 32;
-		rsclen = 6;
-		break;
-	case WPA_CIPHER_NONE:
+	if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
 			"Suite: NONE - do not use pairwise keys");
 		return 0;
-	default:
+	}
+
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Unsupported pairwise cipher %d",
 			sm->pairwise_cipher);
 		return -1;
 	}
 
+	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
+
 	if (sm->proto == WPA_PROTO_RSN) {
 		key_rsc = null_rsc;
 	} else {
@@ -579,63 +569,25 @@
 					     int *key_rsc_len,
 					     enum wpa_alg *alg)
 {
-	int ret = 0;
+	int klen;
 
-	switch (group_cipher) {
-	case WPA_CIPHER_CCMP:
-		if (keylen != 16 || maxkeylen < 16) {
-			ret = -1;
-			break;
-		}
-		*key_rsc_len = 6;
-		*alg = WPA_ALG_CCMP;
-		break;
-	case WPA_CIPHER_GCMP:
-		if (keylen != 16 || maxkeylen < 16) {
-			ret = -1;
-			break;
-		}
-		*key_rsc_len = 6;
-		*alg = WPA_ALG_GCMP;
-		break;
-	case WPA_CIPHER_TKIP:
-		if (keylen != 32 || maxkeylen < 32) {
-			ret = -1;
-			break;
-		}
-		*key_rsc_len = 6;
-		*alg = WPA_ALG_TKIP;
-		break;
-	case WPA_CIPHER_WEP104:
-		if (keylen != 13 || maxkeylen < 13) {
-			ret = -1;
-			break;
-		}
-		*key_rsc_len = 0;
-		*alg = WPA_ALG_WEP;
-		break;
-	case WPA_CIPHER_WEP40:
-		if (keylen != 5 || maxkeylen < 5) {
-			ret = -1;
-			break;
-		}
-		*key_rsc_len = 0;
-		*alg = WPA_ALG_WEP;
-		break;
-	default:
+	*alg = wpa_cipher_to_alg(group_cipher);
+	if (*alg == WPA_ALG_NONE) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Unsupported Group Cipher %d",
 			group_cipher);
 		return -1;
 	}
+	*key_rsc_len = wpa_cipher_rsc_len(group_cipher);
 
-	if (ret < 0 ) {
+	klen = wpa_cipher_key_len(group_cipher);
+	if (keylen != klen || maxkeylen < klen) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Unsupported %s Group Cipher key length %d (%d)",
 			wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+		return -1;
 	}
-
-	return ret;
+	return 0;
 }
 
 
@@ -1135,31 +1087,12 @@
 	}
 
 	keylen = WPA_GET_BE16(key->key_length);
-	switch (sm->pairwise_cipher) {
-	case WPA_CIPHER_CCMP:
-		if (keylen != 16) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: Invalid CCMP key length %d (src=" MACSTR
-				")", keylen, MAC2STR(sm->bssid));
-			goto failed;
-		}
-		break;
-	case WPA_CIPHER_GCMP:
-		if (keylen != 16) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: Invalid GCMP key length %d (src=" MACSTR
-				")", keylen, MAC2STR(sm->bssid));
-			goto failed;
-		}
-		break;
-	case WPA_CIPHER_TKIP:
-		if (keylen != 32) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: Invalid TKIP key length %d (src=" MACSTR
-				")", keylen, MAC2STR(sm->bssid));
-			goto failed;
-		}
-		break;
+	if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Invalid %s key length %d (src=" MACSTR
+			")", wpa_cipher_txt(sm->pairwise_cipher), keylen,
+			MAC2STR(sm->bssid));
+		goto failed;
 	}
 
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
@@ -1880,25 +1813,6 @@
 
 
 #ifdef CONFIG_CTRL_IFACE
-static int wpa_cipher_bits(int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_CCMP:
-		return 128;
-	case WPA_CIPHER_GCMP:
-		return 128;
-	case WPA_CIPHER_TKIP:
-		return 256;
-	case WPA_CIPHER_WEP104:
-		return 104;
-	case WPA_CIPHER_WEP40:
-		return 40;
-	default:
-		return 0;
-	}
-}
-
-
 static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
 {
 	switch (sm->key_mgmt) {
@@ -1930,32 +1844,6 @@
 }
 
 
-static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_CCMP:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
-	case WPA_CIPHER_GCMP:
-		return RSN_CIPHER_SUITE_GCMP;
-	case WPA_CIPHER_TKIP:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
-	case WPA_CIPHER_WEP104:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
-	case WPA_CIPHER_WEP40:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
-	case WPA_CIPHER_NONE:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
-	default:
-		return 0;
-	}
-}
-
-
 #define RSN_SUITE "%02x-%02x-%02x-%d"
 #define RSN_SUITE_ARG(s) \
 ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
@@ -2003,7 +1891,7 @@
 			  rsna ? "TRUE" : "FALSE",
 			  rsna ? "TRUE" : "FALSE",
 			  RSN_VERSION,
-			  wpa_cipher_bits(sm->group_cipher),
+			  wpa_cipher_key_len(sm->group_cipher) * 8,
 			  sm->dot11RSNAConfigPMKLifetime,
 			  sm->dot11RSNAConfigPMKReauthThreshold,
 			  sm->dot11RSNAConfigSATimeout);
@@ -2023,12 +1911,16 @@
 		"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
 		"dot11RSNA4WayHandshakeFailures=%u\n",
 		RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
-		RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
-		RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->pairwise_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->group_cipher)),
 		pmkid_txt,
 		RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
-		RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
-		RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->pairwise_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->group_cipher)),
 		sm->dot11RSNA4WayHandshakeFailures);
 	if (ret >= 0 && (size_t) ret < buflen)
 		len += ret;
@@ -2717,33 +2609,10 @@
 	os_memset(&igd, 0, sizeof(igd));
 #endif /* CONFIG_IEEE80211W */
 
-	switch (sm->group_cipher) {
-	case WPA_CIPHER_CCMP:
-		keylen = 16;
-		gd.key_rsc_len = 6;
-		gd.alg = WPA_ALG_CCMP;
-		break;
-	case WPA_CIPHER_GCMP:
-		keylen = 16;
-		gd.key_rsc_len = 6;
-		gd.alg = WPA_ALG_GCMP;
-		break;
-	case WPA_CIPHER_TKIP:
-		keylen = 32;
-		gd.key_rsc_len = 6;
-		gd.alg = WPA_ALG_TKIP;
-		break;
-	case WPA_CIPHER_WEP104:
-		keylen = 13;
-		gd.key_rsc_len = 0;
-		gd.alg = WPA_ALG_WEP;
-		break;
-	case WPA_CIPHER_WEP40:
-		keylen = 5;
-		gd.key_rsc_len = 0;
-		gd.alg = WPA_ALG_WEP;
-		break;
-	default:
+	keylen = wpa_cipher_key_len(sm->group_cipher);
+	gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+	gd.alg = wpa_cipher_to_alg(sm->group_cipher);
+	if (gd.alg == WPA_ALG_NONE) {
 		wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
 		return -1;
 	}
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index bdf389b..2df060c 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -171,18 +171,16 @@
 	pos = (u8 *) (rsnie + 1);
 
 	/* Group Suite Selector */
-	if (sm->group_cipher == WPA_CIPHER_CCMP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	else if (sm->group_cipher == WPA_CIPHER_GCMP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
-	else if (sm->group_cipher == WPA_CIPHER_TKIP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	else {
+	if (sm->group_cipher != WPA_CIPHER_CCMP &&
+	    sm->group_cipher != WPA_CIPHER_GCMP &&
+	    sm->group_cipher != WPA_CIPHER_TKIP) {
 		wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
 			   sm->group_cipher);
 		os_free(buf);
 		return NULL;
 	}
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->group_cipher));
 	pos += RSN_SELECTOR_LEN;
 
 	/* Pairwise Suite Count */
@@ -190,18 +188,14 @@
 	pos += 2;
 
 	/* Pairwise Suite List */
-	if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	else if (sm->pairwise_cipher == WPA_CIPHER_GCMP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
-	else if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	else {
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
 		wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
 			   sm->pairwise_cipher);
 		os_free(buf);
 		return NULL;
 	}
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->pairwise_cipher));
 	pos += RSN_SELECTOR_LEN;
 
 	/* Authenticated Key Management Suite Count */
@@ -327,25 +321,15 @@
 
 	wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
 
-	switch (sm->pairwise_cipher) {
-	case WPA_CIPHER_CCMP:
-		alg = WPA_ALG_CCMP;
-		keylen = 16;
-		break;
-	case WPA_CIPHER_GCMP:
-		alg = WPA_ALG_GCMP;
-		keylen = 16;
-		break;
-	case WPA_CIPHER_TKIP:
-		alg = WPA_ALG_TKIP;
-		keylen = 32;
-		break;
-	default:
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
 		wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
 			   sm->pairwise_cipher);
 		return -1;
 	}
 
+	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+
 	if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
 			   sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) {
 		wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
@@ -579,33 +563,10 @@
 		return -1;
 	}
 
-	switch (sm->group_cipher) {
-	case WPA_CIPHER_CCMP:
-		keylen = 16;
-		rsc_len = 6;
-		alg = WPA_ALG_CCMP;
-		break;
-	case WPA_CIPHER_GCMP:
-		keylen = 16;
-		rsc_len = 6;
-		alg = WPA_ALG_GCMP;
-		break;
-	case WPA_CIPHER_TKIP:
-		keylen = 32;
-		rsc_len = 6;
-		alg = WPA_ALG_TKIP;
-		break;
-	case WPA_CIPHER_WEP104:
-		keylen = 13;
-		rsc_len = 0;
-		alg = WPA_ALG_WEP;
-		break;
-	case WPA_CIPHER_WEP40:
-		keylen = 5;
-		rsc_len = 0;
-		alg = WPA_ALG_WEP;
-		break;
-	default:
+	keylen = wpa_cipher_key_len(sm->group_cipher);
+	rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+	alg = wpa_cipher_to_alg(sm->group_cipher);
+	if (alg == WPA_ALG_NONE) {
 		wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
 			   sm->group_cipher);
 		return -1;
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 16268d5..6a8f9f1 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -41,6 +41,7 @@
 {
 	u8 *pos;
 	struct wpa_ie_hdr *hdr;
+	u32 suite;
 
 	if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
 	    2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
@@ -52,34 +53,26 @@
 	WPA_PUT_LE16(hdr->version, WPA_VERSION);
 	pos = (u8 *) (hdr + 1);
 
-	if (group_cipher == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
-	} else if (group_cipher == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
-	} else if (group_cipher == WPA_CIPHER_WEP104) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
-	} else if (group_cipher == WPA_CIPHER_WEP40) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher);
+	if (suite == 0) {
 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
 			   group_cipher);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += WPA_SELECTOR_LEN;
 
 	*pos++ = 1;
 	*pos++ = 0;
-	if (pairwise_cipher == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
-	} else if (pairwise_cipher == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
-	} else if (pairwise_cipher == WPA_CIPHER_NONE) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher);
+	if (suite == 0 ||
+	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+	     pairwise_cipher != WPA_CIPHER_NONE)) {
 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
 			   pairwise_cipher);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += WPA_SELECTOR_LEN;
 
 	*pos++ = 1;
@@ -116,6 +109,7 @@
 	u8 *pos;
 	struct rsn_ie_hdr *hdr;
 	u16 capab;
+	u32 suite;
 
 	if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
 	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
@@ -130,38 +124,26 @@
 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
 	pos = (u8 *) (hdr + 1);
 
-	if (group_cipher == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	} else if (group_cipher == WPA_CIPHER_GCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
-	} else if (group_cipher == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	} else if (group_cipher == WPA_CIPHER_WEP104) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
-	} else if (group_cipher == WPA_CIPHER_WEP40) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
+	if (suite == 0) {
 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
 			   group_cipher);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += RSN_SELECTOR_LEN;
 
 	*pos++ = 1;
 	*pos++ = 0;
-	if (pairwise_cipher == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	} else if (pairwise_cipher == WPA_CIPHER_GCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
-	} else if (pairwise_cipher == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	} else if (pairwise_cipher == WPA_CIPHER_NONE) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
+	if (suite == 0 ||
+	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+	     pairwise_cipher != WPA_CIPHER_NONE)) {
 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
 			   pairwise_cipher);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += RSN_SELECTOR_LEN;
 
 	*pos++ = 1;