Cumulative patch from commit 2e988392436227c51002b573ee27a8cee37f70e9

2e98839 P2P: Disable DNS server from dnsmasq
c07f261 P2P NFC: Add script for connection handover with nfcpy
12288d8 WPS NFC: Protect nfcpy pretty print calls against exceptions
c209dd1 WPS NFC: nfcpy script to use new connection handover design
6202500 WPS NFC: Logging level configuration to wps-nfc.py and wps-ap-nfc.py
1f1b5b3 WPS NFC: Clean up nfcpy script no-wait operations
79ede5a WPS NFC: Validate ctrl_iface response before decoding it
ab1db08 WPS NFC: Use argparse in the nfcpy scripts
6f8fa6e WPS NFC: Update wps-nfc.py and wps-ap-nfc.py to use new nfcpy API
b56f6c8 P2P NFC: Add support for freq option in NFC ctrl_iface commands
91a6501 WPS NFC: Use BSSID and AP Channel from handover select
91226e0 WPS: Add testing option to corrupt public key hash
7312776 WPS NFC: add more debug prints for connection handover report
5cd4f66 WPS NFC: Use AP Channel information from credential container
d2f1837 WPS NFC: Add BSSID and AP channel info to Configuration Token
75dbf98 WPS-STRICT: Update valid Device Password ID and Config Error range
5cd4740 P2P NFC: WPA state machine config with driver-based BSS selection
8e9f53c P2P NFC: Static handover with NFC Tag on client
dd87677 P2P NFC: Enable own NFC Tag on GO Registrar
abe44e3 P2P NFC: Add GO info into handover message when in client role
23318be P2P NFC: Optimize join-a-group operation based on NFC information
86e3208 P2P NFC: Copy DH parameters to a separate group interface
d4b4d7f WPS NFC: Update DH keys for ER operations
ac08752 WPS NFC: Use pubkey mismatch config error from Enrollee
59b45d1 P2P NFC: Add processing of P2P client while NFC handover case
74df9ec P2P NFC: Do not try to join peer if both devices are already GO
201b0f5 P2P: Add test option to disable IP address assignment request
25ef852 P2P: Add support for IP address assignment in 4-way handshake
fdd48ff P2P NFC: Optimize GO Negotiation retries
c4f87a7 P2P NFC: Add NFC tag enabling for static handover
dd37a93 P2P NFC: Report handover select from tag for static handover
db6ae69 P2P NFC: Report connection handover as trigger for P2P
9358878 P2P NFC: Build connection handover messages
c00ab85 P2P NFC: Define WPS_NFC config method
0deab08 P2P NFC: Allow separate WPS/P2P IES to be parsed
fca9958 P2P NFC: Pass OOB Dev Password through P2P parser
ab9e344 P2P NFC: Pass OOB Device Password ID to P2P
5154689 P2P NFC: Add WPS attribute building for P2P NFC
01afd8d P2P NFC: Add NDEF helpers for P2P connection handover messages
9e323a2 P2P NFC: Add OOB GO Negotiation Channel attribute
14d8645 WPS NFC: Allow BSSID and channel to be included in handover select
50d1f89 NFC: Update WPS ER to use the new connection handover design
d950793 WPS NFC: Add support for wpa_supplicant AP/GO mode to use handover
fa4c298 WPS NFC: Process new style handover select
068cdb1 WPS NFC: New style connection handover select from AP/Registrar
3189ca0 WPS NFC: Add AP mode connection handover report
41f9ffb WPS NFC: Build new style carrier record for connection handover request
3f1639d WPS NFC: Split DH key generation to a separate function
9754917 WPS NFC: Update NFC connection handover design
34b6795 WPS NFC: Use abbreviated handshake if both PK hashes delivered OOB
57630e6 WPS: Preparations for allowing SSID filtering for provisioning step
5f45455 WPS NFC: Validate peer public key hash on Enrollee
ff40cd6 WPS NFC: Send M2D with config error 20 on pkhash mismatch
e435417 WPS: Remove Version attribute from NFC messages
72403ec WPS: Add builder functions for AP Channel and RF Bands attributes
ea43ad9 P2P: Make group operating channel available
9f7cd9a P2P: Split add-group-info into a helper function
253f2e3 P2P: Apply unsafe frequency rules to available channels
1682c62 Add a header file defining QCA OUI and vendor extensions

Change-Id: Ia7604d018e1ffb25e06bdc01ce258fc4a0569245
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 0bb937e..b4860a0 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -384,6 +384,12 @@
 #define P2P_MANAGE BIT(3)
 #define P2P_ALLOW_CROSS_CONNECTION BIT(4)
 	int p2p;
+#ifdef CONFIG_P2P
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
 
 	int disassoc_low_ack;
 	int skip_inactivity_poll;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 670b834..24e764d 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -908,6 +908,7 @@
 	char buf[100];
 #ifdef CONFIG_P2P
 	u8 addr[ETH_ALEN];
+	u8 ip_addr_buf[4];
 #endif /* CONFIG_P2P */
 
 	if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
@@ -929,12 +930,25 @@
 		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
 
 	if (authorized) {
-		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s", buf);
+		char ip_addr[100];
+		ip_addr[0] = '\0';
+#ifdef CONFIG_P2P
+		if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+			os_snprintf(ip_addr, sizeof(ip_addr),
+				    " ip_addr=%u.%u.%u.%u",
+				    ip_addr_buf[0], ip_addr_buf[1],
+				    ip_addr_buf[2], ip_addr_buf[3]);
+		}
+#endif /* CONFIG_P2P */
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s",
+			buf, ip_addr);
 
 		if (hapd->msg_ctx_parent &&
 		    hapd->msg_ctx_parent != hapd->msg_ctx)
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
-					  AP_STA_CONNECTED "%s", buf);
+					  AP_STA_CONNECTED "%s%s",
+					  buf, ip_addr);
 
 		sta->flags |= WLAN_STA_AUTHORIZED;
 	} else {
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 5993edf..707a63f 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/state_machine.h"
+#include "utils/bitfield.h"
 #include "common/ieee802_11_defs.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
@@ -424,6 +425,17 @@
 				       wpa_rekey_gtk, wpa_auth, NULL);
 	}
 
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(conf->ip_addr_start)) {
+		int count = WPA_GET_BE32(conf->ip_addr_end) -
+			WPA_GET_BE32(conf->ip_addr_start) + 1;
+		if (count > 1000)
+			count = 1000;
+		if (count > 0)
+			wpa_auth->ip_pool = bitfield_alloc(count);
+	}
+#endif /* CONFIG_P2P */
+
 	return wpa_auth;
 }
 
@@ -466,6 +478,11 @@
 	wpa_auth->ft_pmk_cache = NULL;
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_P2P
+	bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
 	os_free(wpa_auth->wpa_ie);
 
 	group = wpa_auth->group;
@@ -583,6 +600,19 @@
 
 static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 {
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr)) {
+		u32 start;
+		wpa_printf(MSG_DEBUG, "P2P: Free assigned IP "
+			   "address %u.%u.%u.%u from " MACSTR,
+			   sm->ip_addr[0], sm->ip_addr[1],
+			   sm->ip_addr[2], sm->ip_addr[3],
+			   MAC2STR(sm->addr));
+		start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
+		bitfield_clear(sm->wpa_auth->ip_pool,
+			       WPA_GET_BE32(sm->ip_addr) - start);
+	}
+#endif /* CONFIG_P2P */
 	if (sm->GUpdateStationKeys) {
 		sm->group->GKeyDoneStations--;
 		sm->GUpdateStationKeys = FALSE;
@@ -1000,6 +1030,26 @@
 			return;
 		}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+		if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+		    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+			int idx;
+			wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
+				   "EAPOL-Key exchange");
+			idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+			if (idx >= 0) {
+				u32 start = WPA_GET_BE32(wpa_auth->conf.
+							 ip_addr_start);
+				bitfield_set(wpa_auth->ip_pool, idx);
+				WPA_PUT_BE32(sm->ip_addr, start + idx);
+				wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
+					   "address %u.%u.%u.%u to " MACSTR,
+					   sm->ip_addr[0], sm->ip_addr[1],
+					   sm->ip_addr[2], sm->ip_addr[3],
+					   MAC2STR(sm->addr));
+			}
+		}
+#endif /* CONFIG_P2P */
 		break;
 	case PAIRWISE_4:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1995,6 +2045,10 @@
 		kde_len += 300; /* FTIE + 2 * TIE */
 	}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0)
+		kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
 	kde = os_malloc(kde_len);
 	if (kde == NULL)
 		return;
@@ -2056,6 +2110,16 @@
 		pos += 4;
 	}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0) {
+		u8 addr[3 * 4];
+		os_memcpy(addr, sm->ip_addr, 4);
+		os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4);
+		os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4);
+		pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+				  addr, sizeof(addr), NULL, 0);
+	}
+#endif /* CONFIG_P2P */
 
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
@@ -3103,3 +3167,14 @@
 		return 0;
 	return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
 }
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+	if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0)
+		return -1;
+	os_memcpy(addr, sm->ip_addr, 4);
+	return 0;
+}
+#endif /* CONFIG_P2P */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index da45ae4..bc3dec4 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -163,6 +163,12 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	double corrupt_gtk_rekey_mic_probability;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
 };
 
 typedef enum {
@@ -297,4 +303,6 @@
 int wpa_auth_uses_sae(struct wpa_state_machine *sm);
 int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
 
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 4c1d625..5af1495 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -78,6 +78,12 @@
 	wconf->corrupt_gtk_rekey_mic_probability =
 		iconf->corrupt_gtk_rekey_mic_probability;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+	os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
+	os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4);
+	os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
+	os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
+#endif /* CONFIG_P2P */
 }
 
 
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 9736874..fcd5878 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -121,6 +121,10 @@
 #endif /* CONFIG_IEEE80211R */
 
 	int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+	u8 ip_addr[4];
+#endif /* CONFIG_P2P */
 };
 
 
@@ -185,6 +189,10 @@
 
 	struct rsn_pmksa_cache *pmksa;
 	struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+	struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
 };
 
 
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index cdfcca1..274f4d6 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -708,6 +708,25 @@
 	}
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_P2P
+	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+
+	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: IP Address Allocation in EAPOL-Key",
+			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+
 	return 0;
 }
 
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 4999139..f945882 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -39,6 +39,10 @@
 	const u8 *ftie;
 	size_t ftie_len;
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+	const u8 *ip_addr_req;
+	const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
 };
 
 int wpa_parse_kde_ies(const u8 *buf, size_t len,
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 4c94210..1b1dce4 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1784,7 +1784,8 @@
 	if (hapd->wps == NULL)
 		return NULL;
 
-	ret = wps_get_oob_cred(hapd->wps);
+	ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
+			       hapd->iconf->channel);
 	if (ndef && ret) {
 		struct wpabuf *tmp;
 		tmp = ndef_build_wifi(ret);
@@ -1800,11 +1801,136 @@
 
 struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
 {
+	struct wpabuf *ret;
+
+	if (hapd->wps == NULL)
+		return NULL;
+
+	if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
+		struct wps_context *wps = hapd->wps;
+		if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
+				   &hapd->conf->wps_nfc_dh_privkey) < 0)
+			return NULL;
+		hostapd_wps_nfc_clear(wps);
+		wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+		wps->ap_nfc_dh_pubkey =
+			wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+		wps->ap_nfc_dh_privkey =
+			wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+		if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+			hostapd_wps_nfc_clear(wps);
+			return NULL;
+		}
+	}
+
+	ret = wps_build_nfc_handover_sel(hapd->wps,
+					 hapd->conf->wps_nfc_dh_pubkey,
+					 hapd->own_addr, hapd->iface->freq);
+
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel)
+{
+	struct wpabuf *wps;
+	int ret = -1;
+	u16 wsc_len;
+	const u8 *pos;
+	struct wpabuf msg;
+	struct wps_parse_attr attr;
+	u16 dev_pw_id;
+
 	/*
-	 * Handover Select carrier record for WPS uses the same format as
-	 * configuration token.
+	 * Enrollee/station is always initiator of the NFC connection handover,
+	 * so use the request message here to find Enrollee public key hash.
 	 */
-	return hostapd_wps_nfc_config_token(hapd, ndef);
+	wps = ndef_parse_wifi(req);
+	if (wps == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+		   "payload from NFC connection handover");
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+	if (wpabuf_len(wps) < 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+			   "Message");
+		goto out;
+	}
+	pos = wpabuf_head(wps);
+	wsc_len = WPA_GET_BE16(pos);
+	if (wsc_len > wpabuf_len(wps) - 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+			   "in rt Wi-Fi Handover Request Message", wsc_len);
+		goto out;
+	}
+	pos += 2;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "WPS: WSC attributes in Wi-Fi Handover Request Message",
+		    pos, wsc_len);
+	if (wsc_len < wpabuf_len(wps) - 2) {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPS: Ignore extra data after WSC attributes",
+			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+	}
+
+	wpabuf_set(&msg, pos, wsc_len);
+	ret = wps_parse_msg(&msg, &attr);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+			   "Wi-Fi Handover Request Message");
+		goto out;
+	}
+
+	if (attr.oob_dev_password == NULL ||
+	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+			   "included in Wi-Fi Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	if (attr.uuid_e == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+			   "Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+		    attr.oob_dev_password, attr.oob_dev_password_len);
+	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+				 WPS_OOB_PUBKEY_HASH_LEN);
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+			   "%u in Wi-Fi Handover Request Message", dev_pw_id);
+		ret = -1;
+		goto out;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+	ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
+					     attr.oob_dev_password,
+					     DEV_PW_NFC_CONNECTION_HANDOVER,
+					     NULL, 0, 1);
+
+out:
+	wpabuf_free(wps);
+	return ret;
 }
 
 
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
index a292598..204bd82 100644
--- a/src/ap/wps_hostapd.h
+++ b/src/ap/wps_hostapd.h
@@ -37,6 +37,9 @@
 struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
 					     int ndef);
 struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel);
 struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
 int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
 void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 592ae54..6f7f777 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -926,6 +926,7 @@
 	P2P_ATTR_INTERFACE = 16,
 	P2P_ATTR_OPERATING_CHANNEL = 17,
 	P2P_ATTR_INVITATION_FLAGS = 18,
+	P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
 	P2P_ATTR_VENDOR_SPECIFIC = 221
 };
 
@@ -947,6 +948,7 @@
 #define P2P_GROUP_CAPAB_CROSS_CONN BIT(4)
 #define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5)
 #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
+#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
 
 /* Invitation Flags */
 #define P2P_INVITATION_FLAGS_TYPE BIT(0)
@@ -971,6 +973,12 @@
 	P2P_SC_FAIL_REJECTED_BY_USER = 11,
 };
 
+enum p2p_role_indication {
+	P2P_DEVICE_NOT_IN_GROUP = 0x00,
+	P2P_CLIENT_IN_A_GROUP = 0x01,
+	P2P_GO_IN_A_GROUP = 0x02,
+};
+
 #define P2P_WILDCARD_SSID "DIRECT-"
 #define P2P_WILDCARD_SSID_LEN 7
 
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
new file mode 100644
index 0000000..0d83920
--- /dev/null
+++ b/src/common/qca-vendor.h
@@ -0,0 +1,51 @@
+/*
+ * Qualcomm Atheros OUI and vendor specific assignments
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef QCA_VENDOR_H
+#define QCA_VENDOR_H
+
+/*
+ * This file is a registry of identifier assignments from the Qualcomm Atheros
+ * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers
+ * can be assigned through normal review process for changes to the upstream
+ * hostap.git repository.
+ */
+
+#define OUI_QCA 0x001374
+
+/**
+ * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
+ *	ranges to avoid to reduce issues due to interference or internal
+ *	co-existence information in the driver. The event data structure is
+ *	defined in struct qca_avoid_freq_list.
+ */
+enum qca_nl80211_vendor_subcmds {
+	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
+	QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
+	/* subcmds 2..9 not yet allocated */
+	QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
+};
+
+
+struct qca_avoid_freq_range {
+	u32 start_freq;
+	u32 end_freq;
+} STRUCT_PACKED;
+
+struct qca_avoid_freq_list {
+	u32 count;
+	struct qca_avoid_freq_range range[0];
+} STRUCT_PACKED;
+
+#endif /* QCA_VENDOR_H */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 62505f1..dcc035c 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -109,6 +109,9 @@
 #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
 #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
 
+#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
+#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
+
 #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
 
 #define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val))
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 9bd59a9..759cee4 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -74,6 +74,8 @@
  * be used again.
  */
 #define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
+/** Frequency ranges that the driver recommends to avoid */
+#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
 /** WPS overlap detected in PBC mode */
 #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
 /** Available WPS AP with active PBC found in scan results */
@@ -148,9 +150,13 @@
 #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
 #define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
 #define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
+#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO "
+#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT "
+#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT "
 
 /* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
 #define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
+#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
 
 #define INTERWORKING_AP "INTERWORKING-AP "
 #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 4fd16ed..2f4536e 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -3147,7 +3147,16 @@
 	 * EVENT_SCAN_RESULTS is used to indicate when the scan has been
 	 * completed (either successfully or by getting cancelled).
 	 */
-	EVENT_SCAN_STARTED
+	EVENT_SCAN_STARTED,
+
+	/**
+	 * EVENT_AVOID_FREQUENCIES - Received avoid frequency range
+	 *
+	 * This event indicates a set of frequency ranges that should be avoided
+	 * to reduce issues due to interference or internal co-existence
+	 * information in the driver.
+	 */
+	EVENT_AVOID_FREQUENCIES
 };
 
 
@@ -3766,6 +3775,13 @@
 	struct channel_list_changed {
 		enum reg_change_initiator initiator;
 	} channel_list_changed;
+
+	/**
+	 * freq_range - List of frequency ranges
+	 *
+	 * This is used as the data with EVENT_AVOID_FREQUENCIES.
+	 */
+	struct wpa_freq_range_list freq_range;
 };
 
 /**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 64bdddb..3058cd5 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -78,6 +78,7 @@
 	E2S(DFS_NOP_FINISHED);
 	E2S(SURVEY);
 	E2S(SCAN_STARTED);
+	E2S(AVOID_FREQUENCIES);
 	}
 
 	return "UNKNOWN";
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index b5bf368..646d3f8 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -28,6 +28,7 @@
 #include "common.h"
 #include "eloop.h"
 #include "utils/list.h"
+#include "common/qca-vendor.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "l2_packet/l2_packet.h"
@@ -2716,6 +2717,69 @@
 }
 
 
+static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
+				   const u8 *data, size_t len)
+{
+	u32 i, count;
+	union wpa_event_data event;
+	struct wpa_freq_range *range = NULL;
+	const struct qca_avoid_freq_list *freq_range;
+
+	freq_range = (const struct qca_avoid_freq_list *) data;
+	if (len < sizeof(freq_range->count))
+		return;
+
+	count = freq_range->count;
+	if (len < sizeof(freq_range->count) +
+	    count * sizeof(struct qca_avoid_freq_range)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
+			   (unsigned int) len);
+		return;
+	}
+
+	if (count > 0) {
+		range = os_calloc(count, sizeof(struct wpa_freq_range));
+		if (range == NULL)
+			return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	for (i = 0; i < count; i++) {
+		unsigned int idx = event.freq_range.num;
+		range[idx].min = freq_range->range[i].start_freq;
+		range[idx].max = freq_range->range[i].end_freq;
+		wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
+			   range[idx].min, range[idx].max);
+		if (range[idx].min > range[idx].max) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
+			continue;
+		}
+		event.freq_range.num++;
+	}
+	event.freq_range.range = range;
+
+	wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
+
+	os_free(range);
+}
+
+
+static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
+				     u32 subcmd, u8 *data, size_t len)
+{
+	switch (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+		qca_nl80211_avoid_freq(drv, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore unsupported QCA vendor event %u",
+			   subcmd);
+		break;
+	}
+}
+
+
 static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
 				 struct nlattr **tb)
 {
@@ -2751,6 +2815,9 @@
 	}
 
 	switch (vendor_id) {
+	case OUI_QCA:
+		nl80211_vendor_event_qca(drv, subcmd, data, len);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
 		break;
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 8edb1ca..6bdd341 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -144,12 +144,13 @@
 	size_t identity_len;
 	int registrar;
 	struct wps_config cfg;
-	const char *pos;
+	const char *pos, *end;
 	const char *phase1;
 	struct wps_context *wps;
 	struct wps_credential new_ap_settings;
 	int res;
 	int nfc = 0;
+	u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
 
 	wps = sm->wps;
 	if (wps == NULL) {
@@ -209,6 +210,15 @@
 			cfg.pbc = 1;
 	}
 
+	pos = os_strstr(phase1, "dev_pw_id=");
+	if (pos) {
+		u16 id = atoi(pos + 10);
+		if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
+			nfc = 1;
+		if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
+			cfg.dev_pw_id = id;
+	}
+
 	if (cfg.pin == NULL && !cfg.pbc && !nfc) {
 		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
 			   "configuration data");
@@ -216,9 +226,23 @@
 		return NULL;
 	}
 
-	pos = os_strstr(phase1, "dev_pw_id=");
-	if (pos && cfg.pin)
-		cfg.dev_pw_id = atoi(pos + 10);
+	pos = os_strstr(phase1, " pkhash=");
+	if (pos) {
+		size_t len;
+		pos += 8;
+		end = os_strchr(pos, ' ');
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
+		    hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
+			wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
+			os_free(data);
+			return NULL;
+		}
+		cfg.peer_pubkey_hash = pkhash;
+	}
 
 	res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
 	if (res < 0) {
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 8aabfc0..ca347e7 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -211,6 +211,7 @@
 	if (p2p->go_neg_peer) {
 		p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
 		p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+		p2p->go_neg_peer->oob_pw_id = 0;
 	}
 	p2p->go_neg_peer = NULL;
 
@@ -1292,15 +1293,16 @@
 		int go_intent, const u8 *own_interface_addr,
 		unsigned int force_freq, int persistent_group,
 		const u8 *force_ssid, size_t force_ssid_len,
-		int pd_before_go_neg, unsigned int pref_freq)
+		int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id)
 {
 	struct p2p_device *dev;
 
 	p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d persistent_group=%d pd_before_go_neg=%d",
+		" wps_method=%d persistent_group=%d pd_before_go_neg=%d "
+		"oob_pw_id=%u",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group, pd_before_go_neg);
+		wps_method, persistent_group, pd_before_go_neg, oob_pw_id);
 
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -1384,6 +1386,7 @@
 	}
 
 	dev->wps_method = wps_method;
+	dev->oob_pw_id = oob_pw_id;
 	dev->status = P2P_SC_SUCCESS;
 
 	if (p2p->p2p_scan_running) {
@@ -1403,15 +1406,15 @@
 		  int go_intent, const u8 *own_interface_addr,
 		  unsigned int force_freq, int persistent_group,
 		  const u8 *force_ssid, size_t force_ssid_len,
-		  unsigned int pref_freq)
+		  unsigned int pref_freq, u16 oob_pw_id)
 {
 	struct p2p_device *dev;
 
 	p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d  persistent_group=%d",
+		" wps_method=%d  persistent_group=%d oob_pw_id=%u",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group);
+		wps_method, persistent_group, oob_pw_id);
 
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL) {
@@ -1442,6 +1445,7 @@
 	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
 
 	dev->wps_method = wps_method;
+	dev->oob_pw_id = oob_pw_id;
 	dev->status = P2P_SC_SUCCESS;
 
 	return 0;
@@ -1593,6 +1597,7 @@
 	p2p->ssid_set = 0;
 	peer->go_neg_req_sent = 0;
 	peer->wps_method = WPS_NOT_READY;
+	peer->oob_pw_id = 0;
 
 	p2p_set_state(p2p, P2P_PROVISIONING);
 	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -1757,7 +1762,8 @@
 	if (p2p->invite_peer == NULL)
 		return;
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
-	p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+	p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+			p2p->invite_dev_pw_id);
 }
 
 
@@ -2459,6 +2465,7 @@
 		p2p->go_neg_peer = NULL;
 
 	dev->wps_method = WPS_NOT_READY;
+	dev->oob_pw_id = 0;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 
@@ -3122,7 +3129,12 @@
 		p2p_connect_send(p2p, p2p->go_neg_peer);
 		return;
 	}
-
+	if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) {
+		p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)");
+		p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+		p2p_set_timeout(p2p, 0, 30000);
+		return;
+	}
 	p2p_set_state(p2p, P2P_CONNECT_LISTEN);
 	p2p_listen_in_find(p2p, 0);
 }
@@ -3266,7 +3278,7 @@
 	if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
 		p2p_set_state(p2p, P2P_INVITE);
 		p2p_invite_send(p2p, p2p->invite_peer,
-				p2p->invite_go_dev_addr);
+				p2p->invite_go_dev_addr, p2p->invite_dev_pw_id);
 	} else {
 		if (p2p->invite_peer) {
 			p2p_dbg(p2p, "Invitation Request retry limit reached");
@@ -3381,6 +3393,8 @@
 		return "Keypad";
 	case WPS_PBC:
 		return "PBC";
+	case WPS_NFC:
+		return "NFC";
 	}
 
 	return "??";
@@ -4362,3 +4376,219 @@
 	va_end(ap);
 	p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf);
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p,
+					      int client_freq,
+					      const u8 *go_dev_addr,
+					      const u8 *ssid, size_t ssid_len)
+{
+	struct wpabuf *buf;
+	u8 op_class, channel;
+	enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	op_class = p2p->cfg->reg_class;
+	channel = p2p->cfg->channel;
+
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+	p2p_buf_add_device_info(buf, p2p, NULL);
+
+	if (p2p->num_groups > 0) {
+		role = P2P_GO_IN_A_GROUP;
+		p2p_freq_to_channel(p2p_group_get_freq(p2p->groups[0]),
+				    &op_class, &channel);
+	} else if (client_freq > 0) {
+		role = P2P_CLIENT_IN_A_GROUP;
+		p2p_freq_to_channel(client_freq, &op_class, &channel);
+	}
+
+	p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class,
+				       channel, role);
+
+	if (p2p->num_groups > 0) {
+		/* Limit number of clients to avoid very long message */
+		p2p_buf_add_group_info(p2p->groups[0], buf, 5);
+		p2p_group_buf_add_id(p2p->groups[0], buf);
+	} else if (client_freq > 0 &&
+		   go_dev_addr && !is_zero_ether_addr(go_dev_addr) &&
+		   ssid && ssid_len > 0) {
+		/*
+		 * Add the optional P2P Group ID to indicate in which group this
+		 * device is a P2P Client.
+		 */
+		p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len);
+	}
+
+	return buf;
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len)
+{
+	return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+				      ssid_len);
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len)
+{
+	return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+				      ssid_len);
+}
+
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+					struct p2p_nfc_params *params)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+	const u8 *p2p_dev_addr;
+	int freq;
+	enum p2p_role_indication role;
+
+	params->next_step = NO_ACTION;
+
+	if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len,
+				   params->p2p_attr, params->p2p_len, &msg)) {
+		p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.p2p_device_addr)
+		p2p_dev_addr = msg.p2p_device_addr;
+	else if (msg.device_id)
+		p2p_dev_addr = msg.device_id;
+	else {
+		p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.oob_dev_password) {
+		os_memcpy(params->oob_dev_pw, msg.oob_dev_password,
+			  msg.oob_dev_password_len);
+		params->oob_dev_pw_len = msg.oob_dev_password_len;
+	}
+
+	dev = p2p_create_device(p2p, p2p_dev_addr);
+	if (dev == NULL) {
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	params->peer = &dev->info;
+
+	os_get_reltime(&dev->last_seen);
+	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+	p2p_copy_wps_info(p2p, dev, 0, &msg);
+
+	if (!msg.oob_go_neg_channel) {
+		p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included");
+		return -1;
+	}
+
+	if (msg.oob_go_neg_channel[3] == 0 &&
+	    msg.oob_go_neg_channel[4] == 0)
+		freq = 0;
+	else
+		freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3],
+					   msg.oob_go_neg_channel[4]);
+	if (freq < 0) {
+		p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
+		return -1;
+	}
+	role = msg.oob_go_neg_channel[5];
+
+	if (role == P2P_GO_IN_A_GROUP) {
+		p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq);
+		params->go_freq = freq;
+	} else if (role == P2P_CLIENT_IN_A_GROUP) {
+		p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz",
+			freq);
+		params->go_freq = freq;
+	} else
+		p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq);
+	dev->oob_go_neg_freq = freq;
+
+	if (!params->sel && role != P2P_GO_IN_A_GROUP) {
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+		if (freq < 0) {
+			p2p_dbg(p2p, "Own listen channel not known");
+			return -1;
+		}
+		p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq);
+		dev->oob_go_neg_freq = freq;
+	}
+
+	if (msg.group_id) {
+		os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN);
+		params->go_ssid_len = msg.group_id_len - ETH_ALEN;
+		os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN,
+			  params->go_ssid_len);
+	}
+
+	p2p_parse_free(&msg);
+
+	if (dev->flags & P2P_DEV_USER_REJECTED) {
+		p2p_dbg(p2p, "Do not report rejected device");
+		return 0;
+	}
+
+	if (!(dev->flags & P2P_DEV_REPORTED)) {
+		p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info,
+				    !(dev->flags & P2P_DEV_REPORTED_ONCE));
+		dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+	}
+
+	if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0)
+		params->next_step = BOTH_GO;
+	else if (role == P2P_GO_IN_A_GROUP)
+		params->next_step = JOIN_GROUP;
+	else if (role == P2P_CLIENT_IN_A_GROUP) {
+		dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+		params->next_step = PEER_CLIENT;
+	} else if (p2p->num_groups > 0)
+		params->next_step = AUTH_JOIN;
+	else if (params->sel)
+		params->next_step = INIT_GO_NEG;
+	else
+		params->next_step = RESP_GO_NEG;
+
+	return 0;
+}
+
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+				      int go_intent,
+				      const u8 *own_interface_addr)
+{
+
+	p2p->authorized_oob_dev_pw_id = dev_pw_id;
+	if (dev_pw_id == 0) {
+		p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover");
+		return;
+	}
+
+	p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover",
+		dev_pw_id);
+
+	p2p->go_intent = go_intent;
+	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 2ce6ea6..08e7176 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -9,6 +9,8 @@
 #ifndef P2P_H
 #define P2P_H
 
+#include "wps/wps_defs.h"
+
 /**
  * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
  */
@@ -50,7 +52,7 @@
 };
 
 enum p2p_wps_method {
-	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC
 };
 
 /**
@@ -706,6 +708,8 @@
 	 *	persistent group (instead of invitation to join an active
 	 *	group)
 	 * @channels: Available operating channels for the group
+	 * @dev_pw_id: Device Password ID for NFC static handover or -1 if not
+	 *	used
 	 * Returns: Status code (P2P_SC_*)
 	 *
 	 * This optional callback can be used to implement persistent reconnect
@@ -727,7 +731,8 @@
 				 const u8 *go_dev_addr, const u8 *ssid,
 				 size_t ssid_len, int *go, u8 *group_bssid,
 				 int *force_freq, int persistent_group,
-				 const struct p2p_channels *channels);
+				 const struct p2p_channels *channels,
+				 int dev_pw_id);
 
 	/**
 	 * invitation_received - Callback on Invitation Request RX
@@ -972,7 +977,7 @@
 		int go_intent, const u8 *own_interface_addr,
 		unsigned int force_freq, int persistent_group,
 		const u8 *force_ssid, size_t force_ssid_len,
-		int pd_before_go_neg, unsigned int pref_freq);
+		int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_authorize - Authorize P2P group formation (GO negotiation)
@@ -1000,7 +1005,7 @@
 		  int go_intent, const u8 *own_interface_addr,
 		  unsigned int force_freq, int persistent_group,
 		  const u8 *force_ssid, size_t force_ssid_len,
-		  unsigned int pref_freq);
+		  unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_reject - Reject peer device (explicitly block connection attempts)
@@ -1102,12 +1107,14 @@
  * @persistent_group: Whether this is to reinvoke a persistent group
  * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
  *	force_freq == 0)
+ * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover
+ *	case or -1 if not used
  * Returns: 0 on success, -1 on failure
  */
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq);
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id);
 
 /**
  * p2p_presence_req - Request GO presence
@@ -1372,6 +1379,11 @@
 	size_t ssid_len;
 
 	/**
+	 * freq - Operating channel of the group
+	 */
+	int freq;
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -1892,4 +1904,41 @@
  */
 const char * p2p_get_state_txt(struct p2p_data *p2p);
 
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len);
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len);
+
+struct p2p_nfc_params {
+	int sel;
+	const u8 *wsc_attr;
+	size_t wsc_len;
+	const u8 *p2p_attr;
+	size_t p2p_len;
+
+	enum {
+		NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG,
+		BOTH_GO, PEER_CLIENT
+	} next_step;
+	struct p2p_peer_info *peer;
+	u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		      WPS_OOB_DEVICE_PASSWORD_LEN];
+	size_t oob_dev_pw_len;
+	int go_freq;
+	u8 go_dev_addr[ETH_ALEN];
+	u8 go_ssid[32];
+	size_t go_ssid_len;
+};
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+					struct p2p_nfc_params *params);
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+				      int go_intent,
+				      const u8 *own_interface_addr);
+
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 42c0232..664fade 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -328,6 +328,23 @@
 }
 
 
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+				    u8 oper_class, u8 channel,
+				    enum p2p_role_indication role)
+{
+	/* OOB Group Owner Negotiation Channel */
+	wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
+	wpabuf_put_le16(buf, 6);
+	wpabuf_put_data(buf, country, 3);
+	wpabuf_put_u8(buf, oper_class); /* Operating Class */
+	wpabuf_put_u8(buf, channel); /* Channel Number */
+	wpabuf_put_u8(buf, (u8) role); /* Role indication */
+	wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
+		   "Class %u Channel %u Role %d",
+		   oper_class, channel, role);
+}
+
+
 static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
 			      const char *val)
 {
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index a887a5e..76436f5 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -103,6 +103,8 @@
 		return DEV_PW_USER_SPECIFIED;
 	case WPS_PBC:
 		return DEV_PW_PUSHBUTTON;
+	case WPS_NFC:
+		return DEV_PW_NFC_CONNECTION_HANDOVER;
 	default:
 		return DEV_PW_DEFAULT;
 	}
@@ -118,6 +120,8 @@
 		return "Keypad";
 	case WPS_PBC:
 		return "PBC";
+	case WPS_NFC:
+		return "NFC";
 	default:
 		return "??";
 	}
@@ -131,6 +135,7 @@
 	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
+	u16 pw_id;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
@@ -172,8 +177,10 @@
 	p2p_buf_update_ie_hdr(buf, len);
 
 	/* WPS IE with Device Password ID attribute */
-	if (p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method),
-			     0) < 0) {
+	pw_id = p2p_wps_method_pw_id(peer->wps_method);
+	if (peer->oob_pw_id)
+		pw_id = peer->oob_pw_id;
+	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
 		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request");
 		wpabuf_free(buf);
 		return NULL;
@@ -210,6 +217,8 @@
 	}
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (dev->oob_go_neg_freq > 0)
+		freq = dev->oob_go_neg_freq;
 	if (freq <= 0) {
 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
 			MACSTR " to send GO Negotiation Request",
@@ -250,6 +259,7 @@
 	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
+	u16 pw_id;
 
 	p2p_dbg(p2p, "Building GO Negotiation Response");
 
@@ -312,9 +322,10 @@
 	p2p_buf_update_ie_hdr(buf, len);
 
 	/* WPS IE with Device Password ID attribute */
-	if (p2p_build_wps_ie(p2p, buf,
-			     p2p_wps_method_pw_id(peer ? peer->wps_method :
-						  WPS_NOT_READY), 0) < 0) {
+	pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY);
+	if (peer && peer->oob_pw_id)
+		pw_id = peer->oob_pw_id;
+	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
 		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response");
 		wpabuf_free(buf);
 		return NULL;
@@ -605,7 +616,11 @@
 	if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
 		p2p_dbg(p2p, "User has rejected this peer");
 		status = P2P_SC_FAIL_REJECTED_BY_USER;
-	} else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+	} else if (dev == NULL ||
+		   (dev->wps_method == WPS_NOT_READY &&
+		    (p2p->authorized_oob_dev_pw_id == 0 ||
+		     p2p->authorized_oob_dev_pw_id !=
+		     msg.dev_password_id))) {
 		p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
 			MAC2STR(sa));
 		status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
@@ -692,6 +707,28 @@
 			}
 			break;
 		default:
+			if (msg.dev_password_id &&
+			    msg.dev_password_id == dev->oob_pw_id) {
+				p2p_dbg(p2p, "Peer using NFC");
+				if (dev->wps_method != WPS_NFC) {
+					p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+						p2p_wps_method_str(
+							dev->wps_method));
+					status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+					goto fail;
+				}
+				break;
+			}
+#ifdef CONFIG_WPS_NFC
+			if (p2p->authorized_oob_dev_pw_id &&
+			    msg.dev_password_id ==
+			    p2p->authorized_oob_dev_pw_id) {
+				p2p_dbg(p2p, "Using static handover with our device password from NFC Tag");
+				dev->wps_method = WPS_NFC;
+				dev->oob_pw_id = p2p->authorized_oob_dev_pw_id;
+				break;
+			}
+#endif /* CONFIG_WPS_NFC */
 			p2p_dbg(p2p, "Unsupported Device Password ID %d",
 				msg.dev_password_id);
 			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -1017,6 +1054,17 @@
 		}
 		break;
 	default:
+		if (msg.dev_password_id &&
+		    msg.dev_password_id == dev->oob_pw_id) {
+			p2p_dbg(p2p, "Peer using NFC");
+			if (dev->wps_method != WPS_NFC) {
+				p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+					p2p_wps_method_str(dev->wps_method));
+				status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+				goto fail;
+			}
+			break;
+		}
 		p2p_dbg(p2p, "Unsupported Device Password ID %d",
 			msg.dev_password_id);
 		status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 92b5583..395ca08 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -154,6 +154,7 @@
 		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
 	if (group->num_members >= group->cfg->max_clients)
 		group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
+	group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
 	p2p_buf_add_capability(ie, dev_capab, group_capab);
 }
 
@@ -397,10 +398,38 @@
 #endif /* CONFIG_WIFI_DISPLAY */
 
 
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+			    int max_clients)
+{
+	u8 *group_info;
+	int count = 0;
+	struct p2p_group_member *m;
+
+	p2p_dbg(group->p2p, "* P2P Group Info");
+	group_info = wpabuf_put(buf, 0);
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO);
+	wpabuf_put_le16(buf, 0); /* Length to be filled */
+	for (m = group->members; m; m = m->next) {
+		p2p_client_info(buf, m);
+		count++;
+		if (max_clients >= 0 && count >= max_clients)
+			break;
+	}
+	WPA_PUT_LE16(group_info + 1,
+		     (u8 *) wpabuf_put(buf, 0) - group_info - 3);
+}
+
+
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf)
+{
+	p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid,
+			     group->cfg->ssid_len);
+}
+
+
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
 	struct wpabuf *p2p_subelems, *ie;
-	struct p2p_group_member *m;
 
 	p2p_subelems = wpabuf_alloc(500);
 	if (p2p_subelems == NULL)
@@ -413,17 +442,8 @@
 	p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
 
 	/* P2P Group Info: Only when at least one P2P Client is connected */
-	if (group->members) {
-		u8 *group_info;
-		group_info = wpabuf_put(p2p_subelems, 0);
-		wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO);
-		wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */
-		for (m = group->members; m; m = m->next)
-			p2p_client_info(p2p_subelems, m);
-		WPA_PUT_LE16(group_info + 1,
-			     (u8 *) wpabuf_put(p2p_subelems, 0) - group_info -
-			     3);
-	}
+	if (group->members)
+		p2p_buf_add_group_info(group, p2p_subelems, -1);
 
 	ie = p2p_group_encaps_probe_resp(p2p_subelems);
 	wpabuf_free(p2p_subelems);
@@ -987,3 +1007,9 @@
 	group->beacon_update = 1;
 	p2p_group_update_ies(group);
 }
+
+
+int p2p_group_get_freq(struct p2p_group *group)
+{
+	return group->cfg->freq;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 3e105eb..6ebaa84 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -12,6 +12,8 @@
 #include "utils/list.h"
 #include "p2p.h"
 
+enum p2p_role_indication;
+
 enum p2p_go_state {
 	UNKNOWN_GO,
 	LOCAL_GO,
@@ -25,7 +27,9 @@
 	struct dl_list list;
 	struct os_reltime last_seen;
 	int listen_freq;
+	int oob_go_neg_freq;
 	enum p2p_wps_method wps_method;
+	u16 oob_pw_id;
 
 	struct p2p_peer_info info;
 
@@ -239,6 +243,7 @@
 
 	const u8 *invite_go_dev_addr;
 	u8 invite_go_dev_addr_buf[ETH_ALEN];
+	int invite_dev_pw_id;
 
 	/**
 	 * sd_peer - Pointer to Service Discovery peer
@@ -462,6 +467,8 @@
 	struct wpabuf *wfd_assoc_bssid;
 	struct wpabuf *wfd_coupled_sink_info;
 #endif /* CONFIG_WIFI_DISPLAY */
+
+	u16 authorized_oob_dev_pw_id;
 };
 
 /**
@@ -503,6 +510,8 @@
 
 	const u8 *minor_reason_code;
 
+	const u8 *oob_go_neg_channel;
+
 	/* P2P Device Info */
 	const u8 *p2p_device_info;
 	size_t p2p_device_info_len;
@@ -514,6 +523,7 @@
 
 	/* WPS IE */
 	u16 dev_password_id;
+	int dev_password_id_present;
 	u16 wps_config_methods;
 	const u8 *wps_pri_dev_type;
 	const u8 *wps_sec_dev_type_list;
@@ -528,6 +538,8 @@
 	size_t model_number_len;
 	const u8 *serial_number;
 	size_t serial_number_len;
+	const u8 *oob_dev_password;
+	size_t oob_dev_password_len;
 
 	/* DS Parameter Set IE */
 	const u8 *ds_params;
@@ -578,6 +590,8 @@
 int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
 int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
 int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+			   size_t p2p_len, struct p2p_message *msg);
 void p2p_parse_free(struct p2p_message *msg);
 int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
 int p2p_group_info_parse(const u8 *gi, size_t gi_len,
@@ -602,6 +616,10 @@
 void p2p_group_update_ies(struct p2p_group *group);
 void p2p_group_force_beacon_update_ies(struct p2p_group *group);
 struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+			    int max_clients);
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf);
+int p2p_group_get_freq(struct p2p_group *group);
 
 
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
@@ -633,6 +651,9 @@
 void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
 				   u16 interval);
 void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+				    u8 oper_class, u8 channel,
+				    enum p2p_role_indication role);
 int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		     int all_attr);
 
@@ -680,7 +701,7 @@
 void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 				 const u8 *data, size_t len);
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
-		    const u8 *go_dev_addr);
+		    const u8 *go_dev_addr, int dev_pw_id);
 void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
 void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
 
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 2734386..98cfb33 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -16,7 +16,8 @@
 
 static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
 						struct p2p_device *peer,
-						const u8 *go_dev_addr)
+						const u8 *go_dev_addr,
+						int dev_pw_id)
 {
 	struct wpabuf *buf;
 	u8 *len;
@@ -85,6 +86,11 @@
 		wpabuf_put_buf(buf, wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (dev_pw_id >= 0) {
+		/* WSC IE in Invitation Request for NFC static handover */
+		p2p_build_wps_ie(p2p, buf, dev_pw_id, 0);
+	}
+
 	return buf;
 }
 
@@ -228,7 +234,8 @@
 		status = p2p->cfg->invitation_process(
 			p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
 			msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
-			&go, group_bssid, &op_freq, persistent, &intersection);
+			&go, group_bssid, &op_freq, persistent, &intersection,
+			msg.dev_password_id_present ? msg.dev_password_id : -1);
 	}
 
 	if (op_freq) {
@@ -450,12 +457,14 @@
 
 
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
-		    const u8 *go_dev_addr)
+		    const u8 *go_dev_addr, int dev_pw_id)
 {
 	struct wpabuf *req;
 	int freq;
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0)
+		freq = dev->oob_go_neg_freq;
 	if (freq <= 0) {
 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
 			MACSTR " to send Invitation Request",
@@ -463,7 +472,7 @@
 		return -1;
 	}
 
-	req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+	req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
 	if (req == NULL)
 		return -1;
 	if (p2p->state != P2P_IDLE)
@@ -528,7 +537,7 @@
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq)
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id)
 {
 	struct p2p_device *dev;
 
@@ -546,9 +555,15 @@
 		p2p->invite_go_dev_addr = NULL;
 	wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID",
 			  ssid, ssid_len);
+	if (dev_pw_id >= 0) {
+		p2p_dbg(p2p, "Invitation to use Device Password ID %d",
+			dev_pw_id);
+	}
+	p2p->invite_dev_pw_id = dev_pw_id;
 
 	dev = p2p_get_device(p2p, peer);
-	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
+			    dev->oob_go_neg_freq <= 0)) {
 		p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR,
 			MAC2STR(peer));
 		return -1;
@@ -586,5 +601,5 @@
 	os_memcpy(p2p->inv_ssid, ssid, ssid_len);
 	p2p->inv_ssid_len = ssid_len;
 	p2p->inv_persistent = persistent_group;
-	return p2p_invite_send(p2p, dev, go_dev_addr);
+	return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id);
 }
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 097a31d..d6144a0 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -268,6 +268,19 @@
 		wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
 			   *msg->minor_reason_code);
 		break;
+	case P2P_ATTR_OOB_GO_NEG_CHANNEL:
+		if (len < 6) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg "
+				   "Channel attribute (length %d)", len);
+			return -1;
+		}
+		msg->oob_go_neg_channel = data;
+		wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: "
+			   "Country %c%c(0x%02x) Operating Class %d "
+			   "Channel Number %d Role %d",
+			   data[0], data[1], data[2], data[3], data[4],
+			   data[5]);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
 			   "(length %d)", id, len);
@@ -340,6 +353,7 @@
 		msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
 		wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
 			   msg->dev_password_id);
+		msg->dev_password_id_present = 1;
 	}
 	if (attr.primary_dev_type) {
 		char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -367,6 +381,9 @@
 	msg->serial_number = attr.serial_number;
 	msg->serial_number_len = attr.serial_number_len;
 
+	msg->oob_dev_password = attr.oob_dev_password;
+	msg->oob_dev_password_len = attr.oob_dev_password_len;
+
 	return 0;
 }
 
@@ -450,6 +467,33 @@
 }
 
 
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+			   size_t p2p_len, struct p2p_message *msg)
+{
+	os_memset(msg, 0, sizeof(*msg));
+
+	msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len);
+	if (msg->wps_attributes &&
+	    p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len);
+	if (msg->p2p_attributes &&
+	    p2p_parse_p2p_ie(msg->p2p_attributes, msg)) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data");
+		if (msg->p2p_attributes)
+			wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data",
+					msg->p2p_attributes);
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 /**
  * p2p_parse_free - Free temporary data from P2P parsing
  * @msg: Parsed attributes
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index a294730..4474c3b 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -379,6 +379,8 @@
 	struct wpa_ptk *ptk;
 	u8 buf[8];
 	int res;
+	u8 *kde, *kde_buf = NULL;
+	size_t kde_len;
 
 	if (wpa_sm_get_network_ctx(sm) == NULL) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
@@ -435,15 +437,39 @@
 	os_memcpy(ptk->u.auth.rx_mic_key, buf, 8);
 	sm->tptk_set = 1;
 
+	kde = sm->assoc_wpa_ie;
+	kde_len = sm->assoc_wpa_ie_len;
+
+#ifdef CONFIG_P2P
+	if (sm->p2p) {
+		kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1);
+		if (kde_buf) {
+			u8 *pos;
+			wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE "
+				   "into EAPOL-Key 2/4");
+			os_memcpy(kde_buf, kde, kde_len);
+			kde = kde_buf;
+			pos = kde + kde_len;
+			*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+			*pos++ = RSN_SELECTOR_LEN + 1;
+			RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
+			pos += RSN_SELECTOR_LEN;
+			*pos++ = 0x01;
+			kde_len = pos - kde;
+		}
+	}
+#endif /* CONFIG_P2P */
+
 	if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
-				       sm->assoc_wpa_ie, sm->assoc_wpa_ie_len,
-				       ptk))
+				       kde, kde_len, ptk))
 		goto failed;
 
+	os_free(kde_buf);
 	os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
 	return;
 
 failed:
+	os_free(kde_buf);
 	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 }
 
@@ -1090,6 +1116,14 @@
 		goto failed;
 	}
 
+#ifdef CONFIG_P2P
+	if (ie.ip_addr_alloc) {
+		os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4);
+		wpa_hexdump(MSG_DEBUG, "P2P: IP address info",
+			    sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr));
+	}
+#endif /* CONFIG_P2P */
+
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
 				       NULL, 0, &sm->ptk)) {
 		goto failed;
@@ -2086,6 +2120,10 @@
 #ifdef CONFIG_TDLS
 	wpa_tdls_assoc(sm);
 #endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_P2P
+	os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr));
+#endif /* CONFIG_P2P */
 }
 
 
@@ -2209,6 +2247,7 @@
 		} else
 			sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
+		sm->p2p = config->p2p;
 	} else {
 		sm->network_ctx = NULL;
 		sm->peerkey_enabled = 0;
@@ -2218,6 +2257,7 @@
 		sm->eap_conf_ctx = NULL;
 		sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = 0;
+		sm->p2p = 0;
 	}
 }
 
@@ -2731,3 +2771,16 @@
 	return 1;
 }
 #endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_P2P
+
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
+{
+	if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0)
+		return -1;
+	os_memcpy(buf, sm->p2p_ip_addr, 3 * 4);
+	return 0;
+}
+
+#endif /* CONFIG_P2P */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 9884ce1..20b3f62 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -95,6 +95,7 @@
 	const u8 *ssid;
 	size_t ssid_len;
 	int wpa_ptk_rekey;
+	int p2p;
 };
 
 #ifndef CONFIG_NO_WPA
@@ -145,6 +146,8 @@
 
 void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
 
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
+
 #else /* CONFIG_NO_WPA */
 
 static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index cad6c8d..75cfb47 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -58,6 +58,7 @@
 	u8 ssid[32];
 	size_t ssid_len;
 	int wpa_ptk_rekey;
+	int p2p;
 
 	u8 own_addr[ETH_ALEN];
 	const char *ifname;
@@ -122,6 +123,10 @@
 	u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
 	size_t assoc_resp_ies_len;
 #endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_P2P
+	u8 p2p_ip_addr[3 * 4];
+#endif /* CONFIG_P2P */
 };
 
 
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index ab8d104..e58bdc4 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -345,6 +345,25 @@
 	}
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_P2P
+	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+
+	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: IP Address Allocation in EAPOL-Key",
+			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+
 	return 0;
 }
 
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index 0375767..82b6fa3 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -59,6 +59,10 @@
 	size_t supp_oper_classes_len;
 	u8 qosinfo;
 	u16 aid;
+#ifdef CONFIG_P2P
+	const u8 *ip_addr_req;
+	const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
 };
 
 int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 96685d2..2b35064 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -30,6 +30,7 @@
 };
 
 static char wifi_handover_type[] = "application/vnd.wfa.wsc";
+static char p2p_handover_type[] = "application/vnd.wfa.p2p";
 
 static int ndef_parse_record(const u8 *data, u32 size,
 			     struct ndef_record *record)
@@ -170,85 +171,26 @@
 }
 
 
-struct wpabuf * ndef_build_wifi_hc(int begin)
+static int p2p_filter(struct ndef_record *record)
 {
-	struct wpabuf *hc, *carrier;
-
-	carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
-	if (carrier == NULL)
-		return NULL;
-	wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
-	wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
-	wpabuf_put_str(carrier, wifi_handover_type);
-
-	hc = ndef_build_record((begin ? FLAG_MESSAGE_BEGIN : 0) |
-			       FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
-			       "0", 1, carrier);
-	wpabuf_free(carrier);
-
-	return hc;
+	if (record->type_length != os_strlen(p2p_handover_type))
+		return 0;
+	if (os_memcmp(record->type, p2p_handover_type,
+		      os_strlen(p2p_handover_type)) != 0)
+		return 0;
+	return 1;
 }
 
 
-struct wpabuf * ndef_build_wifi_hr(void)
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf)
 {
-	struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
-	struct wpabuf *hc;
+	return ndef_parse_records(buf, p2p_filter);
+}
 
-	rn = wpabuf_alloc(2);
-	if (rn == NULL)
-		return NULL;
-	wpabuf_put_be16(rn, os_random() & 0xffff);
 
-	cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
-			       NULL, 0, rn);
-	wpabuf_free(rn);
-
-	if (cr == NULL)
-		return NULL;
-
-	ac_payload = wpabuf_alloc(4);
-	if (ac_payload == NULL) {
-		wpabuf_free(cr);
-		return NULL;
-	}
-	wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
-	wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
-	wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
-	wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
-
-	ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
-			       NULL, 0, ac_payload);
-	wpabuf_free(ac_payload);
-	if (ac == NULL) {
-		wpabuf_free(cr);
-		return NULL;
-	}
-
-	hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
-	if (hr_payload == NULL) {
-		wpabuf_free(cr);
-		wpabuf_free(ac);
-		return NULL;
-	}
-
-	wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
-	wpabuf_put_buf(hr_payload, cr);
-	wpabuf_put_buf(hr_payload, ac);
-	wpabuf_free(cr);
-	wpabuf_free(ac);
-
-	hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
-			       NULL, 0, hr_payload);
-	wpabuf_free(hr_payload);
-	if (hr == NULL)
-		return NULL;
-
-	hc = ndef_build_wifi_hc(0);
-	if (hc == NULL) {
-		wpabuf_free(hr);
-		return NULL;
-	}
-
-	return wpabuf_concat(hr, hc);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf)
+{
+	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
+				 FLAG_TNF_RFC2046, p2p_handover_type,
+				 os_strlen(p2p_handover_type), NULL, 0, buf);
 }
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 22d7eea..3d019f1 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -18,6 +18,7 @@
 #ifdef CONFIG_WPS_TESTING
 int wps_version_number = 0x20;
 int wps_testing_dummy_cred = 0;
+int wps_corrupt_pkhash = 0;
 #endif /* CONFIG_WPS_TESTING */
 
 
@@ -58,6 +59,10 @@
 	}
 
 #ifdef CONFIG_WPS_NFC
+	if (cfg->pin == NULL &&
+	    cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+		data->dev_pw_id = cfg->dev_pw_id;
+
 	if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
 		/* Keep AP PIN as alternative Device Password */
 		data->alt_dev_pw_id = data->dev_pw_id;
@@ -133,6 +138,12 @@
 	data->use_psk_key = cfg->use_psk_key;
 	data->pbc_in_m1 = cfg->pbc_in_m1;
 
+	if (cfg->peer_pubkey_hash) {
+		os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash,
+			  WPS_OOB_PUBKEY_HASH_LEN);
+		data->peer_pubkey_hash_set = 1;
+	}
+
 	return data;
 }
 
@@ -168,7 +179,6 @@
 	wps_device_data_free(&data->peer_dev);
 	os_free(data->new_ap_settings);
 	dh5_free(data->dh_ctx);
-	os_free(data->nfc_pw_token);
 	os_free(data);
 }
 
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 15137a8..6ccce1a 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -183,6 +183,11 @@
 	 * PBC with the AP.
 	 */
 	int pbc_in_m1;
+
+	/**
+	 * peer_pubkey_hash - Peer public key hash or %NULL if not known
+	 */
+	const u8 *peer_pubkey_hash;
 };
 
 struct wps_data * wps_init(const struct wps_config *cfg);
@@ -801,7 +806,8 @@
 			    struct wps_credential *cred);
 int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
 				   const u8 *pubkey_hash, u16 pw_id,
-				   const u8 *dev_pw, size_t dev_pw_len);
+				   const u8 *dev_pw, size_t dev_pw_len,
+				   int pk_hash_provided_oob);
 int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
 					 const u8 *oob_dev_pw,
 					 size_t oob_dev_pw_len);
@@ -815,7 +821,8 @@
 int wps_pin_str_valid(const char *pin);
 void wps_free_pending_msgs(struct upnp_pending_message *msgs);
 
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+				 int channel);
 int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
 int wps_attr_text(struct wpabuf *data, char *buf, char *end);
 const char * wps_ei_str(enum wps_error_indication ei);
@@ -839,6 +846,9 @@
 					      struct wps_credential *cred);
 struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
 					const u8 *addr);
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+					struct wps_context *wps, const u8 *uuid,
+					const u8 *addr, struct wpabuf *pubkey);
 
 int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
 char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
@@ -850,15 +860,27 @@
 				       const struct wpabuf *dev_pw);
 struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
 				    struct wpabuf *dev_pw);
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey);
 struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
 				  struct wpabuf **privkey,
 				  struct wpabuf **dev_pw);
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey,
+					   const u8 *bssid, int freq);
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+					       struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+					       int nfc_dev_pw_id,
+					       struct wpabuf *nfc_dh_pubkey,
+					       struct wpabuf *nfc_dev_pw);
 
 /* ndef.c */
 struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
 struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
-struct wpabuf * ndef_build_wifi_hc(int begin);
-struct wpabuf * ndef_build_wifi_hr(void);
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf);
 
 #ifdef CONFIG_WPS_STRICT
 int wps_validate_beacon(const struct wpabuf *wps_ie);
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index 336246e..62d0feb 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -38,8 +38,15 @@
 		wps->wps->dh_ctx = NULL;
 		pubkey = wpabuf_dup(wps->wps->dh_pubkey);
 #ifdef CONFIG_WPS_NFC
-	} else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
-		   wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+	} else if ((wps->dev_pw_id >= 0x10 ||
+		    wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) &&
+		   (wps->wps->ap ||
+		    (wps->wps->ap_nfc_dh_pubkey &&
+		     wps->wps->ap_nfc_dev_pw_id ==
+		     DEV_PW_NFC_CONNECTION_HANDOVER &&
+		     wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)) &&
+		   (wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id ||
+		    wps->wps->ap_nfc_dh_pubkey)) {
 		wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
 		if (wps->wps->ap_nfc_dh_privkey == NULL) {
 			wpa_printf(MSG_DEBUG,
@@ -392,6 +399,14 @@
 	addr[0] = wpabuf_head(pubkey);
 	hash_len = wpabuf_len(pubkey);
 	sha256_vector(1, addr, &hash_len, pubkey_hash);
+#ifdef CONFIG_WPS_TESTING
+	if (wps_corrupt_pkhash) {
+		wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash",
+			    pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+		wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash");
+		pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++;
+	}
+#endif /* CONFIG_WPS_TESTING */
 
 	wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
 	wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
@@ -399,9 +414,11 @@
 		    pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
 	wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
 	wpabuf_put_be16(msg, dev_pw_id);
-	wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
-			dev_pw, dev_pw_len);
-	wpabuf_put_data(msg, dev_pw, dev_pw_len);
+	if (dev_pw) {
+		wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
+				dev_pw, dev_pw_len);
+		wpabuf_put_data(msg, dev_pw, dev_pw_len);
+	}
 
 	return 0;
 }
@@ -449,3 +466,23 @@
 	wpabuf_put_data(msg, addr, ETH_ALEN);
 	return 0;
 }
+
+
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", rf_bands);
+	wpabuf_put_be16(msg, ATTR_RF_BANDS);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, rf_bands);
+	return 0;
+}
+
+
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * AP Channel (%u)", ap_channel);
+	wpabuf_put_be16(msg, ATTR_AP_CHANNEL);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, ap_channel);
+	return 0;
+}
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 3999b1b..f4e2e38 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -263,10 +263,13 @@
 		attr->dev_password_id = pos;
 		break;
 	case ATTR_OOB_DEVICE_PASSWORD:
-		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
-		    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
 		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
-		    WPS_OOB_DEVICE_PASSWORD_LEN) {
+		    WPS_OOB_DEVICE_PASSWORD_LEN ||
+		    (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		     WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
+		     WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
+		     DEV_PW_NFC_CONNECTION_HANDOVER)) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
 				   "Password length %u", len);
 			return -1;
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 4b431ad..abf3a4f 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -9,6 +9,8 @@
 #include "includes.h"
 
 #include "common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_common.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
 #include "crypto/dh_group5.h"
@@ -16,6 +18,7 @@
 #include "crypto/sha256.h"
 #include "crypto/random.h"
 #include "wps_i.h"
+#include "wps_dev_attr.h"
 
 
 void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
@@ -349,7 +352,8 @@
 
 #ifdef CONFIG_WPS_OOB
 
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+				 int channel)
 {
 	struct wps_data data;
 	struct wpabuf *plain;
@@ -365,8 +369,10 @@
 	data.wps = wps;
 	data.auth_type = wps->auth_types;
 	data.encr_type = wps->encr_types;
-	if (wps_build_version(plain) ||
-	    wps_build_cred(&data, plain) ||
+	if (wps_build_cred(&data, plain) ||
+	    (rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
+	    (channel && wps_build_ap_channel(plain, channel)) ||
+	    wps_build_mac_addr(plain, wps->dev.mac_addr) ||
 	    wps_build_wfa_ext(plain, 0, NULL, 0)) {
 		os_free(data.new_psk);
 		wpabuf_free(plain);
@@ -412,8 +418,7 @@
 	if (data == NULL)
 		return NULL;
 
-	if (wps_build_version(data) ||
-	    wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+	if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
 				 wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
 	    wps_build_wfa_ext(data, 0, NULL, 0)) {
 		wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
@@ -636,12 +641,36 @@
 }
 
 
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey)
+{
+	struct wpabuf *priv = NULL, *pub = NULL;
+	void *dh_ctx;
+
+	dh_ctx = dh5_init(&priv, &pub);
+	if (dh_ctx == NULL)
+		return -1;
+	pub = wpabuf_zeropad(pub, 192);
+	if (pub == NULL) {
+		wpabuf_free(priv);
+		return -1;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub);
+	dh5_free(dh_ctx);
+
+	wpabuf_free(*pubkey);
+	*pubkey = pub;
+	wpabuf_free(*privkey);
+	*privkey = priv;
+
+	return 0;
+}
+
+
 struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
 				  struct wpabuf **privkey,
 				  struct wpabuf **dev_pw)
 {
-	struct wpabuf *priv = NULL, *pub = NULL, *pw;
-	void *dh_ctx;
+	struct wpabuf *pw;
 	u16 val;
 
 	pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
@@ -655,22 +684,223 @@
 		return NULL;
 	}
 
-	dh_ctx = dh5_init(&priv, &pub);
-	if (dh_ctx == NULL) {
+	if (wps_nfc_gen_dh(pubkey, privkey) < 0) {
 		wpabuf_free(pw);
 		return NULL;
 	}
-	dh5_free(dh_ctx);
 
 	*id = 0x10 + val % 0xfff0;
-	wpabuf_free(*pubkey);
-	*pubkey = pub;
-	wpabuf_free(*privkey);
-	*privkey = priv;
 	wpabuf_free(*dev_pw);
 	*dev_pw = pw;
 
 	return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
 }
 
+
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey)
+{
+	struct wpabuf *msg;
+	void *len;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover request");
+
+	if (nfc_dh_pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+			   "configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+	len = wpabuf_put(msg, 2);
+
+	if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+				 nfc_dh_pubkey, NULL, 0) ||
+	    wps_build_uuid_e(msg, ctx->uuid) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+	return msg;
+}
+
+
+static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select",
+			  wps->ssid, wps->ssid_len);
+	wpabuf_put_be16(msg, ATTR_SSID);
+	wpabuf_put_be16(msg, wps->ssid_len);
+	wpabuf_put_data(msg, wps->ssid, wps->ssid_len);
+	return 0;
+}
+
+
+static int wps_build_ap_freq(struct wpabuf *msg, int freq)
+{
+	enum hostapd_hw_mode mode;
+	u8 channel, rf_band;
+	u16 ap_channel;
+
+	if (freq <= 0)
+		return 0;
+
+	mode = ieee80211_freq_to_chan(freq, &channel);
+	if (mode == NUM_HOSTAPD_MODES)
+		return 0; /* Unknown channel */
+
+	if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B)
+		rf_band = WPS_RF_24GHZ;
+	else if (mode == HOSTAPD_MODE_IEEE80211A)
+		rf_band = WPS_RF_50GHZ;
+	else
+		return 0; /* Unknown band */
+	ap_channel = channel;
+
+	if (wps_build_rf_bands_attr(msg, rf_band) ||
+	    wps_build_ap_channel(msg, ap_channel))
+		return -1;
+
+	return 0;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey,
+					   const u8 *bssid, int freq)
+{
+	struct wpabuf *msg;
+	void *len;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover select");
+
+	if (nfc_dh_pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+			   "configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+	len = wpabuf_put(msg, 2);
+
+	if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+				 nfc_dh_pubkey, NULL, 0) ||
+	    wps_build_ssid(msg, ctx) ||
+	    wps_build_ap_freq(msg, freq) ||
+	    (bssid && wps_build_mac_addr(msg, bssid)) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+	return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+					       struct wpabuf *nfc_dh_pubkey)
+{
+	struct wpabuf *msg;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover request (P2P)");
+
+	if (nfc_dh_pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+
+	if (wps_build_manufacturer(&ctx->dev, msg) ||
+	    wps_build_model_name(&ctx->dev, msg) ||
+	    wps_build_model_number(&ctx->dev, msg) ||
+	    wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+				 nfc_dh_pubkey, NULL, 0) ||
+	    wps_build_rf_bands(&ctx->dev, msg, 0) ||
+	    wps_build_serial_number(&ctx->dev, msg) ||
+	    wps_build_uuid_e(msg, ctx->uuid) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+					       int nfc_dev_pw_id,
+					       struct wpabuf *nfc_dh_pubkey,
+					       struct wpabuf *nfc_dev_pw)
+{
+	struct wpabuf *msg;
+	const u8 *dev_pw;
+	size_t dev_pw_len;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover select (P2P)");
+
+	if (nfc_dh_pubkey == NULL ||
+	    (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+	     nfc_dev_pw == NULL)) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+			   "configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+
+	if (nfc_dev_pw) {
+		dev_pw = wpabuf_head(nfc_dev_pw);
+		dev_pw_len = wpabuf_len(nfc_dev_pw);
+	} else {
+		dev_pw = NULL;
+		dev_pw_len = 0;
+	}
+
+	if (wps_build_manufacturer(&ctx->dev, msg) ||
+	    wps_build_model_name(&ctx->dev, msg) ||
+	    wps_build_model_number(&ctx->dev, msg) ||
+	    wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey,
+				 dev_pw, dev_pw_len) ||
+	    wps_build_rf_bands(&ctx->dev, msg, 0) ||
+	    wps_build_serial_number(&ctx->dev, msg) ||
+	    wps_build_uuid_e(msg, ctx->uuid) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
 #endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index 3421ac5..6f8a49f 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -13,6 +13,7 @@
 
 extern int wps_version_number;
 extern int wps_testing_dummy_cred;
+extern int wps_corrupt_pkhash;
 #define WPS_VERSION wps_version_number
 
 #else /* CONFIG_WPS_TESTING */
@@ -155,7 +156,8 @@
 	DEV_PW_MACHINE_SPECIFIED = 0x0002,
 	DEV_PW_REKEY = 0x0003,
 	DEV_PW_PUSHBUTTON = 0x0004,
-	DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+	DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
+	DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007
 };
 
 /* Message Type */
@@ -215,7 +217,9 @@
 	WPS_CFG_SETUP_LOCKED = 15,
 	WPS_CFG_MSG_TIMEOUT = 16,
 	WPS_CFG_REG_SESS_TIMEOUT = 17,
-	WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+	WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18,
+	WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19,
+	WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
 };
 
 /* Vendor specific Error Indication for WPS event messages */
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
index fe736f3..0d01211 100644
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -85,8 +85,7 @@
 }
 
 
-static int wps_build_serial_number(struct wps_device_data *dev,
-				   struct wpabuf *msg)
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg)
 {
 	size_t len;
 	wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
@@ -220,11 +219,7 @@
 int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
 		       u8 rf_band)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", dev->rf_bands);
-	wpabuf_put_be16(msg, ATTR_RF_BANDS);
-	wpabuf_put_be16(msg, 1);
-	wpabuf_put_u8(msg, rf_band ? rf_band : dev->rf_bands);
-	return 0;
+	return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands);
 }
 
 
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
index f0169a7..c9034ad 100644
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -14,6 +14,7 @@
 int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 7b86ff7..9d48ca5 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -514,6 +514,24 @@
 		return -1;
 	}
 
+	if (wps->peer_pubkey_hash_set) {
+		u8 hash[WPS_HASH_LEN];
+		sha256_vector(1, &pk, &pk_len, hash);
+		if (os_memcmp(hash, wps->peer_pubkey_hash,
+			      WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+			wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
+			wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
+				    pk, pk_len);
+			wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
+				    "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
+			wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
+				    wps->peer_pubkey_hash,
+				    WPS_OOB_PUBKEY_HASH_LEN);
+			wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+			return -1;
+		}
+	}
+
 	wpabuf_free(wps->dh_pubkey_r);
 	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
 	if (wps->dh_pubkey_r == NULL)
@@ -929,6 +947,38 @@
 		return WPS_CONTINUE;
 	}
 
+#ifdef CONFIG_WPS_NFC
+	if (wps->peer_pubkey_hash_set) {
+		struct wpabuf *decrypted;
+		struct wps_parse_attr eattr;
+
+		decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+						      attr->encr_settings_len);
+		if (decrypted == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
+				   "Encrypted Settings attribute");
+			wps->state = SEND_WSC_NACK;
+			return WPS_CONTINUE;
+		}
+
+		wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
+			   "Settings attribute");
+		if (wps_parse_msg(decrypted, &eattr) < 0 ||
+		    wps_process_key_wrap_auth(wps, decrypted,
+					      eattr.key_wrap_auth) ||
+		    wps_process_creds(wps, eattr.cred, eattr.cred_len,
+				      eattr.num_cred, attr->version2 != NULL)) {
+			wpabuf_free(decrypted);
+			wps->state = SEND_WSC_NACK;
+			return WPS_CONTINUE;
+		}
+		wpabuf_free(decrypted);
+
+		wps->state = WPS_MSG_DONE;
+		return WPS_CONTINUE;
+	}
+#endif /* CONFIG_WPS_NFC */
+
 	wps->state = SEND_M3;
 	return WPS_CONTINUE;
 }
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index e729617..8e9ee7a 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -2040,8 +2040,7 @@
 	os_memset(&data, 0, sizeof(data));
 	data.wps = wps;
 	data.use_cred = cred;
-	if (wps_build_version(ret) ||
-	    wps_build_cred(&data, ret) ||
+	if (wps_build_cred(&data, ret) ||
 	    wps_build_wfa_ext(ret, 0, NULL, 0)) {
 		wpabuf_free(ret);
 		return NULL;
@@ -2071,4 +2070,29 @@
 	return wps_er_config_token_from_cred(er->wps, ap->ap_settings);
 }
 
+
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+					struct wps_context *wps, const u8 *uuid,
+					const u8 *addr, struct wpabuf *pubkey)
+{
+	struct wps_er_ap *ap;
+
+	if (er == NULL)
+		return NULL;
+
+	ap = wps_er_ap_get(er, NULL, uuid, addr);
+	if (ap == NULL)
+		return NULL;
+	if (ap->ap_settings == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the "
+			   "selected AP");
+		return NULL;
+	}
+
+	os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len);
+	wps->ssid_len = ap->ap_settings->ssid_len;
+
+	return wps_build_nfc_handover_sel(wps, pubkey, addr, 0);
+}
+
 #endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index dac1250..22070db 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -75,6 +75,9 @@
 	size_t alt_dev_password_len;
 	u16 alt_dev_pw_id;
 
+	u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	int peer_pubkey_hash_set;
+
 	/**
 	 * request_type - Request Type attribute from (Re)AssocReq
 	 */
@@ -173,6 +176,8 @@
 			 size_t dev_pw_len);
 struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
 int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr);
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands);
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel);
 
 /* wps_attr_process.c */
 int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 19490a1..6d879be 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -31,9 +31,11 @@
 struct wps_nfc_pw_token {
 	struct dl_list list;
 	u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	unsigned int peer_pk_hash_known:1;
 	u16 pw_id;
 	u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1];
 	size_t dev_pw_len;
+	int pk_hash_provided_oob; /* whether own PK hash was provided OOB */
 };
 
 
@@ -1360,10 +1362,23 @@
 		pin_len = 8;
 #ifdef CONFIG_WPS_NFC
 	} else if (wps->nfc_pw_token) {
+		if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+		{
+			wpa_printf(MSG_DEBUG, "WPS: Using NFC connection "
+				   "handover and abbreviated WPS handshake "
+				   "without Device Password");
+			return 0;
+		}
 		wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
 			   "Password Token");
 		pin = wps->nfc_pw_token->dev_pw;
 		pin_len = wps->nfc_pw_token->dev_pw_len;
+	} else if (wps->dev_pw_id >= 0x10 &&
+		   wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+		   wps->wps->ap_nfc_dev_pw) {
+		wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token");
+		pin = wpabuf_head(wps->wps->ap_nfc_dev_pw);
+		pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw);
 #endif /* CONFIG_WPS_NFC */
 	} else {
 		pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
@@ -1777,6 +1792,7 @@
 static struct wpabuf * wps_build_m2(struct wps_data *wps)
 {
 	struct wpabuf *msg;
+	int config_in_m2 = 0;
 
 	if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
 		return NULL;
@@ -1807,14 +1823,41 @@
 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
 	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
 	    wps_build_os_version(&wps->wps->dev, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
-	    wps_build_authenticator(wps, msg)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+#ifdef CONFIG_WPS_NFC
+	if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob &&
+	    wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+		/*
+		 * Use abbreviated handshake since public key hash allowed
+		 * Enrollee to validate our public key similarly to how Enrollee
+		 * public key was validated. There is no need to validate Device
+		 * Password in this case.
+		 */
+		struct wpabuf *plain = wpabuf_alloc(500);
+		if (plain == NULL ||
+		    wps_build_cred(wps, plain) ||
+		    wps_build_key_wrap_auth(wps, plain) ||
+		    wps_build_encr_settings(wps, msg, plain)) {
+			wpabuf_free(msg);
+			wpabuf_free(plain);
+			return NULL;
+		}
+		wpabuf_free(plain);
+		config_in_m2 = 1;
+	}
+#endif /* CONFIG_WPS_NFC */
+
+	if (wps_build_authenticator(wps, msg)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
 
 	wps->int_reg = 1;
-	wps->state = RECV_M3;
+	wps->state = config_in_m2 ? RECV_DONE : RECV_M3;
 	return msg;
 }
 
@@ -2528,6 +2571,9 @@
 	    wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
 	    wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
 	    wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+#ifdef CONFIG_WPS_NFC
+	    wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+#endif /* CONFIG_WPS_NFC */
 	    (wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
 	     !wps->wps->registrar->pbc)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
@@ -2537,7 +2583,8 @@
 	}
 
 #ifdef CONFIG_WPS_NFC
-	if (wps->dev_pw_id >= 0x10) {
+	if (wps->dev_pw_id >= 0x10 ||
+	    wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
 		struct wps_nfc_pw_token *token;
 		const u8 *addr[1];
 		u8 hash[WPS_HASH_LEN];
@@ -2546,7 +2593,7 @@
 			   wps->dev_pw_id, wps->wps, wps->wps->registrar);
 		token = wps_get_nfc_pw_token(
 			&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
-		if (token) {
+		if (token && token->peer_pk_hash_known) {
 			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
 				   "Password Token");
 			dl_list_del(&token->list);
@@ -2558,8 +2605,19 @@
 				      WPS_OOB_PUBKEY_HASH_LEN) != 0) {
 				wpa_printf(MSG_ERROR, "WPS: Public Key hash "
 					   "mismatch");
-				return WPS_FAILURE;
+				wps->state = SEND_M2D;
+				wps->config_error =
+					WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+				return WPS_CONTINUE;
 			}
+		} else if (token) {
+			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+				   "Password Token (no peer PK hash)");
+			wps->nfc_pw_token = token;
+		} else if (wps->dev_pw_id >= 0x10 &&
+			   wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+			   wps->wps->ap_nfc_dev_pw) {
+			wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token");
 		}
 	}
 #endif /* CONFIG_WPS_NFC */
@@ -3493,25 +3551,39 @@
 
 int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
 				   const u8 *pubkey_hash, u16 pw_id,
-				   const u8 *dev_pw, size_t dev_pw_len)
+				   const u8 *dev_pw, size_t dev_pw_len,
+				   int pk_hash_provided_oob)
 {
 	struct wps_nfc_pw_token *token;
 
 	if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
 		return -1;
 
+	if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER &&
+	    (pubkey_hash == NULL || !pk_hash_provided_oob)) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token "
+			   "addition - missing public key hash");
+		return -1;
+	}
+
 	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
 
 	token = os_zalloc(sizeof(*token));
 	if (token == NULL)
 		return -1;
 
-	os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+	token->peer_pk_hash_known = pubkey_hash != NULL;
+	if (pubkey_hash)
+		os_memcpy(token->pubkey_hash, pubkey_hash,
+			  WPS_OOB_PUBKEY_HASH_LEN);
 	token->pw_id = pw_id;
-	wpa_snprintf_hex_uppercase((char *) token->dev_pw,
-				   sizeof(token->dev_pw),
-				   dev_pw, dev_pw_len);
-	token->dev_pw_len = dev_pw_len * 2;
+	token->pk_hash_provided_oob = pk_hash_provided_oob;
+	if (dev_pw) {
+		wpa_snprintf_hex_uppercase((char *) token->dev_pw,
+					   sizeof(token->dev_pw),
+					   dev_pw, dev_pw_len);
+		token->dev_pw_len = dev_pw_len * 2;
+	}
 
 	dl_list_add(&reg->nfc_pw_tokens, &token->list);
 
@@ -3540,8 +3612,7 @@
 	u16 id;
 	size_t dev_pw_len;
 
-	if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
-	    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+	if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
 	    oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
 	    WPS_OOB_DEVICE_PASSWORD_LEN)
 		return -1;
@@ -3560,7 +3631,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
 
 	return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
-					      dev_pw_len);
+					      dev_pw_len, 0);
 }
 
 
@@ -3570,6 +3641,14 @@
 	wps_registrar_remove_authorized_mac(reg,
 					    (u8 *) "\xff\xff\xff\xff\xff\xff");
 	wps_registrar_selected_registrar_changed(reg, 0);
+
+	/*
+	 * Free the NFC password token if it was used only for a single protocol
+	 * run. The static handover case uses the same password token multiple
+	 * times, so do not free that case here.
+	 */
+	if (token->peer_pk_hash_known)
+		os_free(token);
 }
 
 #endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c
index e366256..1c6a14b 100644
--- a/src/wps/wps_validate.c
+++ b/src/wps/wps_validate.c
@@ -267,7 +267,7 @@
 		return 0;
 	}
 	val = WPA_GET_BE16(config_error);
-	if (val > 18) {
+	if (val > 20) {
 		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
 			   "attribute value 0x%04x", val);
 		return -1;
@@ -290,7 +290,7 @@
 		return 0;
 	}
 	val = WPA_GET_BE16(dev_password_id);
-	if (val >= 0x0006 && val <= 0x000f) {
+	if (val >= 0x0008 && val <= 0x000f) {
 		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
 			   "attribute value 0x%04x", val);
 		return -1;