[wpa_supplicant] Cumulative patch from commit dd2daf084
Bug: 156933657
Test: Confirm random dialog token usage from logs
Test: Verify Passpoint ANQP functionality and Passpoint association
Test: act.py -c ../WifiDppConfig.json -tc WifiDppTest
Test: Connect to Passpoint, Open, WPA2, WPA3 networks and run traffic
Test: Regression test passed (Bug: 156969218)
dd2daf084 HE: Process HE 6 GHz band capab from associating HE STA
db603634a HE: Add 6 GHz Band Capabilities element in Beacon and response frames
88911a0aa HE: Add HE 6 GHz Band Capabilities into ieee802_11_parse_elems()
b2c0b83c6 HE: Remove VHT Operation Information from HE Operation element
e297a5bfd HE: Define 6 GHz band capability elements
39f29f250 defconfig: Enable TDLS
025ab330b ACS: Channel selection based freqlist
4ae3f3972 Add a helper function for recognizing BIP enum wpa_alg values
d3cab56c0 Rename WPA_ALG_IGTK to use the correct cipher name for BIP
bd1aebbd0 hostapd: Extend RESET_PN for BIGTK
df49c53f4 Fix a typo in a comment
4294d221d D-Bus: Increase introspection buffer size
79488da57 wolfssl: Do not hardcode include directory in wpa_supplicant build
eb595b3e3 wolfssl: Fix crypto_bignum_rand() implementation
6a28c4dbc wolfssl: Fix compiler warnings on size_t printf format use
f2dbaa8ac SAE: Fix a typo in a comment
038899290 wpa_gui: Fix build with Inkscape 1.0
3e1a13010 nl80211: Change AKM suite limit from warning to debug print
5a04a76aa Ignore Management frames while AP interface is not fully enabled
c82535edd Move deauthentication at AP start to be after beacon configuration
094c8a621 Remove unnecessary key clearing at AP start with nl80211
04030e8c0 nl80211: Remove AP mode interface from bridge for STA-mode-scan
7adea21d2 dpp-nfc: Enable hostapd beaconing for listen state
134ad50b0 dpp-nfc: Clean up debug prints when handover select is received
5d49c1bf7 dpp-nfc: Do not indicate a single channel 1 by default
d0e2d8091 dpp-nfc: Make handover request collision detection more robust
8791e7461 dpp-nfc: Write debug info to summary log
1e0bc897a dpp-nfc: Collision detection for handover request
9ad3fe934 dpp-nfc: Start handover server regardless of init-on-touch setting
24efcdf74 dpp-nfc: Own MAC address fetching from hostapd
8f96f2c3b dpp-nfc: Be more graceful when wpa_supplicant is not available
0b04d3c57 dpp-nfc: Allow wpa_supplicant control interface directory to be set
69dfbe6a9 dpp-nfc: Use Configurator/Enrollee parameters with tag reading
f85fb349f dpp-nfc: More robust determination of the script directory
98e4b3840 DPP2: Chirping in hostapd Enrollee
95471fc3e Handle hostapd_for_each_interface() at the process termination
99809c7a4 nl80211: Disable offchannel-ok in AP mode only if beaconing
0f58c88fc DPP2: Fix CONFIG_DPP2=y build with OpenSSL 1.0.2
44f786678 Clean up GET_CAPABILITY handling of 'strict' argument
3790f3a6e Use per-interface type driver key_mgmt capabilities when possible
8d7502809 Allow per interface type AKM capabilities to be fetched
b67bedf2e nl80211: Fetch information on supported AKMs from the driver
6fffb320f nl80211: Remove QCA vendor specific AKM capability handling
db59827a3 DPP2: Extend TCP encapsulation case to support Configurator as Initiator
0086c1452 DPP: Extend NFC bootstrapping script for more control by caller
890ae336c DPP2: Clean up CONFIG_DPP2 use with configurator connectivity IE
670e15337 DPP2: Fix DPP_CHIRP listen parameter value validation
7f20a3ebd DPP2: Reconfiguration support in Controller
6dcb8aaf1 DPP2: Reconfig Announcement relaying from AP to Controller
3b4f7dfaa DPP2: Fix Presence Announcement processing in Controller
5e2d877cc DPP: Mark internal-to-file functions static
3aaf269f6 DPP: Move TCP encapsulation into a separate source code file
21c612017 DPP: Move configurator backup into a separate source code file
fdbbb7406 DPP: Move authentication functionality into a separate source code file
182f6ae90 DPP2: Remove reconfigured network
3e48c5d4b DPP2: Reconfig Authentication Confirm processing
24b01c706 DPP2: Reconfig Authentication Response processing and Confirm generation
65e94351d DPP2: Reconfig Authentication Request processing and Response generation
3774b6bd0 DPP2: Reconfig Authentication Request generation and transmission
66ac616cd DPP2: Process received Reconfig Announcement frame
0c043d9de DPP2: Reconfig Announcement transmission
92492dd3a DPP2: Extend connector matching for reconfiguration
961435097 DPP2: Move connStatus object building into a helper function
94f73f90e DPP: Move signed connector checking into a helper function
94a28a494 DPP: Move parsing of own connector into a helper function
d4ae12355 DPP: Move PKEX functionality into a separate source code file
87b657261 DPP: Move crypto routines into a separate source code file
16626dff9 DPP2: Derive bk ("base key")
76029c6e1 DPP: Use EVP_PKEY_get0_EC_KEY() when a const reference is sufficient
0a488ef35 DPP: Track ending time for remain-on-channel operations
481fdfc46 DPP2: Fix URI version parser
7dd768c3c DPP2: Version information in bootstrapping info URI
cbafc8ef4 Fix truncated control interface command detection
5a0718a19 DPP2: Report MUD URL and bandSupport in control interface events
769139c49 DPP2: Do not include Protocol Version in Auth Req when testing v1
fad64b416 DPP: Move dppCon signing to a set of helper functions
12c8eacf7 DPP: Allow version number to be overridden for testing purposes
c3c38bc8b DPP2: Detect PFS downgrade attack while processing EAPOL-Key msg 3/4
9561925b4 DPP2: Detect PFS downgrade attack while processing EAPOL-Key msg 2/4
68422fedb DPP2: Parse DPP KDE in EAPOL-Key Key Data field
143e3d8bc DPP2: Add DPP KDE into EAPOL-Key msg 2/4 when using DPP AKM
b11a12401 DPP2: Add DPP KDE into EAPOL-Key msg 3/4 when using DPP AKM
85d545699 DPP2: Indicate if PFS was used in control interface STATUS
1f5f00008 DPP2: Try to negotiate PFS only if AP supports version 2 or newer
f6c22dcde Use a local pointer to simply current_ssid accesses in sme_associate()
42acf1292 DPP2: Add Protocol Version attribute to network introduction messages
96b6dd21a Increase wpa_supplicant control interface buffer size
a7d6098fb Add PRINTF_FORMAT for printf wrapper functions
Change-Id: I26a15ac578ce81d1e35f426085661e5c7b43b6ef
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 5c01610..aa2ceb0 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -512,6 +512,17 @@
}
+static int is_in_freqlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ if (!iface->conf->acs_freq_list.num)
+ return 1;
+
+ return freq_range_list_includes(&iface->conf->acs_freq_list,
+ chan->freq);
+}
+
+
static void acs_survey_mode_interference_factor(
struct hostapd_iface *iface, struct hostapd_hw_modes *mode)
{
@@ -527,6 +538,9 @@
if (!is_in_chanlist(iface, chan))
continue;
+ if (!is_in_freqlist(iface, chan))
+ continue;
+
wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
chan->chan, chan->freq);
@@ -651,6 +665,9 @@
if (!is_in_chanlist(iface, chan))
continue;
+ if (!is_in_freqlist(iface, chan))
+ continue;
+
if (!chan_bw_allowed(chan, bw, 1, 1)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: BW %u is not supported",
@@ -1013,6 +1030,9 @@
if (!is_in_chanlist(iface, chan))
continue;
+ if (!is_in_freqlist(iface, chan))
+ continue;
+
*freq++ = chan->freq;
}
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 1f284f0..f157659 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -418,6 +418,7 @@
const struct ieee80211_vht_capabilities *vht_capab,
const struct ieee80211_he_capabilities *he_capab,
size_t he_capab_len,
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set)
{
@@ -439,6 +440,7 @@
params.vht_capabilities = vht_capab;
params.he_capab = he_capab;
params.he_capab_len = he_capab_len;
+ params.he_6ghz_capab = he_6ghz_capab;
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
params.vht_opmode = vht_opmode;
params.flags = hostapd_sta_flags_to_drv(flags);
@@ -650,6 +652,12 @@
}
+bool hostapd_drv_nl80211(struct hostapd_data *hapd)
+{
+ return hapd->driver && os_strcmp(hapd->driver->name, "nl80211") == 0;
+}
+
+
int hostapd_driver_scan(struct hostapd_data *hapd,
struct wpa_driver_scan_params *params)
{
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 56d1ad8..5738c1c 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -43,6 +43,7 @@
const struct ieee80211_vht_capabilities *vht_capab,
const struct ieee80211_he_capabilities *he_capab,
size_t he_capab_len,
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
@@ -80,6 +81,7 @@
u16 *flags, u8 *dfs_domain);
int hostapd_driver_commit(struct hostapd_data *hapd);
int hostapd_drv_none(struct hostapd_data *hapd);
+bool hostapd_drv_nl80211(struct hostapd_data *hapd);
int hostapd_driver_scan(struct hostapd_data *hapd,
struct wpa_driver_scan_params *params);
struct wpa_scan_results * hostapd_driver_get_scan_results(
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 47ced9a..22e672c 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -463,6 +463,9 @@
3 + sizeof(struct ieee80211_he_operation) +
3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
3 + sizeof(struct ieee80211_spatial_reuse);
+ if (is_6ghz_op_class(hapd->iconf->op_class))
+ buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
}
#endif /* CONFIG_IEEE80211AX */
@@ -570,6 +573,7 @@
pos = hostapd_eid_he_operation(hapd, pos);
pos = hostapd_eid_spatial_reuse(hapd, pos);
pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
+ pos = hostapd_eid_he_6ghz_band_cap(hapd, pos);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1161,6 +1165,9 @@
3 + sizeof(struct ieee80211_he_operation) +
3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
3 + sizeof(struct ieee80211_spatial_reuse);
+ if (is_6ghz_op_class(hapd->iconf->op_class))
+ tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1288,6 +1295,7 @@
tailpos = hostapd_eid_he_operation(hapd, tailpos);
tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
+ tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos);
}
#endif /* CONFIG_IEEE80211AX */
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index c86f01b..178fd12 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -26,6 +26,10 @@
static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
+#ifdef CONFIG_DPP2
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+#endif /* CONFIG_DPP2 */
static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -237,6 +241,10 @@
hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+#endif /* CONFIG_DPP2 */
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
@@ -539,6 +547,10 @@
hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+#endif /* CONFIG_DPP2 */
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
}
@@ -618,6 +630,10 @@
wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
MAC2STR(src));
+#ifdef CONFIG_DPP2
+ hostapd_dpp_chirp_stop(hapd);
+#endif /* CONFIG_DPP2 */
+
r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
@@ -1197,6 +1213,145 @@
}
}
+
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Reply wait timeout");
+ hostapd_dpp_listen_stop(hapd);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_announcement(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *csign_hash;
+ u16 csign_hash_len;
+ struct dpp_configurator *conf;
+ struct dpp_authentication *auth;
+ unsigned int wait_time, max_wait_time;
+
+ if (hapd->dpp_auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement from " MACSTR,
+ MAC2STR(src));
+
+ csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+ &csign_hash_len);
+ if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Configurator C-sign key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+ csign_hash, csign_hash_len);
+ conf = dpp_configurator_find_kid(hapd->iface->interfaces->dpp,
+ csign_hash);
+ if (!conf) {
+ if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+ src, hdr, buf, len, freq, NULL,
+ NULL) == 0)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching Configurator information found");
+ return;
+ }
+
+ auth = dpp_reconfig_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ conf, freq);
+ if (!auth)
+ return;
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (dpp_set_configurator(auth, hapd->dpp_configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ return;
+ }
+
+ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+ hapd->dpp_auth = auth;
+
+ hapd->dpp_in_response_listen = 0;
+ hapd->dpp_auth_ok_on_ack = 0;
+ wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+ max_wait_time = hapd->dpp_resp_wait_time ?
+ hapd->dpp_resp_wait_time : 2000;
+ if (wait_time > max_wait_time)
+ wait_time = max_wait_time;
+ wait_time += 10; /* give the driver some extra time to complete */
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+ wait_time -= 10;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_REQ);
+ if (hostapd_drv_send_action(hapd, freq, wait_time, src,
+ wpabuf_head(auth->reconfig_req_msg),
+ wpabuf_len(auth->reconfig_req_msg)) < 0) {
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ }
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_auth_resp(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ struct wpabuf *conf;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
+ MACSTR, MAC2STR(src));
+
+ if (!auth || !auth->reconfig || !auth->configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Reconfig Authentication in progress - drop");
+ return;
+ }
+
+ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+ return;
+ }
+
+ conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+ if (!conf)
+ return;
+
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
+ if (hostapd_drv_send_action(hapd, freq, 500, src,
+ wpabuf_head(conf), wpabuf_len(conf)) < 0) {
+ wpabuf_free(conf);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+ wpabuf_free(conf);
+}
+
#endif /* CONFIG_DPP2 */
@@ -1206,9 +1361,13 @@
enum dpp_status_error status)
{
struct wpabuf *msg;
+ size_t len;
- msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
- 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector));
+ len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+#ifdef CONFIG_DPP2
+ len += 5;
+#endif /* CONFIG_DPP2 */
+ msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, len);
if (!msg)
return;
@@ -1281,6 +1440,15 @@
skip_connector:
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1) {
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+ }
+#endif /* CONFIG_DPP2 */
+
wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
" status=%d", MAC2STR(src), status);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
@@ -1650,6 +1818,14 @@
hostapd_dpp_rx_presence_announcement(hapd, src, hdr, buf, len,
freq);
break;
+ case DPP_PA_RECONFIG_ANNOUNCEMENT:
+ hostapd_dpp_rx_reconfig_announcement(hapd, src, hdr, buf, len,
+ freq);
+ break;
+ case DPP_PA_RECONFIG_AUTH_RESP:
+ hostapd_dpp_rx_reconfig_auth_resp(hapd, src, hdr, buf, len,
+ freq);
+ break;
#endif /* CONFIG_DPP2 */
default:
wpa_printf(MSG_DEBUG,
@@ -1679,7 +1855,7 @@
struct wpabuf *resp;
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
- if (!auth || !auth->auth_success ||
+ if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
#ifdef CONFIG_DPP2
if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
@@ -1715,6 +1891,8 @@
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
if (ok && auth->peer_version >= 2 &&
auth->conf_resp_status == DPP_STATUS_OK) {
wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
@@ -1959,10 +2137,13 @@
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
NULL);
eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
NULL);
+ hostapd_dpp_chirp_stop(hapd);
#endif /* CONFIG_DPP2 */
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
@@ -1971,3 +2152,320 @@
os_free(hapd->dpp_configurator_params);
hapd->dpp_configurator_params = NULL;
}
+
+
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx);
+
+static void hostapd_dpp_chirp_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "DPP: No chirp response received");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_chirp_next(hapd, NULL);
+}
+
+
+static void hostapd_dpp_chirp_start(struct hostapd_data *hapd)
+{
+ struct wpabuf *msg;
+ int type;
+
+ msg = hapd->dpp_presence_announcement;
+ type = DPP_PA_PRESENCE_ANNOUNCEMENT;
+ wpa_printf(MSG_DEBUG, "DPP: Chirp on %d MHz", hapd->dpp_chirp_freq);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(broadcast), hapd->dpp_chirp_freq, type);
+ if (hostapd_drv_send_action(
+ hapd, hapd->dpp_chirp_freq, 2000, broadcast,
+ wpabuf_head(msg), wpabuf_len(msg)) < 0 ||
+ eloop_register_timeout(2, 0, hostapd_dpp_chirp_timeout,
+ hapd, NULL) < 0)
+ hostapd_dpp_chirp_stop(hapd);
+}
+
+
+static struct hostapd_hw_modes *
+dpp_get_mode(struct hostapd_data *hapd,
+ enum hostapd_hw_mode mode)
+{
+ struct hostapd_hw_modes *modes = hapd->iface->hw_features;
+ u16 num_modes = hapd->iface->num_hw_features;
+ u16 i;
+
+ for (i = 0; i < num_modes; i++) {
+ if (modes[i].mode != mode ||
+ !modes[i].num_channels || !modes[i].channels)
+ continue;
+ return &modes[i];
+ }
+
+ return NULL;
+}
+
+
+static void
+hostapd_dpp_chirp_scan_res_handler(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct wpa_scan_results *scan_res;
+ struct dpp_bootstrap_info *bi = hapd->dpp_chirp_bi;
+ unsigned int i;
+ struct hostapd_hw_modes *mode;
+ int c;
+
+ if (!bi)
+ return;
+
+ hapd->dpp_chirp_scan_done = 1;
+
+ scan_res = hostapd_driver_get_scan_results(hapd);
+
+ os_free(hapd->dpp_chirp_freqs);
+ hapd->dpp_chirp_freqs = NULL;
+
+ /* Channels from own bootstrapping info */
+ if (bi) {
+ for (i = 0; i < bi->num_freq; i++)
+ int_array_add_unique(&hapd->dpp_chirp_freqs,
+ bi->freq[i]);
+ }
+
+ /* Preferred chirping channels */
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 2437);
+
+ mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211A);
+ if (mode) {
+ int chan44 = 0, chan149 = 0;
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+ if (chan->freq == 5220)
+ chan44 = 1;
+ if (chan->freq == 5745)
+ chan149 = 1;
+ }
+ if (chan149)
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 5745);
+ else if (chan44)
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 5220);
+ }
+
+ mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211AD);
+ if (mode) {
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if ((chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR)) ||
+ chan->freq != 60480)
+ continue;
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 60480);
+ break;
+ }
+ }
+
+ /* Add channels from scan results for APs that advertise Configurator
+ * Connectivity element */
+ for (i = 0; scan_res && i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ size_t ie_len = bss->ie_len;
+
+ if (!ie_len)
+ ie_len = bss->beacon_ie_len;
+ if (get_vendor_ie((const u8 *) (bss + 1), ie_len,
+ DPP_CC_IE_VENDOR_TYPE))
+ int_array_add_unique(&hapd->dpp_chirp_freqs,
+ bss->freq);
+ }
+
+ if (!hapd->dpp_chirp_freqs ||
+ eloop_register_timeout(0, 0, hostapd_dpp_chirp_next,
+ hapd, NULL) < 0)
+ hostapd_dpp_chirp_stop(hapd);
+
+ wpa_scan_results_free(scan_res);
+}
+
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ int i;
+
+ if (hapd->dpp_chirp_listen)
+ hostapd_dpp_listen_stop(hapd);
+
+ if (hapd->dpp_chirp_freq == 0) {
+ if (hapd->dpp_chirp_round % 4 == 0 &&
+ !hapd->dpp_chirp_scan_done) {
+ struct wpa_driver_scan_params params;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Update channel list for chirping");
+ os_memset(¶ms, 0, sizeof(params));
+ ret = hostapd_driver_scan(hapd, ¶ms);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to request a scan ret=%d (%s)",
+ ret, strerror(-ret));
+ hostapd_dpp_chirp_scan_res_handler(hapd->iface);
+ } else {
+ hapd->iface->scan_cb =
+ hostapd_dpp_chirp_scan_res_handler;
+ }
+ return;
+ }
+ hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[0];
+ hapd->dpp_chirp_round++;
+ wpa_printf(MSG_DEBUG, "DPP: Start chirping round %d",
+ hapd->dpp_chirp_round);
+ } else {
+ for (i = 0; hapd->dpp_chirp_freqs[i]; i++)
+ if (hapd->dpp_chirp_freqs[i] == hapd->dpp_chirp_freq)
+ break;
+ if (!hapd->dpp_chirp_freqs[i]) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Previous chirp freq %d not found",
+ hapd->dpp_chirp_freq);
+ return;
+ }
+ i++;
+ if (hapd->dpp_chirp_freqs[i]) {
+ hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[i];
+ } else {
+ hapd->dpp_chirp_iter--;
+ if (hapd->dpp_chirp_iter <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Chirping iterations completed");
+ hostapd_dpp_chirp_stop(hapd);
+ return;
+ }
+ hapd->dpp_chirp_freq = 0;
+ hapd->dpp_chirp_scan_done = 0;
+ if (eloop_register_timeout(30, 0,
+ hostapd_dpp_chirp_next,
+ hapd, NULL) < 0) {
+ hostapd_dpp_chirp_stop(hapd);
+ return;
+ }
+ if (hapd->dpp_chirp_listen) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Listen on %d MHz during chirp 30 second wait",
+ hapd->dpp_chirp_listen);
+ /* TODO: start listen on the channel */
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Wait 30 seconds before starting the next chirping round");
+ }
+ return;
+ }
+ }
+
+ hostapd_dpp_chirp_start(hapd);
+}
+
+
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd)
+{
+ const char *pos;
+ int iter = 1, listen_freq = 0;
+ struct dpp_bootstrap_info *bi;
+
+ pos = os_strstr(cmd, " own=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+ if (!bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Identified bootstrap info not found");
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " iter=");
+ if (pos) {
+ iter = atoi(pos + 6);
+ if (iter <= 0)
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " listen=");
+ if (pos) {
+ listen_freq = atoi(pos + 8);
+ if (listen_freq <= 0)
+ return -1;
+ }
+
+ hostapd_dpp_chirp_stop(hapd);
+ hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+ hapd->dpp_qr_mutual = 0;
+ hapd->dpp_chirp_bi = bi;
+ hapd->dpp_presence_announcement = dpp_build_presence_announcement(bi);
+ if (!hapd->dpp_presence_announcement)
+ return -1;
+ hapd->dpp_chirp_iter = iter;
+ hapd->dpp_chirp_round = 0;
+ hapd->dpp_chirp_scan_done = 0;
+ hapd->dpp_chirp_listen = listen_freq;
+
+ return eloop_register_timeout(0, 0, hostapd_dpp_chirp_next, hapd, NULL);
+}
+
+
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd)
+{
+ if (hapd->dpp_presence_announcement) {
+ hostapd_drv_send_action_cancel_wait(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CHIRP_STOPPED);
+ }
+ hapd->dpp_chirp_bi = NULL;
+ wpabuf_free(hapd->dpp_presence_announcement);
+ hapd->dpp_presence_announcement = NULL;
+ if (hapd->dpp_chirp_listen)
+ hostapd_dpp_listen_stop(hapd);
+ hapd->dpp_chirp_listen = 0;
+ hapd->dpp_chirp_freq = 0;
+ os_free(hapd->dpp_chirp_freqs);
+ hapd->dpp_chirp_freqs = NULL;
+ eloop_cancel_timeout(hostapd_dpp_chirp_next, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_chirp_timeout, hapd, NULL);
+ if (hapd->iface->scan_cb == hostapd_dpp_chirp_scan_res_handler) {
+ /* TODO: abort ongoing scan */
+ hapd->iface->scan_cb = NULL;
+ }
+}
+
+
+static int handle_dpp_remove_bi(struct hostapd_iface *iface, void *ctx)
+{
+ struct dpp_bootstrap_info *bi = ctx;
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+
+ if (bi == hapd->dpp_chirp_bi)
+ hostapd_dpp_chirp_stop(hapd);
+ }
+
+ return 0;
+}
+
+
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi)
+{
+ struct hapd_interfaces *interfaces = ctx;
+
+ hostapd_for_each_interface(interfaces, handle_dpp_remove_bi, bi);
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index b1fa99e..7e74185 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -10,6 +10,8 @@
#ifndef DPP_HOSTAPD_H
#define DPP_HOSTAPD_H
+struct dpp_bootstrap_info;
+
int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd);
int hostapd_dpp_nfc_handover_req(struct hostapd_data *hapd, const char *cmd);
@@ -39,4 +41,8 @@
void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd);
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi);
+
#endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 5515ab3..f9af038 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -76,6 +76,8 @@
int ret;
for (i = 0; i < interfaces->count; i++) {
+ if (!interfaces->iface[i])
+ continue;
ret = cb(interfaces->iface[i], ctx);
if (ret)
return ret;
@@ -1175,12 +1177,12 @@
#endif /* CONFIG_MESH */
if (flush_old_stations)
- hostapd_flush_old_stations(hapd,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_flush(hapd);
hostapd_set_privacy(hapd, 0);
#ifdef CONFIG_WEP
- hostapd_broadcast_wep_clear(hapd);
+ if (!hostapd_drv_nl80211(hapd))
+ hostapd_broadcast_wep_clear(hapd);
if (hostapd_setup_encryption(conf->iface, hapd))
return -1;
#endif /* CONFIG_WEP */
@@ -1369,6 +1371,21 @@
if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
return -1;
+ if (flush_old_stations && !conf->start_disabled &&
+ conf->broadcast_deauth) {
+ u8 addr[ETH_ALEN];
+
+ /* Should any previously associated STA not have noticed that
+ * the AP had stopped and restarted, send one more
+ * deauthentication notification now that the AP is ready to
+ * operate. */
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Deauthenticate all stations at BSS start");
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+
if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
return -1;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index c8f691e..a616136 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -349,6 +349,11 @@
int last_igtk_key_idx;
u8 last_igtk[WPA_IGTK_MAX_LEN];
size_t last_igtk_len;
+
+ enum wpa_alg last_bigtk_alg;
+ int last_bigtk_key_idx;
+ u8 last_bigtk[WPA_BIGTK_MAX_LEN];
+ size_t last_bigtk_len;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_MBO
@@ -386,6 +391,16 @@
unsigned int dpp_resp_wait_time;
unsigned int dpp_resp_max_tries;
unsigned int dpp_resp_retry_time;
+#ifdef CONFIG_DPP2
+ struct wpabuf *dpp_presence_announcement;
+ struct dpp_bootstrap_info *dpp_chirp_bi;
+ int dpp_chirp_freq;
+ int *dpp_chirp_freqs;
+ int dpp_chirp_iter;
+ int dpp_chirp_round;
+ int dpp_chirp_scan_done;
+ int dpp_chirp_listen;
+#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;
char *dpp_discovery_override;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index e6aa83d..565e9af 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -3181,6 +3181,12 @@
elems.he_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ resp = copy_sta_he_6ghz_capab(hapd, sta,
+ elems.he_6ghz_band_cap);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
}
#endif /* CONFIG_IEEE80211AX */
@@ -3365,7 +3371,8 @@
dpp_pfs_free(sta->dpp_pfs);
sta->dpp_pfs = NULL;
- if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ if (DPP_VERSION > 1 &&
+ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
elems.owe_dh) {
@@ -3626,6 +3633,7 @@
sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
sta->flags & WLAN_STA_HE ? &he_cap : NULL,
sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
+ sta->he_6ghz_capab,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
set)) {
@@ -3785,6 +3793,7 @@
p = hostapd_eid_he_operation(hapd, p);
p = hostapd_eid_spatial_reuse(hapd, p);
p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+ p = hostapd_eid_he_6ghz_band_cap(hapd, p);
}
#endif /* CONFIG_IEEE80211AX */
@@ -3843,7 +3852,7 @@
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
- if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
@@ -4857,6 +4866,11 @@
return 0;
}
+ if (hapd->iface->state != HAPD_IFACE_ENABLED) {
+ wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype);
+ return 1;
+ }
if (stype == WLAN_FC_STYPE_PROBE_REQ) {
handle_probe_req(hapd, mgmt, len, ssi_signal);
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index c7bdb4b..ea8c608 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -63,6 +63,7 @@
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -95,6 +96,8 @@
u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
enum ieee80211_op_mode opmode, const u8 *he_capab,
size_t he_capab_len);
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_6ghz_capab);
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
enum ieee80211_op_mode mode);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 57c6b18..f1f2442 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -311,6 +311,54 @@
}
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid)
+{
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ struct ieee80211_he_6ghz_band_cap *cap;
+ u32 vht_cap;
+ u8 ht_info;
+ u8 params;
+ u8 *pos;
+
+ if (!mode || !is_6ghz_op_class(hapd->iconf->op_class))
+ return eid;
+
+ vht_cap = hapd->iface->conf->vht_capab;
+ ht_info = mode->a_mpdu_params;
+
+ pos = eid;
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(*cap);
+ *pos++ = WLAN_EID_EXT_HE_6GHZ_BAND_CAP;
+
+ /* Minimum MPDU Start Spacing B0..B2 */
+ params = (ht_info >> 2) & HE_6GHZ_BAND_CAP_MIN_MPDU_START_SPACE_MASK;
+
+ /* Maximum A-MPDU Length Exponent B3..B5 */
+ params |= ((((vht_cap & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >>
+ VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT) &
+ HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) <<
+ HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
+
+ /* Maximum MPDU Length B6..B7 */
+ params |= ((((vht_cap & VHT_CAP_MAX_MPDU_LENGTH_MASK) >>
+ VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT) &
+ HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_MASK) <<
+ HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_SHIFT);
+
+ cap = (struct ieee80211_he_6ghz_band_cap *) pos;
+ cap->a_mpdu_params = params;
+ cap->info = HE_6GHZ_BAND_CAP_SMPS_DISABLED;
+ if (vht_cap & VHT_CAP_RX_ANTENNA_PATTERN)
+ cap->info |= HE_6GHZ_BAND_CAP_RX_ANTENNA_PATTERN;
+ if (vht_cap & VHT_CAP_TX_ANTENNA_PATTERN)
+ cap->info |= HE_6GHZ_BAND_CAP_TX_ANTENNA_PATTERN;
+ pos += sizeof(*cap);
+
+ return pos;
+}
+
+
void hostapd_get_he_capab(struct hostapd_data *hapd,
const struct ieee80211_he_capabilities *he_cap,
struct ieee80211_he_capabilities *neg_he_cap,
@@ -415,6 +463,32 @@
}
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_6ghz_capab)
+{
+ if (!he_6ghz_capab || !hapd->iconf->ieee80211ax ||
+ !is_6ghz_op_class(hapd->iconf->op_class)) {
+ sta->flags &= ~WLAN_STA_6GHZ;
+ os_free(sta->he_6ghz_capab);
+ sta->he_6ghz_capab = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (!sta->he_6ghz_capab) {
+ sta->he_6ghz_capab =
+ os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
+ if (!sta->he_6ghz_capab)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_6GHZ;
+ os_memcpy(sta->he_6ghz_capab, he_6ghz_capab,
+ sizeof(struct ieee80211_he_6ghz_band_cap));
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
enum ieee80211_op_mode mode)
{
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 113b4ef..74a837f 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -886,9 +886,9 @@
u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len)
{
-#ifdef CONFIG_DPP2
u8 *pos = eid;
+#ifdef CONFIG_DPP2
if (!hapd->conf->dpp_configurator_connectivity || len < 6)
return pos;
@@ -897,10 +897,9 @@
WPA_PUT_BE24(pos, OUI_WFA);
pos += 3;
*pos++ = DPP_CC_OUI_TYPE;
+#endif /* CONFIG_DPP2 */
return pos;
-#endif /* CONFIG_DPP2 */
- return eid;
}
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 93f1f0c..67b5e98 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -326,6 +326,7 @@
os_free(sta->vht_capabilities);
os_free(sta->vht_operation);
os_free(sta->he_capab);
+ os_free(sta->he_6ghz_capab);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
@@ -1424,7 +1425,8 @@
int res;
buf[0] = '\0';
- res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ res = os_snprintf(buf, buflen,
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
(flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
(flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1444,6 +1446,7 @@
(flags & WLAN_STA_HT ? "[HT]" : ""),
(flags & WLAN_STA_VHT ? "[VHT]" : ""),
(flags & WLAN_STA_HE ? "[HE]" : ""),
+ (flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
(flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
(flags & WLAN_STA_WNM_SLEEP_MODE ?
"[WNM_SLEEP_MODE]" : ""));
@@ -1515,7 +1518,7 @@
if (hostapd_sta_add(hapd, sta->addr, 0, 0,
sta->supported_rates,
sta->supported_rates_len,
- 0, NULL, NULL, NULL, 0,
+ 0, NULL, NULL, NULL, 0, NULL,
sta->flags, 0, 0, 0, 0)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 308aa29..940d315 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -38,6 +38,7 @@
#define WLAN_STA_PENDING_FILS_ERP BIT(22)
#define WLAN_STA_MULTI_AP BIT(23)
#define WLAN_STA_HE BIT(24)
+#define WLAN_STA_6GHZ BIT(25)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -170,6 +171,7 @@
u8 vht_opmode;
struct ieee80211_he_capabilities *he_capab;
size_t he_capab_len;
+ struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
int sa_query_count; /* number of pending SA Query requests;
* 0 = no SA Query in progress */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 30e7258..019e535 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -14,6 +14,7 @@
#include "utils/bitfield.h"
#include "common/ieee802_11_defs.h"
#include "common/ocv.h"
+#include "common/dpp.h"
#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/aes_siv.h"
@@ -3079,6 +3080,24 @@
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && kde.dpp_kde) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: peer Protocol Version %u Flags 0x%x",
+ kde.dpp_kde[0], kde.dpp_kde[1]);
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP &&
+ wpa_auth->conf.dpp_pfs != 2 &&
+ (kde.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) &&
+ !sm->dpp_z) {
+ wpa_printf(MSG_INFO,
+ "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+ }
+#endif /* CONFIG_DPP2 */
+
#ifdef CONFIG_IEEE80211R_AP
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
@@ -3397,6 +3416,11 @@
if (conf->transition_disable)
kde_len += 2 + RSN_SELECTOR_LEN + 1;
+#ifdef CONFIG_DPP2
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2;
+#endif /* CONFIG_DPP2 */
+
kde = os_malloc(kde_len);
if (!kde)
goto done;
@@ -3492,6 +3516,22 @@
pos = wpa_add_kde(pos, WFA_KEY_DATA_TRANSITION_DISABLE,
&conf->transition_disable, 1, NULL, 0);
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+ u8 payload[2];
+
+ payload[0] = DPP_VERSION; /* Protocol Version */
+ payload[1] = 0; /* Flags */
+ if (conf->dpp_pfs == 0)
+ payload[1] |= DPP_KDE_PFS_ALLOWED;
+ else if (conf->dpp_pfs == 1)
+ payload[1] |= DPP_KDE_PFS_ALLOWED |
+ DPP_KDE_PFS_REQUIRED;
+ pos = wpa_add_kde(pos, WFA_KEY_DATA_DPP,
+ payload, sizeof(payload), NULL, 0);
+ }
+#endif /* CONFIG_DPP2 */
+
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 44ab830..05d87ac 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -453,15 +453,23 @@
os_memcpy(sta->last_tk, key, key_len);
sta->last_tk_len = key_len;
}
- } else if (alg == WPA_ALG_IGTK ||
+ } else if (alg == WPA_ALG_BIP_CMAC_128 ||
alg == WPA_ALG_BIP_GMAC_128 ||
alg == WPA_ALG_BIP_GMAC_256 ||
alg == WPA_ALG_BIP_CMAC_256) {
- hapd->last_igtk_alg = alg;
- hapd->last_igtk_key_idx = idx;
- if (key)
- os_memcpy(hapd->last_igtk, key, key_len);
- hapd->last_igtk_len = key_len;
+ if (idx == 4 || idx == 5) {
+ hapd->last_igtk_alg = alg;
+ hapd->last_igtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_igtk, key, key_len);
+ hapd->last_igtk_len = key_len;
+ } else if (idx == 6 || idx == 7) {
+ hapd->last_bigtk_alg = alg;
+ hapd->last_bigtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_bigtk, key, key_len);
+ hapd->last_bigtk_len = key_len;
+ }
} else {
hapd->last_gtk_alg = alg;
hapd->last_gtk_key_idx = idx;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index af0aaca..ba08ac2 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -277,7 +277,8 @@
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
logger_level level, const char *txt);
void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
- logger_level level, const char *fmt, ...);
+ logger_level level, const char *fmt, ...)
+ PRINTF_FORMAT(4, 5);
void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int key_info,
const u8 *key_rsc, const u8 *nonce,
diff --git a/src/common/defs.h b/src/common/defs.h
index bcf6f54..bbe3120 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -190,7 +190,7 @@
WPA_ALG_WEP,
WPA_ALG_TKIP,
WPA_ALG_CCMP,
- WPA_ALG_IGTK,
+ WPA_ALG_BIP_CMAC_128,
WPA_ALG_GCMP,
WPA_ALG_SMS4,
WPA_ALG_KRK,
@@ -201,6 +201,14 @@
WPA_ALG_BIP_CMAC_256
};
+static inline int wpa_alg_bip(enum wpa_alg alg)
+{
+ return alg == WPA_ALG_BIP_CMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_256 ||
+ alg == WPA_ALG_BIP_CMAC_256;
+}
+
/**
* enum wpa_states - wpa_supplicant state
*
diff --git a/src/common/dpp.c b/src/common/dpp.c
index b33ab15..d09f0d1 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -8,47 +8,33 @@
*/
#include "utils/includes.h"
-#include <fcntl.h>
#include <openssl/opensslv.h>
#include <openssl/err.h>
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
#include "utils/common.h"
#include "utils/base64.h"
#include "utils/json.h"
-#include "utils/ip_addr.h"
-#include "utils/eloop.h"
#include "common/ieee802_11_common.h"
-#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "common/gas.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
-#include "crypto/sha384.h"
-#include "crypto/sha512.h"
-#include "tls/asn1.h"
#include "drivers/driver.h"
#include "dpp.h"
+#include "dpp_i.h"
static const char * dpp_netrole_str(enum dpp_netrole netrole);
#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_DPP2
+int dpp_version_override = 2;
+#else
+int dpp_version_override = 1;
+#endif
enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
-u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-u8 dpp_pkex_ephemeral_key_override[600];
-size_t dpp_pkex_ephemeral_key_override_len = 0;
-u8 dpp_protocol_key_override[600];
-size_t dpp_protocol_key_override_len = 0;
-u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
-size_t dpp_nonce_override_len = 0;
-
-static int dpp_test_gen_invalid_key(struct wpabuf *msg,
- const struct dpp_curve_params *curve);
#endif /* CONFIG_TESTING_OPTIONS */
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
@@ -56,24 +42,6 @@
LIBRESSL_VERSION_NUMBER < 0x20700000L)
/* Compatibility wrappers for older versions. */
-static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
-{
- sig->r = r;
- sig->s = s;
- return 1;
-}
-
-
-static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
- const BIGNUM **ps)
-{
- if (pr)
- *pr = sig->r;
- if (ps)
- *ps = sig->s;
-}
-
-
#ifdef CONFIG_DPP2
static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
{
@@ -86,700 +54,7 @@
#endif
-struct dpp_connection {
- struct dl_list list;
- struct dpp_controller *ctrl;
- struct dpp_relay_controller *relay;
- struct dpp_global *global;
- struct dpp_authentication *auth;
- int sock;
- u8 mac_addr[ETH_ALEN];
- unsigned int freq;
- u8 msg_len[4];
- size_t msg_len_octets;
- struct wpabuf *msg;
- struct wpabuf *msg_out;
- size_t msg_out_pos;
- unsigned int read_eloop:1;
- unsigned int write_eloop:1;
- unsigned int on_tcp_tx_complete_gas_done:1;
- unsigned int on_tcp_tx_complete_remove:1;
- unsigned int on_tcp_tx_complete_auth_ok:1;
-};
-
-/* Remote Controller */
-struct dpp_relay_controller {
- struct dl_list list;
- struct dpp_global *global;
- u8 pkhash[SHA256_MAC_LEN];
- struct hostapd_ip_addr ipaddr;
- void *cb_ctx;
- void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
- size_t len);
- void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
- int prot, struct wpabuf *buf);
- struct dl_list conn; /* struct dpp_connection */
-};
-
-/* Local Controller */
-struct dpp_controller {
- struct dpp_global *global;
- u8 allowed_roles;
- int qr_mutual;
- int sock;
- struct dl_list conn; /* struct dpp_connection */
- char *configurator_params;
-};
-
-struct dpp_global {
- void *msg_ctx;
- struct dl_list bootstrap; /* struct dpp_bootstrap_info */
- struct dl_list configurator; /* struct dpp_configurator */
-#ifdef CONFIG_DPP2
- struct dl_list controllers; /* struct dpp_relay_controller */
- struct dpp_controller *controller;
- struct dl_list tcp_init; /* struct dpp_connection */
- void *cb_ctx;
- int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
- void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
-#endif /* CONFIG_DPP2 */
-};
-
-static const struct dpp_curve_params dpp_curves[] = {
- /* The mandatory to support and the default NIST P-256 curve needs to
- * be the first entry on this list. */
- { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
- { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
- { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
- { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
- { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
- { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
- { NULL, 0, 0, 0, 0, NULL, 0, NULL }
-};
-
-
-/* Role-specific elements for PKEX */
-
-/* NIST P-256 */
-static const u8 pkex_init_x_p256[32] = {
- 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
- 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
- 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
- 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
- };
-static const u8 pkex_init_y_p256[32] = {
- 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
- 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
- 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
- 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
- };
-static const u8 pkex_resp_x_p256[32] = {
- 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
- 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
- 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
- 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
-};
-static const u8 pkex_resp_y_p256[32] = {
- 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
- 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
- 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
- 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
-};
-
-/* NIST P-384 */
-static const u8 pkex_init_x_p384[48] = {
- 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
- 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
- 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
- 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
- 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
- 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
-};
-static const u8 pkex_init_y_p384[48] = {
- 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
- 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
- 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
- 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
- 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
- 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
-};
-static const u8 pkex_resp_x_p384[48] = {
- 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
- 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
- 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
- 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
- 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
- 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
-};
-static const u8 pkex_resp_y_p384[48] = {
- 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
- 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
- 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
- 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
- 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
- 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
-};
-
-/* NIST P-521 */
-static const u8 pkex_init_x_p521[66] = {
- 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
- 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
- 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
- 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
- 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
- 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
- 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
- 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
- 0x97, 0x76
-};
-static const u8 pkex_init_y_p521[66] = {
- 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
- 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
- 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
- 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
- 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
- 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
- 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
- 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
- 0x03, 0xa8
-};
-static const u8 pkex_resp_x_p521[66] = {
- 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
- 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
- 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
- 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
- 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
- 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
- 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
- 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
- 0x84, 0xb4
-};
-static const u8 pkex_resp_y_p521[66] = {
- 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
- 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
- 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
- 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
- 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
- 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
- 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
- 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
- 0xce, 0xe1
-};
-
-/* Brainpool P-256r1 */
-static const u8 pkex_init_x_bp_p256r1[32] = {
- 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
- 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
- 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
- 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
-};
-static const u8 pkex_init_y_bp_p256r1[32] = {
- 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
- 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
- 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
- 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
-};
-static const u8 pkex_resp_x_bp_p256r1[32] = {
- 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
- 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
- 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
- 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
-};
-static const u8 pkex_resp_y_bp_p256r1[32] = {
- 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
- 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
- 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
- 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
-};
-
-/* Brainpool P-384r1 */
-static const u8 pkex_init_x_bp_p384r1[48] = {
- 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
- 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
- 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
- 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
- 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
- 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
-};
-static const u8 pkex_init_y_bp_p384r1[48] = {
- 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
- 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
- 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
- 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
- 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
- 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
-};
-static const u8 pkex_resp_x_bp_p384r1[48] = {
- 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
- 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
- 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
- 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
- 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
- 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
-};
-static const u8 pkex_resp_y_bp_p384r1[48] = {
- 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
- 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
- 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
- 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
- 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
- 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
-};
-
-/* Brainpool P-512r1 */
-static const u8 pkex_init_x_bp_p512r1[64] = {
- 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
- 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
- 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
- 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
- 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
- 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
- 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
- 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
-};
-static const u8 pkex_init_y_bp_p512r1[64] = {
- 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
- 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
- 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
- 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
- 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
- 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
- 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
- 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
-};
-static const u8 pkex_resp_x_bp_p512r1[64] = {
- 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
- 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
- 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
- 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
- 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
- 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
- 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
- 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
-};
-static const u8 pkex_resp_y_bp_p512r1[64] = {
- 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
- 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
- 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
- 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
- 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
- 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
- 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
- 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
-};
-
-
-static void dpp_debug_print_point(const char *title, const EC_GROUP *group,
- const EC_POINT *point)
-{
- BIGNUM *x, *y;
- BN_CTX *ctx;
- char *x_str = NULL, *y_str = NULL;
-
- if (!wpa_debug_show_keys)
- return;
-
- ctx = BN_CTX_new();
- x = BN_new();
- y = BN_new();
- if (!ctx || !x || !y ||
- EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
- goto fail;
-
- x_str = BN_bn2hex(x);
- y_str = BN_bn2hex(y);
- if (!x_str || !y_str)
- goto fail;
-
- wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
-
-fail:
- OPENSSL_free(x_str);
- OPENSSL_free(y_str);
- BN_free(x);
- BN_free(y);
- BN_CTX_free(ctx);
-}
-
-
-static int dpp_hash_vector(const struct dpp_curve_params *curve,
- size_t num_elem, const u8 *addr[], const size_t *len,
- u8 *mac)
-{
- if (curve->hash_len == 32)
- return sha256_vector(num_elem, addr, len, mac);
- if (curve->hash_len == 48)
- return sha384_vector(num_elem, addr, len, mac);
- if (curve->hash_len == 64)
- return sha512_vector(num_elem, addr, len, mac);
- return -1;
-}
-
-
-static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
- const char *label, u8 *out, size_t outlen)
-{
- if (hash_len == 32)
- return hmac_sha256_kdf(secret, secret_len, NULL,
- (const u8 *) label, os_strlen(label),
- out, outlen);
- if (hash_len == 48)
- return hmac_sha384_kdf(secret, secret_len, NULL,
- (const u8 *) label, os_strlen(label),
- out, outlen);
- if (hash_len == 64)
- return hmac_sha512_kdf(secret, secret_len, NULL,
- (const u8 *) label, os_strlen(label),
- out, outlen);
- return -1;
-}
-
-
-static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
- size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac)
-{
- if (hash_len == 32)
- return hmac_sha256_vector(key, key_len, num_elem, addr, len,
- mac);
- if (hash_len == 48)
- return hmac_sha384_vector(key, key_len, num_elem, addr, len,
- mac);
- if (hash_len == 64)
- return hmac_sha512_vector(key, key_len, num_elem, addr, len,
- mac);
- return -1;
-}
-
-
-static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
- const u8 *data, size_t data_len, u8 *mac)
-{
- if (hash_len == 32)
- return hmac_sha256(key, key_len, data, data_len, mac);
- if (hash_len == 48)
- return hmac_sha384(key, key_len, data, data_len, mac);
- if (hash_len == 64)
- return hmac_sha512(key, key_len, data, data_len, mac);
- return -1;
-}
-
-
-#ifdef CONFIG_DPP2
-
-static int dpp_pbkdf2_f(size_t hash_len,
- const u8 *password, size_t password_len,
- const u8 *salt, size_t salt_len,
- unsigned int iterations, unsigned int count, u8 *digest)
-{
- unsigned char tmp[DPP_MAX_HASH_LEN], tmp2[DPP_MAX_HASH_LEN];
- unsigned int i;
- size_t j;
- u8 count_buf[4];
- const u8 *addr[2];
- size_t len[2];
-
- addr[0] = salt;
- len[0] = salt_len;
- addr[1] = count_buf;
- len[1] = 4;
-
- /* F(P, S, c, i) = U1 xor U2 xor ... Uc
- * U1 = PRF(P, S || i)
- * U2 = PRF(P, U1)
- * Uc = PRF(P, Uc-1)
- */
-
- WPA_PUT_BE32(count_buf, count);
- if (dpp_hmac_vector(hash_len, password, password_len, 2, addr, len,
- tmp))
- return -1;
- os_memcpy(digest, tmp, hash_len);
-
- for (i = 1; i < iterations; i++) {
- if (dpp_hmac(hash_len, password, password_len, tmp, hash_len,
- tmp2))
- return -1;
- os_memcpy(tmp, tmp2, hash_len);
- for (j = 0; j < hash_len; j++)
- digest[j] ^= tmp2[j];
- }
-
- return 0;
-}
-
-
-static int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
- const u8 *salt, size_t salt_len, unsigned int iterations,
- u8 *buf, size_t buflen)
-{
- unsigned int count = 0;
- unsigned char *pos = buf;
- size_t left = buflen, plen;
- unsigned char digest[DPP_MAX_HASH_LEN];
-
- while (left > 0) {
- count++;
- if (dpp_pbkdf2_f(hash_len, password, password_len,
- salt, salt_len, iterations, count, digest))
- return -1;
- plen = left > hash_len ? hash_len : left;
- os_memcpy(pos, digest, plen);
- pos += plen;
- left -= plen;
- }
-
- return 0;
-}
-
-#endif /* CONFIG_DPP2 */
-
-
-static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
-{
- int num_bytes, offset;
-
- num_bytes = BN_num_bytes(bn);
- if ((size_t) num_bytes > len)
- return -1;
- offset = len - num_bytes;
- os_memset(pos, 0, offset);
- BN_bn2bin(bn, pos + offset);
- return 0;
-}
-
-
-static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
-{
- int len, res;
- EC_KEY *eckey;
- struct wpabuf *buf;
- unsigned char *pos;
-
- eckey = EVP_PKEY_get1_EC_KEY(pkey);
- if (!eckey)
- return NULL;
- EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
- len = i2o_ECPublicKey(eckey, NULL);
- if (len <= 0) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to determine public key encoding length");
- EC_KEY_free(eckey);
- return NULL;
- }
-
- buf = wpabuf_alloc(len);
- if (!buf) {
- EC_KEY_free(eckey);
- return NULL;
- }
-
- pos = wpabuf_put(buf, len);
- res = i2o_ECPublicKey(eckey, &pos);
- EC_KEY_free(eckey);
- if (res != len) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to encode public key (res=%d/%d)",
- res, len);
- wpabuf_free(buf);
- return NULL;
- }
-
- if (!prefix) {
- /* Remove 0x04 prefix to match DPP definition */
- pos = wpabuf_mhead(buf);
- os_memmove(pos, pos + 1, len - 1);
- buf->used--;
- }
-
- return buf;
-}
-
-
-static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
- const u8 *buf_x, const u8 *buf_y,
- size_t len)
-{
- EC_KEY *eckey = NULL;
- BN_CTX *ctx;
- EC_POINT *point = NULL;
- BIGNUM *x = NULL, *y = NULL;
- EVP_PKEY *pkey = NULL;
-
- ctx = BN_CTX_new();
- if (!ctx) {
- wpa_printf(MSG_ERROR, "DPP: Out of memory");
- return NULL;
- }
-
- point = EC_POINT_new(group);
- x = BN_bin2bn(buf_x, len, NULL);
- y = BN_bin2bn(buf_y, len, NULL);
- if (!point || !x || !y) {
- wpa_printf(MSG_ERROR, "DPP: Out of memory");
- goto fail;
- }
-
- if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
- wpa_printf(MSG_ERROR,
- "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (!EC_POINT_is_on_curve(group, point, ctx) ||
- EC_POINT_is_at_infinity(group, point)) {
- wpa_printf(MSG_ERROR, "DPP: Invalid point");
- goto fail;
- }
- dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
-
- eckey = EC_KEY_new();
- if (!eckey ||
- EC_KEY_set_group(eckey, group) != 1 ||
- EC_KEY_set_public_key(eckey, point) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to set EC_KEY: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
-
- pkey = EVP_PKEY_new();
- if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
- wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
- goto fail;
- }
-
-out:
- BN_free(x);
- BN_free(y);
- EC_KEY_free(eckey);
- EC_POINT_free(point);
- BN_CTX_free(ctx);
- return pkey;
-fail:
- EVP_PKEY_free(pkey);
- pkey = NULL;
- goto out;
-}
-
-
-static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
- const u8 *buf, size_t len)
-{
- EC_KEY *eckey;
- const EC_GROUP *group;
- EVP_PKEY *pkey = NULL;
-
- if (len & 1)
- return NULL;
-
- eckey = EVP_PKEY_get1_EC_KEY(group_key);
- if (!eckey) {
- wpa_printf(MSG_ERROR,
- "DPP: Could not get EC_KEY from group_key");
- return NULL;
- }
-
- group = EC_KEY_get0_group(eckey);
- if (group)
- pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
- len / 2);
- else
- wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
-
- EC_KEY_free(eckey);
- return pkey;
-}
-
-
-static int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer,
- u8 *secret, size_t *secret_len)
-{
- EVP_PKEY_CTX *ctx;
- int ret = -1;
-
- ERR_clear_error();
- *secret_len = 0;
-
- ctx = EVP_PKEY_CTX_new(own, NULL);
- if (!ctx) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- return -1;
- }
-
- if (EVP_PKEY_derive_init(ctx) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: EVP_PKEY_derive_set_peet failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
- u8 buf[200];
- int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG;
-
- /* It looks like OpenSSL can return unexpectedly large buffer
- * need for shared secret from EVP_PKEY_derive(NULL) in some
- * cases. For example, group 19 has shown cases where secret_len
- * is set to 72 even though the actual length ends up being
- * updated to 32 when EVP_PKEY_derive() is called with a buffer
- * for the value. Work around this by trying to fetch the value
- * and continue if it is within supported range even when the
- * initial buffer need is claimed to be larger. */
- wpa_printf(level,
- "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
- (int) *secret_len);
- if (*secret_len > 200)
- goto fail;
- if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
- wpa_printf(MSG_ERROR,
- "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
- (int) *secret_len);
- goto fail;
- }
- wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change",
- buf, *secret_len);
- os_memcpy(secret, buf, *secret_len);
- forced_memzero(buf, sizeof(buf));
- goto done;
- }
-
- if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
-done:
- ret = 0;
-
-fail:
- EVP_PKEY_CTX_free(ctx);
- return ret;
-}
-
-
-static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
+void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
{
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
}
@@ -1037,57 +312,21 @@
}
-static const struct dpp_curve_params *
-dpp_get_curve_oid(const ASN1_OBJECT *poid)
+int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, const char *version)
{
- ASN1_OBJECT *oid;
- int i;
+#ifdef CONFIG_DPP2
+ if (!version || DPP_VERSION < 2)
+ return 0;
- for (i = 0; dpp_curves[i].name; i++) {
- oid = OBJ_txt2obj(dpp_curves[i].name, 0);
- if (oid && OBJ_cmp(poid, oid) == 0)
- return &dpp_curves[i];
- }
- return NULL;
-}
+ if (*version == '1')
+ bi->version = 1;
+ else if (*version == '2')
+ bi->version = 2;
+ else
+ wpa_printf(MSG_DEBUG, "DPP: Unknown URI version");
-
-static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
-{
- int i, tmp;
-
- if (!nid)
- return NULL;
- for (i = 0; dpp_curves[i].name; i++) {
- tmp = OBJ_txt2nid(dpp_curves[i].name);
- if (tmp == nid)
- return &dpp_curves[i];
- }
- return NULL;
-}
-
-
-static int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
- const u8 *data, size_t data_len)
-{
- const u8 *addr[2];
- size_t len[2];
-
- addr[0] = data;
- len[0] = data_len;
- if (sha256_vector(1, addr, len, bi->pubkey_hash) < 0)
- return -1;
- wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
- bi->pubkey_hash, SHA256_MAC_LEN);
-
- addr[0] = (const u8 *) "chirp";
- len[0] = 5;
- addr[1] = data;
- len[1] = data_len;
- if (sha256_vector(2, addr, len, bi->pubkey_hash_chirp) < 0)
- return -1;
- wpa_hexdump(MSG_DEBUG, "DPP: Public key hash (chirp)",
- bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+ wpa_printf(MSG_DEBUG, "DPP: URI version: %d", bi->version);
+#endif /* CONFIG_DPP2 */
return 0;
}
@@ -1095,28 +334,10 @@
static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
{
- const char *end;
u8 *data;
size_t data_len;
- EVP_PKEY *pkey;
- const unsigned char *p;
int res;
- X509_PUBKEY *pub = NULL;
- ASN1_OBJECT *ppkalg;
- const unsigned char *pk;
- int ppklen;
- X509_ALGOR *pa;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
- (defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20800000L)
- ASN1_OBJECT *pa_oid;
-#else
- const ASN1_OBJECT *pa_oid;
-#endif
- const void *pval;
- int ptype;
- const ASN1_OBJECT *poid;
- char buf[100];
+ const char *end;
end = os_strchr(info, ';');
if (!end)
@@ -1131,101 +352,9 @@
wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
data, data_len);
- if (dpp_bi_pubkey_hash(bi, data, data_len) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
- os_free(data);
- return -1;
- }
-
- /* DER encoded ASN.1 SubjectPublicKeyInfo
- *
- * SubjectPublicKeyInfo ::= SEQUENCE {
- * algorithm AlgorithmIdentifier,
- * subjectPublicKey BIT STRING }
- *
- * AlgorithmIdentifier ::= SEQUENCE {
- * algorithm OBJECT IDENTIFIER,
- * parameters ANY DEFINED BY algorithm OPTIONAL }
- *
- * subjectPublicKey = compressed format public key per ANSI X9.63
- * algorithm = ecPublicKey (1.2.840.10045.2.1)
- * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
- * prime256v1 (1.2.840.10045.3.1.7)
- */
-
- p = data;
- pkey = d2i_PUBKEY(NULL, &p, data_len);
+ res = dpp_get_subject_public_key(bi, data, data_len);
os_free(data);
-
- if (!pkey) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
- return -1;
- }
-
- if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
- wpa_printf(MSG_DEBUG,
- "DPP: SubjectPublicKeyInfo does not describe an EC key");
- EVP_PKEY_free(pkey);
- return -1;
- }
-
- res = X509_PUBKEY_set(&pub, pkey);
- if (res != 1) {
- wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
- goto fail;
- }
-
- res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
- if (res != 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not extract SubjectPublicKeyInfo parameters");
- goto fail;
- }
- res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
- if (res < 0 || (size_t) res >= sizeof(buf)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not extract SubjectPublicKeyInfo algorithm");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
- if (os_strcmp(buf, "id-ecPublicKey") != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported SubjectPublicKeyInfo algorithm");
- goto fail;
- }
-
- X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
- if (ptype != V_ASN1_OBJECT) {
- wpa_printf(MSG_DEBUG,
- "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
- goto fail;
- }
- poid = pval;
- res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
- if (res < 0 || (size_t) res >= sizeof(buf)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
- bi->curve = dpp_get_curve_oid(poid);
- if (!bi->curve) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
- buf);
- goto fail;
- }
-
- wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
-
- X509_PUBKEY_free(pub);
- bi->pubkey = pkey;
- return 0;
-fail:
- X509_PUBKEY_free(pub);
- EVP_PKEY_free(pkey);
- return -1;
+ return res;
}
@@ -1234,6 +363,7 @@
const char *pos = uri;
const char *end;
const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
+ const char *version = NULL;
struct dpp_bootstrap_info *bi;
wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
@@ -1264,6 +394,8 @@
info = pos + 2;
else if (pos[0] == 'K' && pos[1] == ':' && !pk)
pk = pos + 2;
+ else if (pos[0] == 'V' && pos[1] == ':' && !version)
+ version = pos + 2;
else
wpa_hexdump_ascii(MSG_DEBUG,
"DPP: Ignore unrecognized URI parameter",
@@ -1284,6 +416,7 @@
dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
dpp_parse_uri_mac(bi, mac) < 0 ||
dpp_parse_uri_info(bi, info) < 0 ||
+ dpp_parse_uri_version(bi, version) < 0 ||
dpp_parse_uri_pk(bi, pk) < 0) {
dpp_bootstrap_info_free(bi);
bi = NULL;
@@ -1293,462 +426,7 @@
}
-static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
-{
- EC_KEY *eckey;
- BIO *out;
- size_t rlen;
- char *txt;
- int res;
- unsigned char *der = NULL;
- int der_len;
- const EC_GROUP *group;
- const EC_POINT *point;
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- return;
-
- EVP_PKEY_print_private(out, key, 0, NULL);
- rlen = BIO_ctrl_pending(out);
- txt = os_malloc(rlen + 1);
- if (txt) {
- res = BIO_read(out, txt, rlen);
- if (res > 0) {
- txt[res] = '\0';
- wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
- }
- os_free(txt);
- }
- BIO_free(out);
-
- eckey = EVP_PKEY_get1_EC_KEY(key);
- if (!eckey)
- return;
-
- group = EC_KEY_get0_group(eckey);
- point = EC_KEY_get0_public_key(eckey);
- if (group && point)
- dpp_debug_print_point(title, group, point);
-
- der_len = i2d_ECPrivateKey(eckey, &der);
- if (der_len > 0)
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
- OPENSSL_free(der);
- if (der_len <= 0) {
- der = NULL;
- der_len = i2d_EC_PUBKEY(eckey, &der);
- if (der_len > 0)
- wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
- OPENSSL_free(der);
- }
-
- EC_KEY_free(eckey);
-}
-
-
-static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
-{
- EVP_PKEY_CTX *kctx = NULL;
- EC_KEY *ec_params = NULL;
- EVP_PKEY *params = NULL, *key = NULL;
- int nid;
-
- wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
-
- nid = OBJ_txt2nid(curve->name);
- if (nid == NID_undef) {
- wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
- return NULL;
- }
-
- ec_params = EC_KEY_new_by_curve_name(nid);
- if (!ec_params) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate EC_KEY parameters");
- goto fail;
- }
- EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
- params = EVP_PKEY_new();
- if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate EVP_PKEY parameters");
- goto fail;
- }
-
- kctx = EVP_PKEY_CTX_new(params, NULL);
- if (!kctx ||
- EVP_PKEY_keygen_init(kctx) != 1 ||
- EVP_PKEY_keygen(kctx, &key) != 1) {
- wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
- key = NULL;
- goto fail;
- }
-
- if (wpa_debug_show_keys)
- dpp_debug_print_key("Own generated key", key);
-
-fail:
- EC_KEY_free(ec_params);
- EVP_PKEY_free(params);
- EVP_PKEY_CTX_free(kctx);
- return key;
-}
-
-
-static const struct dpp_curve_params *
-dpp_get_curve_name(const char *name)
-{
- int i;
-
- for (i = 0; dpp_curves[i].name; i++) {
- if (os_strcmp(name, dpp_curves[i].name) == 0 ||
- (dpp_curves[i].jwk_crv &&
- os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
- return &dpp_curves[i];
- }
- return NULL;
-}
-
-
-static const struct dpp_curve_params *
-dpp_get_curve_jwk_crv(const char *name)
-{
- int i;
-
- for (i = 0; dpp_curves[i].name; i++) {
- if (dpp_curves[i].jwk_crv &&
- os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
- return &dpp_curves[i];
- }
- return NULL;
-}
-
-
-static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
- const u8 *privkey, size_t privkey_len)
-{
- EVP_PKEY *pkey;
- EC_KEY *eckey;
- const EC_GROUP *group;
- int nid;
-
- pkey = EVP_PKEY_new();
- if (!pkey)
- return NULL;
- eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
- if (!eckey) {
- wpa_printf(MSG_INFO,
- "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- EVP_PKEY_free(pkey);
- return NULL;
- }
- group = EC_KEY_get0_group(eckey);
- if (!group) {
- EC_KEY_free(eckey);
- EVP_PKEY_free(pkey);
- return NULL;
- }
- nid = EC_GROUP_get_curve_name(group);
- *curve = dpp_get_curve_nid(nid);
- if (!*curve) {
- wpa_printf(MSG_INFO,
- "DPP: Unsupported curve (nid=%d) in pre-assigned key",
- nid);
- EC_KEY_free(eckey);
- EVP_PKEY_free(pkey);
- return NULL;
- }
-
- if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
- EC_KEY_free(eckey);
- EVP_PKEY_free(pkey);
- return NULL;
- }
- return pkey;
-}
-
-
-typedef struct {
- /* AlgorithmIdentifier ecPublicKey with optional parameters present
- * as an OID identifying the curve */
- X509_ALGOR *alg;
- /* Compressed format public key per ANSI X9.63 */
- ASN1_BIT_STRING *pub_key;
-} DPP_BOOTSTRAPPING_KEY;
-
-ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
- ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
- ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
-} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
-
-IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
-
-
-static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
-{
- unsigned char *der = NULL;
- int der_len;
- EC_KEY *eckey;
- struct wpabuf *ret = NULL;
- size_t len;
- const EC_GROUP *group;
- const EC_POINT *point;
- BN_CTX *ctx;
- DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
- int nid;
-
- ctx = BN_CTX_new();
- eckey = EVP_PKEY_get1_EC_KEY(key);
- if (!ctx || !eckey)
- goto fail;
-
- group = EC_KEY_get0_group(eckey);
- point = EC_KEY_get0_public_key(eckey);
- if (!group || !point)
- goto fail;
- dpp_debug_print_point("DPP: bootstrap public key", group, point);
- nid = EC_GROUP_get_curve_name(group);
-
- bootstrap = DPP_BOOTSTRAPPING_KEY_new();
- if (!bootstrap ||
- X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
- V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
- goto fail;
-
- len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
- NULL, 0, ctx);
- if (len == 0)
- goto fail;
-
- der = OPENSSL_malloc(len);
- if (!der)
- goto fail;
- len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
- der, len, ctx);
-
- OPENSSL_free(bootstrap->pub_key->data);
- bootstrap->pub_key->data = der;
- der = NULL;
- bootstrap->pub_key->length = len;
- /* No unused bits */
- bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
- bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
-
- der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
- if (der_len <= 0) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to build DER encoded public key");
- goto fail;
- }
-
- ret = wpabuf_alloc_copy(der, der_len);
-fail:
- DPP_BOOTSTRAPPING_KEY_free(bootstrap);
- OPENSSL_free(der);
- EC_KEY_free(eckey);
- BN_CTX_free(ctx);
- return ret;
-}
-
-
-static int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
-{
- struct wpabuf *der;
- int res;
-
- der = dpp_bootstrap_key_der(bi->pubkey);
- if (!der)
- return -1;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
- der);
- res = dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der));
- if (res < 0)
- wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
- wpabuf_free(der);
- return res;
-}
-
-
-static int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
- const u8 *privkey, size_t privkey_len)
-{
- char *base64 = NULL;
- char *pos, *end;
- size_t len;
- struct wpabuf *der = NULL;
-
- if (!curve) {
- bi->curve = &dpp_curves[0];
- } else {
- bi->curve = dpp_get_curve_name(curve);
- if (!bi->curve) {
- wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
- curve);
- return -1;
- }
- }
- if (privkey)
- bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
- else
- bi->pubkey = dpp_gen_keypair(bi->curve);
- if (!bi->pubkey)
- goto fail;
- bi->own = 1;
-
- der = dpp_bootstrap_key_der(bi->pubkey);
- if (!der)
- goto fail;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
- der);
-
- if (dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der)) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
- goto fail;
- }
-
- base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
- wpabuf_free(der);
- der = NULL;
- if (!base64)
- goto fail;
- pos = base64;
- end = pos + len;
- for (;;) {
- pos = os_strchr(pos, '\n');
- if (!pos)
- break;
- os_memmove(pos, pos + 1, end - pos);
- }
- os_free(bi->pk);
- bi->pk = base64;
- return 0;
-fail:
- os_free(base64);
- wpabuf_free(der);
- return -1;
-}
-
-
-static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
- unsigned int hash_len)
-{
- u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
- const char *info = "first intermediate key";
- int res;
-
- /* k1 = HKDF(<>, "first intermediate key", M.x) */
-
- /* HKDF-Extract(<>, M.x) */
- os_memset(salt, 0, hash_len);
- if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
- prk, hash_len);
-
- /* HKDF-Expand(PRK, info, L) */
- res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
- k1, hash_len);
- return 0;
-}
-
-
-static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
- unsigned int hash_len)
-{
- u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
- const char *info = "second intermediate key";
- int res;
-
- /* k2 = HKDF(<>, "second intermediate key", N.x) */
-
- /* HKDF-Extract(<>, N.x) */
- os_memset(salt, 0, hash_len);
- res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
- if (res < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
- prk, hash_len);
-
- /* HKDF-Expand(PRK, info, L) */
- res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
- k2, hash_len);
- return 0;
-}
-
-
-static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
- unsigned int hash_len)
-{
- size_t nonce_len;
- u8 nonces[2 * DPP_MAX_NONCE_LEN];
- const char *info_ke = "DPP Key";
- u8 prk[DPP_MAX_HASH_LEN];
- int res;
- const u8 *addr[3];
- size_t len[3];
- size_t num_elem = 0;
-
- if (!auth->Mx_len || !auth->Nx_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: Mx/Nx not available - cannot derive ke");
- return -1;
- }
-
- /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
-
- /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
- nonce_len = auth->curve->nonce_len;
- os_memcpy(nonces, auth->i_nonce, nonce_len);
- os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
- addr[num_elem] = auth->Mx;
- len[num_elem] = auth->Mx_len;
- num_elem++;
- addr[num_elem] = auth->Nx;
- len[num_elem] = auth->Nx_len;
- num_elem++;
- if (auth->peer_bi && auth->own_bi) {
- if (!auth->Lx_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: Lx not available - cannot derive ke");
- return -1;
- }
- addr[num_elem] = auth->Lx;
- len[num_elem] = auth->secret_len;
- num_elem++;
- }
- res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
- num_elem, addr, len, prk);
- if (res < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
- prk, hash_len);
-
- /* HKDF-Expand(PRK, info, L) */
- res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
- ke, hash_len);
- return 0;
-}
-
-
-static void dpp_build_attr_status(struct wpabuf *msg,
- enum dpp_status_error status)
+void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status)
{
wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
wpabuf_put_le16(msg, DPP_ATTR_STATUS);
@@ -1757,8 +435,7 @@
}
-static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
- const u8 *hash)
+void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash)
{
if (hash) {
wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
@@ -1769,374 +446,6 @@
}
-static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
- const u8 *hash)
-{
- if (hash) {
- wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
- wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
- }
-}
-
-
-static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
- const struct wpabuf *pi,
- size_t nonce_len,
- const u8 *r_pubkey_hash,
- const u8 *i_pubkey_hash,
- unsigned int neg_freq)
-{
- struct wpabuf *msg;
- u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
- u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
- u8 *pos;
- const u8 *addr[2];
- size_t len[2], siv_len, attr_len;
- u8 *attr_start, *attr_end;
-
- /* Build DPP Authentication Request frame attributes */
- attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
- 4 + sizeof(wrapped_data);
- if (neg_freq > 0)
- attr_len += 4 + 2;
-#ifdef CONFIG_DPP2
- attr_len += 5;
-#endif /* CONFIG_DPP2 */
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
- if (!msg)
- return NULL;
-
- attr_start = wpabuf_put(msg, 0);
-
- /* Responder Bootstrapping Key Hash */
- dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
-
- /* Initiator Bootstrapping Key Hash */
- dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
-
- /* Initiator Protocol Key */
- if (pi) {
- wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
- wpabuf_put_le16(msg, wpabuf_len(pi));
- wpabuf_put_buf(msg, pi);
- }
-
- /* Channel */
- if (neg_freq > 0) {
- u8 op_class, channel;
-
- if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
- &channel) ==
- NUM_HOSTAPD_MODES) {
- wpa_printf(MSG_INFO,
- "DPP: Unsupported negotiation frequency request: %d",
- neg_freq);
- wpabuf_free(msg);
- return NULL;
- }
- wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
- wpabuf_put_le16(msg, 2);
- wpabuf_put_u8(msg, op_class);
- wpabuf_put_u8(msg, channel);
- }
-
-#ifdef CONFIG_DPP2
- /* Protocol Version */
- wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, 2);
-#endif /* CONFIG_DPP2 */
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
- goto skip_wrapped_data;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Wrapped data ({I-nonce, I-capabilities}k1) */
- pos = clear;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
- goto skip_i_nonce;
- }
- if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
- WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len - 1);
- pos += 2;
- os_memcpy(pos, auth->i_nonce, nonce_len - 1);
- pos += nonce_len - 1;
- goto skip_i_nonce;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* I-nonce */
- WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, auth->i_nonce, nonce_len);
- pos += nonce_len;
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_i_nonce:
- if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
- goto skip_i_capab;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* I-capabilities */
- WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
- pos += 2;
- WPA_PUT_LE16(pos, 1);
- pos += 2;
- auth->i_capab = auth->allowed_roles;
- *pos++ = auth->i_capab;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
- wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
- pos[-1] = 0;
- }
-skip_i_capab:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- attr_end = wpabuf_put(msg, 0);
-
- /* OUI, OUI type, Crypto Suite, DPP frame type */
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = 3 + 1 + 1 + 1;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
- /* Attributes before Wrapped Data */
- addr[1] = attr_start;
- len[1] = attr_end - attr_start;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- siv_len = pos - clear;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
- if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
- 2, addr, len, wrapped_data) < 0) {
- wpabuf_free(msg);
- return NULL;
- }
- siv_len += AES_BLOCK_SIZE;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, siv_len);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, siv_len);
- wpabuf_put_data(msg, wrapped_data, siv_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_hexdump_buf(MSG_DEBUG,
- "DPP: Authentication Request frame attributes", msg);
-
- return msg;
-}
-
-
-static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
- enum dpp_status_error status,
- const struct wpabuf *pr,
- size_t nonce_len,
- const u8 *r_pubkey_hash,
- const u8 *i_pubkey_hash,
- const u8 *r_nonce, const u8 *i_nonce,
- const u8 *wrapped_r_auth,
- size_t wrapped_r_auth_len,
- const u8 *siv_key)
-{
- struct wpabuf *msg;
-#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
- 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
- u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
- u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
- const u8 *addr[2];
- size_t len[2], siv_len, attr_len;
- u8 *attr_start, *attr_end, *pos;
-
- auth->waiting_auth_conf = 1;
- auth->auth_resp_tries = 0;
-
- /* Build DPP Authentication Response frame attributes */
- attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
- 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
-#ifdef CONFIG_DPP2
- attr_len += 5;
-#endif /* CONFIG_DPP2 */
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
- if (!msg)
- return NULL;
-
- attr_start = wpabuf_put(msg, 0);
-
- /* DPP Status */
- if (status != 255)
- dpp_build_attr_status(msg, status);
-
- /* Responder Bootstrapping Key Hash */
- dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
-
- /* Initiator Bootstrapping Key Hash (mutual authentication) */
- dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
-
- /* Responder Protocol Key */
- if (pr) {
- wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
- wpabuf_put_le16(msg, wpabuf_len(pr));
- wpabuf_put_buf(msg, pr);
- }
-
-#ifdef CONFIG_DPP2
- /* Protocol Version */
- if (auth->peer_version >= 2) {
- wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, 2);
- }
-#endif /* CONFIG_DPP2 */
-
- attr_end = wpabuf_put(msg, 0);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
- goto skip_wrapped_data;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
- pos = clear;
-
- if (r_nonce) {
- /* R-nonce */
- WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, r_nonce, nonce_len);
- pos += nonce_len;
- }
-
- if (i_nonce) {
- /* I-nonce */
- WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, i_nonce, nonce_len);
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
- pos[nonce_len / 2] ^= 0x01;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
- pos += nonce_len;
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
- goto skip_r_capab;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* R-capabilities */
- WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
- pos += 2;
- WPA_PUT_LE16(pos, 1);
- pos += 2;
- auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
- DPP_CAPAB_ENROLLEE;
- *pos++ = auth->r_capab;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
- wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
- pos[-1] = 0;
- } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - incompatible R-capabilities");
- if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
- (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
- pos[-1] = 0;
- else
- pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
- DPP_CAPAB_CONFIGURATOR;
- }
-skip_r_capab:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (wrapped_r_auth) {
- /* {R-auth}ke */
- WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
- pos += 2;
- WPA_PUT_LE16(pos, wrapped_r_auth_len);
- pos += 2;
- os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
- pos += wrapped_r_auth_len;
- }
-
- /* OUI, OUI type, Crypto Suite, DPP frame type */
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = 3 + 1 + 1 + 1;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
- /* Attributes before Wrapped Data */
- addr[1] = attr_start;
- len[1] = attr_end - attr_start;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- siv_len = pos - clear;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
- if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
- 2, addr, len, wrapped_data) < 0) {
- wpabuf_free(msg);
- return NULL;
- }
- siv_len += AES_BLOCK_SIZE;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, siv_len);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, siv_len);
- wpabuf_put_data(msg, wrapped_data, siv_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_hexdump_buf(MSG_DEBUG,
- "DPP: Authentication Response frame attributes", msg);
- return msg;
-}
-
-
static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
u16 num_modes, unsigned int freq)
{
@@ -2255,10 +564,9 @@
}
-static int dpp_prepare_channel_list(struct dpp_authentication *auth,
- unsigned int neg_freq,
- struct hostapd_hw_modes *own_modes,
- u16 num_modes)
+int dpp_prepare_channel_list(struct dpp_authentication *auth,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes, u16 num_modes)
{
int res;
char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
@@ -2269,6 +577,7 @@
return -1;
auth->num_freq = 1;
auth->freq[0] = neg_freq;
+ auth->curr_freq = neg_freq;
return 0;
}
@@ -2304,7 +613,7 @@
}
-static int dpp_gen_uri(struct dpp_bootstrap_info *bi)
+int dpp_gen_uri(struct dpp_bootstrap_info *bi)
{
char macstr[ETH_ALEN * 2 + 10];
size_t len;
@@ -2320,50 +629,27 @@
len += os_strlen(macstr); /* M:...; */
if (bi->info)
len += 3 + os_strlen(bi->info); /* I:...; */
+#ifdef CONFIG_DPP2
+ len += 4; /* V:2; */
+#endif /* CONFIG_DPP2 */
len += 4 + os_strlen(bi->pk); /* K:...;; */
os_free(bi->uri);
bi->uri = os_malloc(len + 1);
if (!bi->uri)
return -1;
- os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%sK:%s;;",
+ os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%sK:%s;;",
bi->chan ? "C:" : "", bi->chan ? bi->chan : "",
bi->chan ? ";" : "",
macstr,
bi->info ? "I:" : "", bi->info ? bi->info : "",
bi->info ? ";" : "",
+ DPP_VERSION == 2 ? "V:2;" : "",
bi->pk);
return 0;
}
-static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
-{
- struct dpp_bootstrap_info *bi;
-
- if (auth->own_bi)
- return 0; /* already generated */
-
- bi = os_zalloc(sizeof(*bi));
- if (!bi)
- return -1;
- bi->type = DPP_BOOTSTRAP_QR_CODE;
- if (dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0) < 0 ||
- dpp_gen_uri(bi) < 0)
- goto fail;
- wpa_printf(MSG_DEBUG,
- "DPP: Auto-generated own bootstrapping key info: URI %s",
- bi->uri);
-
- auth->tmp_own_bi = auth->own_bi = bi;
-
- return 0;
-fail:
- dpp_bootstrap_info_free(bi);
- return -1;
-}
-
-
struct dpp_authentication *
dpp_alloc_auth(struct dpp_global *dpp, void *msg_ctx)
{
@@ -2379,151 +665,6 @@
}
-struct dpp_authentication * dpp_auth_init(struct dpp_global *dpp, void *msg_ctx,
- struct dpp_bootstrap_info *peer_bi,
- struct dpp_bootstrap_info *own_bi,
- u8 dpp_allowed_roles,
- unsigned int neg_freq,
- struct hostapd_hw_modes *own_modes,
- u16 num_modes)
-{
- struct dpp_authentication *auth;
- size_t nonce_len;
- size_t secret_len;
- struct wpabuf *pi = NULL;
- const u8 *r_pubkey_hash, *i_pubkey_hash;
-#ifdef CONFIG_TESTING_OPTIONS
- u8 test_hash[SHA256_MAC_LEN];
-#endif /* CONFIG_TESTING_OPTIONS */
-
- auth = dpp_alloc_auth(dpp, msg_ctx);
- if (!auth)
- return NULL;
- if (peer_bi->configurator_params &&
- dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
- goto fail;
- auth->initiator = 1;
- auth->waiting_auth_resp = 1;
- auth->allowed_roles = dpp_allowed_roles;
- auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
- auth->peer_bi = peer_bi;
- auth->own_bi = own_bi;
- auth->curve = peer_bi->curve;
-
- if (dpp_autogen_bootstrap_key(auth) < 0 ||
- dpp_prepare_channel_list(auth, neg_freq, own_modes, num_modes) < 0)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_nonce_override_len > 0) {
- wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
- nonce_len = dpp_nonce_override_len;
- os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
- } else {
- nonce_len = auth->curve->nonce_len;
- if (random_get_bytes(auth->i_nonce, nonce_len)) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate I-nonce");
- goto fail;
- }
- }
-#else /* CONFIG_TESTING_OPTIONS */
- nonce_len = auth->curve->nonce_len;
- if (random_get_bytes(auth->i_nonce, nonce_len)) {
- wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
- goto fail;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
- wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_protocol_key_override_len) {
- const struct dpp_curve_params *tmp_curve;
-
- wpa_printf(MSG_INFO,
- "DPP: TESTING - override protocol key");
- auth->own_protocol_key = dpp_set_keypair(
- &tmp_curve, dpp_protocol_key_override,
- dpp_protocol_key_override_len);
- } else {
- auth->own_protocol_key = dpp_gen_keypair(auth->curve);
- }
-#else /* CONFIG_TESTING_OPTIONS */
- auth->own_protocol_key = dpp_gen_keypair(auth->curve);
-#endif /* CONFIG_TESTING_OPTIONS */
- if (!auth->own_protocol_key)
- goto fail;
-
- pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- if (!pi)
- goto fail;
-
- /* ECDH: M = pI * BR */
- if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey,
- auth->Mx, &secret_len) < 0)
- goto fail;
- auth->secret_len = secret_len;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
- auth->Mx, auth->secret_len);
- auth->Mx_len = auth->secret_len;
-
- if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
- auth->curve->hash_len) < 0)
- goto fail;
-
- r_pubkey_hash = auth->peer_bi->pubkey_hash;
- i_pubkey_hash = auth->own_bi->pubkey_hash;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
- r_pubkey_hash = NULL;
- } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid R-Bootstrap Key Hash");
- os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- r_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
- i_pubkey_hash = NULL;
- } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid I-Bootstrap Key Hash");
- os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- i_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
- wpabuf_free(pi);
- pi = NULL;
- } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
- wpabuf_free(pi);
- pi = wpabuf_alloc(2 * auth->curve->prime_len);
- if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
- goto fail;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (neg_freq && auth->num_freq == 1 && auth->freq[0] == neg_freq)
- neg_freq = 0;
- auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
- i_pubkey_hash, neg_freq);
- if (!auth->req_msg)
- goto fail;
-
-out:
- wpabuf_free(pi);
- return auth;
-fail:
- dpp_auth_deinit(auth);
- auth = NULL;
- goto out;
-}
-
-
static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth,
const char *json)
{
@@ -2629,7 +770,7 @@
}
-static void dpp_write_adv_proto(struct wpabuf *buf)
+void dpp_write_adv_proto(struct wpabuf *buf)
{
/* Advertisement Protocol IE */
wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
@@ -2643,7 +784,7 @@
}
-static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
+void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
{
/* GAS Query */
wpabuf_put_le16(buf, wpabuf_len(query));
@@ -2738,1704 +879,6 @@
}
-static void dpp_auth_success(struct dpp_authentication *auth)
-{
- wpa_printf(MSG_DEBUG,
- "DPP: Authentication success - clear temporary keys");
- os_memset(auth->Mx, 0, sizeof(auth->Mx));
- auth->Mx_len = 0;
- os_memset(auth->Nx, 0, sizeof(auth->Nx));
- auth->Nx_len = 0;
- os_memset(auth->Lx, 0, sizeof(auth->Lx));
- auth->Lx_len = 0;
- os_memset(auth->k1, 0, sizeof(auth->k1));
- os_memset(auth->k2, 0, sizeof(auth->k2));
-
- auth->auth_success = 1;
-}
-
-
-static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
-{
- struct wpabuf *pix, *prx, *bix, *brx;
- const u8 *addr[7];
- size_t len[7];
- size_t i, num_elem = 0;
- size_t nonce_len;
- u8 zero = 0;
- int res = -1;
-
- /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
- nonce_len = auth->curve->nonce_len;
-
- if (auth->initiator) {
- pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- if (auth->own_bi)
- bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
- else
- bix = NULL;
- brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
- } else {
- pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- if (auth->peer_bi)
- bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
- else
- bix = NULL;
- brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
- }
- if (!pix || !prx || !brx)
- goto fail;
-
- addr[num_elem] = auth->i_nonce;
- len[num_elem] = nonce_len;
- num_elem++;
-
- addr[num_elem] = auth->r_nonce;
- len[num_elem] = nonce_len;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(pix);
- len[num_elem] = wpabuf_len(pix) / 2;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(prx);
- len[num_elem] = wpabuf_len(prx) / 2;
- num_elem++;
-
- if (bix) {
- addr[num_elem] = wpabuf_head(bix);
- len[num_elem] = wpabuf_len(bix) / 2;
- num_elem++;
- }
-
- addr[num_elem] = wpabuf_head(brx);
- len[num_elem] = wpabuf_len(brx) / 2;
- num_elem++;
-
- addr[num_elem] = &zero;
- len[num_elem] = 1;
- num_elem++;
-
- wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
- for (i = 0; i < num_elem; i++)
- wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
- res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
- if (res == 0)
- wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
- auth->curve->hash_len);
-fail:
- wpabuf_free(pix);
- wpabuf_free(prx);
- wpabuf_free(bix);
- wpabuf_free(brx);
- return res;
-}
-
-
-static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
-{
- struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
- const u8 *addr[7];
- size_t len[7];
- size_t i, num_elem = 0;
- size_t nonce_len;
- u8 one = 1;
- int res = -1;
-
- /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
- nonce_len = auth->curve->nonce_len;
-
- if (auth->initiator) {
- pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- if (auth->own_bi)
- bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
- else
- bix = NULL;
- if (!auth->peer_bi)
- goto fail;
- brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
- } else {
- pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- if (auth->peer_bi)
- bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
- else
- bix = NULL;
- if (!auth->own_bi)
- goto fail;
- brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
- }
- if (!pix || !prx || !brx)
- goto fail;
-
- addr[num_elem] = auth->r_nonce;
- len[num_elem] = nonce_len;
- num_elem++;
-
- addr[num_elem] = auth->i_nonce;
- len[num_elem] = nonce_len;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(prx);
- len[num_elem] = wpabuf_len(prx) / 2;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(pix);
- len[num_elem] = wpabuf_len(pix) / 2;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(brx);
- len[num_elem] = wpabuf_len(brx) / 2;
- num_elem++;
-
- if (bix) {
- addr[num_elem] = wpabuf_head(bix);
- len[num_elem] = wpabuf_len(bix) / 2;
- num_elem++;
- }
-
- addr[num_elem] = &one;
- len[num_elem] = 1;
- num_elem++;
-
- wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
- for (i = 0; i < num_elem; i++)
- wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
- res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
- if (res == 0)
- wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
- auth->curve->hash_len);
-fail:
- wpabuf_free(pix);
- wpabuf_free(prx);
- wpabuf_free(bix);
- wpabuf_free(brx);
- return res;
-}
-
-
-static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
-{
- const EC_GROUP *group;
- EC_POINT *l = NULL;
- EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
- const EC_POINT *BI_point;
- BN_CTX *bnctx;
- BIGNUM *lx, *sum, *q;
- const BIGNUM *bR_bn, *pR_bn;
- int ret = -1;
-
- /* L = ((bR + pR) modulo q) * BI */
-
- bnctx = BN_CTX_new();
- sum = BN_new();
- q = BN_new();
- lx = BN_new();
- if (!bnctx || !sum || !q || !lx)
- goto fail;
- BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
- if (!BI)
- goto fail;
- BI_point = EC_KEY_get0_public_key(BI);
- group = EC_KEY_get0_group(BI);
- if (!group)
- goto fail;
-
- bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
- pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
- if (!bR || !pR)
- goto fail;
- bR_bn = EC_KEY_get0_private_key(bR);
- pR_bn = EC_KEY_get0_private_key(pR);
- if (!bR_bn || !pR_bn)
- goto fail;
- if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
- BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
- goto fail;
- l = EC_POINT_new(group);
- if (!l ||
- EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
- bnctx) != 1) {
- wpa_printf(MSG_ERROR,
- "OpenSSL: failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
- goto fail;
- wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
- auth->Lx_len = auth->secret_len;
- ret = 0;
-fail:
- EC_POINT_clear_free(l);
- EC_KEY_free(BI);
- EC_KEY_free(bR);
- EC_KEY_free(pR);
- BN_clear_free(lx);
- BN_clear_free(sum);
- BN_free(q);
- BN_CTX_free(bnctx);
- return ret;
-}
-
-
-static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
-{
- const EC_GROUP *group;
- EC_POINT *l = NULL, *sum = NULL;
- EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
- const EC_POINT *BR_point, *PR_point;
- BN_CTX *bnctx;
- BIGNUM *lx;
- const BIGNUM *bI_bn;
- int ret = -1;
-
- /* L = bI * (BR + PR) */
-
- bnctx = BN_CTX_new();
- lx = BN_new();
- if (!bnctx || !lx)
- goto fail;
- BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
- PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
- if (!BR || !PR)
- goto fail;
- BR_point = EC_KEY_get0_public_key(BR);
- PR_point = EC_KEY_get0_public_key(PR);
-
- bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
- if (!bI)
- goto fail;
- group = EC_KEY_get0_group(bI);
- bI_bn = EC_KEY_get0_private_key(bI);
- if (!group || !bI_bn)
- goto fail;
- sum = EC_POINT_new(group);
- l = EC_POINT_new(group);
- if (!sum || !l ||
- EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
- EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
- bnctx) != 1) {
- wpa_printf(MSG_ERROR,
- "OpenSSL: failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
- goto fail;
- wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
- auth->Lx_len = auth->secret_len;
- ret = 0;
-fail:
- EC_POINT_clear_free(l);
- EC_POINT_clear_free(sum);
- EC_KEY_free(bI);
- EC_KEY_free(BR);
- EC_KEY_free(PR);
- BN_clear_free(lx);
- BN_CTX_free(bnctx);
- return ret;
-}
-
-
-static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
-{
- size_t nonce_len;
- size_t secret_len;
- struct wpabuf *msg, *pr = NULL;
- u8 r_auth[4 + DPP_MAX_HASH_LEN];
- u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
- size_t wrapped_r_auth_len;
- int ret = -1;
- const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
- enum dpp_status_error status = DPP_STATUS_OK;
-#ifdef CONFIG_TESTING_OPTIONS
- u8 test_hash[SHA256_MAC_LEN];
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
- if (!auth->own_bi)
- return -1;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_nonce_override_len > 0) {
- wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
- nonce_len = dpp_nonce_override_len;
- os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
- } else {
- nonce_len = auth->curve->nonce_len;
- if (random_get_bytes(auth->r_nonce, nonce_len)) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate R-nonce");
- goto fail;
- }
- }
-#else /* CONFIG_TESTING_OPTIONS */
- nonce_len = auth->curve->nonce_len;
- if (random_get_bytes(auth->r_nonce, nonce_len)) {
- wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
- goto fail;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
- wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
-
- EVP_PKEY_free(auth->own_protocol_key);
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_protocol_key_override_len) {
- const struct dpp_curve_params *tmp_curve;
-
- wpa_printf(MSG_INFO,
- "DPP: TESTING - override protocol key");
- auth->own_protocol_key = dpp_set_keypair(
- &tmp_curve, dpp_protocol_key_override,
- dpp_protocol_key_override_len);
- } else {
- auth->own_protocol_key = dpp_gen_keypair(auth->curve);
- }
-#else /* CONFIG_TESTING_OPTIONS */
- auth->own_protocol_key = dpp_gen_keypair(auth->curve);
-#endif /* CONFIG_TESTING_OPTIONS */
- if (!auth->own_protocol_key)
- goto fail;
-
- pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- if (!pr)
- goto fail;
-
- /* ECDH: N = pR * PI */
- if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
- auth->Nx, &secret_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
- auth->Nx, auth->secret_len);
- auth->Nx_len = auth->secret_len;
-
- if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
- auth->curve->hash_len) < 0)
- goto fail;
-
- if (auth->own_bi && auth->peer_bi) {
- /* Mutual authentication */
- if (dpp_auth_derive_l_responder(auth) < 0)
- goto fail;
- }
-
- if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
- goto fail;
-
- /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
- WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
- WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
- if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
- goto fail;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
- r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
- if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
- r_auth, 4 + auth->curve->hash_len,
- 0, NULL, NULL, wrapped_r_auth) < 0)
- goto fail;
- wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
- wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
- wrapped_r_auth, wrapped_r_auth_len);
- w_r_auth = wrapped_r_auth;
-
- r_pubkey_hash = auth->own_bi->pubkey_hash;
- if (auth->peer_bi)
- i_pubkey_hash = auth->peer_bi->pubkey_hash;
- else
- i_pubkey_hash = NULL;
-
- i_nonce = auth->i_nonce;
- r_nonce = auth->r_nonce;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
- r_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid R-Bootstrap Key Hash");
- os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- r_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
- i_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid I-Bootstrap Key Hash");
- if (i_pubkey_hash)
- os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
- else
- os_memset(test_hash, 0, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- i_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
- wpabuf_free(pr);
- pr = NULL;
- } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
- wpabuf_free(pr);
- pr = wpabuf_alloc(2 * auth->curve->prime_len);
- if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
- goto fail;
- } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
- w_r_auth = NULL;
- wrapped_r_auth_len = 0;
- } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
- status = 255;
- } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
- status = 254;
- } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
- r_nonce = NULL;
- } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
- i_nonce = NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
- r_pubkey_hash, i_pubkey_hash,
- r_nonce, i_nonce,
- w_r_auth, wrapped_r_auth_len,
- auth->k2);
- if (!msg)
- goto fail;
- wpabuf_free(auth->resp_msg);
- auth->resp_msg = msg;
- ret = 0;
-fail:
- wpabuf_free(pr);
- return ret;
-}
-
-
-static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
- enum dpp_status_error status)
-{
- struct wpabuf *msg;
- const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
-#ifdef CONFIG_TESTING_OPTIONS
- u8 test_hash[SHA256_MAC_LEN];
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!auth->own_bi)
- return -1;
- wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
-
- r_pubkey_hash = auth->own_bi->pubkey_hash;
- if (auth->peer_bi)
- i_pubkey_hash = auth->peer_bi->pubkey_hash;
- else
- i_pubkey_hash = NULL;
-
- i_nonce = auth->i_nonce;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
- r_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid R-Bootstrap Key Hash");
- os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- r_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
- i_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid I-Bootstrap Key Hash");
- if (i_pubkey_hash)
- os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
- else
- os_memset(test_hash, 0, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- i_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
- status = 255;
- } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
- i_nonce = NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
- r_pubkey_hash, i_pubkey_hash,
- NULL, i_nonce, NULL, 0, auth->k1);
- if (!msg)
- return -1;
- wpabuf_free(auth->resp_msg);
- auth->resp_msg = msg;
- return 0;
-}
-
-
-struct dpp_authentication *
-dpp_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, u8 dpp_allowed_roles,
- int qr_mutual, struct dpp_bootstrap_info *peer_bi,
- struct dpp_bootstrap_info *own_bi,
- unsigned int freq, const u8 *hdr, const u8 *attr_start,
- size_t attr_len)
-{
- EVP_PKEY *pi = NULL;
- EVP_PKEY_CTX *ctx = NULL;
- size_t secret_len;
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
- *channel;
- u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
- i_bootstrap_len, channel_len;
- struct dpp_authentication *auth = NULL;
-#ifdef CONFIG_DPP2
- const u8 *version;
- u16 version_len;
-#endif /* CONFIG_DPP2 */
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at Authentication Request");
- return NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Missing or invalid required Wrapped Data attribute");
- return NULL;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
- wrapped_data, wrapped_data_len);
- attr_len = wrapped_data - 4 - attr_start;
-
- auth = dpp_alloc_auth(dpp, msg_ctx);
- if (!auth)
- goto fail;
- if (peer_bi && peer_bi->configurator_params &&
- dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
- goto fail;
- auth->peer_bi = peer_bi;
- auth->own_bi = own_bi;
- auth->curve = own_bi->curve;
- auth->curr_freq = freq;
-
- auth->peer_version = 1; /* default to the first version */
-#ifdef CONFIG_DPP2
- version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
- &version_len);
- if (version) {
- if (version_len < 1 || version[0] == 0) {
- dpp_auth_fail(auth,
- "Invalid Protocol Version attribute");
- goto fail;
- }
- auth->peer_version = version[0];
- wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
- auth->peer_version);
- }
-#endif /* CONFIG_DPP2 */
-
- channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
- &channel_len);
- if (channel) {
- int neg_freq;
-
- if (channel_len < 2) {
- dpp_auth_fail(auth, "Too short Channel attribute");
- goto fail;
- }
-
- neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
- wpa_printf(MSG_DEBUG,
- "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
- channel[0], channel[1], neg_freq);
- if (neg_freq < 0) {
- dpp_auth_fail(auth,
- "Unsupported Channel attribute value");
- goto fail;
- }
-
- if (auth->curr_freq != (unsigned int) neg_freq) {
- wpa_printf(MSG_DEBUG,
- "DPP: Changing negotiation channel from %u MHz to %u MHz",
- freq, neg_freq);
- auth->curr_freq = neg_freq;
- }
- }
-
- i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
- &i_proto_len);
- if (!i_proto) {
- dpp_auth_fail(auth,
- "Missing required Initiator Protocol Key attribute");
- goto fail;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
- i_proto, i_proto_len);
-
- /* M = bR * PI */
- pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
- if (!pi) {
- dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
- goto fail;
- }
- dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
-
- if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0)
- goto fail;
- auth->secret_len = secret_len;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
- auth->Mx, auth->secret_len);
- auth->Mx_len = auth->secret_len;
-
- if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
- auth->curve->hash_len) < 0)
- goto fail;
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
- if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
- &i_nonce_len);
- if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "Missing or invalid I-nonce");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
- os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
-
- i_capab = dpp_get_attr(unwrapped, unwrapped_len,
- DPP_ATTR_I_CAPABILITIES,
- &i_capab_len);
- if (!i_capab || i_capab_len < 1) {
- dpp_auth_fail(auth, "Missing or invalid I-capabilities");
- goto fail;
- }
- auth->i_capab = i_capab[0];
- wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
-
- bin_clear_free(unwrapped, unwrapped_len);
- unwrapped = NULL;
-
- switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
- case DPP_CAPAB_ENROLLEE:
- if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Local policy does not allow Configurator role");
- goto not_compatible;
- }
- wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
- auth->configurator = 1;
- break;
- case DPP_CAPAB_CONFIGURATOR:
- if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Local policy does not allow Enrollee role");
- goto not_compatible;
- }
- wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
- auth->configurator = 0;
- break;
- case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
- if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
- wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
- auth->configurator = 0;
- } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
- wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
- auth->configurator = 1;
- } else {
- wpa_printf(MSG_DEBUG,
- "DPP: Local policy does not allow Configurator/Enrollee role");
- goto not_compatible;
- }
- break;
- default:
- wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
- wpa_msg(auth->msg_ctx, MSG_INFO,
- DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
- auth->i_capab & DPP_CAPAB_ROLE_MASK);
- goto fail;
- }
-
- auth->peer_protocol_key = pi;
- pi = NULL;
- if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
- char hex[SHA256_MAC_LEN * 2 + 1];
-
- wpa_printf(MSG_DEBUG,
- "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
- if (dpp_auth_build_resp_status(auth,
- DPP_STATUS_RESPONSE_PENDING) < 0)
- goto fail;
- i_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
- &i_bootstrap_len);
- if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
- auth->response_pending = 1;
- os_memcpy(auth->waiting_pubkey_hash,
- i_bootstrap, i_bootstrap_len);
- wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
- i_bootstrap_len);
- } else {
- hex[0] = '\0';
- }
-
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
- "%s", hex);
- return auth;
- }
- if (dpp_auth_build_resp_ok(auth) < 0)
- goto fail;
-
- return auth;
-
-not_compatible:
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
- "i-capab=0x%02x", auth->i_capab);
- if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
- auth->configurator = 1;
- else
- auth->configurator = 0;
- auth->peer_protocol_key = pi;
- pi = NULL;
- if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
- goto fail;
-
- auth->remove_on_tx_status = 1;
- return auth;
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
- EVP_PKEY_free(pi);
- EVP_PKEY_CTX_free(ctx);
- dpp_auth_deinit(auth);
- return NULL;
-}
-
-
-int dpp_notify_new_qr_code(struct dpp_authentication *auth,
- struct dpp_bootstrap_info *peer_bi)
-{
- if (!auth || !auth->response_pending ||
- os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0)
- return 0;
-
- wpa_printf(MSG_DEBUG,
- "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
- MACSTR, MAC2STR(auth->peer_mac_addr));
- auth->peer_bi = peer_bi;
-
- if (dpp_auth_build_resp_ok(auth) < 0)
- return -1;
-
- return 1;
-}
-
-
-static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
- enum dpp_status_error status)
-{
- struct wpabuf *msg;
- u8 i_auth[4 + DPP_MAX_HASH_LEN];
- size_t i_auth_len;
- u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
- size_t r_nonce_len;
- const u8 *addr[2];
- size_t len[2], attr_len;
- u8 *wrapped_i_auth;
- u8 *wrapped_r_nonce;
- u8 *attr_start, *attr_end;
- const u8 *r_pubkey_hash, *i_pubkey_hash;
-#ifdef CONFIG_TESTING_OPTIONS
- u8 test_hash[SHA256_MAC_LEN];
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
-
- i_auth_len = 4 + auth->curve->hash_len;
- r_nonce_len = 4 + auth->curve->nonce_len;
- /* Build DPP Authentication Confirmation frame attributes */
- attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
- 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
- if (!msg)
- goto fail;
-
- attr_start = wpabuf_put(msg, 0);
-
- r_pubkey_hash = auth->peer_bi->pubkey_hash;
- if (auth->own_bi)
- i_pubkey_hash = auth->own_bi->pubkey_hash;
- else
- i_pubkey_hash = NULL;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
- goto skip_status;
- } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
- status = 254;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* DPP Status */
- dpp_build_attr_status(msg, status);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_status:
- if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
- r_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid R-Bootstrap Key Hash");
- os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- r_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
- i_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid I-Bootstrap Key Hash");
- if (i_pubkey_hash)
- os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
- else
- os_memset(test_hash, 0, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- i_pubkey_hash = test_hash;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Responder Bootstrapping Key Hash */
- dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
-
- /* Initiator Bootstrapping Key Hash (mutual authentication) */
- dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
- goto skip_wrapped_data;
- if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
- i_auth_len = 0;
-#endif /* CONFIG_TESTING_OPTIONS */
-
- attr_end = wpabuf_put(msg, 0);
-
- /* OUI, OUI type, Crypto Suite, DPP frame type */
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = 3 + 1 + 1 + 1;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
- /* Attributes before Wrapped Data */
- addr[1] = attr_start;
- len[1] = attr_end - attr_start;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- if (status == DPP_STATUS_OK) {
- /* I-auth wrapped with ke */
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
- wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
- goto skip_i_auth;
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
- * 1) */
- WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
- WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
- if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
- i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
- }
-skip_i_auth:
-#endif /* CONFIG_TESTING_OPTIONS */
- if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
- i_auth, i_auth_len,
- 2, addr, len, wrapped_i_auth) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
- wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
- } else {
- /* R-nonce wrapped with k2 */
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
- wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
-
- WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
- WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
- os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
-
- if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
- r_nonce, r_nonce_len,
- 2, addr, len, wrapped_r_nonce) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
- wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_hexdump_buf(MSG_DEBUG,
- "DPP: Authentication Confirmation frame attributes",
- msg);
- if (status == DPP_STATUS_OK)
- dpp_auth_success(auth);
-
- return msg;
-
-fail:
- wpabuf_free(msg);
- return NULL;
-}
-
-
-static void
-dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
- const u8 *attr_start, size_t attr_len,
- const u8 *wrapped_data, u16 wrapped_data_len,
- enum dpp_status_error status)
-{
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- const u8 *i_nonce, *r_capab;
- u16 i_nonce_len, r_capab_len;
-
- if (status == DPP_STATUS_NOT_COMPATIBLE) {
- wpa_printf(MSG_DEBUG,
- "DPP: Responder reported incompatible roles");
- } else if (status == DPP_STATUS_RESPONSE_PENDING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Responder reported more time needed");
- } else {
- wpa_printf(MSG_DEBUG,
- "DPP: Responder reported failure (status %d)",
- status);
- dpp_auth_fail(auth, "Responder reported failure");
- return;
- }
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
- if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
- &i_nonce_len);
- if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "Missing or invalid I-nonce");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
- if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
- dpp_auth_fail(auth, "I-nonce mismatch");
- goto fail;
- }
-
- r_capab = dpp_get_attr(unwrapped, unwrapped_len,
- DPP_ATTR_R_CAPABILITIES,
- &r_capab_len);
- if (!r_capab || r_capab_len < 1) {
- dpp_auth_fail(auth, "Missing or invalid R-capabilities");
- goto fail;
- }
- auth->r_capab = r_capab[0];
- wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
- if (status == DPP_STATUS_NOT_COMPATIBLE) {
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
- "r-capab=0x%02x", auth->r_capab);
- } else if (status == DPP_STATUS_RESPONSE_PENDING) {
- u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
-
- if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
- (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
- wpa_msg(auth->msg_ctx, MSG_INFO,
- DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
- role);
- } else {
- wpa_printf(MSG_DEBUG,
- "DPP: Continue waiting for full DPP Authentication Response");
- wpa_msg(auth->msg_ctx, MSG_INFO,
- DPP_EVENT_RESPONSE_PENDING "%s",
- auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
- }
- }
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
-}
-
-
-struct wpabuf *
-dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
- const u8 *attr_start, size_t attr_len)
-{
- EVP_PKEY *pr;
- size_t secret_len;
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL, *unwrapped2 = NULL;
- size_t unwrapped_len = 0, unwrapped2_len = 0;
- const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
- *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
- u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
- r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
- wrapped2_len, r_auth_len;
- u8 r_auth2[DPP_MAX_HASH_LEN];
- u8 role;
-#ifdef CONFIG_DPP2
- const u8 *version;
- u16 version_len;
-#endif /* CONFIG_DPP2 */
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at Authentication Response");
- return NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!auth->initiator || !auth->peer_bi) {
- dpp_auth_fail(auth, "Unexpected Authentication Response");
- return NULL;
- }
-
- auth->waiting_auth_resp = 0;
-
- wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- dpp_auth_fail(auth,
- "Missing or invalid required Wrapped Data attribute");
- return NULL;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
- wrapped_data, wrapped_data_len);
-
- attr_len = wrapped_data - 4 - attr_start;
-
- r_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
- &r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
- dpp_auth_fail(auth,
- "Missing or invalid required Responder Bootstrapping Key Hash attribute");
- return NULL;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
- r_bootstrap, r_bootstrap_len);
- if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0) {
- dpp_auth_fail(auth,
- "Unexpected Responder Bootstrapping Key Hash value");
- wpa_hexdump(MSG_DEBUG,
- "DPP: Expected Responder Bootstrapping Key Hash",
- auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
- return NULL;
- }
-
- i_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
- &i_bootstrap_len);
- if (i_bootstrap) {
- if (i_bootstrap_len != SHA256_MAC_LEN) {
- dpp_auth_fail(auth,
- "Invalid Initiator Bootstrapping Key Hash attribute");
- return NULL;
- }
- wpa_hexdump(MSG_MSGDUMP,
- "DPP: Initiator Bootstrapping Key Hash",
- i_bootstrap, i_bootstrap_len);
- if (!auth->own_bi ||
- os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0) {
- dpp_auth_fail(auth,
- "Initiator Bootstrapping Key Hash attribute did not match");
- return NULL;
- }
- } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
- /* PKEX bootstrapping mandates use of mutual authentication */
- dpp_auth_fail(auth,
- "Missing Initiator Bootstrapping Key Hash attribute");
- return NULL;
- } else if (auth->own_bi &&
- auth->own_bi->type == DPP_BOOTSTRAP_NFC_URI &&
- auth->own_bi->nfc_negotiated) {
- /* NFC negotiated connection handover bootstrapping mandates
- * use of mutual authentication */
- dpp_auth_fail(auth,
- "Missing Initiator Bootstrapping Key Hash attribute");
- return NULL;
- }
-
- auth->peer_version = 1; /* default to the first version */
-#ifdef CONFIG_DPP2
- version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
- &version_len);
- if (version) {
- if (version_len < 1 || version[0] == 0) {
- dpp_auth_fail(auth,
- "Invalid Protocol Version attribute");
- return NULL;
- }
- auth->peer_version = version[0];
- wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
- auth->peer_version);
- }
-#endif /* CONFIG_DPP2 */
-
- status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
- &status_len);
- if (!status || status_len < 1) {
- dpp_auth_fail(auth,
- "Missing or invalid required DPP Status attribute");
- return NULL;
- }
- wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
- auth->auth_resp_status = status[0];
- if (status[0] != DPP_STATUS_OK) {
- dpp_auth_resp_rx_status(auth, hdr, attr_start,
- attr_len, wrapped_data,
- wrapped_data_len, status[0]);
- return NULL;
- }
-
- if (!i_bootstrap && auth->own_bi) {
- wpa_printf(MSG_DEBUG,
- "DPP: Responder decided not to use mutual authentication");
- auth->own_bi = NULL;
- }
-
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
- auth->own_bi != NULL);
-
- r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
- &r_proto_len);
- if (!r_proto) {
- dpp_auth_fail(auth,
- "Missing required Responder Protocol Key attribute");
- return NULL;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
- r_proto, r_proto_len);
-
- /* N = pI * PR */
- pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
- if (!pr) {
- dpp_auth_fail(auth, "Invalid Responder Protocol Key");
- return NULL;
- }
- dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
-
- if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) {
- dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
- goto fail;
- }
- EVP_PKEY_free(auth->peer_protocol_key);
- auth->peer_protocol_key = pr;
- pr = NULL;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
- auth->Nx, auth->secret_len);
- auth->Nx_len = auth->secret_len;
-
- if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
- auth->curve->hash_len) < 0)
- goto fail;
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
- if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
- &r_nonce_len);
- if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
- os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
-
- i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
- &i_nonce_len);
- if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "Missing or invalid I-nonce");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
- if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
- dpp_auth_fail(auth, "I-nonce mismatch");
- goto fail;
- }
-
- if (auth->own_bi) {
- /* Mutual authentication */
- if (dpp_auth_derive_l_initiator(auth) < 0)
- goto fail;
- }
-
- r_capab = dpp_get_attr(unwrapped, unwrapped_len,
- DPP_ATTR_R_CAPABILITIES,
- &r_capab_len);
- if (!r_capab || r_capab_len < 1) {
- dpp_auth_fail(auth, "Missing or invalid R-capabilities");
- goto fail;
- }
- auth->r_capab = r_capab[0];
- wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
- role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
- if ((auth->allowed_roles ==
- (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
- (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
- /* Peer selected its role, so move from "either role" to the
- * role that is compatible with peer's selection. */
- auth->configurator = role == DPP_CAPAB_ENROLLEE;
- wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
- auth->configurator ? "Configurator" : "Enrollee");
- } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
- (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
- wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Unexpected role in R-capabilities 0x%02x",
- role);
- if (role != DPP_CAPAB_ENROLLEE &&
- role != DPP_CAPAB_CONFIGURATOR)
- goto fail;
- bin_clear_free(unwrapped, unwrapped_len);
- auth->remove_on_tx_status = 1;
- return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
- }
-
- wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
- DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
- if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
- dpp_auth_fail(auth,
- "Missing or invalid Secondary Wrapped Data");
- goto fail;
- }
-
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped2, wrapped2_len);
-
- if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
- goto fail;
-
- unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
- unwrapped2 = os_malloc(unwrapped2_len);
- if (!unwrapped2)
- goto fail;
- if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
- wrapped2, wrapped2_len,
- 0, NULL, NULL, unwrapped2) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped2, unwrapped2_len);
-
- if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
- dpp_auth_fail(auth,
- "Invalid attribute in secondary unwrapped data");
- goto fail;
- }
-
- r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
- &r_auth_len);
- if (!r_auth || r_auth_len != auth->curve->hash_len) {
- dpp_auth_fail(auth,
- "Missing or invalid Responder Authenticating Tag");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
- r_auth, r_auth_len);
- /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
- if (dpp_gen_r_auth(auth, r_auth2) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
- r_auth2, r_auth_len);
- if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
- dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
- bin_clear_free(unwrapped, unwrapped_len);
- bin_clear_free(unwrapped2, unwrapped2_len);
- auth->remove_on_tx_status = 1;
- return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
- }
-
- bin_clear_free(unwrapped, unwrapped_len);
- bin_clear_free(unwrapped2, unwrapped2_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - Authentication Response in place of Confirm");
- if (dpp_auth_build_resp_ok(auth) < 0)
- return NULL;
- return wpabuf_dup(auth->resp_msg);
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- return dpp_auth_build_conf(auth, DPP_STATUS_OK);
-
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
- bin_clear_free(unwrapped2, unwrapped2_len);
- EVP_PKEY_free(pr);
- return NULL;
-}
-
-
-static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
- const u8 *hdr,
- const u8 *attr_start, size_t attr_len,
- const u8 *wrapped_data,
- u16 wrapped_data_len,
- enum dpp_status_error status)
-{
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- const u8 *r_nonce;
- u16 r_nonce_len;
-
- /* Authentication Confirm failure cases are expected to include
- * {R-nonce}k2 in the Wrapped Data attribute. */
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped) {
- dpp_auth_fail(auth, "Authentication failed");
- goto fail;
- }
- if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
- &r_nonce_len);
- if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
- goto fail;
- }
- if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
- wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
- r_nonce, r_nonce_len);
- wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
- auth->r_nonce, r_nonce_len);
- dpp_auth_fail(auth, "R-nonce mismatch");
- goto fail;
- }
-
- if (status == DPP_STATUS_NOT_COMPATIBLE)
- dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
- else if (status == DPP_STATUS_AUTH_FAILURE)
- dpp_auth_fail(auth, "Peer reported authentication failure)");
-
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
- return -1;
-}
-
-
-int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
- const u8 *attr_start, size_t attr_len)
-{
- const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
- u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
- i_auth_len;
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- u8 i_auth2[DPP_MAX_HASH_LEN];
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at Authentication Confirm");
- return -1;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (auth->initiator || !auth->own_bi || !auth->waiting_auth_conf) {
- wpa_printf(MSG_DEBUG,
- "DPP: initiator=%d own_bi=%d waiting_auth_conf=%d",
- auth->initiator, !!auth->own_bi,
- auth->waiting_auth_conf);
- dpp_auth_fail(auth, "Unexpected Authentication Confirm");
- return -1;
- }
-
- auth->waiting_auth_conf = 0;
-
- wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- dpp_auth_fail(auth,
- "Missing or invalid required Wrapped Data attribute");
- return -1;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
- wrapped_data, wrapped_data_len);
-
- attr_len = wrapped_data - 4 - attr_start;
-
- r_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
- &r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
- dpp_auth_fail(auth,
- "Missing or invalid required Responder Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
- r_bootstrap, r_bootstrap_len);
- if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0) {
- wpa_hexdump(MSG_DEBUG,
- "DPP: Expected Responder Bootstrapping Key Hash",
- auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
- dpp_auth_fail(auth,
- "Responder Bootstrapping Key Hash mismatch");
- return -1;
- }
-
- i_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
- &i_bootstrap_len);
- if (i_bootstrap) {
- if (i_bootstrap_len != SHA256_MAC_LEN) {
- dpp_auth_fail(auth,
- "Invalid Initiator Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP,
- "DPP: Initiator Bootstrapping Key Hash",
- i_bootstrap, i_bootstrap_len);
- if (!auth->peer_bi ||
- os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0) {
- dpp_auth_fail(auth,
- "Initiator Bootstrapping Key Hash mismatch");
- return -1;
- }
- } else if (auth->peer_bi) {
- /* Mutual authentication and peer did not include its
- * Bootstrapping Key Hash attribute. */
- dpp_auth_fail(auth,
- "Missing Initiator Bootstrapping Key Hash attribute");
- return -1;
- }
-
- status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
- &status_len);
- if (!status || status_len < 1) {
- dpp_auth_fail(auth,
- "Missing or invalid required DPP Status attribute");
- return -1;
- }
- wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
- if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
- status[0] == DPP_STATUS_AUTH_FAILURE)
- return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
- attr_len, wrapped_data,
- wrapped_data_len, status[0]);
-
- if (status[0] != DPP_STATUS_OK) {
- dpp_auth_fail(auth, "Authentication failed");
- return -1;
- }
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- return -1;
- if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
- &i_auth_len);
- if (!i_auth || i_auth_len != auth->curve->hash_len) {
- dpp_auth_fail(auth,
- "Missing or invalid Initiator Authenticating Tag");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
- i_auth, i_auth_len);
- /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
- if (dpp_gen_i_auth(auth, i_auth2) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
- i_auth2, i_auth_len);
- if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
- dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
- goto fail;
- }
-
- bin_clear_free(unwrapped, unwrapped_len);
- dpp_auth_success(auth);
- return 0;
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
- return -1;
-}
-
-
static int bin_str_eq(const char *val, size_t len, const char *cmp)
{
return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0;
@@ -4747,7 +1190,7 @@
wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
pos = os_strstr(cmd, " configurator=");
- if (pos) {
+ if (!auth->conf && pos) {
pos += 14;
auth->conf = dpp_configurator_get_id(auth->global, atoi(pos));
if (!auth->conf) {
@@ -4781,20 +1224,6 @@
}
-static void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key)
-{
- while (key) {
- struct dpp_asymmetric_key *next = key->next;
-
- EVP_PKEY_free(key->csign);
- str_clear_free(key->config_template);
- str_clear_free(key->connector_template);
- os_free(key);
- key = next;
- }
-}
-
-
void dpp_auth_deinit(struct dpp_authentication *auth)
{
unsigned int i;
@@ -4807,16 +1236,21 @@
dpp_configuration_free(auth->conf2_sta);
EVP_PKEY_free(auth->own_protocol_key);
EVP_PKEY_free(auth->peer_protocol_key);
+ EVP_PKEY_free(auth->reconfig_old_protocol_key);
wpabuf_free(auth->req_msg);
wpabuf_free(auth->resp_msg);
wpabuf_free(auth->conf_req);
+ wpabuf_free(auth->reconfig_req_msg);
+ wpabuf_free(auth->reconfig_resp_msg);
for (i = 0; i < auth->num_conf_obj; i++) {
struct dpp_config_obj *conf = &auth->conf_obj[i];
os_free(conf->connector);
wpabuf_free(conf->c_sign_key);
}
+#ifdef CONFIG_DPP2
dpp_free_asymmetric_key(auth->conf_key_pkg);
+#endif /* CONFIG_DPP2 */
wpabuf_free(auth->net_access_key);
dpp_bootstrap_info_free(auth->tmp_own_bi);
#ifdef CONFIG_TESTING_OPTIONS
@@ -4876,8 +1310,8 @@
}
-static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
- const char *kid, const struct dpp_curve_params *curve)
+int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+ const char *kid, const struct dpp_curve_params *curve)
{
struct wpabuf *pub;
const u8 *pos;
@@ -4948,20 +1382,10 @@
struct dpp_configuration *conf)
{
struct wpabuf *buf = NULL;
- char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+ char *signed_conn = NULL;
size_t tailroom;
const struct dpp_curve_params *curve;
- struct wpabuf *jws_prot_hdr;
- size_t signed1_len, signed2_len, signed3_len;
struct wpabuf *dppcon = NULL;
- unsigned char *signature = NULL;
- const unsigned char *p;
- size_t signature_len;
- EVP_MD_CTX *md_ctx = NULL;
- ECDSA_SIG *sig = NULL;
- char *dot = ".";
- const EVP_MD *sign_md;
- const BIGNUM *r, *s;
size_t extra_len = 1000;
int incl_legacy;
enum dpp_akm akm;
@@ -4973,16 +1397,6 @@
goto fail;
}
curve = auth->conf->curve;
- if (curve->hash_len == SHA256_MAC_LEN) {
- sign_md = EVP_sha256();
- } else if (curve->hash_len == SHA384_MAC_LEN) {
- sign_md = EVP_sha384();
- } else if (curve->hash_len == SHA512_MAC_LEN) {
- sign_md = EVP_sha512();
- } else {
- wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
- goto fail;
- }
akm = conf->akm;
if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
@@ -5030,7 +1444,8 @@
#ifdef CONFIG_TESTING_OPTIONS
skip_groups:
#endif /* CONFIG_TESTING_OPTIONS */
- if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
+ if (!auth->peer_protocol_key ||
+ dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
auth->curve) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
goto fail;
@@ -5055,79 +1470,14 @@
wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
(const char *) wpabuf_head(dppcon));
- jws_prot_hdr = wpabuf_alloc(100);
- if (!jws_prot_hdr)
- goto fail;
- json_start_object(jws_prot_hdr, NULL);
- json_add_string(jws_prot_hdr, "typ", "dppCon");
- json_value_sep(jws_prot_hdr);
- json_add_string(jws_prot_hdr, "kid", auth->conf->kid);
- json_value_sep(jws_prot_hdr);
- json_add_string(jws_prot_hdr, "alg", curve->jws_alg);
- json_end_object(jws_prot_hdr);
- signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
- wpabuf_len(jws_prot_hdr),
- &signed1_len);
- wpabuf_free(jws_prot_hdr);
- signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
- &signed2_len);
- if (!signed1 || !signed2)
- goto fail;
-
- md_ctx = EVP_MD_CTX_create();
- if (!md_ctx)
- goto fail;
-
- ERR_clear_error();
- if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
- auth->conf->csign) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
- EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
- EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- signature = os_malloc(signature_len);
- if (!signature)
- goto fail;
- if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
- signature, signature_len);
- /* Convert to raw coordinates r,s */
- p = signature;
- sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
- if (!sig)
- goto fail;
- ECDSA_SIG_get0(sig, &r, &s);
- if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
- dpp_bn2bin_pad(s, signature + curve->prime_len,
- curve->prime_len) < 0)
- goto fail;
- signature_len = 2 * curve->prime_len;
- wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
- signature, signature_len);
- signed3 = base64_url_encode(signature, signature_len, &signed3_len);
- if (!signed3)
+ signed_conn = dpp_sign_connector(auth->conf, dppcon);
+ if (!signed_conn)
goto fail;
incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm);
tailroom = 1000;
tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
- tailroom += signed1_len + signed2_len + signed3_len;
+ tailroom += os_strlen(signed_conn);
if (incl_legacy)
tailroom += 1000;
buf = dpp_build_conf_start(auth, conf, tailroom);
@@ -5146,11 +1496,7 @@
json_value_sep(buf);
}
wpabuf_put_str(buf, "\"signedConnector\":\"");
- wpabuf_put_str(buf, signed1);
- wpabuf_put_u8(buf, '.');
- wpabuf_put_str(buf, signed2);
- wpabuf_put_u8(buf, '.');
- wpabuf_put_str(buf, signed3);
+ wpabuf_put_str(buf, signed_conn);
wpabuf_put_str(buf, "\"");
json_value_sep(buf);
if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
@@ -5166,12 +1512,7 @@
wpabuf_head(buf), wpabuf_len(buf));
out:
- EVP_MD_CTX_destroy(md_ctx);
- ECDSA_SIG_free(sig);
- os_free(signed1);
- os_free(signed2);
- os_free(signed3);
- os_free(signature);
+ os_free(signed_conn);
wpabuf_free(dppcon);
return buf;
fail:
@@ -5252,488 +1593,6 @@
}
-#ifdef CONFIG_DPP2
-
-static struct wpabuf * dpp_build_conf_params(void)
-{
- struct wpabuf *buf;
- size_t len;
- /* TODO: proper template values */
- const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
- const char *connector_template = NULL;
-
- len = 100 + os_strlen(conf_template);
- if (connector_template)
- len += os_strlen(connector_template);
- buf = wpabuf_alloc(len);
- if (!buf)
- return NULL;
-
- /*
- * DPPConfigurationParameters ::= SEQUENCE {
- * configurationTemplate UTF8String,
- * connectorTemplate UTF8String OPTIONAL}
- */
-
- asn1_put_utf8string(buf, conf_template);
- if (connector_template)
- asn1_put_utf8string(buf, connector_template);
- return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf * dpp_build_attribute(void)
-{
- struct wpabuf *conf_params, *attr;
-
- /*
- * aa-DPPConfigurationParameters ATTRIBUTE ::=
- * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
- *
- * Attribute ::= SEQUENCE {
- * type OBJECT IDENTIFIER,
- * values SET SIZE(1..MAX) OF Type
- */
- conf_params = dpp_build_conf_params();
- conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
- ASN1_TAG_SET);
- if (!conf_params)
- return NULL;
-
- attr = wpabuf_alloc(100 + wpabuf_len(conf_params));
- if (!attr) {
- wpabuf_clear_free(conf_params);
- return NULL;
- }
-
- asn1_put_oid(attr, &asn1_dpp_config_params_oid);
- wpabuf_put_buf(attr, conf_params);
- wpabuf_clear_free(conf_params);
-
- return asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf * dpp_build_key_alg(const struct dpp_curve_params *curve)
-{
- const struct asn1_oid *oid;
- struct wpabuf *params, *res;
-
- switch (curve->ike_group) {
- case 19:
- oid = &asn1_prime256v1_oid;
- break;
- case 20:
- oid = &asn1_secp384r1_oid;
- break;
- case 21:
- oid = &asn1_secp521r1_oid;
- break;
- case 28:
- oid = &asn1_brainpoolP256r1_oid;
- break;
- case 29:
- oid = &asn1_brainpoolP384r1_oid;
- break;
- case 30:
- oid = &asn1_brainpoolP512r1_oid;
- break;
- default:
- return NULL;
- }
-
- params = wpabuf_alloc(20);
- if (!params)
- return NULL;
- asn1_put_oid(params, oid); /* namedCurve */
-
- res = asn1_build_alg_id(&asn1_ec_public_key_oid, params);
- wpabuf_free(params);
- return res;
-}
-
-
-static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
-{
- struct wpabuf *key = NULL, *attr, *alg, *priv_key = NULL;
- EC_KEY *eckey;
- unsigned char *der = NULL;
- int der_len;
-
- eckey = EVP_PKEY_get0_EC_KEY(auth->conf->csign);
- if (!eckey)
- return NULL;
-
- EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
- der_len = i2d_ECPrivateKey(eckey, &der);
- if (der_len > 0)
- priv_key = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
-
- alg = dpp_build_key_alg(auth->conf->curve);
-
- /* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
- attr = dpp_build_attribute();
- attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
- if (!priv_key || !attr || !alg)
- goto fail;
-
- /*
- * OneAsymmetricKey ::= SEQUENCE {
- * version Version,
- * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
- * privateKey PrivateKey,
- * attributes [0] Attributes OPTIONAL,
- * ...,
- * [[2: publicKey [1] BIT STRING OPTIONAL ]],
- * ...
- * }
- */
-
- key = wpabuf_alloc(100 + wpabuf_len(alg) + wpabuf_len(priv_key) +
- wpabuf_len(attr));
- if (!key)
- goto fail;
-
- asn1_put_integer(key, 1); /* version = v2(1) */
-
- /* PrivateKeyAlgorithmIdentifier */
- wpabuf_put_buf(key, alg);
-
- /* PrivateKey ::= OCTET STRING */
- asn1_put_octet_string(key, priv_key);
-
- /* [0] Attributes OPTIONAL */
- asn1_put_hdr(key, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0, wpabuf_len(attr));
- wpabuf_put_buf(key, attr);
-
-fail:
- wpabuf_clear_free(attr);
- wpabuf_clear_free(priv_key);
- wpabuf_free(alg);
-
- /*
- * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
- *
- * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
- *
- * OneAsymmetricKey ::= SEQUENCE
- */
- return asn1_encaps(asn1_encaps(key,
- ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE),
- ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf * dpp_build_pbkdf2_alg_id(const struct wpabuf *salt,
- size_t hash_len)
-{
- struct wpabuf *params = NULL, *buf = NULL, *prf = NULL;
- const struct asn1_oid *oid;
-
- /*
- * PBKDF2-params ::= SEQUENCE {
- * salt CHOICE {
- * specified OCTET STRING,
- * otherSource AlgorithmIdentifier}
- * iterationCount INTEGER (1..MAX),
- * keyLength INTEGER (1..MAX),
- * prf AlgorithmIdentifier}
- *
- * salt is an 64 octet value, iterationCount is 1000, keyLength is based
- * on Configurator signing key length, prf is
- * id-hmacWithSHA{256,384,512} based on Configurator signing key.
- */
-
- if (hash_len == 32)
- oid = &asn1_pbkdf2_hmac_sha256_oid;
- else if (hash_len == 48)
- oid = &asn1_pbkdf2_hmac_sha384_oid;
- else if (hash_len == 64)
- oid = &asn1_pbkdf2_hmac_sha512_oid;
- else
- goto fail;
- prf = asn1_build_alg_id(oid, NULL);
- if (!prf)
- goto fail;
- params = wpabuf_alloc(100 + wpabuf_len(salt) + wpabuf_len(prf));
- if (!params)
- goto fail;
- asn1_put_octet_string(params, salt); /* salt.specified */
- asn1_put_integer(params, 1000); /* iterationCount */
- asn1_put_integer(params, hash_len); /* keyLength */
- wpabuf_put_buf(params, prf);
- params = asn1_encaps(params, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
- if (!params)
- goto fail;
- buf = asn1_build_alg_id(&asn1_pbkdf2_oid, params);
-fail:
- wpabuf_free(params);
- wpabuf_free(prf);
- return buf;
-}
-
-
-static struct wpabuf *
-dpp_build_pw_recipient_info(struct dpp_authentication *auth, size_t hash_len,
- const struct wpabuf *cont_enc_key)
-{
- struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
- *key_enc_alg = NULL, *salt;
- u8 kek[DPP_MAX_HASH_LEN];
- const u8 *key;
- size_t key_len;
-
- salt = wpabuf_alloc(64);
- if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
- goto fail;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
-
- /* TODO: For initial testing, use ke as the key. Replace this with a
- * new key once that has been defined. */
- key = auth->ke;
- key_len = auth->curve->hash_len;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
-
- if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
- kek, hash_len)) {
- wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
- goto fail;
- }
- wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
- kek, hash_len);
-
- enc_key = wpabuf_alloc(hash_len + AES_BLOCK_SIZE);
- if (!enc_key ||
- aes_siv_encrypt(kek, hash_len, wpabuf_head(cont_enc_key),
- wpabuf_len(cont_enc_key), 0, NULL, NULL,
- wpabuf_put(enc_key, hash_len + AES_BLOCK_SIZE)) < 0)
- goto fail;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: encryptedKey", enc_key);
-
- /*
- * PasswordRecipientInfo ::= SEQUENCE {
- * version CMSVersion,
- * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
- * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
- * encryptedKey EncryptedKey}
- *
- * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
- * parameters contains PBKDF2-params SEQUENCE.
- */
-
- key_der_alg = dpp_build_pbkdf2_alg_id(salt, hash_len);
- key_enc_alg = asn1_build_alg_id(&asn1_aes_siv_cmac_aead_256_oid, NULL);
- if (!key_der_alg || !key_enc_alg)
- goto fail;
- pwri = wpabuf_alloc(100 + wpabuf_len(key_der_alg) +
- wpabuf_len(key_enc_alg) + wpabuf_len(enc_key));
- if (!pwri)
- goto fail;
-
- /* version = 0 */
- asn1_put_integer(pwri, 0);
-
- /* [0] KeyDerivationAlgorithmIdentifier */
- asn1_put_hdr(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0,
- wpabuf_len(key_der_alg));
- wpabuf_put_buf(pwri, key_der_alg);
-
- /* KeyEncryptionAlgorithmIdentifier */
- wpabuf_put_buf(pwri, key_enc_alg);
-
- /* EncryptedKey ::= OCTET STRING */
- asn1_put_octet_string(pwri, enc_key);
-
-fail:
- wpabuf_clear_free(key_der_alg);
- wpabuf_free(key_enc_alg);
- wpabuf_free(enc_key);
- wpabuf_free(salt);
- forced_memzero(kek, sizeof(kek));
- return asn1_encaps(pwri, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf *
-dpp_build_recipient_info(struct dpp_authentication *auth, size_t hash_len,
- const struct wpabuf *cont_enc_key)
-{
- struct wpabuf *pwri;
-
- /*
- * RecipientInfo ::= CHOICE {
- * ktri KeyTransRecipientInfo,
- * kari [1] KeyAgreeRecipientInfo,
- * kekri [2] KEKRecipientInfo,
- * pwri [3] PasswordRecipientInfo,
- * ori [4] OtherRecipientInfo}
- *
- * Shall always use the pwri CHOICE.
- */
-
- pwri = dpp_build_pw_recipient_info(auth, hash_len, cont_enc_key);
- return asn1_encaps(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
-}
-
-
-static struct wpabuf *
-dpp_build_enc_cont_info(struct dpp_authentication *auth, size_t hash_len,
- const struct wpabuf *cont_enc_key)
-{
- struct wpabuf *key_pkg, *enc_cont_info = NULL, *enc_cont = NULL,
- *enc_alg;
- const struct asn1_oid *oid;
- size_t enc_cont_len;
-
- /*
- * EncryptedContentInfo ::= SEQUENCE {
- * contentType ContentType,
- * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
- * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
- */
-
- if (hash_len == 32)
- oid = &asn1_aes_siv_cmac_aead_256_oid;
- else if (hash_len == 48)
- oid = &asn1_aes_siv_cmac_aead_384_oid;
- else if (hash_len == 64)
- oid = &asn1_aes_siv_cmac_aead_512_oid;
- else
- return NULL;
-
- key_pkg = dpp_build_key_pkg(auth);
- enc_alg = asn1_build_alg_id(oid, NULL);
- if (!key_pkg || !enc_alg)
- goto fail;
-
- wpa_hexdump_buf_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
- key_pkg);
-
- enc_cont_len = wpabuf_len(key_pkg) + AES_BLOCK_SIZE;
- enc_cont = wpabuf_alloc(enc_cont_len);
- if (!enc_cont ||
- aes_siv_encrypt(wpabuf_head(cont_enc_key), wpabuf_len(cont_enc_key),
- wpabuf_head(key_pkg), wpabuf_len(key_pkg),
- 0, NULL, NULL,
- wpabuf_put(enc_cont, enc_cont_len)) < 0)
- goto fail;
-
- enc_cont_info = wpabuf_alloc(100 + wpabuf_len(enc_alg) +
- wpabuf_len(enc_cont));
- if (!enc_cont_info)
- goto fail;
-
- /* ContentType ::= OBJECT IDENTIFIER */
- asn1_put_oid(enc_cont_info, &asn1_dpp_asymmetric_key_package_oid);
-
- /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
- wpabuf_put_buf(enc_cont_info, enc_alg);
-
- /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
- * EncryptedContent ::= OCTET STRING */
- asn1_put_hdr(enc_cont_info, ASN1_CLASS_CONTEXT_SPECIFIC, 0, 0,
- wpabuf_len(enc_cont));
- wpabuf_put_buf(enc_cont_info, enc_cont);
-
-fail:
- wpabuf_clear_free(key_pkg);
- wpabuf_free(enc_cont);
- wpabuf_free(enc_alg);
- return enc_cont_info;
-}
-
-
-static struct wpabuf * dpp_gen_random(size_t len)
-{
- struct wpabuf *key;
-
- key = wpabuf_alloc(len);
- if (!key || os_get_random(wpabuf_put(key, len), len) < 0) {
- wpabuf_free(key);
- key = NULL;
- }
- wpa_hexdump_buf_key(MSG_DEBUG, "DPP: content-encryption key", key);
- return key;
-}
-
-
-static struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth)
-{
- struct wpabuf *env = NULL;
- struct wpabuf *recipient_info = NULL, *enc_cont_info = NULL;
- struct wpabuf *cont_enc_key = NULL;
- size_t hash_len;
-
- if (!auth->conf) {
- wpa_printf(MSG_DEBUG,
- "DPP: No Configurator instance selected for the session - cannot build DPPEnvelopedData");
- return NULL;
- }
-
- if (!auth->provision_configurator) {
- wpa_printf(MSG_DEBUG,
- "DPP: Configurator provisioning not allowed");
- return NULL;
- }
-
- wpa_printf(MSG_DEBUG, "DPP: Building DPPEnvelopedData");
-
- hash_len = auth->conf->curve->hash_len;
- cont_enc_key = dpp_gen_random(hash_len);
- if (!cont_enc_key)
- goto fail;
- recipient_info = dpp_build_recipient_info(auth, hash_len, cont_enc_key);
- enc_cont_info = dpp_build_enc_cont_info(auth, hash_len, cont_enc_key);
- if (!recipient_info || !enc_cont_info)
- goto fail;
-
- env = wpabuf_alloc(wpabuf_len(recipient_info) +
- wpabuf_len(enc_cont_info) +
- 100);
- if (!env)
- goto fail;
-
- /*
- * DPPEnvelopedData ::= EnvelopedData
- *
- * EnvelopedData ::= SEQUENCE {
- * version CMSVersion,
- * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
- * recipientInfos RecipientInfos,
- * encryptedContentInfo EncryptedContentInfo,
- * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
- *
- * For DPP, version is 3, both originatorInfo and
- * unprotectedAttrs are omitted, and recipientInfos contains a single
- * RecipientInfo.
- */
-
- /* EnvelopedData.version = 3 */
- asn1_put_integer(env, 3);
-
- /* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo */
- asn1_put_set(env, recipient_info);
-
- /* EncryptedContentInfo ::= SEQUENCE */
- asn1_put_sequence(env, enc_cont_info);
-
- env = asn1_encaps(env, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
- wpa_hexdump_buf(MSG_MSGDUMP, "DPP: DPPEnvelopedData", env);
-out:
- wpabuf_clear_free(cont_enc_key);
- wpabuf_clear_free(recipient_info);
- wpabuf_free(enc_cont_info);
- return env;
-fail:
- wpabuf_free(env);
- env = NULL;
- goto out;
-}
-
-#endif /* CONFIG_DPP2 */
-
-
static struct wpabuf *
dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
u16 e_nonce_len, enum dpp_netrole netrole)
@@ -6022,12 +1881,19 @@
}
token = json_get_member(root, "mudurl");
- if (token && token->type == JSON_STRING)
+ if (token && token->type == JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: mudurl = '%s'", token->string);
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_MUD_URL "%s",
+ token->string);
+ }
token = json_get_member(root, "bandSupport");
auth->band_list_size = 0;
if (token && token->type == JSON_ARRAY) {
+ int *opclass = NULL;
+ char txt[200], *pos, *end;
+ int i, res;
+
memset(auth->band_list, 0, sizeof(auth->band_list));
wpa_printf(MSG_DEBUG, "DPP: bandSupport");
token = token->child;
@@ -6042,9 +1908,26 @@
wpa_printf(MSG_DEBUG,
"DPP: Supported global operating class: %d",
token->number);
+ int_array_add_unique(&opclass, token->number);
}
token = token->sibling;
}
+
+ txt[0] = '\0';
+ pos = txt;
+ end = txt + sizeof(txt);
+ for (i = 0; opclass && opclass[i]; i++) {
+ res = os_snprintf(pos, end - pos, "%s%d",
+ pos == txt ? "" : ",", opclass[i]);
+ if (os_snprintf_error(end - pos, res)) {
+ *pos = '\0';
+ break;
+ }
+ pos += res;
+ }
+ os_free(opclass);
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_BAND_SUPPORT "%s",
+ txt);
}
resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole);
@@ -6056,86 +1939,6 @@
}
-static struct wpabuf *
-dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
- const u8 *prot_hdr, u16 prot_hdr_len,
- const EVP_MD **ret_md)
-{
- struct json_token *root, *token;
- struct wpabuf *kid = NULL;
-
- root = json_parse((const char *) prot_hdr, prot_hdr_len);
- if (!root) {
- wpa_printf(MSG_DEBUG,
- "DPP: JSON parsing failed for JWS Protected Header");
- goto fail;
- }
-
- if (root->type != JSON_OBJECT) {
- wpa_printf(MSG_DEBUG,
- "DPP: JWS Protected Header root is not an object");
- goto fail;
- }
-
- token = json_get_member(root, "typ");
- if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
- token->string);
- if (os_strcmp(token->string, "dppCon") != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported JWS Protected Header typ=%s",
- token->string);
- goto fail;
- }
-
- token = json_get_member(root, "alg");
- if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
- token->string);
- if (os_strcmp(token->string, curve->jws_alg) != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
- token->string, curve->jws_alg);
- goto fail;
- }
- if (os_strcmp(token->string, "ES256") == 0 ||
- os_strcmp(token->string, "BS256") == 0)
- *ret_md = EVP_sha256();
- else if (os_strcmp(token->string, "ES384") == 0 ||
- os_strcmp(token->string, "BS384") == 0)
- *ret_md = EVP_sha384();
- else if (os_strcmp(token->string, "ES512") == 0 ||
- os_strcmp(token->string, "BS512") == 0)
- *ret_md = EVP_sha512();
- else
- *ret_md = NULL;
- if (!*ret_md) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported JWS Protected Header alg=%s",
- token->string);
- goto fail;
- }
-
- kid = json_get_member_base64url(root, "kid");
- if (!kid) {
- wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
- goto fail;
- }
- wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
- kid);
-
-fail:
- json_free(root);
- return kid;
-}
-
-
static int dpp_parse_cred_legacy(struct dpp_config_obj *conf,
struct json_token *cred)
{
@@ -6183,8 +1986,8 @@
}
-static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
- const struct dpp_curve_params **key_curve)
+EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+ const struct dpp_curve_params **key_curve)
{
struct json_token *token;
const struct dpp_curve_params *curve;
@@ -6438,38 +2241,6 @@
}
-static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
-{
- struct wpabuf *uncomp;
- int res;
- u8 hash[SHA256_MAC_LEN];
- const u8 *addr[1];
- size_t len[1];
-
- if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
- return -1;
- uncomp = dpp_get_pubkey_point(pub, 1);
- if (!uncomp)
- return -1;
- addr[0] = wpabuf_head(uncomp);
- len[0] = wpabuf_len(uncomp);
- wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
- addr[0], len[0]);
- res = sha256_vector(1, addr, len, hash);
- wpabuf_free(uncomp);
- if (res < 0)
- return -1;
- if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Received hash value does not match calculated public key hash value");
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
- hash, SHA256_MAC_LEN);
- return -1;
- }
- return 0;
-}
-
-
static void dpp_copy_csign(struct dpp_config_obj *conf, EVP_PKEY *csign)
{
unsigned char *der = NULL;
@@ -6490,8 +2261,15 @@
unsigned char *der = NULL;
int der_len;
EC_KEY *eckey;
+ EVP_PKEY *own_key;
- eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+ own_key = auth->own_protocol_key;
+#ifdef CONFIG_DPP2
+ if (auth->reconfig_connector_key == DPP_CONFIG_REUSEKEY &&
+ auth->reconfig_old_protocol_key)
+ own_key = auth->reconfig_old_protocol_key;
+#endif /* CONFIG_DPP2 */
+ eckey = EVP_PKEY_get1_EC_KEY(own_key);
if (!eckey)
return;
@@ -6507,174 +2285,6 @@
}
-struct dpp_signed_connector_info {
- unsigned char *payload;
- size_t payload_len;
-};
-
-static enum dpp_status_error
-dpp_process_signed_connector(struct dpp_signed_connector_info *info,
- EVP_PKEY *csign_pub, const char *connector)
-{
- enum dpp_status_error ret = 255;
- const char *pos, *end, *signed_start, *signed_end;
- struct wpabuf *kid = NULL;
- unsigned char *prot_hdr = NULL, *signature = NULL;
- size_t prot_hdr_len = 0, signature_len = 0;
- const EVP_MD *sign_md = NULL;
- unsigned char *der = NULL;
- int der_len;
- int res;
- EVP_MD_CTX *md_ctx = NULL;
- ECDSA_SIG *sig = NULL;
- BIGNUM *r = NULL, *s = NULL;
- const struct dpp_curve_params *curve;
- EC_KEY *eckey;
- const EC_GROUP *group;
- int nid;
-
- eckey = EVP_PKEY_get1_EC_KEY(csign_pub);
- if (!eckey)
- goto fail;
- group = EC_KEY_get0_group(eckey);
- if (!group)
- goto fail;
- nid = EC_GROUP_get_curve_name(group);
- curve = dpp_get_curve_nid(nid);
- if (!curve)
- goto fail;
- wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
- os_memset(info, 0, sizeof(*info));
-
- signed_start = pos = connector;
- end = os_strchr(pos, '.');
- if (!end) {
- wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
- if (!prot_hdr) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to base64url decode signedConnector JWS Protected Header");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- wpa_hexdump_ascii(MSG_DEBUG,
- "DPP: signedConnector - JWS Protected Header",
- prot_hdr, prot_hdr_len);
- kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
- if (!kid) {
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- if (wpabuf_len(kid) != SHA256_MAC_LEN) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
- (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
-
- pos = end + 1;
- end = os_strchr(pos, '.');
- if (!end) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing dot(2) in signedConnector");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- signed_end = end - 1;
- info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
- if (!info->payload) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to base64url decode signedConnector JWS Payload");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- wpa_hexdump_ascii(MSG_DEBUG,
- "DPP: signedConnector - JWS Payload",
- info->payload, info->payload_len);
- pos = end + 1;
- signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
- if (!signature) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to base64url decode signedConnector signature");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
- signature, signature_len);
-
- if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
- ret = DPP_STATUS_NO_MATCH;
- goto fail;
- }
-
- if (signature_len & 0x01) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected signedConnector signature length (%d)",
- (int) signature_len);
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
-
- /* JWS Signature encodes the signature (r,s) as two octet strings. Need
- * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
- r = BN_bin2bn(signature, signature_len / 2, NULL);
- s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
- sig = ECDSA_SIG_new();
- if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
- goto fail;
- r = NULL;
- s = NULL;
-
- der_len = i2d_ECDSA_SIG(sig, &der);
- if (der_len <= 0) {
- wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
- md_ctx = EVP_MD_CTX_create();
- if (!md_ctx)
- goto fail;
-
- ERR_clear_error();
- if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
- signed_end - signed_start + 1) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
- if (res != 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
- res, ERR_error_string(ERR_get_error(), NULL));
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
-
- ret = DPP_STATUS_OK;
-fail:
- EC_KEY_free(eckey);
- EVP_MD_CTX_destroy(md_ctx);
- os_free(prot_hdr);
- wpabuf_free(kid);
- os_free(signature);
- ECDSA_SIG_free(sig);
- BN_free(r);
- BN_free(s);
- OPENSSL_free(der);
- return ret;
-}
-
-
static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
struct dpp_config_obj *conf,
struct json_token *cred)
@@ -6974,709 +2584,6 @@
}
-#ifdef CONFIG_DPP2
-
-struct dpp_enveloped_data {
- const u8 *enc_cont;
- size_t enc_cont_len;
- const u8 *enc_key;
- size_t enc_key_len;
- const u8 *salt;
- size_t pbkdf2_key_len;
- size_t prf_hash_len;
-};
-
-
-static int dpp_parse_recipient_infos(const u8 *pos, size_t len,
- struct dpp_enveloped_data *data)
-{
- struct asn1_hdr hdr;
- const u8 *end = pos + len;
- const u8 *next, *e_end;
- struct asn1_oid oid;
- int val;
- const u8 *params;
- size_t params_len;
-
- wpa_hexdump(MSG_MSGDUMP, "DPP: RecipientInfos", pos, len);
-
- /*
- * RecipientInfo ::= CHOICE {
- * ktri KeyTransRecipientInfo,
- * kari [1] KeyAgreeRecipientInfo,
- * kekri [2] KEKRecipientInfo,
- * pwri [3] PasswordRecipientInfo,
- * ori [4] OtherRecipientInfo}
- *
- * Shall always use the pwri CHOICE.
- */
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 3) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected CHOICE [3] (pwri) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: PasswordRecipientInfo",
- hdr.payload, hdr.length);
- pos = hdr.payload;
- end = pos + hdr.length;
-
- /*
- * PasswordRecipientInfo ::= SEQUENCE {
- * version CMSVersion,
- * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
- * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
- * encryptedKey EncryptedKey}
- *
- * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
- * parameters contains PBKDF2-params SEQUENCE.
- */
-
- if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
- return -1;
- pos = hdr.payload;
-
- if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
- return -1;
- if (val != 0) {
- wpa_printf(MSG_DEBUG, "DPP: pwri.version != 0");
- return -1;
- }
-
- wpa_hexdump(MSG_MSGDUMP, "DPP: Remaining PasswordRecipientInfo after version",
- pos, end - pos);
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected keyDerivationAlgorithm [0] - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- pos = hdr.payload;
- e_end = pos + hdr.length;
-
- /* KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier */
- if (asn1_get_alg_id(pos, e_end - pos, &oid, ¶ms, ¶ms_len,
- &next) < 0)
- return -1;
- if (!asn1_oid_equal(&oid, &asn1_pbkdf2_oid)) {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected KeyDerivationAlgorithmIdentifier %s",
- buf);
- return -1;
- }
-
- /*
- * PBKDF2-params ::= SEQUENCE {
- * salt CHOICE {
- * specified OCTET STRING,
- * otherSource AlgorithmIdentifier}
- * iterationCount INTEGER (1..MAX),
- * keyLength INTEGER (1..MAX),
- * prf AlgorithmIdentifier}
- *
- * salt is an 64 octet value, iterationCount is 1000, keyLength is based
- * on Configurator signing key length, prf is
- * id-hmacWithSHA{256,384,512} based on Configurator signing key.
- */
- if (!params ||
- asn1_get_sequence(params, params_len, &hdr, &e_end) < 0)
- return -1;
- pos = hdr.payload;
-
- if (asn1_get_next(pos, e_end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_OCTETSTRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected OCTETSTRING (salt.specified) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: salt.specified",
- hdr.payload, hdr.length);
- if (hdr.length != 64) {
- wpa_printf(MSG_DEBUG, "DPP: Unexpected salt length %u",
- hdr.length);
- return -1;
- }
- data->salt = hdr.payload;
- pos = hdr.payload + hdr.length;
-
- if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
- return -1;
- if (val != 1000) {
- wpa_printf(MSG_DEBUG, "DPP: Unexpected iterationCount %d", val);
- return -1;
- }
-
- if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
- return -1;
- if (val != 32 && val != 48 && val != 64) {
- wpa_printf(MSG_DEBUG, "DPP: Unexpected keyLength %d", val);
- return -1;
- }
- data->pbkdf2_key_len = val;
-
- if (asn1_get_sequence(pos, e_end - pos, &hdr, NULL) < 0 ||
- asn1_get_oid(hdr.payload, hdr.length, &oid, &pos) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Could not parse prf");
- return -1;
- }
- if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha256_oid)) {
- data->prf_hash_len = 32;
- } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha384_oid)) {
- data->prf_hash_len = 48;
- } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha512_oid)) {
- data->prf_hash_len = 64;
- } else {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG, "DPP: Unexpected PBKDF2-params.prf %s",
- buf);
- return -1;
- }
-
- pos = next;
-
- /* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier
- *
- * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
- *
- * id-alg-AES-SIV-CMAC-aed-256, id-alg-AES-SIV-CMAC-aed-384, or
- * id-alg-AES-SIV-CMAC-aed-512. */
- if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
- return -1;
- if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
- !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
- !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected KeyEncryptionAlgorithmIdentifier %s",
- buf);
- return -1;
- }
-
- /*
- * encryptedKey EncryptedKey
- *
- * EncryptedKey ::= OCTET STRING
- */
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_OCTETSTRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected OCTETSTRING (pwri.encryptedKey) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: pwri.encryptedKey",
- hdr.payload, hdr.length);
- data->enc_key = hdr.payload;
- data->enc_key_len = hdr.length;
-
- return 0;
-}
-
-
-static int dpp_parse_encrypted_content_info(const u8 *pos, const u8 *end,
- struct dpp_enveloped_data *data)
-{
- struct asn1_hdr hdr;
- struct asn1_oid oid;
-
- /*
- * EncryptedContentInfo ::= SEQUENCE {
- * contentType ContentType,
- * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
- * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
- */
- if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
- return -1;
- wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContentInfo",
- hdr.payload, hdr.length);
- if (pos < end) {
- wpa_hexdump(MSG_DEBUG,
- "DPP: Unexpected extra data after EncryptedContentInfo",
- pos, end - pos);
- return -1;
- }
-
- end = pos;
- pos = hdr.payload;
-
- /* ContentType ::= OBJECT IDENTIFIER */
- if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Could not parse ContentType");
- return -1;
- }
- if (!asn1_oid_equal(&oid, &asn1_dpp_asymmetric_key_package_oid)) {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG, "DPP: Unexpected ContentType %s", buf);
- return -1;
- }
-
- /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
- if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
- return -1;
- if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
- !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
- !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected ContentEncryptionAlgorithmIdentifier %s",
- buf);
- return -1;
- }
- /* ignore optional parameters */
-
- /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
- * EncryptedContent ::= OCTET STRING */
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected [0] IMPLICIT (EncryptedContent) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContent",
- hdr.payload, hdr.length);
- data->enc_cont = hdr.payload;
- data->enc_cont_len = hdr.length;
- return 0;
-}
-
-
-static int dpp_parse_enveloped_data(const u8 *env_data, size_t env_data_len,
- struct dpp_enveloped_data *data)
-{
- struct asn1_hdr hdr;
- const u8 *pos, *end;
- int val;
-
- os_memset(data, 0, sizeof(*data));
-
- /*
- * DPPEnvelopedData ::= EnvelopedData
- *
- * EnvelopedData ::= SEQUENCE {
- * version CMSVersion,
- * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
- * recipientInfos RecipientInfos,
- * encryptedContentInfo EncryptedContentInfo,
- * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
- *
- * CMSVersion ::= INTEGER
- *
- * RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
- *
- * For DPP, version is 3, both originatorInfo and
- * unprotectedAttrs are omitted, and recipientInfos contains a single
- * RecipientInfo.
- */
- if (asn1_get_sequence(env_data, env_data_len, &hdr, &end) < 0)
- return -1;
- pos = hdr.payload;
- if (end < env_data + env_data_len) {
- wpa_hexdump(MSG_DEBUG,
- "DPP: Unexpected extra data after DPPEnvelopedData",
- end, env_data + env_data_len - end);
- return -1;
- }
-
- if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
- return -1;
- if (val != 3) {
- wpa_printf(MSG_DEBUG, "DPP: EnvelopedData.version != 3");
- return -1;
- }
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected SET (RecipientInfos) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
-
- if (dpp_parse_recipient_infos(hdr.payload, hdr.length, data) < 0)
- return -1;
- return dpp_parse_encrypted_content_info(hdr.payload + hdr.length, end,
- data);
-}
-
-
-static struct dpp_asymmetric_key *
-dpp_parse_one_asymmetric_key(const u8 *buf, size_t len)
-{
- struct asn1_hdr hdr;
- const u8 *pos = buf, *end = buf + len, *next;
- int val;
- const u8 *params;
- size_t params_len;
- struct asn1_oid oid;
- char txt[80];
- struct dpp_asymmetric_key *key;
- EC_KEY *eckey;
-
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: OneAsymmetricKey", buf, len);
-
- key = os_zalloc(sizeof(*key));
- if (!key)
- return NULL;
-
- /*
- * OneAsymmetricKey ::= SEQUENCE {
- * version Version,
- * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
- * privateKey PrivateKey,
- * attributes [0] Attributes OPTIONAL,
- * ...,
- * [[2: publicKey [1] BIT STRING OPTIONAL ]],
- * ...
- * }
- */
- if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
- goto fail;
- pos = hdr.payload;
-
- /* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
- if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
- goto fail;
- if (val != 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
- val);
- goto fail;
- }
-
- /* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier */
- if (asn1_get_alg_id(pos, end - pos, &oid, ¶ms, ¶ms_len,
- &pos) < 0)
- goto fail;
- if (!asn1_oid_equal(&oid, &asn1_ec_public_key_oid)) {
- asn1_oid_to_str(&oid, txt, sizeof(txt));
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported PrivateKeyAlgorithmIdentifier %s",
- txt);
- goto fail;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: PrivateKeyAlgorithmIdentifier params",
- params, params_len);
- /*
- * ECParameters ::= CHOICE {
- * namedCurve OBJECT IDENTIFIER
- * -- implicitCurve NULL
- * -- specifiedCurve SpecifiedECDomain}
- */
- if (!params || asn1_get_oid(params, params_len, &oid, &next) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not parse ECParameters.namedCurve");
- goto fail;
- }
- asn1_oid_to_str(&oid, txt, sizeof(txt));
- wpa_printf(MSG_MSGDUMP, "DPP: namedCurve %s", txt);
- /* Assume the curve is identified within ECPrivateKey, so that this
- * separate indication is not really needed. */
-
- /*
- * PrivateKey ::= OCTET STRING
- * (Contains DER encoding of ECPrivateKey)
- */
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_OCTETSTRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: PrivateKey",
- hdr.payload, hdr.length);
- pos = hdr.payload + hdr.length;
- eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
- if (!eckey) {
- wpa_printf(MSG_INFO,
- "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- key->csign = EVP_PKEY_new();
- if (!key->csign || EVP_PKEY_assign_EC_KEY(key->csign, eckey) != 1) {
- EC_KEY_free(eckey);
- goto fail;
- }
- if (wpa_debug_show_keys)
- dpp_debug_print_key("DPP: Received c-sign-key", key->csign);
-
- /*
- * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
- *
- * Exactly one instance of type Attribute in OneAsymmetricKey.
- */
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected [0] Attributes - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: Attributes",
- hdr.payload, hdr.length);
- if (hdr.payload + hdr.length < end) {
- wpa_hexdump_key(MSG_MSGDUMP,
- "DPP: Ignore additional data at the end of OneAsymmetricKey",
- hdr.payload + hdr.length,
- end - (hdr.payload + hdr.length));
- }
- pos = hdr.payload;
- end = hdr.payload + hdr.length;
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected SET (Attributes) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- if (hdr.payload + hdr.length < end) {
- wpa_hexdump_key(MSG_MSGDUMP,
- "DPP: Ignore additional data at the end of OneAsymmetricKey (after SET)",
- hdr.payload + hdr.length,
- end - (hdr.payload + hdr.length));
- }
- pos = hdr.payload;
- end = hdr.payload + hdr.length;
-
- /*
- * OneAsymmetricKeyAttributes ATTRIBUTE ::= {
- * aa-DPPConfigurationParameters,
- * ... -- For local profiles
- * }
- *
- * aa-DPPConfigurationParameters ATTRIBUTE ::=
- * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
- *
- * Attribute ::= SEQUENCE {
- * type OBJECT IDENTIFIER,
- * values SET SIZE(1..MAX) OF Type
- *
- * Exactly one instance of ATTRIBUTE in attrValues.
- */
- if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
- goto fail;
- if (pos < end) {
- wpa_hexdump_key(MSG_MSGDUMP,
- "DPP: Ignore additional data at the end of ATTRIBUTE",
- pos, end - pos);
- }
- end = pos;
- pos = hdr.payload;
-
- if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0)
- goto fail;
- if (!asn1_oid_equal(&oid, &asn1_dpp_config_params_oid)) {
- asn1_oid_to_str(&oid, txt, sizeof(txt));
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected Attribute identifier %s", txt);
- goto fail;
- }
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected SET (Attribute) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- pos = hdr.payload;
- end = hdr.payload + hdr.length;
-
- /*
- * DPPConfigurationParameters ::= SEQUENCE {
- * configurationTemplate UTF8String,
- * connectorTemplate UTF8String OPTIONAL}
- */
-
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPConfigurationParameters",
- pos, end - pos);
- if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
- goto fail;
- if (pos < end) {
- wpa_hexdump_key(MSG_MSGDUMP,
- "DPP: Ignore additional data after DPPConfigurationParameters",
- pos, end - pos);
- }
- end = pos;
- pos = hdr.payload;
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_UTF8STRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected UTF8STRING (configurationTemplate) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: configurationTemplate",
- hdr.payload, hdr.length);
- key->config_template = os_zalloc(hdr.length + 1);
- if (!key->config_template)
- goto fail;
- os_memcpy(key->config_template, hdr.payload, hdr.length);
-
- pos = hdr.payload + hdr.length;
-
- if (pos < end) {
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_UTF8STRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected UTF8STRING (connectorTemplate) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: connectorTemplate",
- hdr.payload, hdr.length);
- key->connector_template = os_zalloc(hdr.length + 1);
- if (!key->connector_template)
- goto fail;
- os_memcpy(key->connector_template, hdr.payload, hdr.length);
- }
-
- return key;
-fail:
- wpa_printf(MSG_DEBUG, "DPP: Failed to parse OneAsymmetricKey");
- dpp_free_asymmetric_key(key);
- return NULL;
-}
-
-
-static struct dpp_asymmetric_key *
-dpp_parse_dpp_asymmetric_key_package(const u8 *key_pkg, size_t key_pkg_len)
-{
- struct asn1_hdr hdr;
- const u8 *pos = key_pkg, *end = key_pkg + key_pkg_len;
- struct dpp_asymmetric_key *first = NULL, *last = NULL, *key;
-
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
- key_pkg, key_pkg_len);
-
- /*
- * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
- *
- * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
- */
- while (pos < end) {
- if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0 ||
- !(key = dpp_parse_one_asymmetric_key(hdr.payload,
- hdr.length))) {
- dpp_free_asymmetric_key(first);
- return NULL;
- }
- if (!last) {
- first = last = key;
- } else {
- last->next = key;
- last = key;
- }
- }
-
- return first;
-}
-
-
-static int dpp_conf_resp_env_data(struct dpp_authentication *auth,
- const u8 *env_data, size_t env_data_len)
-{
- const u8 *key;
- size_t key_len;
- u8 kek[DPP_MAX_HASH_LEN];
- u8 cont_encr_key[DPP_MAX_HASH_LEN];
- size_t cont_encr_key_len;
- int res;
- u8 *key_pkg;
- size_t key_pkg_len;
- struct dpp_enveloped_data data;
- struct dpp_asymmetric_key *keys;
-
- wpa_hexdump(MSG_DEBUG, "DPP: DPPEnvelopedData", env_data, env_data_len);
-
- if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
- return -1;
-
- /* TODO: For initial testing, use ke as the key. Replace this with a
- * new key once that has been defined. */
- key = auth->ke;
- key_len = auth->curve->hash_len;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
-
- if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
- kek, data.pbkdf2_key_len)) {
- wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
- return -1;
- }
- wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
- kek, data.pbkdf2_key_len);
-
- if (data.enc_key_len < AES_BLOCK_SIZE ||
- data.enc_key_len > sizeof(cont_encr_key) + AES_BLOCK_SIZE) {
- wpa_printf(MSG_DEBUG, "DPP: Invalid encryptedKey length");
- return -1;
- }
- res = aes_siv_decrypt(kek, data.pbkdf2_key_len,
- data.enc_key, data.enc_key_len,
- 0, NULL, NULL, cont_encr_key);
- forced_memzero(kek, data.pbkdf2_key_len);
- if (res < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: AES-SIV decryption of encryptedKey failed");
- return -1;
- }
- cont_encr_key_len = data.enc_key_len - AES_BLOCK_SIZE;
- wpa_hexdump_key(MSG_DEBUG, "DPP: content-encryption key",
- cont_encr_key, cont_encr_key_len);
-
- if (data.enc_cont_len < AES_BLOCK_SIZE)
- return -1;
- key_pkg_len = data.enc_cont_len - AES_BLOCK_SIZE;
- key_pkg = os_malloc(key_pkg_len);
- if (!key_pkg)
- return -1;
- res = aes_siv_decrypt(cont_encr_key, cont_encr_key_len,
- data.enc_cont, data.enc_cont_len,
- 0, NULL, NULL, key_pkg);
- forced_memzero(cont_encr_key, cont_encr_key_len);
- if (res < 0) {
- bin_clear_free(key_pkg, key_pkg_len);
- wpa_printf(MSG_DEBUG,
- "DPP: AES-SIV decryption of encryptedContent failed");
- return -1;
- }
-
- keys = dpp_parse_dpp_asymmetric_key_package(key_pkg, key_pkg_len);
- bin_clear_free(key_pkg, key_pkg_len);
- dpp_free_asymmetric_key(auth->conf_key_pkg);
- auth->conf_key_pkg = keys;
-
- return keys != NULL;;
-}
-
-#endif /* CONFIG_DPP2 */
-
-
int dpp_conf_resp_rx(struct dpp_authentication *auth,
const struct wpabuf *resp)
{
@@ -8072,6 +2979,36 @@
}
+struct wpabuf * dpp_build_conn_status(enum dpp_status_error result,
+ const u8 *ssid, size_t ssid_len,
+ const char *channel_list)
+{
+ struct wpabuf *json;
+
+ json = wpabuf_alloc(1000);
+ if (!json)
+ return NULL;
+ json_start_object(json, NULL);
+ json_add_int(json, "result", result);
+ if (ssid) {
+ json_value_sep(json);
+ if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0) {
+ wpabuf_free(json);
+ return NULL;
+ }
+ }
+ if (channel_list) {
+ json_value_sep(json);
+ json_add_string(json, "channelList", channel_list);
+ }
+ json_end_object(json);
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
+ wpabuf_head(json), wpabuf_len(json));
+
+ return json;
+}
+
+
struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth,
enum dpp_status_error result,
const u8 *ssid, size_t ssid_len,
@@ -8083,23 +3020,9 @@
size_t len[2];
u8 *wrapped;
- json = wpabuf_alloc(1000);
+ json = dpp_build_conn_status(result, ssid, ssid_len, channel_list);
if (!json)
return NULL;
- json_start_object(json, NULL);
- json_add_int(json, "result", result);
- if (ssid) {
- json_value_sep(json);
- if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0)
- goto fail;
- }
- if (channel_list) {
- json_value_sep(json);
- json_add_string(json, "channelList", channel_list);
- }
- json_end_object(json);
- wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
- wpabuf_head(json), wpabuf_len(json));
nonce_len = auth->curve->nonce_len;
clear_len = 5 + 4 + nonce_len + 4 + wpabuf_len(json);
@@ -8161,6 +3084,8 @@
return;
EVP_PKEY_free(conf->csign);
os_free(conf->kid);
+ os_free(conf->connector);
+ EVP_PKEY_free(conf->connector_key);
os_free(conf);
}
@@ -8192,7 +3117,6 @@
static int dpp_configurator_gen_kid(struct dpp_configurator *conf)
{
struct wpabuf *csign_pub = NULL;
- u8 kid_hash[SHA256_MAC_LEN];
const u8 *addr[1];
size_t len[1];
int res;
@@ -8206,7 +3130,7 @@
/* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
addr[0] = wpabuf_head(csign_pub);
len[0] = wpabuf_len(csign_pub);
- res = sha256_vector(1, addr, len, kid_hash);
+ res = sha256_vector(1, addr, len, conf->kid_hash);
wpabuf_free(csign_pub);
if (res < 0) {
wpa_printf(MSG_DEBUG,
@@ -8214,7 +3138,8 @@
return -1;
}
- conf->kid = base64_url_encode(kid_hash, sizeof(kid_hash), NULL);
+ conf->kid = base64_url_encode(conf->kid_hash, sizeof(conf->kid_hash),
+ NULL);
return conf->kid ? 0 : -1;
}
@@ -8229,17 +3154,13 @@
if (!conf)
return NULL;
- if (!curve) {
- conf->curve = &dpp_curves[0];
- } else {
- conf->curve = dpp_get_curve_name(curve);
- if (!conf->curve) {
- wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
- curve);
- os_free(conf);
- return NULL;
- }
+ conf->curve = dpp_get_curve_name(curve);
+ if (!conf->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+ os_free(conf);
+ return NULL;
}
+
if (privkey)
conf->csign = dpp_set_keypair(&conf->curve, privkey,
privkey_len);
@@ -8269,16 +3190,12 @@
return -1;
}
- if (!curve) {
- auth->curve = &dpp_curves[0];
- } else {
- auth->curve = dpp_get_curve_name(curve);
- if (!auth->curve) {
- wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
- curve);
- return -1;
- }
+ auth->curve = dpp_get_curve_name(curve);
+ if (!auth->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+ return -1;
}
+
wpa_printf(MSG_DEBUG,
"DPP: Building own configuration/connector with curve %s",
auth->curve->name);
@@ -8314,7 +3231,8 @@
static int dpp_connector_compatible_group(struct json_token *root,
const char *group_id,
- const char *net_role)
+ const char *net_role,
+ bool reconfig)
{
struct json_token *groups, *token;
@@ -8338,7 +3256,9 @@
os_strcmp(id->string, group_id) != 0)
continue;
- if (dpp_compatible_netrole(role->string, net_role))
+ if (reconfig && os_strcmp(net_role, "configurator") == 0)
+ return 1;
+ if (!reconfig && dpp_compatible_netrole(role->string, net_role))
return 1;
}
@@ -8346,8 +3266,8 @@
}
-static int dpp_connector_match_groups(struct json_token *own_root,
- struct json_token *peer_root)
+int dpp_connector_match_groups(struct json_token *own_root,
+ struct json_token *peer_root, bool reconfig)
{
struct json_token *groups, *token;
@@ -8377,7 +3297,7 @@
"DPP: peer connector group: groupId='%s' netRole='%s'",
id->string, role->string);
if (dpp_connector_compatible_group(own_root, id->string,
- role->string)) {
+ role->string, reconfig)) {
wpa_printf(MSG_DEBUG,
"DPP: Compatible group/netRole in own connector");
return 1;
@@ -8388,71 +3308,37 @@
}
-static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk,
- unsigned int hash_len)
+struct json_token * dpp_parse_own_connector(const char *own_connector)
{
- u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
- const char *info = "DPP PMK";
- int res;
+ unsigned char *own_conn;
+ size_t own_conn_len;
+ const char *pos, *end;
+ struct json_token *own_root;
- /* PMK = HKDF(<>, "DPP PMK", N.x) */
-
- /* HKDF-Extract(<>, N.x) */
- os_memset(salt, 0, hash_len);
- if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
- prk, hash_len);
-
- /* HKDF-Expand(PRK, info, L) */
- res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
- pmk, hash_len);
- return 0;
-}
-
-
-static int dpp_derive_pmkid(const struct dpp_curve_params *curve,
- EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
-{
- struct wpabuf *nkx, *pkx;
- int ret = -1, res;
- const u8 *addr[2];
- size_t len[2];
- u8 hash[SHA256_MAC_LEN];
-
- /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
- nkx = dpp_get_pubkey_point(own_key, 0);
- pkx = dpp_get_pubkey_point(peer_key, 0);
- if (!nkx || !pkx)
- goto fail;
- addr[0] = wpabuf_head(nkx);
- len[0] = wpabuf_len(nkx) / 2;
- addr[1] = wpabuf_head(pkx);
- len[1] = wpabuf_len(pkx) / 2;
- if (len[0] != len[1])
- goto fail;
- if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
- addr[0] = wpabuf_head(pkx);
- addr[1] = wpabuf_head(nkx);
+ pos = os_strchr(own_connector, '.');
+ if (!pos) {
+ wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
+ return NULL;
}
- wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
- res = sha256_vector(2, addr, len, hash);
- if (res < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
- os_memcpy(pmkid, hash, PMKID_LEN);
- wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
- ret = 0;
-fail:
- wpabuf_free(nkx);
- wpabuf_free(pkx);
- return ret;
+ pos++;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
+ return NULL;
+ }
+ own_conn = base64_url_decode(pos, end - pos, &own_conn_len);
+ if (!own_conn) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode own signedConnector JWS Payload");
+ return NULL;
+ }
+
+ own_root = json_parse((const char *) own_conn, own_conn_len);
+ os_free(own_conn);
+ if (!own_root)
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
+
+ return own_root;
}
@@ -8470,12 +3356,6 @@
struct wpabuf *own_key_pub = NULL;
const struct dpp_curve_params *curve, *own_curve;
struct dpp_signed_connector_info info;
- const unsigned char *p;
- EVP_PKEY *csign = NULL;
- char *signed_connector = NULL;
- const char *pos, *end;
- unsigned char *own_conn = NULL;
- size_t own_conn_len;
size_t Nx_len;
u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
@@ -8484,14 +3364,6 @@
if (expiry)
*expiry = 0;
- p = csign_key;
- csign = d2i_PUBKEY(NULL, &p, csign_key_len);
- if (!csign) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to parse local C-sign-key information");
- goto fail;
- }
-
own_key = dpp_set_keypair(&own_curve, net_access_key,
net_access_key_len);
if (!own_key) {
@@ -8499,39 +3371,12 @@
goto fail;
}
- pos = os_strchr(own_connector, '.');
- if (!pos) {
- wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
+ own_root = dpp_parse_own_connector(own_connector);
+ if (!own_root)
goto fail;
- }
- pos++;
- end = os_strchr(pos, '.');
- if (!end) {
- wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
- goto fail;
- }
- own_conn = base64_url_decode(pos, end - pos, &own_conn_len);
- if (!own_conn) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to base64url decode own signedConnector JWS Payload");
- goto fail;
- }
- own_root = json_parse((const char *) own_conn, own_conn_len);
- if (!own_root) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
- goto fail;
- }
-
- wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
- peer_connector, peer_connector_len);
- signed_connector = os_malloc(peer_connector_len + 1);
- if (!signed_connector)
- goto fail;
- os_memcpy(signed_connector, peer_connector, peer_connector_len);
- signed_connector[peer_connector_len] = '\0';
-
- res = dpp_process_signed_connector(&info, csign, signed_connector);
+ res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
+ peer_connector, peer_connector_len);
if (res != DPP_STATUS_OK) {
ret = res;
goto fail;
@@ -8544,7 +3389,7 @@
goto fail;
}
- if (!dpp_connector_match_groups(own_root, root)) {
+ if (!dpp_connector_match_groups(own_root, root, false)) {
wpa_printf(MSG_DEBUG,
"DPP: Peer connector does not include compatible group netrole with own connector");
ret = DPP_STATUS_NO_MATCH;
@@ -8612,1751 +3457,17 @@
if (ret != DPP_STATUS_OK)
os_memset(intro, 0, sizeof(*intro));
os_memset(Nx, 0, sizeof(Nx));
- os_free(own_conn);
- os_free(signed_connector);
os_free(info.payload);
EVP_PKEY_free(own_key);
wpabuf_free(own_key_pub);
EVP_PKEY_free(peer_key);
- EVP_PKEY_free(csign);
json_free(root);
json_free(own_root);
return ret;
}
-static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
- int init)
-{
- EC_GROUP *group;
- size_t len = curve->prime_len;
- const u8 *x, *y;
- EVP_PKEY *res;
-
- switch (curve->ike_group) {
- case 19:
- x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
- y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
- break;
- case 20:
- x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
- y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
- break;
- case 21:
- x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
- y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
- break;
- case 28:
- x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
- y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
- break;
- case 29:
- x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
- y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
- break;
- case 30:
- x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
- y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
- break;
- default:
- return NULL;
- }
-
- group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
- if (!group)
- return NULL;
- res = dpp_set_pubkey_point_group(group, x, y, len);
- EC_GROUP_free(group);
- return res;
-}
-
-
-static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
- const u8 *mac_init, const char *code,
- const char *identifier, BN_CTX *bnctx,
- EC_GROUP **ret_group)
-{
- u8 hash[DPP_MAX_HASH_LEN];
- const u8 *addr[3];
- size_t len[3];
- unsigned int num_elem = 0;
- EC_POINT *Qi = NULL;
- EVP_PKEY *Pi = NULL;
- EC_KEY *Pi_ec = NULL;
- const EC_POINT *Pi_point;
- BIGNUM *hash_bn = NULL;
- const EC_GROUP *group = NULL;
- EC_GROUP *group2 = NULL;
-
- /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
-
- wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
- addr[num_elem] = mac_init;
- len[num_elem] = ETH_ALEN;
- num_elem++;
- if (identifier) {
- wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
- identifier);
- addr[num_elem] = (const u8 *) identifier;
- len[num_elem] = os_strlen(identifier);
- num_elem++;
- }
- wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
- addr[num_elem] = (const u8 *) code;
- len[num_elem] = os_strlen(code);
- num_elem++;
- if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
- goto fail;
- wpa_hexdump_key(MSG_DEBUG,
- "DPP: H(MAC-Initiator | [identifier |] code)",
- hash, curve->hash_len);
- Pi = dpp_pkex_get_role_elem(curve, 1);
- if (!Pi)
- goto fail;
- dpp_debug_print_key("DPP: Pi", Pi);
- Pi_ec = EVP_PKEY_get1_EC_KEY(Pi);
- if (!Pi_ec)
- goto fail;
- Pi_point = EC_KEY_get0_public_key(Pi_ec);
-
- group = EC_KEY_get0_group(Pi_ec);
- if (!group)
- goto fail;
- group2 = EC_GROUP_dup(group);
- if (!group2)
- goto fail;
- Qi = EC_POINT_new(group2);
- if (!Qi) {
- EC_GROUP_free(group2);
- goto fail;
- }
- hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
- if (!hash_bn ||
- EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
- goto fail;
- if (EC_POINT_is_at_infinity(group, Qi)) {
- wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
- goto fail;
- }
- dpp_debug_print_point("DPP: Qi", group, Qi);
-out:
- EC_KEY_free(Pi_ec);
- EVP_PKEY_free(Pi);
- BN_clear_free(hash_bn);
- if (ret_group && Qi)
- *ret_group = group2;
- else
- EC_GROUP_free(group2);
- return Qi;
-fail:
- EC_POINT_free(Qi);
- Qi = NULL;
- goto out;
-}
-
-
-static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
- const u8 *mac_resp, const char *code,
- const char *identifier, BN_CTX *bnctx,
- EC_GROUP **ret_group)
-{
- u8 hash[DPP_MAX_HASH_LEN];
- const u8 *addr[3];
- size_t len[3];
- unsigned int num_elem = 0;
- EC_POINT *Qr = NULL;
- EVP_PKEY *Pr = NULL;
- EC_KEY *Pr_ec = NULL;
- const EC_POINT *Pr_point;
- BIGNUM *hash_bn = NULL;
- const EC_GROUP *group = NULL;
- EC_GROUP *group2 = NULL;
-
- /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
-
- wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
- addr[num_elem] = mac_resp;
- len[num_elem] = ETH_ALEN;
- num_elem++;
- if (identifier) {
- wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
- identifier);
- addr[num_elem] = (const u8 *) identifier;
- len[num_elem] = os_strlen(identifier);
- num_elem++;
- }
- wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
- addr[num_elem] = (const u8 *) code;
- len[num_elem] = os_strlen(code);
- num_elem++;
- if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
- goto fail;
- wpa_hexdump_key(MSG_DEBUG,
- "DPP: H(MAC-Responder | [identifier |] code)",
- hash, curve->hash_len);
- Pr = dpp_pkex_get_role_elem(curve, 0);
- if (!Pr)
- goto fail;
- dpp_debug_print_key("DPP: Pr", Pr);
- Pr_ec = EVP_PKEY_get1_EC_KEY(Pr);
- if (!Pr_ec)
- goto fail;
- Pr_point = EC_KEY_get0_public_key(Pr_ec);
-
- group = EC_KEY_get0_group(Pr_ec);
- if (!group)
- goto fail;
- group2 = EC_GROUP_dup(group);
- if (!group2)
- goto fail;
- Qr = EC_POINT_new(group2);
- if (!Qr) {
- EC_GROUP_free(group2);
- goto fail;
- }
- hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
- if (!hash_bn ||
- EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
- goto fail;
- if (EC_POINT_is_at_infinity(group, Qr)) {
- wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
- goto fail;
- }
- dpp_debug_print_point("DPP: Qr", group, Qr);
-out:
- EC_KEY_free(Pr_ec);
- EVP_PKEY_free(Pr);
- BN_clear_free(hash_bn);
- if (ret_group && Qr)
- *ret_group = group2;
- else
- EC_GROUP_free(group2);
- return Qr;
-fail:
- EC_POINT_free(Qr);
- Qr = NULL;
- goto out;
-}
-
-
-#ifdef CONFIG_TESTING_OPTIONS
-static int dpp_test_gen_invalid_key(struct wpabuf *msg,
- const struct dpp_curve_params *curve)
-{
- BN_CTX *ctx;
- BIGNUM *x, *y;
- int ret = -1;
- EC_GROUP *group;
- EC_POINT *point;
-
- group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
- if (!group)
- return -1;
-
- ctx = BN_CTX_new();
- point = EC_POINT_new(group);
- x = BN_new();
- y = BN_new();
- if (!ctx || !point || !x || !y)
- goto fail;
-
- if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
- goto fail;
-
- /* Generate a random y coordinate that results in a point that is not
- * on the curve. */
- for (;;) {
- if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
- goto fail;
-
- if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
- ctx) != 1) {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
- /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
- * return an error from EC_POINT_set_affine_coordinates_GFp()
- * when the point is not on the curve. */
- break;
-#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
- goto fail;
-#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
- }
-
- if (!EC_POINT_is_on_curve(group, point, ctx))
- break;
- }
-
- if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0 ||
- dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0)
- goto fail;
-
- ret = 0;
-fail:
- if (ret < 0)
- wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
- BN_free(x);
- BN_free(y);
- EC_POINT_free(point);
- BN_CTX_free(ctx);
- EC_GROUP_free(group);
-
- return ret;
-}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-
-static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
-{
- EC_KEY *X_ec = NULL;
- const EC_POINT *X_point;
- BN_CTX *bnctx = NULL;
- EC_GROUP *group = NULL;
- EC_POINT *Qi = NULL, *M = NULL;
- struct wpabuf *M_buf = NULL;
- BIGNUM *Mx = NULL, *My = NULL;
- struct wpabuf *msg = NULL;
- size_t attr_len;
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
-
- wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
-
- /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
- Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
- pkex->identifier, bnctx, &group);
- if (!Qi)
- goto fail;
-
- /* Generate a random ephemeral keypair x/X */
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_pkex_ephemeral_key_override_len) {
- const struct dpp_curve_params *tmp_curve;
-
- wpa_printf(MSG_INFO,
- "DPP: TESTING - override ephemeral key x/X");
- pkex->x = dpp_set_keypair(&tmp_curve,
- dpp_pkex_ephemeral_key_override,
- dpp_pkex_ephemeral_key_override_len);
- } else {
- pkex->x = dpp_gen_keypair(curve);
- }
-#else /* CONFIG_TESTING_OPTIONS */
- pkex->x = dpp_gen_keypair(curve);
-#endif /* CONFIG_TESTING_OPTIONS */
- if (!pkex->x)
- goto fail;
-
- /* M = X + Qi */
- X_ec = EVP_PKEY_get1_EC_KEY(pkex->x);
- if (!X_ec)
- goto fail;
- X_point = EC_KEY_get0_public_key(X_ec);
- if (!X_point)
- goto fail;
- dpp_debug_print_point("DPP: X", group, X_point);
- M = EC_POINT_new(group);
- Mx = BN_new();
- My = BN_new();
- if (!M || !Mx || !My ||
- EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
- goto fail;
- dpp_debug_print_point("DPP: M", group, M);
-
- /* Initiator -> Responder: group, [identifier,] M */
- attr_len = 4 + 2;
- if (pkex->identifier)
- attr_len += 4 + os_strlen(pkex->identifier);
- attr_len += 4 + 2 * curve->prime_len;
- msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
- if (!msg)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
- goto skip_finite_cyclic_group;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Finite Cyclic Group attribute */
- wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
- wpabuf_put_le16(msg, 2);
- wpabuf_put_le16(msg, curve->ike_group);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_finite_cyclic_group:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Code Identifier attribute */
- if (pkex->identifier) {
- wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
- wpabuf_put_le16(msg, os_strlen(pkex->identifier));
- wpabuf_put_str(msg, pkex->identifier);
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
- goto out;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* M in Encrypted Key attribute */
- wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
- wpabuf_put_le16(msg, 2 * curve->prime_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
- if (dpp_test_gen_invalid_key(msg, curve) < 0)
- goto fail;
- goto out;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0 ||
- dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
- dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0)
- goto fail;
-
-out:
- wpabuf_free(M_buf);
- EC_KEY_free(X_ec);
- EC_POINT_free(M);
- EC_POINT_free(Qi);
- BN_clear_free(Mx);
- BN_clear_free(My);
- BN_CTX_free(bnctx);
- EC_GROUP_free(group);
- return msg;
-fail:
- wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
- wpabuf_free(msg);
- msg = NULL;
- goto out;
-}
-
-
-static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
-{
- wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
-}
-
-
-struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
- const u8 *own_mac,
- const char *identifier,
- const char *code)
-{
- struct dpp_pkex *pkex;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
- wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
- MAC2STR(dpp_pkex_own_mac_override));
- own_mac = dpp_pkex_own_mac_override;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- pkex = os_zalloc(sizeof(*pkex));
- if (!pkex)
- return NULL;
- pkex->msg_ctx = msg_ctx;
- pkex->initiator = 1;
- pkex->own_bi = bi;
- os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
- if (identifier) {
- pkex->identifier = os_strdup(identifier);
- if (!pkex->identifier)
- goto fail;
- }
- pkex->code = os_strdup(code);
- if (!pkex->code)
- goto fail;
- pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
- if (!pkex->exchange_req)
- goto fail;
- return pkex;
-fail:
- dpp_pkex_free(pkex);
- return NULL;
-}
-
-
-static struct wpabuf *
-dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
- enum dpp_status_error status,
- const BIGNUM *Nx, const BIGNUM *Ny)
-{
- struct wpabuf *msg = NULL;
- size_t attr_len;
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
-
- /* Initiator -> Responder: DPP Status, [identifier,] N */
- attr_len = 4 + 1;
- if (pkex->identifier)
- attr_len += 4 + os_strlen(pkex->identifier);
- attr_len += 4 + 2 * curve->prime_len;
- msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
- if (!msg)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
- goto skip_status;
- }
-
- if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
- status = 255;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* DPP Status */
- dpp_build_attr_status(msg, status);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_status:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Code Identifier attribute */
- if (pkex->identifier) {
- wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
- wpabuf_put_le16(msg, os_strlen(pkex->identifier));
- wpabuf_put_str(msg, pkex->identifier);
- }
-
- if (status != DPP_STATUS_OK)
- goto skip_encrypted_key;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
- goto skip_encrypted_key;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* N in Encrypted Key attribute */
- wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
- wpabuf_put_le16(msg, 2 * curve->prime_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
- if (dpp_test_gen_invalid_key(msg, curve) < 0)
- goto fail;
- goto skip_encrypted_key;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0 ||
- dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
- dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0)
- goto fail;
-
-skip_encrypted_key:
- if (status == DPP_STATUS_BAD_GROUP) {
- /* Finite Cyclic Group attribute */
- wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
- wpabuf_put_le16(msg, 2);
- wpabuf_put_le16(msg, curve->ike_group);
- }
-
- return msg;
-fail:
- wpabuf_free(msg);
- return NULL;
-}
-
-
-static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
- const u8 *Mx, size_t Mx_len,
- const u8 *Nx, size_t Nx_len,
- const char *code,
- const u8 *Kx, size_t Kx_len,
- u8 *z, unsigned int hash_len)
-{
- u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
- int res;
- u8 *info, *pos;
- size_t info_len;
-
- /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
- */
-
- /* HKDF-Extract(<>, IKM=K.x) */
- os_memset(salt, 0, hash_len);
- if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
- prk, hash_len);
- info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
- info = os_malloc(info_len);
- if (!info)
- return -1;
- pos = info;
- os_memcpy(pos, mac_init, ETH_ALEN);
- pos += ETH_ALEN;
- os_memcpy(pos, mac_resp, ETH_ALEN);
- pos += ETH_ALEN;
- os_memcpy(pos, Mx, Mx_len);
- pos += Mx_len;
- os_memcpy(pos, Nx, Nx_len);
- pos += Nx_len;
- os_memcpy(pos, code, os_strlen(code));
-
- /* HKDF-Expand(PRK, info, L) */
- if (hash_len == 32)
- res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
- z, hash_len);
- else if (hash_len == 48)
- res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
- z, hash_len);
- else if (hash_len == 64)
- res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
- z, hash_len);
- else
- res = -1;
- os_free(info);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
- z, hash_len);
- return 0;
-}
-
-
-static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
- const char *identifier)
-{
- if (!attr_id && identifier) {
- wpa_printf(MSG_DEBUG,
- "DPP: No PKEX code identifier received, but expected one");
- return 0;
- }
-
- if (attr_id && !identifier) {
- wpa_printf(MSG_DEBUG,
- "DPP: PKEX code identifier received, but not expecting one");
- return 0;
- }
-
- if (attr_id && identifier &&
- (os_strlen(identifier) != attr_id_len ||
- os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
- wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
- return 0;
- }
-
- return 1;
-}
-
-
-struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
- struct dpp_bootstrap_info *bi,
- const u8 *own_mac,
- const u8 *peer_mac,
- const char *identifier,
- const char *code,
- const u8 *buf, size_t len)
-{
- const u8 *attr_group, *attr_id, *attr_key;
- u16 attr_group_len, attr_id_len, attr_key_len;
- const struct dpp_curve_params *curve = bi->curve;
- u16 ike_group;
- struct dpp_pkex *pkex = NULL;
- EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
- BN_CTX *bnctx = NULL;
- EC_GROUP *group = NULL;
- BIGNUM *Mx = NULL, *My = NULL;
- EC_KEY *Y_ec = NULL, *X_ec = NULL;;
- const EC_POINT *Y_point;
- BIGNUM *Nx = NULL, *Ny = NULL;
- u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
- size_t Kx_len;
- int res;
-
- if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "PKEX counter t limit reached - ignore message");
- return NULL;
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
- wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
- MAC2STR(dpp_pkex_peer_mac_override));
- peer_mac = dpp_pkex_peer_mac_override;
- }
- if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
- wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
- MAC2STR(dpp_pkex_own_mac_override));
- own_mac = dpp_pkex_own_mac_override;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- attr_id_len = 0;
- attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
- &attr_id_len);
- if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
- return NULL;
-
- attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
- &attr_group_len);
- if (!attr_group || attr_group_len != 2) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Missing or invalid Finite Cyclic Group attribute");
- return NULL;
- }
- ike_group = WPA_GET_LE16(attr_group);
- if (ike_group != curve->ike_group) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Mismatching PKEX curve: peer=%u own=%u",
- ike_group, curve->ike_group);
- pkex = os_zalloc(sizeof(*pkex));
- if (!pkex)
- goto fail;
- pkex->own_bi = bi;
- pkex->failed = 1;
- pkex->exchange_resp = dpp_pkex_build_exchange_resp(
- pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
- if (!pkex->exchange_resp)
- goto fail;
- return pkex;
- }
-
- /* M in Encrypted Key attribute */
- attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
- &attr_key_len);
- if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
- attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Missing Encrypted Key attribute");
- return NULL;
- }
-
- /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
- Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
- &group);
- if (!Qi)
- goto fail;
-
- /* X' = M - Qi */
- X = EC_POINT_new(group);
- M = EC_POINT_new(group);
- Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
- My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
- if (!X || !M || !Mx || !My ||
- EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, M) ||
- !EC_POINT_is_on_curve(group, M, bnctx) ||
- EC_POINT_invert(group, Qi, bnctx) != 1 ||
- EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, X) ||
- !EC_POINT_is_on_curve(group, X, bnctx)) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Invalid Encrypted Key value");
- bi->pkex_t++;
- goto fail;
- }
- dpp_debug_print_point("DPP: M", group, M);
- dpp_debug_print_point("DPP: X'", group, X);
-
- pkex = os_zalloc(sizeof(*pkex));
- if (!pkex)
- goto fail;
- pkex->t = bi->pkex_t;
- pkex->msg_ctx = msg_ctx;
- pkex->own_bi = bi;
- os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
- os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
- if (identifier) {
- pkex->identifier = os_strdup(identifier);
- if (!pkex->identifier)
- goto fail;
- }
- pkex->code = os_strdup(code);
- if (!pkex->code)
- goto fail;
-
- os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
-
- X_ec = EC_KEY_new();
- if (!X_ec ||
- EC_KEY_set_group(X_ec, group) != 1 ||
- EC_KEY_set_public_key(X_ec, X) != 1)
- goto fail;
- pkex->x = EVP_PKEY_new();
- if (!pkex->x ||
- EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
- goto fail;
-
- /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
- Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
- if (!Qr)
- goto fail;
-
- /* Generate a random ephemeral keypair y/Y */
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_pkex_ephemeral_key_override_len) {
- const struct dpp_curve_params *tmp_curve;
-
- wpa_printf(MSG_INFO,
- "DPP: TESTING - override ephemeral key y/Y");
- pkex->y = dpp_set_keypair(&tmp_curve,
- dpp_pkex_ephemeral_key_override,
- dpp_pkex_ephemeral_key_override_len);
- } else {
- pkex->y = dpp_gen_keypair(curve);
- }
-#else /* CONFIG_TESTING_OPTIONS */
- pkex->y = dpp_gen_keypair(curve);
-#endif /* CONFIG_TESTING_OPTIONS */
- if (!pkex->y)
- goto fail;
-
- /* N = Y + Qr */
- Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
- if (!Y_ec)
- goto fail;
- Y_point = EC_KEY_get0_public_key(Y_ec);
- if (!Y_point)
- goto fail;
- dpp_debug_print_point("DPP: Y", group, Y_point);
- N = EC_POINT_new(group);
- Nx = BN_new();
- Ny = BN_new();
- if (!N || !Nx || !Ny ||
- EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
- goto fail;
- dpp_debug_print_point("DPP: N", group, N);
-
- pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
- Nx, Ny);
- if (!pkex->exchange_resp)
- goto fail;
-
- /* K = y * X' */
- if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
- Kx, Kx_len);
-
- /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
- */
- res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
- pkex->Mx, curve->prime_len,
- pkex->Nx, curve->prime_len, pkex->code,
- Kx, Kx_len, pkex->z, curve->hash_len);
- os_memset(Kx, 0, Kx_len);
- if (res < 0)
- goto fail;
-
- pkex->exchange_done = 1;
-
-out:
- BN_CTX_free(bnctx);
- EC_POINT_free(Qi);
- EC_POINT_free(Qr);
- BN_free(Mx);
- BN_free(My);
- BN_free(Nx);
- BN_free(Ny);
- EC_POINT_free(M);
- EC_POINT_free(N);
- EC_POINT_free(X);
- EC_KEY_free(X_ec);
- EC_KEY_free(Y_ec);
- EC_GROUP_free(group);
- return pkex;
-fail:
- wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
- dpp_pkex_free(pkex);
- pkex = NULL;
- goto out;
-}
-
-
-static struct wpabuf *
-dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
- const struct wpabuf *A_pub, const u8 *u)
-{
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- struct wpabuf *msg = NULL;
- size_t clear_len, attr_len;
- struct wpabuf *clear = NULL;
- u8 *wrapped;
- u8 octet;
- const u8 *addr[2];
- size_t len[2];
-
- /* {A, u, [bootstrapping info]}z */
- clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
- clear = wpabuf_alloc(clear_len);
- attr_len = 4 + clear_len + AES_BLOCK_SIZE;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
- if (!clear || !msg)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
- goto skip_bootstrap_key;
- }
- if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, 2 * curve->prime_len);
- if (dpp_test_gen_invalid_key(clear, curve) < 0)
- goto fail;
- goto skip_bootstrap_key;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* A in Bootstrap Key attribute */
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, wpabuf_len(A_pub));
- wpabuf_put_buf(clear, A_pub);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_bootstrap_key:
- if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
- goto skip_i_auth_tag;
- }
- if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
- wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
- wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, u, curve->hash_len - 1);
- wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
- goto skip_i_auth_tag;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* u in I-Auth tag attribute */
- wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
- wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, u, curve->hash_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_i_auth_tag:
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
- goto skip_wrapped_data;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = DPP_HDR_LEN;
- octet = 0;
- addr[1] = &octet;
- len[1] = sizeof(octet);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
- wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
- wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
- if (aes_siv_encrypt(pkex->z, curve->hash_len,
- wpabuf_head(clear), wpabuf_len(clear),
- 2, addr, len, wrapped) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
-out:
- wpabuf_free(clear);
- return msg;
-
-fail:
- wpabuf_free(msg);
- msg = NULL;
- goto out;
-}
-
-
-struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
- const u8 *peer_mac,
- const u8 *buf, size_t buflen)
-{
- const u8 *attr_status, *attr_id, *attr_key, *attr_group;
- u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
- EC_GROUP *group = NULL;
- BN_CTX *bnctx = NULL;
- struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
- BIGNUM *Nx = NULL, *Ny = NULL;
- EC_KEY *Y_ec = NULL;
- size_t Jx_len, Kx_len;
- u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
- const u8 *addr[4];
- size_t len[4];
- u8 u[DPP_MAX_HASH_LEN];
- int res;
-
- if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
- return NULL;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at PKEX Exchange Response");
- pkex->failed = 1;
- return NULL;
- }
-
- if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
- wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
- MAC2STR(dpp_pkex_peer_mac_override));
- peer_mac = dpp_pkex_peer_mac_override;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
-
- attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
- &attr_status_len);
- if (!attr_status || attr_status_len != 1) {
- dpp_pkex_fail(pkex, "No DPP Status attribute");
- return NULL;
- }
- wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
-
- if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
- attr_group = dpp_get_attr(buf, buflen,
- DPP_ATTR_FINITE_CYCLIC_GROUP,
- &attr_group_len);
- if (attr_group && attr_group_len == 2) {
- wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Peer indicated mismatching PKEX group - proposed %u",
- WPA_GET_LE16(attr_group));
- return NULL;
- }
- }
-
- if (attr_status[0] != DPP_STATUS_OK) {
- dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
- return NULL;
- }
-
- attr_id_len = 0;
- attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
- &attr_id_len);
- if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
- pkex->identifier)) {
- dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
- return NULL;
- }
-
- /* N in Encrypted Key attribute */
- attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
- &attr_key_len);
- if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
- dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
- return NULL;
- }
-
- /* Qr = H(MAC-Responder | [identifier |] code) * Pr */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
- Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
- pkex->identifier, bnctx, &group);
- if (!Qr)
- goto fail;
-
- /* Y' = N - Qr */
- Y = EC_POINT_new(group);
- N = EC_POINT_new(group);
- Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
- Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
- if (!Y || !N || !Nx || !Ny ||
- EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, N) ||
- !EC_POINT_is_on_curve(group, N, bnctx) ||
- EC_POINT_invert(group, Qr, bnctx) != 1 ||
- EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, Y) ||
- !EC_POINT_is_on_curve(group, Y, bnctx)) {
- dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
- pkex->t++;
- goto fail;
- }
- dpp_debug_print_point("DPP: N", group, N);
- dpp_debug_print_point("DPP: Y'", group, Y);
-
- pkex->exchange_done = 1;
-
- /* ECDH: J = a * Y’ */
- Y_ec = EC_KEY_new();
- if (!Y_ec ||
- EC_KEY_set_group(Y_ec, group) != 1 ||
- EC_KEY_set_public_key(Y_ec, Y) != 1)
- goto fail;
- pkex->y = EVP_PKEY_new();
- if (!pkex->y ||
- EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
- goto fail;
- if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
- Jx, Jx_len);
-
- /* u = HMAC(J.x, MAC-Initiator | A.x | Y’.x | X.x ) */
- A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
- Y_pub = dpp_get_pubkey_point(pkex->y, 0);
- X_pub = dpp_get_pubkey_point(pkex->x, 0);
- if (!A_pub || !Y_pub || !X_pub)
- goto fail;
- addr[0] = pkex->own_mac;
- len[0] = ETH_ALEN;
- addr[1] = wpabuf_head(A_pub);
- len[1] = wpabuf_len(A_pub) / 2;
- addr[2] = wpabuf_head(Y_pub);
- len[2] = wpabuf_len(Y_pub) / 2;
- addr[3] = wpabuf_head(X_pub);
- len[3] = wpabuf_len(X_pub) / 2;
- if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
-
- /* K = x * Y’ */
- if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
- Kx, Kx_len);
-
- /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
- */
- res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
- pkex->Mx, curve->prime_len,
- attr_key /* N.x */, attr_key_len / 2,
- pkex->code, Kx, Kx_len,
- pkex->z, curve->hash_len);
- os_memset(Kx, 0, Kx_len);
- if (res < 0)
- goto fail;
-
- msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
- if (!msg)
- goto fail;
-
-out:
- wpabuf_free(A_pub);
- wpabuf_free(X_pub);
- wpabuf_free(Y_pub);
- EC_POINT_free(Qr);
- EC_POINT_free(Y);
- EC_POINT_free(N);
- BN_free(Nx);
- BN_free(Ny);
- EC_KEY_free(Y_ec);
- BN_CTX_free(bnctx);
- EC_GROUP_free(group);
- return msg;
-fail:
- wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
- goto out;
-}
-
-
-static struct wpabuf *
-dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
- const struct wpabuf *B_pub, const u8 *v)
-{
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- struct wpabuf *msg = NULL;
- const u8 *addr[2];
- size_t len[2];
- u8 octet;
- u8 *wrapped;
- struct wpabuf *clear = NULL;
- size_t clear_len, attr_len;
-
- /* {B, v [bootstrapping info]}z */
- clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
- clear = wpabuf_alloc(clear_len);
- attr_len = 4 + clear_len + AES_BLOCK_SIZE;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
- if (!clear || !msg)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
- goto skip_bootstrap_key;
- }
- if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, 2 * curve->prime_len);
- if (dpp_test_gen_invalid_key(clear, curve) < 0)
- goto fail;
- goto skip_bootstrap_key;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* B in Bootstrap Key attribute */
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, wpabuf_len(B_pub));
- wpabuf_put_buf(clear, B_pub);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_bootstrap_key:
- if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
- goto skip_r_auth_tag;
- }
- if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
- wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
- wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, v, curve->hash_len - 1);
- wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
- goto skip_r_auth_tag;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* v in R-Auth tag attribute */
- wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
- wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, v, curve->hash_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_r_auth_tag:
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
- goto skip_wrapped_data;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = DPP_HDR_LEN;
- octet = 1;
- addr[1] = &octet;
- len[1] = sizeof(octet);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
- wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
- wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
- if (aes_siv_encrypt(pkex->z, curve->hash_len,
- wpabuf_head(clear), wpabuf_len(clear),
- 2, addr, len, wrapped) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
-out:
- wpabuf_free(clear);
- return msg;
-
-fail:
- wpabuf_free(msg);
- msg = NULL;
- goto out;
-}
-
-
-struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
- const u8 *hdr,
- const u8 *buf, size_t buflen)
-{
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- size_t Jx_len, Lx_len;
- u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
- u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
- const u8 *wrapped_data, *b_key, *peer_u;
- u16 wrapped_data_len, b_key_len, peer_u_len = 0;
- const u8 *addr[4];
- size_t len[4];
- u8 octet;
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
- struct wpabuf *B_pub = NULL;
- u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at PKEX CR Request");
- pkex->failed = 1;
- return NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!pkex->exchange_done || pkex->failed ||
- pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
- goto fail;
-
- wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- dpp_pkex_fail(pkex,
- "Missing or invalid required Wrapped Data attribute");
- goto fail;
- }
-
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- octet = 0;
- addr[1] = &octet;
- len[1] = sizeof(octet);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- if (aes_siv_decrypt(pkex->z, curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_pkex_fail(pkex,
- "AES-SIV decryption failed - possible PKEX code mismatch");
- pkex->failed = 1;
- pkex->t++;
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
- &b_key_len);
- if (!b_key || b_key_len != 2 * curve->prime_len) {
- dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
- goto fail;
- }
- pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
- b_key_len);
- if (!pkex->peer_bootstrap_key) {
- dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
- goto fail;
- }
- dpp_debug_print_key("DPP: Peer bootstrap public key",
- pkex->peer_bootstrap_key);
-
- /* ECDH: J' = y * A' */
- if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
- Jx, Jx_len);
-
- /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
- A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
- Y_pub = dpp_get_pubkey_point(pkex->y, 0);
- X_pub = dpp_get_pubkey_point(pkex->x, 0);
- if (!A_pub || !Y_pub || !X_pub)
- goto fail;
- addr[0] = pkex->peer_mac;
- len[0] = ETH_ALEN;
- addr[1] = wpabuf_head(A_pub);
- len[1] = wpabuf_len(A_pub) / 2;
- addr[2] = wpabuf_head(Y_pub);
- len[2] = wpabuf_len(Y_pub) / 2;
- addr[3] = wpabuf_head(X_pub);
- len[3] = wpabuf_len(X_pub) / 2;
- if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
- goto fail;
-
- peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
- &peer_u_len);
- if (!peer_u || peer_u_len != curve->hash_len ||
- os_memcmp(peer_u, u, curve->hash_len) != 0) {
- dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
- u, curve->hash_len);
- wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
- pkex->t++;
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
-
- /* ECDH: L = b * X' */
- if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
- Lx, Lx_len);
-
- /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
- B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
- if (!B_pub)
- goto fail;
- addr[0] = pkex->own_mac;
- len[0] = ETH_ALEN;
- addr[1] = wpabuf_head(B_pub);
- len[1] = wpabuf_len(B_pub) / 2;
- addr[2] = wpabuf_head(X_pub);
- len[2] = wpabuf_len(X_pub) / 2;
- addr[3] = wpabuf_head(Y_pub);
- len[3] = wpabuf_len(Y_pub) / 2;
- if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
-
- msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
- if (!msg)
- goto fail;
-
-out:
- os_free(unwrapped);
- wpabuf_free(A_pub);
- wpabuf_free(B_pub);
- wpabuf_free(X_pub);
- wpabuf_free(Y_pub);
- return msg;
-fail:
- wpa_printf(MSG_DEBUG,
- "DPP: PKEX Commit-Reveal Request processing failed");
- goto out;
-}
-
-
-int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
- const u8 *buf, size_t buflen)
-{
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- const u8 *wrapped_data, *b_key, *peer_v;
- u16 wrapped_data_len, b_key_len, peer_v_len = 0;
- const u8 *addr[4];
- size_t len[4];
- u8 octet;
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- int ret = -1;
- u8 v[DPP_MAX_HASH_LEN];
- size_t Lx_len;
- u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
- struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at PKEX CR Response");
- pkex->failed = 1;
- goto fail;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!pkex->exchange_done || pkex->failed ||
- pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
- goto fail;
-
- wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- dpp_pkex_fail(pkex,
- "Missing or invalid required Wrapped Data attribute");
- goto fail;
- }
-
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- octet = 1;
- addr[1] = &octet;
- len[1] = sizeof(octet);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- if (aes_siv_decrypt(pkex->z, curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_pkex_fail(pkex,
- "AES-SIV decryption failed - possible PKEX code mismatch");
- pkex->t++;
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
- &b_key_len);
- if (!b_key || b_key_len != 2 * curve->prime_len) {
- dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
- goto fail;
- }
- pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
- b_key_len);
- if (!pkex->peer_bootstrap_key) {
- dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
- goto fail;
- }
- dpp_debug_print_key("DPP: Peer bootstrap public key",
- pkex->peer_bootstrap_key);
-
- /* ECDH: L' = x * B' */
- if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
- Lx, Lx_len);
-
- /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
- B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
- X_pub = dpp_get_pubkey_point(pkex->x, 0);
- Y_pub = dpp_get_pubkey_point(pkex->y, 0);
- if (!B_pub || !X_pub || !Y_pub)
- goto fail;
- addr[0] = pkex->peer_mac;
- len[0] = ETH_ALEN;
- addr[1] = wpabuf_head(B_pub);
- len[1] = wpabuf_len(B_pub) / 2;
- addr[2] = wpabuf_head(X_pub);
- len[2] = wpabuf_len(X_pub) / 2;
- addr[3] = wpabuf_head(Y_pub);
- len[3] = wpabuf_len(Y_pub) / 2;
- if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
- goto fail;
-
- peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
- &peer_v_len);
- if (!peer_v || peer_v_len != curve->hash_len ||
- os_memcmp(peer_v, v, curve->hash_len) != 0) {
- dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
- v, curve->hash_len);
- wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
- pkex->t++;
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
-
- ret = 0;
-out:
- wpabuf_free(B_pub);
- wpabuf_free(X_pub);
- wpabuf_free(Y_pub);
- os_free(unwrapped);
- return ret;
-fail:
- goto out;
-}
-
-
-void dpp_pkex_free(struct dpp_pkex *pkex)
-{
- if (!pkex)
- return;
-
- os_free(pkex->identifier);
- os_free(pkex->code);
- EVP_PKEY_free(pkex->x);
- EVP_PKEY_free(pkex->y);
- EVP_PKEY_free(pkex->peer_bootstrap_key);
- wpabuf_free(pkex->exchange_req);
- wpabuf_free(pkex->exchange_resp);
- os_free(pkex);
-}
-
-
-#ifdef CONFIG_TESTING_OPTIONS
-char * dpp_corrupt_connector_signature(const char *connector)
-{
- char *tmp, *pos, *signed3 = NULL;
- unsigned char *signature = NULL;
- size_t signature_len = 0, signed3_len;
-
- tmp = os_zalloc(os_strlen(connector) + 5);
- if (!tmp)
- goto fail;
- os_memcpy(tmp, connector, os_strlen(connector));
-
- pos = os_strchr(tmp, '.');
- if (!pos)
- goto fail;
-
- pos = os_strchr(pos + 1, '.');
- if (!pos)
- goto fail;
- pos++;
-
- wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
- pos);
- signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
- if (!signature || signature_len == 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
- signature, signature_len);
- signature[signature_len - 1] ^= 0x01;
- wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
- signature, signature_len);
- signed3 = base64_url_encode(signature, signature_len, &signed3_len);
- if (!signed3)
- goto fail;
- os_memcpy(pos, signed3, signed3_len);
- pos[signed3_len] = '\0';
- wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
- pos);
-
-out:
- os_free(signature);
- os_free(signed3);
- return tmp;
-fail:
- os_free(tmp);
- tmp = NULL;
- goto out;
-}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-
-#ifdef CONFIG_DPP2
-
-struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
- size_t net_access_key_len)
-{
- struct wpabuf *pub = NULL;
- EVP_PKEY *own_key;
- struct dpp_pfs *pfs;
-
- pfs = os_zalloc(sizeof(*pfs));
- if (!pfs)
- return NULL;
-
- own_key = dpp_set_keypair(&pfs->curve, net_access_key,
- net_access_key_len);
- if (!own_key) {
- wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
- goto fail;
- }
- EVP_PKEY_free(own_key);
-
- pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
- if (!pfs->ecdh)
- goto fail;
-
- pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
- pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
- if (!pub)
- goto fail;
-
- pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
- if (!pfs->ie)
- goto fail;
- wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
- wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
- wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
- wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
- wpabuf_put_buf(pfs->ie, pub);
- wpabuf_free(pub);
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
- pfs->ie);
-
- return pfs;
-fail:
- wpabuf_free(pub);
- dpp_pfs_free(pfs);
- return NULL;
-}
-
-
-int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
-{
- if (peer_ie_len < 2)
- return -1;
- if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
- wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
- return -1;
- }
-
- pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
- peer_ie_len - 2);
- pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
- if (!pfs->secret) {
- wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
- return -1;
- }
- wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
- return 0;
-}
-
-
-void dpp_pfs_free(struct dpp_pfs *pfs)
-{
- if (!pfs)
- return;
- crypto_ecdh_deinit(pfs->ecdh);
- wpabuf_free(pfs->ie);
- wpabuf_clear_free(pfs->secret);
- os_free(pfs);
-}
-
-#endif /* CONFIG_DPP2 */
-
-
-static unsigned int dpp_next_id(struct dpp_global *dpp)
+unsigned int dpp_next_id(struct dpp_global *dpp)
{
struct dpp_bootstrap_info *bi;
unsigned int max_id = 0;
@@ -10527,33 +3638,6 @@
}
-struct dpp_bootstrap_info *
-dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
- unsigned int freq)
-{
- struct dpp_bootstrap_info *bi;
-
- bi = os_zalloc(sizeof(*bi));
- if (!bi)
- return NULL;
- bi->id = dpp_next_id(dpp);
- bi->type = DPP_BOOTSTRAP_PKEX;
- os_memcpy(bi->mac_addr, peer, ETH_ALEN);
- bi->num_freq = 1;
- bi->freq[0] = freq;
- bi->curve = pkex->own_bi->curve;
- bi->pubkey = pkex->peer_bootstrap_key;
- pkex->peer_bootstrap_key = NULL;
- if (dpp_bootstrap_key_hash(bi) < 0) {
- dpp_bootstrap_info_free(bi);
- return NULL;
- }
- dpp_pkex_free(pkex);
- dl_list_add(&dpp->bootstrap, &bi->list);
- return bi;
-}
-
-
const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id)
{
struct dpp_bootstrap_info *bi;
@@ -10582,14 +3666,16 @@
"num_freq=%u\n"
"use_freq=%u\n"
"curve=%s\n"
- "pkhash=%s\n",
+ "pkhash=%s\n"
+ "version=%d\n",
dpp_bootstrap_type_txt(bi->type),
MAC2STR(bi->mac_addr),
bi->info ? bi->info : "",
bi->num_freq,
bi->num_freq == 1 ? bi->freq[0] : 0,
bi->curve->name,
- pkhash);
+ pkhash,
+ bi->version);
}
@@ -10897,68 +3983,20 @@
}
-static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
- void *timeout_ctx);
-
-
-static void dpp_connection_free(struct dpp_connection *conn)
+struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp,
+ const u8 *kid)
{
- if (conn->sock >= 0) {
- wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d",
- conn->sock);
- eloop_unregister_sock(conn->sock, EVENT_TYPE_READ);
- eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
- close(conn->sock);
- }
- eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
- conn, NULL);
- wpabuf_free(conn->msg);
- wpabuf_free(conn->msg_out);
- dpp_auth_deinit(conn->auth);
- os_free(conn);
-}
-
-
-static void dpp_connection_remove(struct dpp_connection *conn)
-{
- dl_list_del(&conn->list);
- dpp_connection_free(conn);
-}
-
-
-static void dpp_tcp_init_flush(struct dpp_global *dpp)
-{
- struct dpp_connection *conn, *tmp;
-
- dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection,
- list)
- dpp_connection_remove(conn);
-}
-
-
-static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
-{
- struct dpp_connection *conn, *tmp;
-
- dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
- list)
- dpp_connection_remove(conn);
- os_free(ctrl);
-}
-
-
-static void dpp_relay_flush_controllers(struct dpp_global *dpp)
-{
- struct dpp_relay_controller *ctrl, *tmp;
+ struct dpp_configurator *conf;
if (!dpp)
- return;
+ return NULL;
- dl_list_for_each_safe(ctrl, tmp, &dpp->controllers,
- struct dpp_relay_controller, list) {
- dl_list_del(&ctrl->list);
- dpp_relay_controller_free(ctrl);
+ dl_list_for_each(conf, &dpp->configurator,
+ struct dpp_configurator, list) {
+ if (os_memcmp(conf->kid_hash, kid, SHA256_MAC_LEN) == 0)
+ return conf;
}
+ return NULL;
}
#endif /* CONFIG_DPP2 */
@@ -11012,1301 +4050,6 @@
#ifdef CONFIG_DPP2
-
-static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
-static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
-static void dpp_controller_auth_success(struct dpp_connection *conn,
- int initiator);
-
-
-int dpp_relay_add_controller(struct dpp_global *dpp,
- struct dpp_relay_config *config)
-{
- struct dpp_relay_controller *ctrl;
-
- if (!dpp)
- return -1;
-
- ctrl = os_zalloc(sizeof(*ctrl));
- if (!ctrl)
- return -1;
- dl_list_init(&ctrl->conn);
- ctrl->global = dpp;
- os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
- os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
- ctrl->cb_ctx = config->cb_ctx;
- ctrl->tx = config->tx;
- ctrl->gas_resp_tx = config->gas_resp_tx;
- dl_list_add(&dpp->controllers, &ctrl->list);
- return 0;
-}
-
-
-static struct dpp_relay_controller *
-dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash)
-{
- struct dpp_relay_controller *ctrl;
-
- if (!dpp)
- return NULL;
-
- dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
- list) {
- if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0)
- return ctrl;
- }
-
- return NULL;
-}
-
-
-static void dpp_controller_gas_done(struct dpp_connection *conn)
-{
- struct dpp_authentication *auth = conn->auth;
-
- if (auth->peer_version >= 2 &&
- auth->conf_resp_status == DPP_STATUS_OK) {
- wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
- auth->waiting_conf_result = 1;
- return;
- }
-
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
- dpp_connection_remove(conn);
-}
-
-
-static int dpp_tcp_send(struct dpp_connection *conn)
-{
- int res;
-
- if (!conn->msg_out) {
- eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
- conn->write_eloop = 0;
- return -1;
- }
- res = send(conn->sock,
- wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos,
- wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s",
- strerror(errno));
- dpp_connection_remove(conn);
- return -1;
- }
-
- conn->msg_out_pos += res;
- if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) {
- wpa_printf(MSG_DEBUG,
- "DPP: %u/%u bytes of message sent to Controller",
- (unsigned int) conn->msg_out_pos,
- (unsigned int) wpabuf_len(conn->msg_out));
- if (!conn->write_eloop &&
- eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
- dpp_conn_tx_ready, conn, NULL) == 0)
- conn->write_eloop = 1;
- return 1;
- }
-
- wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP");
- wpabuf_free(conn->msg_out);
- conn->msg_out = NULL;
- conn->msg_out_pos = 0;
- eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
- conn->write_eloop = 0;
- if (!conn->read_eloop &&
- eloop_register_sock(conn->sock, EVENT_TYPE_READ,
- dpp_controller_rx, conn, NULL) == 0)
- conn->read_eloop = 1;
- if (conn->on_tcp_tx_complete_remove) {
- dpp_connection_remove(conn);
- } else if (conn->ctrl && conn->on_tcp_tx_complete_gas_done &&
- conn->auth) {
- dpp_controller_gas_done(conn);
- } else if (conn->on_tcp_tx_complete_auth_ok) {
- conn->on_tcp_tx_complete_auth_ok = 0;
- dpp_controller_auth_success(conn, 1);
- }
-
- return 0;
-}
-
-
-static int dpp_tcp_send_msg(struct dpp_connection *conn,
- const struct wpabuf *msg)
-{
- wpabuf_free(conn->msg_out);
- conn->msg_out_pos = 0;
- conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
- if (!conn->msg_out)
- return -1;
- wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1);
- wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1,
- wpabuf_len(msg) - 1);
-
- if (dpp_tcp_send(conn) == 1) {
- if (!conn->write_eloop) {
- if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
- dpp_conn_tx_ready,
- conn, NULL) < 0)
- return -1;
- conn->write_eloop = 1;
- }
- }
-
- return 0;
-}
-
-
-static void dpp_controller_start_gas_client(struct dpp_connection *conn)
-{
- struct dpp_authentication *auth = conn->auth;
- struct wpabuf *buf;
- int netrole_ap = 0; /* TODO: make this configurable */
-
- buf = dpp_build_conf_req_helper(auth, "Test", netrole_ap, NULL, NULL);
- if (!buf) {
- wpa_printf(MSG_DEBUG,
- "DPP: No configuration request data available");
- return;
- }
-
- dpp_tcp_send_msg(conn, buf);
- wpabuf_free(buf);
-}
-
-
-static void dpp_controller_auth_success(struct dpp_connection *conn,
- int initiator)
-{
- struct dpp_authentication *auth = conn->auth;
-
- if (!auth)
- return;
-
- wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
- wpa_msg(conn->global->msg_ctx, MSG_INFO,
- DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at Authentication Confirm");
- if (auth->configurator) {
- /* Prevent GAS response */
- auth->auth_success = 0;
- }
- return;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!auth->configurator)
- dpp_controller_start_gas_client(conn);
-}
-
-
-static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
-{
- struct dpp_connection *conn = eloop_ctx;
-
- wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock);
- dpp_tcp_send(conn);
-}
-
-
-static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen,
- const struct hostapd_ip_addr *ipaddr,
- int port)
-{
- struct sockaddr_in *dst;
-#ifdef CONFIG_IPV6
- struct sockaddr_in6 *dst6;
-#endif /* CONFIG_IPV6 */
-
- switch (ipaddr->af) {
- case AF_INET:
- dst = (struct sockaddr_in *) addr;
- os_memset(dst, 0, sizeof(*dst));
- dst->sin_family = AF_INET;
- dst->sin_addr.s_addr = ipaddr->u.v4.s_addr;
- dst->sin_port = htons(port);
- *addrlen = sizeof(*dst);
- break;
-#ifdef CONFIG_IPV6
- case AF_INET6:
- dst6 = (struct sockaddr_in6 *) addr;
- os_memset(dst6, 0, sizeof(*dst6));
- dst6->sin6_family = AF_INET6;
- os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6,
- sizeof(struct in6_addr));
- dst6->sin6_port = htons(port);
- *addrlen = sizeof(*dst6);
- break;
-#endif /* CONFIG_IPV6 */
- default:
- return -1;
- }
-
- return 0;
-}
-
-
-static struct dpp_connection *
-dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src,
- unsigned int freq)
-{
- struct dpp_connection *conn;
- struct sockaddr_storage addr;
- socklen_t addrlen;
- char txt[100];
-
- if (dl_list_len(&ctrl->conn) >= 15) {
- wpa_printf(MSG_DEBUG,
- "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
- return NULL;
- }
-
- if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen,
- &ctrl->ipaddr, DPP_TCP_PORT) < 0)
- return NULL;
-
- conn = os_zalloc(sizeof(*conn));
- if (!conn)
- return NULL;
-
- conn->global = ctrl->global;
- conn->relay = ctrl;
- os_memcpy(conn->mac_addr, src, ETH_ALEN);
- conn->freq = freq;
-
- conn->sock = socket(AF_INET, SOCK_STREAM, 0);
- if (conn->sock < 0)
- goto fail;
- wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s",
- conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
-
- if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
- strerror(errno));
- goto fail;
- }
-
- if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) {
- if (errno != EINPROGRESS) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
- strerror(errno));
- goto fail;
- }
-
- /*
- * Continue connecting in the background; eloop will call us
- * once the connection is ready (or failed).
- */
- }
-
- if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
- dpp_conn_tx_ready, conn, NULL) < 0)
- goto fail;
- conn->write_eloop = 1;
-
- /* TODO: eloop timeout to clear a connection if it does not complete
- * properly */
-
- dl_list_add(&ctrl->conn, &conn->list);
- return conn;
-fail:
- dpp_connection_free(conn);
- return NULL;
-}
-
-
-static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len)
-{
- struct wpabuf *msg;
-
- msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len);
- if (!msg)
- return NULL;
- wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len);
- wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
- wpabuf_put_data(msg, hdr, DPP_HDR_LEN);
- wpabuf_put_data(msg, buf, len);
- wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
- return msg;
-}
-
-
-static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr,
- const u8 *buf, size_t len)
-{
- u8 type = hdr[DPP_HDR_LEN - 1];
-
- wpa_printf(MSG_DEBUG,
- "DPP: Continue already established Relay/Controller connection for this session");
- wpabuf_free(conn->msg_out);
- conn->msg_out_pos = 0;
- conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
- if (!conn->msg_out) {
- dpp_connection_remove(conn);
- return -1;
- }
-
- /* TODO: for proto ver 1, need to do remove connection based on GAS Resp
- * TX status */
- if (type == DPP_PA_CONFIGURATION_RESULT)
- conn->on_tcp_tx_complete_remove = 1;
- dpp_tcp_send(conn);
- return 0;
-}
-
-
-int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
- const u8 *buf, size_t len, unsigned int freq,
- const u8 *i_bootstrap, const u8 *r_bootstrap)
-{
- struct dpp_relay_controller *ctrl;
- struct dpp_connection *conn;
- u8 type = hdr[DPP_HDR_LEN - 1];
-
- /* Check if there is an already started session for this peer and if so,
- * continue that session (send this over TCP) and return 0.
- */
- if (type != DPP_PA_PEER_DISCOVERY_REQ &&
- type != DPP_PA_PEER_DISCOVERY_RESP &&
- type != DPP_PA_PRESENCE_ANNOUNCEMENT) {
- dl_list_for_each(ctrl, &dpp->controllers,
- struct dpp_relay_controller, list) {
- dl_list_for_each(conn, &ctrl->conn,
- struct dpp_connection, list) {
- if (os_memcmp(src, conn->mac_addr,
- ETH_ALEN) == 0)
- return dpp_relay_tx(conn, hdr, buf, len);
- }
- }
- }
-
- if (!r_bootstrap)
- return -1;
-
- if (type == DPP_PA_PRESENCE_ANNOUNCEMENT) {
- /* TODO: Could send this to all configured Controllers. For now,
- * only the first Controller is supported. */
- ctrl = dl_list_first(&dpp->controllers,
- struct dpp_relay_controller, list);
- } else {
- ctrl = dpp_relay_controller_get(dpp, r_bootstrap);
- }
- if (!ctrl)
- return -1;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Authentication Request for a configured Controller");
- conn = dpp_relay_new_conn(ctrl, src, freq);
- if (!conn)
- return -1;
-
- conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
- if (!conn->msg_out) {
- dpp_connection_remove(conn);
- return -1;
- }
- /* Message will be sent in dpp_conn_tx_ready() */
-
- return 0;
-}
-
-
-int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
- size_t data_len)
-{
- struct dpp_relay_controller *ctrl;
- struct dpp_connection *conn, *found = NULL;
- struct wpabuf *msg;
-
- /* Check if there is a successfully completed authentication for this
- * and if so, continue that session (send this over TCP) and return 0.
- */
- dl_list_for_each(ctrl, &dpp->controllers,
- struct dpp_relay_controller, list) {
- if (found)
- break;
- dl_list_for_each(conn, &ctrl->conn,
- struct dpp_connection, list) {
- if (os_memcmp(src, conn->mac_addr,
- ETH_ALEN) == 0) {
- found = conn;
- break;
- }
- }
- }
-
- if (!found)
- return -1;
-
- msg = wpabuf_alloc(4 + 1 + data_len);
- if (!msg)
- return -1;
- wpabuf_put_be32(msg, 1 + data_len);
- wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ);
- wpabuf_put_data(msg, data, data_len);
- wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
-
- wpabuf_free(conn->msg_out);
- conn->msg_out_pos = 0;
- conn->msg_out = msg;
- dpp_tcp_send(conn);
- return 0;
-}
-
-
-static void dpp_controller_free(struct dpp_controller *ctrl)
-{
- struct dpp_connection *conn, *tmp;
-
- if (!ctrl)
- return;
-
- dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
- list)
- dpp_connection_remove(conn);
-
- if (ctrl->sock >= 0) {
- close(ctrl->sock);
- eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ);
- }
- os_free(ctrl->configurator_params);
- os_free(ctrl);
-}
-
-
-static int dpp_controller_rx_auth_req(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf, size_t len)
-{
- const u8 *r_bootstrap, *i_bootstrap;
- u16 r_bootstrap_len, i_bootstrap_len;
- struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
-
- if (!conn->ctrl)
- return 0;
-
- wpa_printf(MSG_DEBUG, "DPP: Authentication Request");
-
- r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
- &r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_INFO,
- "Missing or invalid required Responder Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
- r_bootstrap, r_bootstrap_len);
-
- i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
- &i_bootstrap_len);
- if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_INFO,
- "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
- i_bootstrap, i_bootstrap_len);
-
- /* Try to find own and peer bootstrapping key matches based on the
- * received hash values */
- dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap,
- &own_bi, &peer_bi);
- if (!own_bi) {
- wpa_printf(MSG_INFO,
- "No matching own bootstrapping key found - ignore message");
- return -1;
- }
-
- if (conn->auth) {
- wpa_printf(MSG_INFO,
- "Already in DPP authentication exchange - ignore new one");
- return 0;
- }
-
- conn->auth = dpp_auth_req_rx(conn->ctrl->global,
- conn->ctrl->global->msg_ctx,
- conn->ctrl->allowed_roles,
- conn->ctrl->qr_mutual,
- peer_bi, own_bi, -1, hdr, buf, len);
- if (!conn->auth) {
- wpa_printf(MSG_DEBUG, "DPP: No response generated");
- return -1;
- }
-
- if (dpp_set_configurator(conn->auth,
- conn->ctrl->configurator_params) < 0) {
- dpp_connection_remove(conn);
- return -1;
- }
-
- return dpp_tcp_send_msg(conn, conn->auth->resp_msg);
-}
-
-
-static int dpp_controller_rx_auth_resp(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf, size_t len)
-{
- struct dpp_authentication *auth = conn->auth;
- struct wpabuf *msg;
- int res;
-
- if (!auth)
- return -1;
-
- wpa_printf(MSG_DEBUG, "DPP: Authentication Response");
-
- msg = dpp_auth_resp_rx(auth, hdr, buf, len);
- if (!msg) {
- if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Start wait for full response");
- return -1;
- }
- wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
- dpp_connection_remove(conn);
- return -1;
- }
-
- conn->on_tcp_tx_complete_auth_ok = 1;
- res = dpp_tcp_send_msg(conn, msg);
- wpabuf_free(msg);
- return res;
-}
-
-
-static int dpp_controller_rx_auth_conf(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf, size_t len)
-{
- struct dpp_authentication *auth = conn->auth;
-
- wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation");
-
- if (!auth) {
- wpa_printf(MSG_DEBUG,
- "DPP: No DPP Authentication in progress - drop");
- return -1;
- }
-
- if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
- return -1;
- }
-
- dpp_controller_auth_success(conn, 0);
- return 0;
-}
-
-
-static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
- void *timeout_ctx)
-{
- struct dpp_connection *conn = eloop_ctx;
-
- if (!conn->auth->waiting_conf_result)
- return;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Timeout while waiting for Connection Status Result");
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONN_STATUS_RESULT "timeout");
- dpp_connection_remove(conn);
-}
-
-
-static int dpp_controller_rx_conf_result(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf,
- size_t len)
-{
- struct dpp_authentication *auth = conn->auth;
- enum dpp_status_error status;
-
- if (!conn->ctrl)
- return 0;
-
- wpa_printf(MSG_DEBUG, "DPP: Configuration Result");
-
- if (!auth || !auth->waiting_conf_result) {
- wpa_printf(MSG_DEBUG,
- "DPP: No DPP Configuration waiting for result - drop");
- return -1;
- }
-
- status = dpp_conf_result_rx(auth, hdr, buf, len);
- if (status == DPP_STATUS_OK && auth->send_conn_status) {
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONF_SENT "wait_conn_status=1");
- wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
- eloop_cancel_timeout(
- dpp_controller_conn_status_result_wait_timeout,
- conn, NULL);
- eloop_register_timeout(
- 16, 0, dpp_controller_conn_status_result_wait_timeout,
- conn, NULL);
- return 0;
- }
- if (status == DPP_STATUS_OK)
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONF_SENT);
- else
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONF_FAILED);
- return -1; /* to remove the completed connection */
-}
-
-
-static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf,
- size_t len)
-{
- struct dpp_authentication *auth = conn->auth;
- enum dpp_status_error status;
- u8 ssid[SSID_MAX_LEN];
- size_t ssid_len = 0;
- char *channel_list = NULL;
-
- if (!conn->ctrl)
- return 0;
-
- wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
-
- if (!auth || !auth->waiting_conn_status_result) {
- wpa_printf(MSG_DEBUG,
- "DPP: No DPP Configuration waiting for connection status result - drop");
- return -1;
- }
-
- status = dpp_conn_status_result_rx(auth, hdr, buf, len,
- ssid, &ssid_len, &channel_list);
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONN_STATUS_RESULT
- "result=%d ssid=%s channel_list=%s",
- status, wpa_ssid_txt(ssid, ssid_len),
- channel_list ? channel_list : "N/A");
- os_free(channel_list);
- return -1; /* to remove the completed connection */
-}
-
-
-static int dpp_controller_rx_presence_announcement(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf,
- size_t len)
-{
- const u8 *r_bootstrap;
- u16 r_bootstrap_len;
- struct dpp_bootstrap_info *peer_bi;
- struct dpp_authentication *auth;
- struct dpp_global *dpp = conn->ctrl->global;
-
- if (conn->auth) {
- wpa_printf(MSG_DEBUG,
- "DPP: Ignore Presence Announcement during ongoing Authentication");
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
-
- r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
- &r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
- wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Missing or invalid required Responder Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
- r_bootstrap, r_bootstrap_len);
- peer_bi = dpp_bootstrap_find_chirp(dpp, r_bootstrap);
- if (!peer_bi) {
- wpa_printf(MSG_DEBUG,
- "DPP: No matching bootstrapping information found");
- return -1;
- }
-
- auth = dpp_auth_init(dpp, dpp->msg_ctx, peer_bi, NULL,
- DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
- if (!auth)
- return -1;
- if (dpp_set_configurator(conn->auth,
- conn->ctrl->configurator_params) < 0) {
- dpp_auth_deinit(auth);
- dpp_connection_remove(conn);
- return -1;
- }
-
- conn->auth = auth;
- return dpp_tcp_send_msg(conn, conn->auth->req_msg);
-}
-
-
-static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg,
- size_t len)
-{
- const u8 *pos, *end;
- u8 type;
-
- wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP");
- pos = msg;
- end = msg + len;
-
- if (end - pos < DPP_HDR_LEN ||
- WPA_GET_BE24(pos) != OUI_WFA ||
- pos[3] != DPP_OUI_TYPE) {
- wpa_printf(MSG_DEBUG, "DPP: Unrecognized header");
- return -1;
- }
-
- if (pos[4] != 1) {
- wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u",
- pos[4]);
- return -1;
- }
- type = pos[5];
- wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type);
- pos += DPP_HDR_LEN;
-
- wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes",
- pos, end - pos);
- if (dpp_check_attrs(pos, end - pos) < 0)
- return -1;
-
- if (conn->relay) {
- wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
- conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr,
- conn->freq, msg, len);
- return 0;
- }
-
- switch (type) {
- case DPP_PA_AUTHENTICATION_REQ:
- return dpp_controller_rx_auth_req(conn, msg, pos, end - pos);
- case DPP_PA_AUTHENTICATION_RESP:
- return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos);
- case DPP_PA_AUTHENTICATION_CONF:
- return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos);
- case DPP_PA_CONFIGURATION_RESULT:
- return dpp_controller_rx_conf_result(conn, msg, pos, end - pos);
- case DPP_PA_CONNECTION_STATUS_RESULT:
- return dpp_controller_rx_conn_status_result(conn, msg, pos,
- end - pos);
- case DPP_PA_PRESENCE_ANNOUNCEMENT:
- return dpp_controller_rx_presence_announcement(conn, msg, pos,
- end - pos);
- default:
- /* TODO: missing messages types */
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported frame subtype %d", type);
- return -1;
- }
-}
-
-
-static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
- size_t len)
-{
- const u8 *pos, *end, *next;
- u8 dialog_token;
- const u8 *adv_proto;
- u16 slen;
- struct wpabuf *resp, *buf;
- struct dpp_authentication *auth = conn->auth;
-
- if (len < 1 + 2)
- return -1;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Received DPP Configuration Request over TCP");
-
- if (!conn->ctrl || !auth || !auth->auth_success) {
- wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
- return -1;
- }
-
- pos = msg;
- end = msg + len;
-
- dialog_token = *pos++;
- adv_proto = pos++;
- slen = *pos++;
- if (*adv_proto != WLAN_EID_ADV_PROTO ||
- slen > end - pos || slen < 2)
- return -1;
-
- next = pos + slen;
- pos++; /* skip QueryRespLenLimit and PAME-BI */
-
- if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
- pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
- pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
- return -1;
-
- pos = next;
- /* Query Request */
- if (end - pos < 2)
- return -1;
- slen = WPA_GET_LE16(pos);
- pos += 2;
- if (slen > end - pos)
- return -1;
-
- resp = dpp_conf_req_rx(auth, pos, slen);
- if (!resp)
- return -1;
-
- buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp));
- if (!buf) {
- wpabuf_free(resp);
- return -1;
- }
-
- wpabuf_put_be32(buf, 18 + wpabuf_len(resp));
-
- wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
- wpabuf_put_u8(buf, dialog_token);
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
- wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
-
- dpp_write_adv_proto(buf);
- dpp_write_gas_query(buf, resp);
- wpabuf_free(resp);
-
- /* Send Config Response over TCP; GAS fragmentation is taken care of by
- * the Relay */
- wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
- wpabuf_free(conn->msg_out);
- conn->msg_out_pos = 0;
- conn->msg_out = buf;
- conn->on_tcp_tx_complete_gas_done = 1;
- dpp_tcp_send(conn);
- return 0;
-}
-
-
-static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
-{
- struct dpp_authentication *auth = conn->auth;
- int res;
- struct wpabuf *msg;
- enum dpp_status_error status;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Configuration Response for local stack from TCP");
-
- res = dpp_conf_resp_rx(auth, resp);
- wpabuf_free(resp);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
- return -1;
- }
-
- if (conn->global->process_conf_obj)
- res = conn->global->process_conf_obj(conn->global->cb_ctx,
- auth);
- else
- res = 0;
-
- if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK)
- return -1;
-
- wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
- status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK;
- msg = dpp_build_conf_result(auth, status);
- if (!msg)
- return -1;
-
- conn->on_tcp_tx_complete_remove = 1;
- res = dpp_tcp_send_msg(conn, msg);
- wpabuf_free(msg);
-
- /* This exchange will be terminated in the TX status handler */
-
- return res;
-}
-
-
-static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
- size_t len)
-{
- struct wpabuf *buf;
- u8 dialog_token;
- const u8 *pos, *end, *next, *adv_proto;
- u16 status, slen;
-
- if (len < 5 + 2)
- return -1;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Received DPP Configuration Response over TCP");
-
- pos = msg;
- end = msg + len;
-
- dialog_token = *pos++;
- status = WPA_GET_LE16(pos);
- if (status != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status);
- return -1;
- }
- pos += 2;
- pos += 2; /* ignore GAS Comeback Delay */
-
- adv_proto = pos++;
- slen = *pos++;
- if (*adv_proto != WLAN_EID_ADV_PROTO ||
- slen > end - pos || slen < 2)
- return -1;
-
- next = pos + slen;
- pos++; /* skip QueryRespLenLimit and PAME-BI */
-
- if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
- pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
- pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
- return -1;
-
- pos = next;
- /* Query Response */
- if (end - pos < 2)
- return -1;
- slen = WPA_GET_LE16(pos);
- pos += 2;
- if (slen > end - pos)
- return -1;
-
- buf = wpabuf_alloc(slen);
- if (!buf)
- return -1;
- wpabuf_put_data(buf, pos, slen);
-
- if (!conn->relay && !conn->ctrl)
- return dpp_tcp_rx_gas_resp(conn, buf);
-
- if (!conn->relay) {
- wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
- wpabuf_free(buf);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
- conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr,
- dialog_token, 0, buf);
-
- return 0;
-}
-
-
-static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx)
-{
- struct dpp_connection *conn = eloop_ctx;
- int res;
- const u8 *pos;
-
- wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)",
- sd);
-
- if (conn->msg_len_octets < 4) {
- u32 msglen;
-
- res = recv(sd, &conn->msg_len[conn->msg_len_octets],
- 4 - conn->msg_len_octets, 0);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "DPP: recv failed: %s",
- strerror(errno));
- dpp_connection_remove(conn);
- return;
- }
- if (res == 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: No more data available over TCP");
- dpp_connection_remove(conn);
- return;
- }
- wpa_printf(MSG_DEBUG,
- "DPP: Received %d/%d octet(s) of message length field",
- res, (int) (4 - conn->msg_len_octets));
- conn->msg_len_octets += res;
-
- if (conn->msg_len_octets < 4) {
- wpa_printf(MSG_DEBUG,
- "DPP: Need %d more octets of message length field",
- (int) (4 - conn->msg_len_octets));
- return;
- }
-
- msglen = WPA_GET_BE32(conn->msg_len);
- wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen);
- if (msglen > 65535) {
- wpa_printf(MSG_INFO, "DPP: Unexpectedly long message");
- dpp_connection_remove(conn);
- return;
- }
-
- wpabuf_free(conn->msg);
- conn->msg = wpabuf_alloc(msglen);
- }
-
- if (!conn->msg) {
- wpa_printf(MSG_DEBUG,
- "DPP: No buffer available for receiving the message");
- dpp_connection_remove(conn);
- return;
- }
-
- wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload",
- (unsigned int) wpabuf_tailroom(conn->msg));
-
- res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno));
- dpp_connection_remove(conn);
- return;
- }
- if (res == 0) {
- wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP");
- dpp_connection_remove(conn);
- return;
- }
- wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res);
- wpabuf_put(conn->msg, res);
-
- if (wpabuf_tailroom(conn->msg) > 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Need %u more octets of message payload",
- (unsigned int) wpabuf_tailroom(conn->msg));
- return;
- }
-
- conn->msg_len_octets = 0;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg);
- if (wpabuf_len(conn->msg) < 1) {
- dpp_connection_remove(conn);
- return;
- }
-
- pos = wpabuf_head(conn->msg);
- switch (*pos) {
- case WLAN_PA_VENDOR_SPECIFIC:
- if (dpp_controller_rx_action(conn, pos + 1,
- wpabuf_len(conn->msg) - 1) < 0)
- dpp_connection_remove(conn);
- break;
- case WLAN_PA_GAS_INITIAL_REQ:
- if (dpp_controller_rx_gas_req(conn, pos + 1,
- wpabuf_len(conn->msg) - 1) < 0)
- dpp_connection_remove(conn);
- break;
- case WLAN_PA_GAS_INITIAL_RESP:
- if (dpp_rx_gas_resp(conn, pos + 1,
- wpabuf_len(conn->msg) - 1) < 0)
- dpp_connection_remove(conn);
- break;
- default:
- wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u",
- *pos);
- break;
- }
-}
-
-
-static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
-{
- struct dpp_controller *ctrl = eloop_ctx;
- struct sockaddr_in addr;
- socklen_t addr_len = sizeof(addr);
- int fd;
- struct dpp_connection *conn;
-
- wpa_printf(MSG_DEBUG, "DPP: New TCP connection");
-
- fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len);
- if (fd < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to accept new connection: %s",
- strerror(errno));
- return;
- }
- wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
- inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
-
- conn = os_zalloc(sizeof(*conn));
- if (!conn)
- goto fail;
-
- conn->global = ctrl->global;
- conn->ctrl = ctrl;
- conn->sock = fd;
-
- if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
- strerror(errno));
- goto fail;
- }
-
- if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
- dpp_controller_rx, conn, NULL) < 0)
- goto fail;
- conn->read_eloop = 1;
-
- /* TODO: eloop timeout to expire connections that do not complete in
- * reasonable time */
- dl_list_add(&ctrl->conn, &conn->list);
- return;
-
-fail:
- close(fd);
- os_free(conn);
-}
-
-
-int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
- const struct hostapd_ip_addr *addr, int port)
-{
- struct dpp_connection *conn;
- struct sockaddr_storage saddr;
- socklen_t addrlen;
- const u8 *hdr, *pos, *end;
- char txt[100];
-
- wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d",
- hostapd_ip_txt(addr, txt, sizeof(txt)), port);
- if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen,
- addr, port) < 0) {
- dpp_auth_deinit(auth);
- return -1;
- }
-
- conn = os_zalloc(sizeof(*conn));
- if (!conn) {
- dpp_auth_deinit(auth);
- return -1;
- }
-
- conn->global = dpp;
- conn->auth = auth;
- conn->sock = socket(AF_INET, SOCK_STREAM, 0);
- if (conn->sock < 0)
- goto fail;
-
- if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
- strerror(errno));
- goto fail;
- }
-
- if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) {
- if (errno != EINPROGRESS) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
- strerror(errno));
- goto fail;
- }
-
- /*
- * Continue connecting in the background; eloop will call us
- * once the connection is ready (or failed).
- */
- }
-
- if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
- dpp_conn_tx_ready, conn, NULL) < 0)
- goto fail;
- conn->write_eloop = 1;
-
- hdr = wpabuf_head(auth->req_msg);
- end = hdr + wpabuf_len(auth->req_msg);
- hdr += 2; /* skip Category and Actiom */
- pos = hdr + DPP_HDR_LEN;
- conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos);
- if (!conn->msg_out)
- goto fail;
- /* Message will be sent in dpp_conn_tx_ready() */
-
- /* TODO: eloop timeout to clear a connection if it does not complete
- * properly */
- dl_list_add(&dpp->tcp_init, &conn->list);
- return 0;
-fail:
- dpp_connection_free(conn);
- return -1;
-}
-
-
-int dpp_controller_start(struct dpp_global *dpp,
- struct dpp_controller_config *config)
-{
- struct dpp_controller *ctrl;
- int on = 1;
- struct sockaddr_in sin;
- int port;
-
- if (!dpp || dpp->controller)
- return -1;
-
- ctrl = os_zalloc(sizeof(*ctrl));
- if (!ctrl)
- return -1;
- ctrl->global = dpp;
- if (config->configurator_params)
- ctrl->configurator_params =
- os_strdup(config->configurator_params);
- dl_list_init(&ctrl->conn);
- /* TODO: configure these somehow */
- ctrl->allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
- ctrl->qr_mutual = 0;
-
- ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
- if (ctrl->sock < 0)
- goto fail;
-
- if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR,
- &on, sizeof(on)) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: setsockopt(SO_REUSEADDR) failed: %s",
- strerror(errno));
- /* try to continue anyway */
- }
-
- if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) {
- wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
- strerror(errno));
- goto fail;
- }
-
- /* TODO: IPv6 */
- os_memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT;
- sin.sin_port = htons(port);
- if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- wpa_printf(MSG_INFO,
- "DPP: Failed to bind Controller TCP port: %s",
- strerror(errno));
- goto fail;
- }
- if (listen(ctrl->sock, 10 /* max backlog */) < 0 ||
- fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 ||
- eloop_register_sock(ctrl->sock, EVENT_TYPE_READ,
- dpp_controller_tcp_cb, ctrl, NULL))
- goto fail;
-
- dpp->controller = ctrl;
- wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port);
- return 0;
-fail:
- dpp_controller_free(ctrl);
- return -1;
-}
-
-
-void dpp_controller_stop(struct dpp_global *dpp)
-{
- if (dpp) {
- dpp_controller_free(dpp->controller);
- dpp->controller = NULL;
- }
-}
-
-
struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi)
{
struct wpabuf *msg;
@@ -12323,5 +4066,4 @@
"DPP: Presence Announcement frame attributes", msg);
return msg;
}
-
#endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 585d398..d77762e 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -20,6 +20,18 @@
struct crypto_ecdh;
struct hostapd_ip_addr;
struct dpp_global;
+struct json_token;
+
+#ifdef CONFIG_TESTING_OPTIONS
+#define DPP_VERSION (dpp_version_override)
+extern int dpp_version_override;
+#else /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_DPP2
+#define DPP_VERSION 2
+#else
+#define DPP_VERSION 1
+#endif
+#endif /* CONFIG_TESTING_OPTIONS */
#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
#define DPP_TCP_PORT 7871
@@ -94,6 +106,12 @@
DPP_STATUS_CSR_BAD = 13,
};
+/* DPP Reconfig Flags object - connectorKey values */
+enum dpp_connector_key {
+ DPP_CONFIG_REUSEKEY = 0,
+ DPP_CONFIG_REPLACEKEY = 1,
+};
+
#define DPP_CAPAB_ENROLLEE BIT(0)
#define DPP_CAPAB_CONFIGURATOR BIT(1)
#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
@@ -131,6 +149,7 @@
char *pk;
unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
unsigned int num_freq;
+ u8 version;
int own;
EVP_PKEY *pubkey;
u8 pubkey_hash[SHA256_MAC_LEN];
@@ -224,6 +243,8 @@
struct dpp_bootstrap_info *tmp_own_bi;
u8 waiting_pubkey_hash[SHA256_MAC_LEN];
int response_pending;
+ int reconfig;
+ enum dpp_connector_key reconfig_connector_key;
enum dpp_status_error auth_resp_status;
enum dpp_status_error conf_resp_status;
u8 peer_mac_addr[ETH_ALEN];
@@ -234,8 +255,11 @@
u8 r_capab;
EVP_PKEY *own_protocol_key;
EVP_PKEY *peer_protocol_key;
+ EVP_PKEY *reconfig_old_protocol_key;
struct wpabuf *req_msg;
struct wpabuf *resp_msg;
+ struct wpabuf *reconfig_req_msg;
+ struct wpabuf *reconfig_resp_msg;
/* Intersection of possible frequencies for initiating DPP
* Authentication exchange */
unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
@@ -253,6 +277,7 @@
u8 k1[DPP_MAX_HASH_LEN];
u8 k2[DPP_MAX_HASH_LEN];
u8 ke[DPP_MAX_HASH_LEN];
+ u8 bk[DPP_MAX_HASH_LEN];
int initiator;
int waiting_auth_resp;
int waiting_auth_conf;
@@ -265,6 +290,7 @@
int waiting_conf_result;
int waiting_conn_status_result;
int auth_success;
+ bool reconfig_success;
struct wpabuf *conf_req;
const struct wpabuf *conf_resp; /* owned by GAS server */
struct dpp_configuration *conf_ap;
@@ -292,6 +318,7 @@
int conn_status_requested;
int akm_use_selector;
int configurator_set;
+ u8 transaction_id;
#ifdef CONFIG_TESTING_OPTIONS
char *config_obj_override;
char *discovery_override;
@@ -307,8 +334,11 @@
unsigned int id;
int own;
EVP_PKEY *csign;
+ u8 kid_hash[SHA256_MAC_LEN];
char *kid;
const struct dpp_curve_params *curve;
+ char *connector; /* own Connector for reconfiguration */
+ EVP_PKEY *connector_key;
};
struct dpp_introduction {
@@ -331,6 +361,7 @@
struct dpp_controller_config {
const char *configurator_params;
int tcp_port;
+ u8 allowed_roles;
};
#ifdef CONFIG_TESTING_OPTIONS
@@ -591,6 +622,8 @@
char *buf, size_t buflen);
int dpp_configurator_from_backup(struct dpp_global *dpp,
struct dpp_asymmetric_key *key);
+struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp,
+ const u8 *kid);
int dpp_relay_add_controller(struct dpp_global *dpp,
struct dpp_relay_config *config);
int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
@@ -616,5 +649,25 @@
void dpp_global_clear(struct dpp_global *dpp);
void dpp_global_deinit(struct dpp_global *dpp);
+/* dpp_reconfig.c */
+
+struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
+ size_t csign_key_len);
+struct dpp_authentication *
+dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_configurator *conf, unsigned int freq);
+struct dpp_authentication *
+dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
+ const char *own_connector,
+ const u8 *net_access_key, size_t net_access_key_len,
+ const u8 *csign_key, size_t csign_key_len,
+ unsigned int freq, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len);
+struct wpabuf *
+dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len);
+int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len);
+
#endif /* CONFIG_DPP */
#endif /* DPP_H */
diff --git a/src/common/dpp_auth.c b/src/common/dpp_auth.c
new file mode 100644
index 0000000..f79cfef
--- /dev/null
+++ b/src/common/dpp_auth.c
@@ -0,0 +1,1976 @@
+/*
+ * DPP authentication exchange
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/random.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+u8 dpp_protocol_key_override[600];
+size_t dpp_protocol_key_override_len = 0;
+u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
+size_t dpp_nonce_override_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
+ const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+static void dpp_auth_success(struct dpp_authentication *auth)
+{
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication success - clear temporary keys");
+ os_memset(auth->Mx, 0, sizeof(auth->Mx));
+ auth->Mx_len = 0;
+ os_memset(auth->Nx, 0, sizeof(auth->Nx));
+ auth->Nx_len = 0;
+ os_memset(auth->Lx, 0, sizeof(auth->Lx));
+ auth->Lx_len = 0;
+ os_memset(auth->k1, 0, sizeof(auth->k1));
+ os_memset(auth->k2, 0, sizeof(auth->k2));
+
+ auth->auth_success = 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
+ const struct wpabuf *pi,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ unsigned int neg_freq)
+{
+ struct wpabuf *msg;
+ u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+ u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+ u8 *pos;
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end;
+
+ /* Build DPP Authentication Request frame attributes */
+ attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
+ 4 + sizeof(wrapped_data);
+ if (neg_freq > 0)
+ attr_len += 4 + 2;
+#ifdef CONFIG_DPP2
+ attr_len += 5;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Initiator Protocol Key */
+ if (pi) {
+ wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pi));
+ wpabuf_put_buf(msg, pi);
+ }
+
+ /* Channel */
+ if (neg_freq > 0) {
+ u8 op_class, channel;
+
+ if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
+ &channel) ==
+ NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported negotiation frequency request: %d",
+ neg_freq);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_u8(msg, op_class);
+ wpabuf_put_u8(msg, channel);
+ }
+
+#ifdef CONFIG_DPP2
+ /* Protocol Version */
+ if (DPP_VERSION > 1) {
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+ }
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({I-nonce, I-capabilities}k1) */
+ pos = clear;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ goto skip_i_nonce;
+ }
+ if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len - 1);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len - 1);
+ pos += nonce_len - 1;
+ goto skip_i_nonce;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len);
+ pos += nonce_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_nonce:
+ if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
+ goto skip_i_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->i_capab = auth->allowed_roles;
+ *pos++ = auth->i_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
+ pos[-1] = 0;
+ }
+skip_i_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Request frame attributes", msg);
+
+ return msg;
+}
+
+
+static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
+ enum dpp_status_error status,
+ const struct wpabuf *pr,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ const u8 *r_nonce, const u8 *i_nonce,
+ const u8 *wrapped_r_auth,
+ size_t wrapped_r_auth_len,
+ const u8 *siv_key)
+{
+ struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+ 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
+ u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+ u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end, *pos;
+
+ auth->waiting_auth_conf = 1;
+ auth->auth_resp_tries = 0;
+
+ /* Build DPP Authentication Response frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
+#ifdef CONFIG_DPP2
+ attr_len += 5;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* DPP Status */
+ if (status != 255)
+ dpp_build_attr_status(msg, status);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Responder Protocol Key */
+ if (pr) {
+ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pr));
+ wpabuf_put_buf(msg, pr);
+ }
+
+#ifdef CONFIG_DPP2
+ /* Protocol Version */
+ if (auth->peer_version >= 2) {
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+ }
+#endif /* CONFIG_DPP2 */
+
+ attr_end = wpabuf_put(msg, 0);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+ pos = clear;
+
+ if (r_nonce) {
+ /* R-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, r_nonce, nonce_len);
+ pos += nonce_len;
+ }
+
+ if (i_nonce) {
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, i_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
+ pos[nonce_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ pos += nonce_len;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
+ goto skip_r_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* R-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+ DPP_CAPAB_ENROLLEE;
+ *pos++ = auth->r_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
+ pos[-1] = 0;
+ } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - incompatible R-capabilities");
+ if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
+ pos[-1] = 0;
+ else
+ pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
+ DPP_CAPAB_CONFIGURATOR;
+ }
+skip_r_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wrapped_r_auth) {
+ /* {R-auth}ke */
+ WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+ pos += 2;
+ WPA_PUT_LE16(pos, wrapped_r_auth_len);
+ pos += 2;
+ os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+ pos += wrapped_r_auth_len;
+ }
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Response frame attributes", msg);
+ return msg;
+}
+
+
+static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
+{
+ size_t nonce_len;
+ size_t secret_len;
+ struct wpabuf *msg, *pr = NULL;
+ u8 r_auth[4 + DPP_MAX_HASH_LEN];
+ u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
+ size_t wrapped_r_auth_len;
+ int ret = -1;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
+ enum dpp_status_error status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+ if (!auth->own_bi)
+ return -1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+
+ EVP_PKEY_free(auth->own_protocol_key);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pr)
+ goto fail;
+
+ /* ECDH: N = pR * PI */
+ if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
+ auth->Nx, &secret_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ if (auth->own_bi && auth->peer_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_responder(auth) < 0)
+ goto fail;
+ }
+
+ if (dpp_derive_bk_ke(auth) < 0)
+ goto fail;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
+ WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
+ if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
+ goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
+ r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ r_auth, 4 + auth->curve->hash_len,
+ 0, NULL, NULL, wrapped_r_auth) < 0)
+ goto fail;
+ wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
+ wrapped_r_auth, wrapped_r_auth_len);
+ w_r_auth = wrapped_r_auth;
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+ r_nonce = auth->r_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
+ wpabuf_free(pr);
+ pr = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
+ wpabuf_free(pr);
+ pr = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
+ goto fail;
+ } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
+ w_r_auth = NULL;
+ wrapped_r_auth_len = 0;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
+ r_nonce = NULL;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ r_nonce, i_nonce,
+ w_r_auth, wrapped_r_auth_len,
+ auth->k2);
+ if (!msg)
+ goto fail;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ ret = 0;
+fail:
+ wpabuf_free(pr);
+ return ret;
+}
+
+
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->own_bi)
+ return -1;
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ NULL, i_nonce, NULL, 0, auth->k1);
+ if (!msg)
+ return -1;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ return 0;
+}
+
+
+struct dpp_authentication *
+dpp_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, u8 dpp_allowed_roles,
+ int qr_mutual, struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ unsigned int freq, const u8 *hdr, const u8 *attr_start,
+ size_t attr_len)
+{
+ EVP_PKEY *pi = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
+ *channel;
+ u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
+ i_bootstrap_len, channel_len;
+ struct dpp_authentication *auth = NULL;
+#ifdef CONFIG_DPP2
+ const u8 *version;
+ u16 version_len;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Request");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ goto fail;
+ if (peer_bi && peer_bi->configurator_params &&
+ dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
+ goto fail;
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = own_bi->curve;
+ auth->curr_freq = freq;
+
+ auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version && DPP_VERSION > 1) {
+ if (version_len < 1 || version[0] == 0) {
+ dpp_auth_fail(auth,
+ "Invalid Protocol Version attribute");
+ goto fail;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+ }
+#endif /* CONFIG_DPP2 */
+
+ channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
+ &channel_len);
+ if (channel) {
+ int neg_freq;
+
+ if (channel_len < 2) {
+ dpp_auth_fail(auth, "Too short Channel attribute");
+ goto fail;
+ }
+
+ neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
+ channel[0], channel[1], neg_freq);
+ if (neg_freq < 0) {
+ dpp_auth_fail(auth,
+ "Unsupported Channel attribute value");
+ goto fail;
+ }
+
+ if (auth->curr_freq != (unsigned int) neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Changing negotiation channel from %u MHz to %u MHz",
+ freq, neg_freq);
+ auth->curr_freq = neg_freq;
+ }
+ }
+
+ i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
+ &i_proto_len);
+ if (!i_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Initiator Protocol Key attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
+ i_proto, i_proto_len);
+
+ /* M = bR * PI */
+ pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
+ if (!pi) {
+ dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
+ goto fail;
+ }
+ dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
+
+ if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0)
+ goto fail;
+ auth->secret_len = secret_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+ i_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_I_CAPABILITIES,
+ &i_capab_len);
+ if (!i_capab || i_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid I-capabilities");
+ goto fail;
+ }
+ auth->i_capab = i_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ unwrapped = NULL;
+
+ switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
+ case DPP_CAPAB_ENROLLEE:
+ if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ break;
+ case DPP_CAPAB_CONFIGURATOR:
+ if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Enrollee role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ break;
+ case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
+ if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator/Enrollee role");
+ goto not_compatible;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
+ auth->i_capab & DPP_CAPAB_ROLE_MASK);
+ goto fail;
+ }
+
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
+ char hex[SHA256_MAC_LEN * 2 + 1];
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
+ if (dpp_auth_build_resp_status(auth,
+ DPP_STATUS_RESPONSE_PENDING) < 0)
+ goto fail;
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
+ auth->response_pending = 1;
+ os_memcpy(auth->waiting_pubkey_hash,
+ i_bootstrap, i_bootstrap_len);
+ wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
+ i_bootstrap_len);
+ } else {
+ hex[0] = '\0';
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
+ "%s", hex);
+ return auth;
+ }
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ goto fail;
+
+ return auth;
+
+not_compatible:
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "i-capab=0x%02x", auth->i_capab);
+ if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
+ auth->configurator = 1;
+ else
+ auth->configurator = 0;
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
+ goto fail;
+
+ auth->remove_on_tx_status = 1;
+ return auth;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ EVP_PKEY_free(pi);
+ EVP_PKEY_CTX_free(ctx);
+ dpp_auth_deinit(auth);
+ return NULL;
+}
+
+
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+ struct dpp_bootstrap_info *peer_bi)
+{
+ if (!auth || !auth->response_pending ||
+ os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0)
+ return 0;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
+ MACSTR, MAC2STR(auth->peer_mac_addr));
+ auth->peer_bi = peer_bi;
+
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return -1;
+
+ return 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ u8 i_auth[4 + DPP_MAX_HASH_LEN];
+ size_t i_auth_len;
+ u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
+ size_t r_nonce_len;
+ const u8 *addr[2];
+ size_t len[2], attr_len;
+ u8 *wrapped_i_auth;
+ u8 *wrapped_r_nonce;
+ u8 *attr_start, *attr_end;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
+
+ i_auth_len = 4 + auth->curve->hash_len;
+ r_nonce_len = 4 + auth->curve->nonce_len;
+ /* Build DPP Authentication Confirmation frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ if (auth->own_bi)
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
+ goto skip_wrapped_data;
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ i_auth_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (status == DPP_STATUS_OK) {
+ /* I-auth wrapped with ke */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+ wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ goto skip_i_auth;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
+ * 1) */
+ WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+ WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+ if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
+ i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+skip_i_auth:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ i_auth, i_auth_len,
+ 2, addr, len, wrapped_i_auth) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+ wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+ } else {
+ /* R-nonce wrapped with k2 */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
+ wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
+
+ WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
+ WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
+ os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
+
+ if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
+ r_nonce, r_nonce_len,
+ 2, addr, len, wrapped_r_nonce) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
+ wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Confirmation frame attributes",
+ msg);
+ if (status == DPP_STATUS_OK)
+ dpp_auth_success(auth);
+
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
+{
+ struct dpp_bootstrap_info *bi;
+
+ if (auth->own_bi)
+ return 0; /* already generated */
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return -1;
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ if (dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0) < 0 ||
+ dpp_gen_uri(bi) < 0)
+ goto fail;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Auto-generated own bootstrapping key info: URI %s",
+ bi->uri);
+
+ auth->tmp_own_bi = auth->own_bi = bi;
+
+ return 0;
+fail:
+ dpp_bootstrap_info_free(bi);
+ return -1;
+}
+
+
+struct dpp_authentication * dpp_auth_init(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ u8 dpp_allowed_roles,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ struct dpp_authentication *auth;
+ size_t nonce_len;
+ size_t secret_len;
+ struct wpabuf *pi = NULL;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ return NULL;
+ if (peer_bi->configurator_params &&
+ dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
+ goto fail;
+ auth->initiator = 1;
+ auth->waiting_auth_resp = 1;
+ auth->allowed_roles = dpp_allowed_roles;
+ auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = peer_bi->curve;
+
+ if (dpp_autogen_bootstrap_key(auth) < 0 ||
+ dpp_prepare_channel_list(auth, neg_freq, own_modes, num_modes) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pi)
+ goto fail;
+
+ /* ECDH: M = pI * BR */
+ if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey,
+ auth->Mx, &secret_len) < 0)
+ goto fail;
+ auth->secret_len = secret_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
+ wpabuf_free(pi);
+ pi = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
+ wpabuf_free(pi);
+ pi = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (neg_freq && auth->num_freq == 1 && auth->freq[0] == neg_freq)
+ neg_freq = 0;
+ auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
+ i_pubkey_hash, neg_freq);
+ if (!auth->req_msg)
+ goto fail;
+
+out:
+ wpabuf_free(pi);
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+static void
+dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data, u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *i_nonce, *r_capab;
+ u16 i_nonce_len, r_capab_len;
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported incompatible roles");
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported more time needed");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported failure (status %d)",
+ status);
+ dpp_auth_fail(auth, "Responder reported failure");
+ return;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "r-capab=0x%02x", auth->r_capab);
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+
+ if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
+ role);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue waiting for full DPP Authentication Response");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_RESPONSE_PENDING "%s",
+ auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
+ }
+ }
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+}
+
+
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ EVP_PKEY *pr;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL, *unwrapped2 = NULL;
+ size_t unwrapped_len = 0, unwrapped2_len = 0;
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
+ *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
+ wrapped2_len, r_auth_len;
+ u8 r_auth2[DPP_MAX_HASH_LEN];
+ u8 role;
+#ifdef CONFIG_DPP2
+ const u8 *version;
+ u16 version_len;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Response");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->initiator || !auth->peer_bi || auth->reconfig) {
+ dpp_auth_fail(auth, "Unexpected Authentication Response");
+ return NULL;
+ }
+
+ auth->waiting_auth_resp = 0;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Unexpected Responder Bootstrapping Key Hash value");
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ return NULL;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->own_bi ||
+ os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash attribute did not match");
+ return NULL;
+ }
+ } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
+ /* PKEX bootstrapping mandates use of mutual authentication */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ } else if (auth->own_bi &&
+ auth->own_bi->type == DPP_BOOTSTRAP_NFC_URI &&
+ auth->own_bi->nfc_negotiated) {
+ /* NFC negotiated connection handover bootstrapping mandates
+ * use of mutual authentication */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+
+ auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version && DPP_VERSION > 1) {
+ if (version_len < 1 || version[0] == 0) {
+ dpp_auth_fail(auth,
+ "Invalid Protocol Version attribute");
+ return NULL;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+ }
+#endif /* CONFIG_DPP2 */
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ auth->auth_resp_status = status[0];
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_resp_rx_status(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+ return NULL;
+ }
+
+ if (!i_bootstrap && auth->own_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder decided not to use mutual authentication");
+ auth->own_bi = NULL;
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
+ auth->own_bi != NULL);
+
+ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+ &r_proto_len);
+ if (!r_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Responder Protocol Key attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+ r_proto, r_proto_len);
+
+ /* N = pI * PR */
+ pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
+ if (!pr) {
+ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+ return NULL;
+ }
+ dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+
+ if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) {
+ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
+ goto fail;
+ }
+ EVP_PKEY_free(auth->peer_protocol_key);
+ auth->peer_protocol_key = pr;
+ pr = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+ os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ if (auth->own_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_initiator(auth) < 0)
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+ if ((auth->allowed_roles ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
+ (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
+ /* Peer selected its role, so move from "either role" to the
+ * role that is compatible with peer's selection. */
+ auth->configurator = role == DPP_CAPAB_ENROLLEE;
+ wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
+ auth->configurator ? "Configurator" : "Enrollee");
+ } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Unexpected role in R-capabilities 0x%02x",
+ role);
+ if (role != DPP_CAPAB_ENROLLEE &&
+ role != DPP_CAPAB_CONFIGURATOR)
+ goto fail;
+ bin_clear_free(unwrapped, unwrapped_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
+ }
+
+ wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
+ if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Secondary Wrapped Data");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped2, wrapped2_len);
+
+ if (dpp_derive_bk_ke(auth) < 0)
+ goto fail;
+
+ unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
+ unwrapped2 = os_malloc(unwrapped2_len);
+ if (!unwrapped2)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped2, wrapped2_len,
+ 0, NULL, NULL, unwrapped2) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped2, unwrapped2_len);
+
+ if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
+ dpp_auth_fail(auth,
+ "Invalid attribute in secondary unwrapped data");
+ goto fail;
+ }
+
+ r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
+ &r_auth_len);
+ if (!r_auth || r_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Responder Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
+ r_auth, r_auth_len);
+ /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ if (dpp_gen_r_auth(auth, r_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
+ r_auth2, r_auth_len);
+ if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - Authentication Response in place of Confirm");
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return NULL;
+ return wpabuf_dup(auth->resp_msg);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return dpp_auth_build_conf(auth, DPP_STATUS_OK);
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ EVP_PKEY_free(pr);
+ return NULL;
+}
+
+
+static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
+ const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data,
+ u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *r_nonce;
+ u16 r_nonce_len;
+
+ /* Authentication Confirm failure cases are expected to include
+ * {R-nonce}k2 in the Wrapped Data attribute. */
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped) {
+ dpp_auth_fail(auth, "Authentication failed");
+ goto fail;
+ }
+ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+ wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
+ r_nonce, r_nonce_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
+ auth->r_nonce, r_nonce_len);
+ dpp_auth_fail(auth, "R-nonce mismatch");
+ goto fail;
+ }
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE)
+ dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
+ else if (status == DPP_STATUS_AUTH_FAILURE)
+ dpp_auth_fail(auth, "Peer reported authentication failure)");
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
+
+
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ i_auth_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ u8 i_auth2[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return -1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (auth->initiator || !auth->own_bi || !auth->waiting_auth_conf ||
+ auth->reconfig) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: initiator=%d own_bi=%d waiting_auth_conf=%d",
+ auth->initiator, !!auth->own_bi,
+ auth->waiting_auth_conf);
+ dpp_auth_fail(auth, "Unexpected Authentication Confirm");
+ return -1;
+ }
+
+ auth->waiting_auth_conf = 0;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ dpp_auth_fail(auth,
+ "Responder Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->peer_bi ||
+ os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+ } else if (auth->peer_bi) {
+ /* Mutual authentication and peer did not include its
+ * Bootstrapping Key Hash attribute. */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
+ status[0] == DPP_STATUS_AUTH_FAILURE)
+ return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_fail(auth, "Authentication failed");
+ return -1;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ return -1;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+ &i_auth_len);
+ if (!i_auth || i_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Initiator Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
+ i_auth, i_auth_len);
+ /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ if (dpp_gen_i_auth(auth, i_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
+ i_auth2, i_auth_len);
+ if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
+ goto fail;
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ dpp_auth_success(auth);
+ return 0;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
diff --git a/src/common/dpp_backup.c b/src/common/dpp_backup.c
new file mode 100644
index 0000000..c675c42
--- /dev/null
+++ b/src/common/dpp_backup.c
@@ -0,0 +1,1227 @@
+/*
+ * DPP configurator backup
+ * Copyright (c) 2019-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "tls/asn1.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+#ifdef CONFIG_DPP2
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
+#endif
+
+
+void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key)
+{
+ while (key) {
+ struct dpp_asymmetric_key *next = key->next;
+
+ EVP_PKEY_free(key->csign);
+ str_clear_free(key->config_template);
+ str_clear_free(key->connector_template);
+ os_free(key);
+ key = next;
+ }
+}
+
+
+static struct wpabuf * dpp_build_conf_params(void)
+{
+ struct wpabuf *buf;
+ size_t len;
+ /* TODO: proper template values */
+ const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
+ const char *connector_template = NULL;
+
+ len = 100 + os_strlen(conf_template);
+ if (connector_template)
+ len += os_strlen(connector_template);
+ buf = wpabuf_alloc(len);
+ if (!buf)
+ return NULL;
+
+ /*
+ * DPPConfigurationParameters ::= SEQUENCE {
+ * configurationTemplate UTF8String,
+ * connectorTemplate UTF8String OPTIONAL}
+ */
+
+ asn1_put_utf8string(buf, conf_template);
+ if (connector_template)
+ asn1_put_utf8string(buf, connector_template);
+ return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_attribute(void)
+{
+ struct wpabuf *conf_params, *attr;
+
+ /*
+ * aa-DPPConfigurationParameters ATTRIBUTE ::=
+ * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+ *
+ * Attribute ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * values SET SIZE(1..MAX) OF Type
+ */
+ conf_params = dpp_build_conf_params();
+ conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
+ ASN1_TAG_SET);
+ if (!conf_params)
+ return NULL;
+
+ attr = wpabuf_alloc(100 + wpabuf_len(conf_params));
+ if (!attr) {
+ wpabuf_clear_free(conf_params);
+ return NULL;
+ }
+
+ asn1_put_oid(attr, &asn1_dpp_config_params_oid);
+ wpabuf_put_buf(attr, conf_params);
+ wpabuf_clear_free(conf_params);
+
+ return asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_key_alg(const struct dpp_curve_params *curve)
+{
+ const struct asn1_oid *oid;
+ struct wpabuf *params, *res;
+
+ switch (curve->ike_group) {
+ case 19:
+ oid = &asn1_prime256v1_oid;
+ break;
+ case 20:
+ oid = &asn1_secp384r1_oid;
+ break;
+ case 21:
+ oid = &asn1_secp521r1_oid;
+ break;
+ case 28:
+ oid = &asn1_brainpoolP256r1_oid;
+ break;
+ case 29:
+ oid = &asn1_brainpoolP384r1_oid;
+ break;
+ case 30:
+ oid = &asn1_brainpoolP512r1_oid;
+ break;
+ default:
+ return NULL;
+ }
+
+ params = wpabuf_alloc(20);
+ if (!params)
+ return NULL;
+ asn1_put_oid(params, oid); /* namedCurve */
+
+ res = asn1_build_alg_id(&asn1_ec_public_key_oid, params);
+ wpabuf_free(params);
+ return res;
+}
+
+
+static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
+{
+ struct wpabuf *key = NULL, *attr, *alg, *priv_key = NULL;
+ EC_KEY *eckey;
+ unsigned char *der = NULL;
+ int der_len;
+
+ eckey = EVP_PKEY_get0_EC_KEY(auth->conf->csign);
+ if (!eckey)
+ return NULL;
+
+ EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ priv_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+
+ alg = dpp_build_key_alg(auth->conf->curve);
+
+ /* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
+ attr = dpp_build_attribute();
+ attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
+ if (!priv_key || !attr || !alg)
+ goto fail;
+
+ /*
+ * OneAsymmetricKey ::= SEQUENCE {
+ * version Version,
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ * privateKey PrivateKey,
+ * attributes [0] Attributes OPTIONAL,
+ * ...,
+ * [[2: publicKey [1] BIT STRING OPTIONAL ]],
+ * ...
+ * }
+ */
+
+ key = wpabuf_alloc(100 + wpabuf_len(alg) + wpabuf_len(priv_key) +
+ wpabuf_len(attr));
+ if (!key)
+ goto fail;
+
+ asn1_put_integer(key, 1); /* version = v2(1) */
+
+ /* PrivateKeyAlgorithmIdentifier */
+ wpabuf_put_buf(key, alg);
+
+ /* PrivateKey ::= OCTET STRING */
+ asn1_put_octet_string(key, priv_key);
+
+ /* [0] Attributes OPTIONAL */
+ asn1_put_hdr(key, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0, wpabuf_len(attr));
+ wpabuf_put_buf(key, attr);
+
+fail:
+ wpabuf_clear_free(attr);
+ wpabuf_clear_free(priv_key);
+ wpabuf_free(alg);
+
+ /*
+ * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+ *
+ * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+ *
+ * OneAsymmetricKey ::= SEQUENCE
+ */
+ return asn1_encaps(asn1_encaps(key,
+ ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE),
+ ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_pbkdf2_alg_id(const struct wpabuf *salt,
+ size_t hash_len)
+{
+ struct wpabuf *params = NULL, *buf = NULL, *prf = NULL;
+ const struct asn1_oid *oid;
+
+ /*
+ * PBKDF2-params ::= SEQUENCE {
+ * salt CHOICE {
+ * specified OCTET STRING,
+ * otherSource AlgorithmIdentifier}
+ * iterationCount INTEGER (1..MAX),
+ * keyLength INTEGER (1..MAX),
+ * prf AlgorithmIdentifier}
+ *
+ * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+ * on Configurator signing key length, prf is
+ * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+ */
+
+ if (hash_len == 32)
+ oid = &asn1_pbkdf2_hmac_sha256_oid;
+ else if (hash_len == 48)
+ oid = &asn1_pbkdf2_hmac_sha384_oid;
+ else if (hash_len == 64)
+ oid = &asn1_pbkdf2_hmac_sha512_oid;
+ else
+ goto fail;
+ prf = asn1_build_alg_id(oid, NULL);
+ if (!prf)
+ goto fail;
+ params = wpabuf_alloc(100 + wpabuf_len(salt) + wpabuf_len(prf));
+ if (!params)
+ goto fail;
+ asn1_put_octet_string(params, salt); /* salt.specified */
+ asn1_put_integer(params, 1000); /* iterationCount */
+ asn1_put_integer(params, hash_len); /* keyLength */
+ wpabuf_put_buf(params, prf);
+ params = asn1_encaps(params, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+ if (!params)
+ goto fail;
+ buf = asn1_build_alg_id(&asn1_pbkdf2_oid, params);
+fail:
+ wpabuf_free(params);
+ wpabuf_free(prf);
+ return buf;
+}
+
+
+static struct wpabuf *
+dpp_build_pw_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+ const struct wpabuf *cont_enc_key)
+{
+ struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
+ *key_enc_alg = NULL, *salt;
+ u8 kek[DPP_MAX_HASH_LEN];
+ const u8 *key;
+ size_t key_len;
+
+ salt = wpabuf_alloc(64);
+ if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
+
+ /* TODO: For initial testing, use ke as the key. Replace this with a
+ * new key once that has been defined. */
+ key = auth->ke;
+ key_len = auth->curve->hash_len;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+ if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
+ kek, hash_len)) {
+ wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+ kek, hash_len);
+
+ enc_key = wpabuf_alloc(hash_len + AES_BLOCK_SIZE);
+ if (!enc_key ||
+ aes_siv_encrypt(kek, hash_len, wpabuf_head(cont_enc_key),
+ wpabuf_len(cont_enc_key), 0, NULL, NULL,
+ wpabuf_put(enc_key, hash_len + AES_BLOCK_SIZE)) < 0)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: encryptedKey", enc_key);
+
+ /*
+ * PasswordRecipientInfo ::= SEQUENCE {
+ * version CMSVersion,
+ * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * encryptedKey EncryptedKey}
+ *
+ * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+ * parameters contains PBKDF2-params SEQUENCE.
+ */
+
+ key_der_alg = dpp_build_pbkdf2_alg_id(salt, hash_len);
+ key_enc_alg = asn1_build_alg_id(&asn1_aes_siv_cmac_aead_256_oid, NULL);
+ if (!key_der_alg || !key_enc_alg)
+ goto fail;
+ pwri = wpabuf_alloc(100 + wpabuf_len(key_der_alg) +
+ wpabuf_len(key_enc_alg) + wpabuf_len(enc_key));
+ if (!pwri)
+ goto fail;
+
+ /* version = 0 */
+ asn1_put_integer(pwri, 0);
+
+ /* [0] KeyDerivationAlgorithmIdentifier */
+ asn1_put_hdr(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0,
+ wpabuf_len(key_der_alg));
+ wpabuf_put_buf(pwri, key_der_alg);
+
+ /* KeyEncryptionAlgorithmIdentifier */
+ wpabuf_put_buf(pwri, key_enc_alg);
+
+ /* EncryptedKey ::= OCTET STRING */
+ asn1_put_octet_string(pwri, enc_key);
+
+fail:
+ wpabuf_clear_free(key_der_alg);
+ wpabuf_free(key_enc_alg);
+ wpabuf_free(enc_key);
+ wpabuf_free(salt);
+ forced_memzero(kek, sizeof(kek));
+ return asn1_encaps(pwri, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf *
+dpp_build_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+ const struct wpabuf *cont_enc_key)
+{
+ struct wpabuf *pwri;
+
+ /*
+ * RecipientInfo ::= CHOICE {
+ * ktri KeyTransRecipientInfo,
+ * kari [1] KeyAgreeRecipientInfo,
+ * kekri [2] KEKRecipientInfo,
+ * pwri [3] PasswordRecipientInfo,
+ * ori [4] OtherRecipientInfo}
+ *
+ * Shall always use the pwri CHOICE.
+ */
+
+ pwri = dpp_build_pw_recipient_info(auth, hash_len, cont_enc_key);
+ return asn1_encaps(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
+}
+
+
+static struct wpabuf *
+dpp_build_enc_cont_info(struct dpp_authentication *auth, size_t hash_len,
+ const struct wpabuf *cont_enc_key)
+{
+ struct wpabuf *key_pkg, *enc_cont_info = NULL, *enc_cont = NULL,
+ *enc_alg;
+ const struct asn1_oid *oid;
+ size_t enc_cont_len;
+
+ /*
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
+ */
+
+ if (hash_len == 32)
+ oid = &asn1_aes_siv_cmac_aead_256_oid;
+ else if (hash_len == 48)
+ oid = &asn1_aes_siv_cmac_aead_384_oid;
+ else if (hash_len == 64)
+ oid = &asn1_aes_siv_cmac_aead_512_oid;
+ else
+ return NULL;
+
+ key_pkg = dpp_build_key_pkg(auth);
+ enc_alg = asn1_build_alg_id(oid, NULL);
+ if (!key_pkg || !enc_alg)
+ goto fail;
+
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+ key_pkg);
+
+ enc_cont_len = wpabuf_len(key_pkg) + AES_BLOCK_SIZE;
+ enc_cont = wpabuf_alloc(enc_cont_len);
+ if (!enc_cont ||
+ aes_siv_encrypt(wpabuf_head(cont_enc_key), wpabuf_len(cont_enc_key),
+ wpabuf_head(key_pkg), wpabuf_len(key_pkg),
+ 0, NULL, NULL,
+ wpabuf_put(enc_cont, enc_cont_len)) < 0)
+ goto fail;
+
+ enc_cont_info = wpabuf_alloc(100 + wpabuf_len(enc_alg) +
+ wpabuf_len(enc_cont));
+ if (!enc_cont_info)
+ goto fail;
+
+ /* ContentType ::= OBJECT IDENTIFIER */
+ asn1_put_oid(enc_cont_info, &asn1_dpp_asymmetric_key_package_oid);
+
+ /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+ wpabuf_put_buf(enc_cont_info, enc_alg);
+
+ /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+ * EncryptedContent ::= OCTET STRING */
+ asn1_put_hdr(enc_cont_info, ASN1_CLASS_CONTEXT_SPECIFIC, 0, 0,
+ wpabuf_len(enc_cont));
+ wpabuf_put_buf(enc_cont_info, enc_cont);
+
+fail:
+ wpabuf_clear_free(key_pkg);
+ wpabuf_free(enc_cont);
+ wpabuf_free(enc_alg);
+ return enc_cont_info;
+}
+
+
+static struct wpabuf * dpp_gen_random(size_t len)
+{
+ struct wpabuf *key;
+
+ key = wpabuf_alloc(len);
+ if (!key || os_get_random(wpabuf_put(key, len), len) < 0) {
+ wpabuf_free(key);
+ key = NULL;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: content-encryption key", key);
+ return key;
+}
+
+
+struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth)
+{
+ struct wpabuf *env = NULL;
+ struct wpabuf *recipient_info = NULL, *enc_cont_info = NULL;
+ struct wpabuf *cont_enc_key = NULL;
+ size_t hash_len;
+
+ if (!auth->conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No Configurator instance selected for the session - cannot build DPPEnvelopedData");
+ return NULL;
+ }
+
+ if (!auth->provision_configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Configurator provisioning not allowed");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Building DPPEnvelopedData");
+
+ hash_len = auth->conf->curve->hash_len;
+ cont_enc_key = dpp_gen_random(hash_len);
+ if (!cont_enc_key)
+ goto fail;
+ recipient_info = dpp_build_recipient_info(auth, hash_len, cont_enc_key);
+ enc_cont_info = dpp_build_enc_cont_info(auth, hash_len, cont_enc_key);
+ if (!recipient_info || !enc_cont_info)
+ goto fail;
+
+ env = wpabuf_alloc(wpabuf_len(recipient_info) +
+ wpabuf_len(enc_cont_info) +
+ 100);
+ if (!env)
+ goto fail;
+
+ /*
+ * DPPEnvelopedData ::= EnvelopedData
+ *
+ * EnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
+ *
+ * For DPP, version is 3, both originatorInfo and
+ * unprotectedAttrs are omitted, and recipientInfos contains a single
+ * RecipientInfo.
+ */
+
+ /* EnvelopedData.version = 3 */
+ asn1_put_integer(env, 3);
+
+ /* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo */
+ asn1_put_set(env, recipient_info);
+
+ /* EncryptedContentInfo ::= SEQUENCE */
+ asn1_put_sequence(env, enc_cont_info);
+
+ env = asn1_encaps(env, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: DPPEnvelopedData", env);
+out:
+ wpabuf_clear_free(cont_enc_key);
+ wpabuf_clear_free(recipient_info);
+ wpabuf_free(enc_cont_info);
+ return env;
+fail:
+ wpabuf_free(env);
+ env = NULL;
+ goto out;
+}
+
+
+struct dpp_enveloped_data {
+ const u8 *enc_cont;
+ size_t enc_cont_len;
+ const u8 *enc_key;
+ size_t enc_key_len;
+ const u8 *salt;
+ size_t pbkdf2_key_len;
+ size_t prf_hash_len;
+};
+
+
+static int dpp_parse_recipient_infos(const u8 *pos, size_t len,
+ struct dpp_enveloped_data *data)
+{
+ struct asn1_hdr hdr;
+ const u8 *end = pos + len;
+ const u8 *next, *e_end;
+ struct asn1_oid oid;
+ int val;
+ const u8 *params;
+ size_t params_len;
+
+ wpa_hexdump(MSG_MSGDUMP, "DPP: RecipientInfos", pos, len);
+
+ /*
+ * RecipientInfo ::= CHOICE {
+ * ktri KeyTransRecipientInfo,
+ * kari [1] KeyAgreeRecipientInfo,
+ * kekri [2] KEKRecipientInfo,
+ * pwri [3] PasswordRecipientInfo,
+ * ori [4] OtherRecipientInfo}
+ *
+ * Shall always use the pwri CHOICE.
+ */
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 3) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected CHOICE [3] (pwri) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: PasswordRecipientInfo",
+ hdr.payload, hdr.length);
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ /*
+ * PasswordRecipientInfo ::= SEQUENCE {
+ * version CMSVersion,
+ * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * encryptedKey EncryptedKey}
+ *
+ * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+ * parameters contains PBKDF2-params SEQUENCE.
+ */
+
+ if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+ return -1;
+ pos = hdr.payload;
+
+ if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: pwri.version != 0");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Remaining PasswordRecipientInfo after version",
+ pos, end - pos);
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected keyDerivationAlgorithm [0] - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ e_end = pos + hdr.length;
+
+ /* KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier */
+ if (asn1_get_alg_id(pos, e_end - pos, &oid, ¶ms, ¶ms_len,
+ &next) < 0)
+ return -1;
+ if (!asn1_oid_equal(&oid, &asn1_pbkdf2_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected KeyDerivationAlgorithmIdentifier %s",
+ buf);
+ return -1;
+ }
+
+ /*
+ * PBKDF2-params ::= SEQUENCE {
+ * salt CHOICE {
+ * specified OCTET STRING,
+ * otherSource AlgorithmIdentifier}
+ * iterationCount INTEGER (1..MAX),
+ * keyLength INTEGER (1..MAX),
+ * prf AlgorithmIdentifier}
+ *
+ * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+ * on Configurator signing key length, prf is
+ * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+ */
+ if (!params ||
+ asn1_get_sequence(params, params_len, &hdr, &e_end) < 0)
+ return -1;
+ pos = hdr.payload;
+
+ if (asn1_get_next(pos, e_end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected OCTETSTRING (salt.specified) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: salt.specified",
+ hdr.payload, hdr.length);
+ if (hdr.length != 64) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected salt length %u",
+ hdr.length);
+ return -1;
+ }
+ data->salt = hdr.payload;
+ pos = hdr.payload + hdr.length;
+
+ if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 1000) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected iterationCount %d", val);
+ return -1;
+ }
+
+ if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 32 && val != 48 && val != 64) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected keyLength %d", val);
+ return -1;
+ }
+ data->pbkdf2_key_len = val;
+
+ if (asn1_get_sequence(pos, e_end - pos, &hdr, NULL) < 0 ||
+ asn1_get_oid(hdr.payload, hdr.length, &oid, &pos) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not parse prf");
+ return -1;
+ }
+ if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha256_oid)) {
+ data->prf_hash_len = 32;
+ } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha384_oid)) {
+ data->prf_hash_len = 48;
+ } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha512_oid)) {
+ data->prf_hash_len = 64;
+ } else {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected PBKDF2-params.prf %s",
+ buf);
+ return -1;
+ }
+
+ pos = next;
+
+ /* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier
+ *
+ * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * id-alg-AES-SIV-CMAC-aed-256, id-alg-AES-SIV-CMAC-aed-384, or
+ * id-alg-AES-SIV-CMAC-aed-512. */
+ if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+ return -1;
+ if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected KeyEncryptionAlgorithmIdentifier %s",
+ buf);
+ return -1;
+ }
+
+ /*
+ * encryptedKey EncryptedKey
+ *
+ * EncryptedKey ::= OCTET STRING
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected OCTETSTRING (pwri.encryptedKey) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: pwri.encryptedKey",
+ hdr.payload, hdr.length);
+ data->enc_key = hdr.payload;
+ data->enc_key_len = hdr.length;
+
+ return 0;
+}
+
+
+static int dpp_parse_encrypted_content_info(const u8 *pos, const u8 *end,
+ struct dpp_enveloped_data *data)
+{
+ struct asn1_hdr hdr;
+ struct asn1_oid oid;
+
+ /*
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
+ */
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+ return -1;
+ wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContentInfo",
+ hdr.payload, hdr.length);
+ if (pos < end) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Unexpected extra data after EncryptedContentInfo",
+ pos, end - pos);
+ return -1;
+ }
+
+ end = pos;
+ pos = hdr.payload;
+
+ /* ContentType ::= OBJECT IDENTIFIER */
+ if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not parse ContentType");
+ return -1;
+ }
+ if (!asn1_oid_equal(&oid, &asn1_dpp_asymmetric_key_package_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected ContentType %s", buf);
+ return -1;
+ }
+
+ /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+ if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+ return -1;
+ if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected ContentEncryptionAlgorithmIdentifier %s",
+ buf);
+ return -1;
+ }
+ /* ignore optional parameters */
+
+ /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+ * EncryptedContent ::= OCTET STRING */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected [0] IMPLICIT (EncryptedContent) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContent",
+ hdr.payload, hdr.length);
+ data->enc_cont = hdr.payload;
+ data->enc_cont_len = hdr.length;
+ return 0;
+}
+
+
+static int dpp_parse_enveloped_data(const u8 *env_data, size_t env_data_len,
+ struct dpp_enveloped_data *data)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+ int val;
+
+ os_memset(data, 0, sizeof(*data));
+
+ /*
+ * DPPEnvelopedData ::= EnvelopedData
+ *
+ * EnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
+ *
+ * CMSVersion ::= INTEGER
+ *
+ * RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
+ *
+ * For DPP, version is 3, both originatorInfo and
+ * unprotectedAttrs are omitted, and recipientInfos contains a single
+ * RecipientInfo.
+ */
+ if (asn1_get_sequence(env_data, env_data_len, &hdr, &end) < 0)
+ return -1;
+ pos = hdr.payload;
+ if (end < env_data + env_data_len) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Unexpected extra data after DPPEnvelopedData",
+ end, env_data + env_data_len - end);
+ return -1;
+ }
+
+ if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 3) {
+ wpa_printf(MSG_DEBUG, "DPP: EnvelopedData.version != 3");
+ return -1;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected SET (RecipientInfos) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ if (dpp_parse_recipient_infos(hdr.payload, hdr.length, data) < 0)
+ return -1;
+ return dpp_parse_encrypted_content_info(hdr.payload + hdr.length, end,
+ data);
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_one_asymmetric_key(const u8 *buf, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos = buf, *end = buf + len, *next;
+ int val;
+ const u8 *params;
+ size_t params_len;
+ struct asn1_oid oid;
+ char txt[80];
+ struct dpp_asymmetric_key *key;
+ EC_KEY *eckey;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: OneAsymmetricKey", buf, len);
+
+ key = os_zalloc(sizeof(*key));
+ if (!key)
+ return NULL;
+
+ /*
+ * OneAsymmetricKey ::= SEQUENCE {
+ * version Version,
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ * privateKey PrivateKey,
+ * attributes [0] Attributes OPTIONAL,
+ * ...,
+ * [[2: publicKey [1] BIT STRING OPTIONAL ]],
+ * ...
+ * }
+ */
+ if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+ goto fail;
+ pos = hdr.payload;
+
+ /* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
+ if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+ goto fail;
+ if (val != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
+ val);
+ goto fail;
+ }
+
+ /* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier */
+ if (asn1_get_alg_id(pos, end - pos, &oid, ¶ms, ¶ms_len,
+ &pos) < 0)
+ goto fail;
+ if (!asn1_oid_equal(&oid, &asn1_ec_public_key_oid)) {
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported PrivateKeyAlgorithmIdentifier %s",
+ txt);
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: PrivateKeyAlgorithmIdentifier params",
+ params, params_len);
+ /*
+ * ECParameters ::= CHOICE {
+ * namedCurve OBJECT IDENTIFIER
+ * -- implicitCurve NULL
+ * -- specifiedCurve SpecifiedECDomain}
+ */
+ if (!params || asn1_get_oid(params, params_len, &oid, &next) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not parse ECParameters.namedCurve");
+ goto fail;
+ }
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ wpa_printf(MSG_MSGDUMP, "DPP: namedCurve %s", txt);
+ /* Assume the curve is identified within ECPrivateKey, so that this
+ * separate indication is not really needed. */
+
+ /*
+ * PrivateKey ::= OCTET STRING
+ * (Contains DER encoding of ECPrivateKey)
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: PrivateKey",
+ hdr.payload, hdr.length);
+ pos = hdr.payload + hdr.length;
+ eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ key->csign = EVP_PKEY_new();
+ if (!key->csign || EVP_PKEY_assign_EC_KEY(key->csign, eckey) != 1) {
+ EC_KEY_free(eckey);
+ goto fail;
+ }
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("DPP: Received c-sign-key", key->csign);
+
+ /*
+ * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
+ *
+ * Exactly one instance of type Attribute in OneAsymmetricKey.
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected [0] Attributes - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: Attributes",
+ hdr.payload, hdr.length);
+ if (hdr.payload + hdr.length < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data at the end of OneAsymmetricKey",
+ hdr.payload + hdr.length,
+ end - (hdr.payload + hdr.length));
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected SET (Attributes) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ if (hdr.payload + hdr.length < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data at the end of OneAsymmetricKey (after SET)",
+ hdr.payload + hdr.length,
+ end - (hdr.payload + hdr.length));
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ /*
+ * OneAsymmetricKeyAttributes ATTRIBUTE ::= {
+ * aa-DPPConfigurationParameters,
+ * ... -- For local profiles
+ * }
+ *
+ * aa-DPPConfigurationParameters ATTRIBUTE ::=
+ * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+ *
+ * Attribute ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * values SET SIZE(1..MAX) OF Type
+ *
+ * Exactly one instance of ATTRIBUTE in attrValues.
+ */
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+ goto fail;
+ if (pos < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data at the end of ATTRIBUTE",
+ pos, end - pos);
+ }
+ end = pos;
+ pos = hdr.payload;
+
+ if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0)
+ goto fail;
+ if (!asn1_oid_equal(&oid, &asn1_dpp_config_params_oid)) {
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected Attribute identifier %s", txt);
+ goto fail;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected SET (Attribute) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ /*
+ * DPPConfigurationParameters ::= SEQUENCE {
+ * configurationTemplate UTF8String,
+ * connectorTemplate UTF8String OPTIONAL}
+ */
+
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPConfigurationParameters",
+ pos, end - pos);
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+ goto fail;
+ if (pos < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data after DPPConfigurationParameters",
+ pos, end - pos);
+ }
+ end = pos;
+ pos = hdr.payload;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_UTF8STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected UTF8STRING (configurationTemplate) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: configurationTemplate",
+ hdr.payload, hdr.length);
+ key->config_template = os_zalloc(hdr.length + 1);
+ if (!key->config_template)
+ goto fail;
+ os_memcpy(key->config_template, hdr.payload, hdr.length);
+
+ pos = hdr.payload + hdr.length;
+
+ if (pos < end) {
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_UTF8STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected UTF8STRING (connectorTemplate) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: connectorTemplate",
+ hdr.payload, hdr.length);
+ key->connector_template = os_zalloc(hdr.length + 1);
+ if (!key->connector_template)
+ goto fail;
+ os_memcpy(key->connector_template, hdr.payload, hdr.length);
+ }
+
+ return key;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse OneAsymmetricKey");
+ dpp_free_asymmetric_key(key);
+ return NULL;
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_dpp_asymmetric_key_package(const u8 *key_pkg, size_t key_pkg_len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos = key_pkg, *end = key_pkg + key_pkg_len;
+ struct dpp_asymmetric_key *first = NULL, *last = NULL, *key;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+ key_pkg, key_pkg_len);
+
+ /*
+ * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+ *
+ * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+ */
+ while (pos < end) {
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0 ||
+ !(key = dpp_parse_one_asymmetric_key(hdr.payload,
+ hdr.length))) {
+ dpp_free_asymmetric_key(first);
+ return NULL;
+ }
+ if (!last) {
+ first = last = key;
+ } else {
+ last->next = key;
+ last = key;
+ }
+ }
+
+ return first;
+}
+
+
+int dpp_conf_resp_env_data(struct dpp_authentication *auth,
+ const u8 *env_data, size_t env_data_len)
+{
+ const u8 *key;
+ size_t key_len;
+ u8 kek[DPP_MAX_HASH_LEN];
+ u8 cont_encr_key[DPP_MAX_HASH_LEN];
+ size_t cont_encr_key_len;
+ int res;
+ u8 *key_pkg;
+ size_t key_pkg_len;
+ struct dpp_enveloped_data data;
+ struct dpp_asymmetric_key *keys;
+
+ wpa_hexdump(MSG_DEBUG, "DPP: DPPEnvelopedData", env_data, env_data_len);
+
+ if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
+ return -1;
+
+ /* TODO: For initial testing, use ke as the key. Replace this with a
+ * new key once that has been defined. */
+ key = auth->ke;
+ key_len = auth->curve->hash_len;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+ if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
+ kek, data.pbkdf2_key_len)) {
+ wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+ kek, data.pbkdf2_key_len);
+
+ if (data.enc_key_len < AES_BLOCK_SIZE ||
+ data.enc_key_len > sizeof(cont_encr_key) + AES_BLOCK_SIZE) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid encryptedKey length");
+ return -1;
+ }
+ res = aes_siv_decrypt(kek, data.pbkdf2_key_len,
+ data.enc_key, data.enc_key_len,
+ 0, NULL, NULL, cont_encr_key);
+ forced_memzero(kek, data.pbkdf2_key_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: AES-SIV decryption of encryptedKey failed");
+ return -1;
+ }
+ cont_encr_key_len = data.enc_key_len - AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: content-encryption key",
+ cont_encr_key, cont_encr_key_len);
+
+ if (data.enc_cont_len < AES_BLOCK_SIZE)
+ return -1;
+ key_pkg_len = data.enc_cont_len - AES_BLOCK_SIZE;
+ key_pkg = os_malloc(key_pkg_len);
+ if (!key_pkg)
+ return -1;
+ res = aes_siv_decrypt(cont_encr_key, cont_encr_key_len,
+ data.enc_cont, data.enc_cont_len,
+ 0, NULL, NULL, key_pkg);
+ forced_memzero(cont_encr_key, cont_encr_key_len);
+ if (res < 0) {
+ bin_clear_free(key_pkg, key_pkg_len);
+ wpa_printf(MSG_DEBUG,
+ "DPP: AES-SIV decryption of encryptedContent failed");
+ return -1;
+ }
+
+ keys = dpp_parse_dpp_asymmetric_key_package(key_pkg, key_pkg_len);
+ bin_clear_free(key_pkg, key_pkg_len);
+ dpp_free_asymmetric_key(auth->conf_key_pkg);
+ auth->conf_key_pkg = keys;
+
+ return keys != NULL;
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
new file mode 100644
index 0000000..8bf2a74
--- /dev/null
+++ b/src/common/dpp_crypto.c
@@ -0,0 +1,2785 @@
+/*
+ * DPP crypto functionality
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/json.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+ sig->r = r;
+ sig->s = s;
+ return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+ const BIGNUM **ps)
+{
+ if (pr)
+ *pr = sig->r;
+ if (ps)
+ *ps = sig->s;
+}
+
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
+#endif
+
+static const struct dpp_curve_params dpp_curves[] = {
+ /* The mandatory to support and the default NIST P-256 curve needs to
+ * be the first entry on this list. */
+ { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
+ { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
+ { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
+ { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
+ { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
+ { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
+ { NULL, 0, 0, 0, 0, NULL, 0, NULL }
+};
+
+
+const struct dpp_curve_params * dpp_get_curve_name(const char *name)
+{
+ int i;
+
+ if (!name)
+ return &dpp_curves[0];
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (os_strcmp(name, dpp_curves[i].name) == 0 ||
+ (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_oid(const ASN1_OBJECT *poid)
+{
+ ASN1_OBJECT *oid;
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ oid = OBJ_txt2obj(dpp_curves[i].name, 0);
+ if (oid && OBJ_cmp(poid, oid) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_nid(int nid)
+{
+ int i, tmp;
+
+ if (!nid)
+ return NULL;
+ for (i = 0; dpp_curves[i].name; i++) {
+ tmp = OBJ_txt2nid(dpp_curves[i].name);
+ if (tmp == nid)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+ const EC_POINT *point)
+{
+ BIGNUM *x, *y;
+ BN_CTX *ctx;
+ char *x_str = NULL, *y_str = NULL;
+
+ if (!wpa_debug_show_keys)
+ return;
+
+ ctx = BN_CTX_new();
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !x || !y ||
+ EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
+ goto fail;
+
+ x_str = BN_bn2hex(x);
+ y_str = BN_bn2hex(y);
+ if (!x_str || !y_str)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+ OPENSSL_free(x_str);
+ OPENSSL_free(y_str);
+ BN_free(x);
+ BN_free(y);
+ BN_CTX_free(ctx);
+}
+
+
+void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+{
+ EC_KEY *eckey;
+ BIO *out;
+ size_t rlen;
+ char *txt;
+ int res;
+ unsigned char *der = NULL;
+ int der_len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ EVP_PKEY_print_private(out, key, 0, NULL);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!eckey)
+ return;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (group && point)
+ dpp_debug_print_point(title, group, point);
+
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
+ OPENSSL_free(der);
+ if (der_len <= 0) {
+ der = NULL;
+ der_len = i2d_EC_PUBKEY(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
+ OPENSSL_free(der);
+ }
+
+ EC_KEY_free(eckey);
+}
+
+
+static int dpp_hash_vector(const struct dpp_curve_params *curve,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ if (curve->hash_len == 32)
+ return sha256_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 48)
+ return sha384_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 64)
+ return sha512_vector(num_elem, addr, len, mac);
+ return -1;
+}
+
+
+int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+ const char *label, u8 *out, size_t outlen)
+{
+ if (hash_len == 32)
+ return hmac_sha256_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 48)
+ return hmac_sha384_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 64)
+ return hmac_sha512_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ return -1;
+}
+
+
+int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 48)
+ return hmac_sha384_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 64)
+ return hmac_sha512_vector(key, key_len, num_elem, addr, len,
+ mac);
+ return -1;
+}
+
+
+static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
+ const u8 *data, size_t data_len, u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256(key, key_len, data, data_len, mac);
+ if (hash_len == 48)
+ return hmac_sha384(key, key_len, data, data_len, mac);
+ if (hash_len == 64)
+ return hmac_sha512(key, key_len, data, data_len, mac);
+ return -1;
+}
+
+
+#ifdef CONFIG_DPP2
+
+static int dpp_pbkdf2_f(size_t hash_len,
+ const u8 *password, size_t password_len,
+ const u8 *salt, size_t salt_len,
+ unsigned int iterations, unsigned int count, u8 *digest)
+{
+ unsigned char tmp[DPP_MAX_HASH_LEN], tmp2[DPP_MAX_HASH_LEN];
+ unsigned int i;
+ size_t j;
+ u8 count_buf[4];
+ const u8 *addr[2];
+ size_t len[2];
+
+ addr[0] = salt;
+ len[0] = salt_len;
+ addr[1] = count_buf;
+ len[1] = 4;
+
+ /* F(P, S, c, i) = U1 xor U2 xor ... Uc
+ * U1 = PRF(P, S || i)
+ * U2 = PRF(P, U1)
+ * Uc = PRF(P, Uc-1)
+ */
+
+ WPA_PUT_BE32(count_buf, count);
+ if (dpp_hmac_vector(hash_len, password, password_len, 2, addr, len,
+ tmp))
+ return -1;
+ os_memcpy(digest, tmp, hash_len);
+
+ for (i = 1; i < iterations; i++) {
+ if (dpp_hmac(hash_len, password, password_len, tmp, hash_len,
+ tmp2))
+ return -1;
+ os_memcpy(tmp, tmp2, hash_len);
+ for (j = 0; j < hash_len; j++)
+ digest[j] ^= tmp2[j];
+ }
+
+ return 0;
+}
+
+
+int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
+ const u8 *salt, size_t salt_len, unsigned int iterations,
+ u8 *buf, size_t buflen)
+{
+ unsigned int count = 0;
+ unsigned char *pos = buf;
+ size_t left = buflen, plen;
+ unsigned char digest[DPP_MAX_HASH_LEN];
+
+ while (left > 0) {
+ count++;
+ if (dpp_pbkdf2_f(hash_len, password, password_len,
+ salt, salt_len, iterations, count, digest))
+ return -1;
+ plen = left > hash_len ? hash_len : left;
+ os_memcpy(pos, digest, plen);
+ pos += plen;
+ left -= plen;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+ int num_bytes, offset;
+
+ num_bytes = BN_num_bytes(bn);
+ if ((size_t) num_bytes > len)
+ return -1;
+ offset = len - num_bytes;
+ os_memset(pos, 0, offset);
+ BN_bn2bin(bn, pos + offset);
+ return 0;
+}
+
+
+struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
+{
+ int len, res;
+ EC_KEY *eckey;
+ struct wpabuf *buf;
+ unsigned char *pos;
+
+ eckey = EVP_PKEY_get1_EC_KEY(pkey);
+ if (!eckey)
+ return NULL;
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+ len = i2o_ECPublicKey(eckey, NULL);
+ if (len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to determine public key encoding length");
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ buf = wpabuf_alloc(len);
+ if (!buf) {
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ pos = wpabuf_put(buf, len);
+ res = i2o_ECPublicKey(eckey, &pos);
+ EC_KEY_free(eckey);
+ if (res != len) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to encode public key (res=%d/%d)",
+ res, len);
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ if (!prefix) {
+ /* Remove 0x04 prefix to match DPP definition */
+ pos = wpabuf_mhead(buf);
+ os_memmove(pos, pos + 1, len - 1);
+ buf->used--;
+ }
+
+ return buf;
+}
+
+
+EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+ const u8 *buf_x, const u8 *buf_y,
+ size_t len)
+{
+ EC_KEY *eckey = NULL;
+ BN_CTX *ctx;
+ EC_POINT *point = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ ctx = BN_CTX_new();
+ if (!ctx) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ return NULL;
+ }
+
+ point = EC_POINT_new(group);
+ x = BN_bin2bn(buf_x, len, NULL);
+ y = BN_bin2bn(buf_y, len, NULL);
+ if (!point || !x || !y) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ goto fail;
+ }
+
+ if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx) ||
+ EC_POINT_is_at_infinity(group, point)) {
+ wpa_printf(MSG_ERROR, "DPP: Invalid point");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
+
+ eckey = EC_KEY_new();
+ if (!eckey ||
+ EC_KEY_set_group(eckey, group) != 1 ||
+ EC_KEY_set_public_key(eckey, point) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to set EC_KEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+ goto fail;
+ }
+
+out:
+ BN_free(x);
+ BN_free(y);
+ EC_KEY_free(eckey);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+ return pkey;
+fail:
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto out;
+}
+
+
+EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len)
+{
+ const EC_KEY *eckey;
+ const EC_GROUP *group;
+ EVP_PKEY *pkey = NULL;
+
+ if (len & 1)
+ return NULL;
+
+ eckey = EVP_PKEY_get0_EC_KEY(group_key);
+ if (!eckey) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Could not get EC_KEY from group_key");
+ return NULL;
+ }
+
+ group = EC_KEY_get0_group(eckey);
+ if (group)
+ pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
+ len / 2);
+ else
+ wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
+
+ return pkey;
+}
+
+
+EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+{
+ EVP_PKEY_CTX *kctx = NULL;
+ EC_KEY *ec_params = NULL;
+ EVP_PKEY *params = NULL, *key = NULL;
+ int nid;
+
+ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+
+ nid = OBJ_txt2nid(curve->name);
+ if (nid == NID_undef) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
+ return NULL;
+ }
+
+ ec_params = EC_KEY_new_by_curve_name(nid);
+ if (!ec_params) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EC_KEY parameters");
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+ params = EVP_PKEY_new();
+ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EVP_PKEY parameters");
+ goto fail;
+ }
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (!kctx ||
+ EVP_PKEY_keygen_init(kctx) != 1 ||
+ EVP_PKEY_keygen(kctx, &key) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+ key = NULL;
+ goto fail;
+ }
+
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("Own generated key", key);
+
+fail:
+ EC_KEY_free(ec_params);
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+ return key;
+}
+
+
+EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ EVP_PKEY *pkey;
+ EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ pkey = EVP_PKEY_new();
+ if (!pkey)
+ return NULL;
+ eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ group = EC_KEY_get0_group(eckey);
+ if (!group) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ nid = EC_GROUP_get_curve_name(group);
+ *curve = dpp_get_curve_nid(nid);
+ if (!*curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+ nid);
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+
+ if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ return pkey;
+}
+
+
+typedef struct {
+ /* AlgorithmIdentifier ecPublicKey with optional parameters present
+ * as an OID identifying the curve */
+ X509_ALGOR *alg;
+ /* Compressed format public key per ANSI X9.63 */
+ ASN1_BIT_STRING *pub_key;
+} DPP_BOOTSTRAPPING_KEY;
+
+ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
+
+
+static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
+{
+ unsigned char *der = NULL;
+ int der_len;
+ const EC_KEY *eckey;
+ struct wpabuf *ret = NULL;
+ size_t len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+ BN_CTX *ctx;
+ DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
+ int nid;
+
+ ctx = BN_CTX_new();
+ eckey = EVP_PKEY_get0_EC_KEY(key);
+ if (!ctx || !eckey)
+ goto fail;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (!group || !point)
+ goto fail;
+ dpp_debug_print_point("DPP: bootstrap public key", group, point);
+ nid = EC_GROUP_get_curve_name(group);
+
+ bootstrap = DPP_BOOTSTRAPPING_KEY_new();
+ if (!bootstrap ||
+ X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
+ V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+ goto fail;
+
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ NULL, 0, ctx);
+ if (len == 0)
+ goto fail;
+
+ der = OPENSSL_malloc(len);
+ if (!der)
+ goto fail;
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ der, len, ctx);
+
+ OPENSSL_free(bootstrap->pub_key->data);
+ bootstrap->pub_key->data = der;
+ der = NULL;
+ bootstrap->pub_key->length = len;
+ /* No unused bits */
+ bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+ der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to build DER encoded public key");
+ goto fail;
+ }
+
+ ret = wpabuf_alloc_copy(der, der_len);
+fail:
+ DPP_BOOTSTRAPPING_KEY_free(bootstrap);
+ OPENSSL_free(der);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+ struct wpabuf *der;
+ int res;
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ return -1;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+ res = dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der));
+ if (res < 0)
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ wpabuf_free(der);
+ return res;
+}
+
+
+int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ char *base64 = NULL;
+ char *pos, *end;
+ size_t len;
+ struct wpabuf *der = NULL;
+
+ bi->curve = dpp_get_curve_name(curve);
+ if (!bi->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+ return -1;
+ }
+
+ if (privkey)
+ bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+ else
+ bi->pubkey = dpp_gen_keypair(bi->curve);
+ if (!bi->pubkey)
+ goto fail;
+ bi->own = 1;
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+
+ if (dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der)) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ goto fail;
+ }
+
+ base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
+ wpabuf_free(der);
+ der = NULL;
+ if (!base64)
+ goto fail;
+ pos = base64;
+ end = pos + len;
+ for (;;) {
+ pos = os_strchr(pos, '\n');
+ if (!pos)
+ break;
+ os_memmove(pos, pos + 1, end - pos);
+ }
+ os_free(bi->pk);
+ bi->pk = base64;
+ return 0;
+fail:
+ os_free(base64);
+ wpabuf_free(der);
+ return -1;
+}
+
+
+int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "first intermediate key";
+ int res;
+
+ /* k1 = HKDF(<>, "first intermediate key", M.x) */
+
+ /* HKDF-Extract(<>, M.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
+ k1, hash_len);
+ return 0;
+}
+
+
+int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "second intermediate key";
+ int res;
+
+ /* k2 = HKDF(<>, "second intermediate key", N.x) */
+
+ /* HKDF-Extract(<>, N.x) */
+ os_memset(salt, 0, hash_len);
+ res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
+ k2, hash_len);
+ return 0;
+}
+
+
+int dpp_derive_bk_ke(struct dpp_authentication *auth)
+{
+ unsigned int hash_len = auth->curve->hash_len;
+ size_t nonce_len = auth->curve->nonce_len;
+ u8 nonces[2 * DPP_MAX_NONCE_LEN];
+ const char *info_ke = "DPP Key";
+ int res;
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem = 0;
+
+ if (!auth->Mx_len || !auth->Nx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mx/Nx not available - cannot derive ke");
+ return -1;
+ }
+
+ /* bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
+ os_memcpy(nonces, auth->i_nonce, nonce_len);
+ os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
+ addr[num_elem] = auth->Mx;
+ len[num_elem] = auth->Mx_len;
+ num_elem++;
+ addr[num_elem] = auth->Nx;
+ len[num_elem] = auth->Nx_len;
+ num_elem++;
+ if (auth->peer_bi && auth->own_bi) {
+ if (!auth->Lx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Lx not available - cannot derive ke");
+ return -1;
+ }
+ addr[num_elem] = auth->Lx;
+ len[num_elem] = auth->secret_len;
+ num_elem++;
+ }
+ res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
+ num_elem, addr, len, auth->bk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x])",
+ auth->bk, hash_len);
+
+ /* ke = HKDF-Expand(bkK, "DPP Key", length) */
+ res = dpp_hkdf_expand(hash_len, auth->bk, hash_len, info_ke, auth->ke,
+ hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: ke = HKDF-Expand(bk, \"DPP Key\", length)",
+ auth->ke, hash_len);
+
+ return 0;
+}
+
+
+int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len)
+{
+ EVP_PKEY_CTX *ctx;
+ int ret = -1;
+
+ ERR_clear_error();
+ *secret_len = 0;
+
+ ctx = EVP_PKEY_CTX_new(own, NULL);
+ if (!ctx) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(ctx) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: EVP_PKEY_derive_set_peet failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+ u8 buf[200];
+ int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG;
+
+ /* It looks like OpenSSL can return unexpectedly large buffer
+ * need for shared secret from EVP_PKEY_derive(NULL) in some
+ * cases. For example, group 19 has shown cases where secret_len
+ * is set to 72 even though the actual length ends up being
+ * updated to 32 when EVP_PKEY_derive() is called with a buffer
+ * for the value. Work around this by trying to fetch the value
+ * and continue if it is within supported range even when the
+ * initial buffer need is claimed to be larger. */
+ wpa_printf(level,
+ "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+ (int) *secret_len);
+ if (*secret_len > 200)
+ goto fail;
+ if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+ (int) *secret_len);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change",
+ buf, *secret_len);
+ os_memcpy(secret, buf, *secret_len);
+ forced_memzero(buf, sizeof(buf));
+ goto done;
+ }
+
+ if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+done:
+ ret = 0;
+
+fail:
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+
+int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len)
+{
+ const u8 *addr[2];
+ size_t len[2];
+
+ addr[0] = data;
+ len[0] = data_len;
+ if (sha256_vector(1, addr, len, bi->pubkey_hash) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
+ bi->pubkey_hash, SHA256_MAC_LEN);
+
+ addr[0] = (const u8 *) "chirp";
+ len[0] = 5;
+ addr[1] = data;
+ len[1] = data_len;
+ if (sha256_vector(2, addr, len, bi->pubkey_hash_chirp) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash (chirp)",
+ bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len)
+{
+ EVP_PKEY *pkey;
+ const unsigned char *p;
+ int res;
+ X509_PUBKEY *pub = NULL;
+ ASN1_OBJECT *ppkalg;
+ const unsigned char *pk;
+ int ppklen;
+ X509_ALGOR *pa;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20800000L)
+ ASN1_OBJECT *pa_oid;
+#else
+ const ASN1_OBJECT *pa_oid;
+#endif
+ const void *pval;
+ int ptype;
+ const ASN1_OBJECT *poid;
+ char buf[100];
+
+ if (dpp_bi_pubkey_hash(bi, data, data_len) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ return -1;
+ }
+
+ /* DER encoded ASN.1 SubjectPublicKeyInfo
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ *
+ * subjectPublicKey = compressed format public key per ANSI X9.63
+ * algorithm = ecPublicKey (1.2.840.10045.2.1)
+ * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
+ * prime256v1 (1.2.840.10045.3.1.7)
+ */
+
+ p = data;
+ pkey = d2i_PUBKEY(NULL, &p, data_len);
+
+ if (!pkey) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
+ return -1;
+ }
+
+ if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo does not describe an EC key");
+ EVP_PKEY_free(pkey);
+ return -1;
+ }
+
+ res = X509_PUBKEY_set(&pub, pkey);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
+ goto fail;
+ }
+
+ res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters");
+ goto fail;
+ }
+ res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
+ if (os_strcmp(buf, "id-ecPublicKey") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+
+ X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
+ if (ptype != V_ASN1_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
+ goto fail;
+ }
+ poid = pval;
+ res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
+ bi->curve = dpp_get_curve_oid(poid);
+ if (!bi->curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
+ buf);
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
+
+ X509_PUBKEY_free(pub);
+ bi->pubkey = pkey;
+ return 0;
+fail:
+ X509_PUBKEY_free(pub);
+ EVP_PKEY_free(pkey);
+ return -1;
+}
+
+
+static struct wpabuf *
+dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
+ const u8 *prot_hdr, u16 prot_hdr_len,
+ const EVP_MD **ret_md)
+{
+ struct json_token *root, *token;
+ struct wpabuf *kid = NULL;
+
+ root = json_parse((const char *) prot_hdr, prot_hdr_len);
+ if (!root) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JSON parsing failed for JWS Protected Header");
+ goto fail;
+ }
+
+ if (root->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JWS Protected Header root is not an object");
+ goto fail;
+ }
+
+ token = json_get_member(root, "typ");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
+ token->string);
+ if (os_strcmp(token->string, "dppCon") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header typ=%s",
+ token->string);
+ goto fail;
+ }
+
+ token = json_get_member(root, "alg");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
+ token->string);
+ if (os_strcmp(token->string, curve->jws_alg) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
+ token->string, curve->jws_alg);
+ goto fail;
+ }
+ if (os_strcmp(token->string, "ES256") == 0 ||
+ os_strcmp(token->string, "BS256") == 0)
+ *ret_md = EVP_sha256();
+ else if (os_strcmp(token->string, "ES384") == 0 ||
+ os_strcmp(token->string, "BS384") == 0)
+ *ret_md = EVP_sha384();
+ else if (os_strcmp(token->string, "ES512") == 0 ||
+ os_strcmp(token->string, "BS512") == 0)
+ *ret_md = EVP_sha512();
+ else
+ *ret_md = NULL;
+ if (!*ret_md) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header alg=%s",
+ token->string);
+ goto fail;
+ }
+
+ kid = json_get_member_base64url(root, "kid");
+ if (!kid) {
+ wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
+ goto fail;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
+ kid);
+
+fail:
+ json_free(root);
+ return kid;
+}
+
+
+static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
+{
+ struct wpabuf *uncomp;
+ int res;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[1];
+ size_t len[1];
+
+ if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
+ return -1;
+ uncomp = dpp_get_pubkey_point(pub, 1);
+ if (!uncomp)
+ return -1;
+ addr[0] = wpabuf_head(uncomp);
+ len[0] = wpabuf_len(uncomp);
+ wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
+ addr[0], len[0]);
+ res = sha256_vector(1, addr, len, hash);
+ wpabuf_free(uncomp);
+ if (res < 0)
+ return -1;
+ if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received hash value does not match calculated public key hash value");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
+ hash, SHA256_MAC_LEN);
+ return -1;
+ }
+ return 0;
+}
+
+
+enum dpp_status_error
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+ EVP_PKEY *csign_pub, const char *connector)
+{
+ enum dpp_status_error ret = 255;
+ const char *pos, *end, *signed_start, *signed_end;
+ struct wpabuf *kid = NULL;
+ unsigned char *prot_hdr = NULL, *signature = NULL;
+ size_t prot_hdr_len = 0, signature_len = 0;
+ const EVP_MD *sign_md = NULL;
+ unsigned char *der = NULL;
+ int der_len;
+ int res;
+ EVP_MD_CTX *md_ctx = NULL;
+ ECDSA_SIG *sig = NULL;
+ BIGNUM *r = NULL, *s = NULL;
+ const struct dpp_curve_params *curve;
+ const EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ eckey = EVP_PKEY_get0_EC_KEY(csign_pub);
+ if (!eckey)
+ goto fail;
+ group = EC_KEY_get0_group(eckey);
+ if (!group)
+ goto fail;
+ nid = EC_GROUP_get_curve_name(group);
+ curve = dpp_get_curve_nid(nid);
+ if (!curve)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
+ os_memset(info, 0, sizeof(*info));
+
+ signed_start = pos = connector;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
+ if (!prot_hdr) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector JWS Protected Header");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: signedConnector - JWS Protected Header",
+ prot_hdr, prot_hdr_len);
+ kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
+ if (!kid) {
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ if (wpabuf_len(kid) != SHA256_MAC_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
+ (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ pos = end + 1;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing dot(2) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ signed_end = end - 1;
+ info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
+ if (!info->payload) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector JWS Payload");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: signedConnector - JWS Payload",
+ info->payload, info->payload_len);
+ pos = end + 1;
+ signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
+ if (!signature) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector signature");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
+ signature, signature_len);
+
+ if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
+ ret = DPP_STATUS_NO_MATCH;
+ goto fail;
+ }
+
+ if (signature_len & 0x01) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected signedConnector signature length (%d)",
+ (int) signature_len);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ /* JWS Signature encodes the signature (r,s) as two octet strings. Need
+ * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
+ r = BN_bin2bn(signature, signature_len / 2, NULL);
+ s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
+ sig = ECDSA_SIG_new();
+ if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
+ goto fail;
+ r = NULL;
+ s = NULL;
+
+ der_len = i2d_ECDSA_SIG(sig, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
+ md_ctx = EVP_MD_CTX_create();
+ if (!md_ctx)
+ goto fail;
+
+ ERR_clear_error();
+ if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
+ signed_end - signed_start + 1) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
+ res, ERR_error_string(ERR_get_error(), NULL));
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ ret = DPP_STATUS_OK;
+fail:
+ EVP_MD_CTX_destroy(md_ctx);
+ os_free(prot_hdr);
+ wpabuf_free(kid);
+ os_free(signature);
+ ECDSA_SIG_free(sig);
+ BN_free(r);
+ BN_free(s);
+ OPENSSL_free(der);
+ return ret;
+}
+
+
+enum dpp_status_error
+dpp_check_signed_connector(struct dpp_signed_connector_info *info,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len)
+{
+ const unsigned char *p;
+ EVP_PKEY *csign = NULL;
+ char *signed_connector = NULL;
+ enum dpp_status_error res = DPP_STATUS_INVALID_CONNECTOR;
+
+ p = csign_key;
+ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ if (!csign) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to parse local C-sign-key information");
+ goto fail;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
+ peer_connector, peer_connector_len);
+ signed_connector = os_malloc(peer_connector_len + 1);
+ if (!signed_connector)
+ goto fail;
+ os_memcpy(signed_connector, peer_connector, peer_connector_len);
+ signed_connector[peer_connector_len] = '\0';
+ res = dpp_process_signed_connector(info, csign, signed_connector);
+fail:
+ os_free(signed_connector);
+ EVP_PKEY_free(csign);
+ return res;
+}
+
+
+int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
+{
+ struct wpabuf *pix, *prx, *bix, *brx;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 zero = 0;
+ int res = -1;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ addr[num_elem] = &zero;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
+{
+ struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 one = 1;
+ int res = -1;
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->peer_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->own_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = &one;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL;
+ const EC_KEY *BI, *bR, *pR;
+ const EC_POINT *BI_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx, *sum, *q;
+ const BIGNUM *bR_bn, *pR_bn;
+ int ret = -1;
+
+ /* L = ((bR + pR) modulo q) * BI */
+
+ bnctx = BN_CTX_new();
+ sum = BN_new();
+ q = BN_new();
+ lx = BN_new();
+ if (!bnctx || !sum || !q || !lx)
+ goto fail;
+ BI = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
+ if (!BI)
+ goto fail;
+ BI_point = EC_KEY_get0_public_key(BI);
+ group = EC_KEY_get0_group(BI);
+ if (!group)
+ goto fail;
+
+ bR = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
+ pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
+ if (!bR || !pR)
+ goto fail;
+ bR_bn = EC_KEY_get0_private_key(bR);
+ pR_bn = EC_KEY_get0_private_key(pR);
+ if (!bR_bn || !pR_bn)
+ goto fail;
+ if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+ BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
+ goto fail;
+ l = EC_POINT_new(group);
+ if (!l ||
+ EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ BN_clear_free(lx);
+ BN_clear_free(sum);
+ BN_free(q);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL, *sum = NULL;
+ const EC_KEY *bI, *BR, *PR;
+ const EC_POINT *BR_point, *PR_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx;
+ const BIGNUM *bI_bn;
+ int ret = -1;
+
+ /* L = bI * (BR + PR) */
+
+ bnctx = BN_CTX_new();
+ lx = BN_new();
+ if (!bnctx || !lx)
+ goto fail;
+ BR = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
+ PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
+ if (!BR || !PR)
+ goto fail;
+ BR_point = EC_KEY_get0_public_key(BR);
+ PR_point = EC_KEY_get0_public_key(PR);
+
+ bI = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
+ if (!bI)
+ goto fail;
+ group = EC_KEY_get0_group(bI);
+ bI_bn = EC_KEY_get0_private_key(bI);
+ if (!group || !bI_bn)
+ goto fail;
+ sum = EC_POINT_new(group);
+ l = EC_POINT_new(group);
+ if (!sum || !l ||
+ EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
+ EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ EC_POINT_clear_free(sum);
+ BN_clear_free(lx);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "DPP PMK";
+ int res;
+
+ /* PMK = HKDF(<>, "DPP PMK", N.x) */
+
+ /* HKDF-Extract(<>, N.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
+ pmk, hash_len);
+ return 0;
+}
+
+
+int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+ EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
+{
+ struct wpabuf *nkx, *pkx;
+ int ret = -1, res;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 hash[SHA256_MAC_LEN];
+
+ /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+ nkx = dpp_get_pubkey_point(own_key, 0);
+ pkx = dpp_get_pubkey_point(peer_key, 0);
+ if (!nkx || !pkx)
+ goto fail;
+ addr[0] = wpabuf_head(nkx);
+ len[0] = wpabuf_len(nkx) / 2;
+ addr[1] = wpabuf_head(pkx);
+ len[1] = wpabuf_len(pkx) / 2;
+ if (len[0] != len[1])
+ goto fail;
+ if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
+ addr[0] = wpabuf_head(pkx);
+ addr[1] = wpabuf_head(nkx);
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
+ res = sha256_vector(2, addr, len, hash);
+ if (res < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
+ os_memcpy(pmkid, hash, PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
+ ret = 0;
+fail:
+ wpabuf_free(nkx);
+ wpabuf_free(pkx);
+ return ret;
+}
+
+
+/* Role-specific elements for PKEX */
+
+/* NIST P-256 */
+static const u8 pkex_init_x_p256[32] = {
+ 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
+ 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
+ 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
+ 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
+ };
+static const u8 pkex_init_y_p256[32] = {
+ 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
+ 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
+ 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
+ 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
+ };
+static const u8 pkex_resp_x_p256[32] = {
+ 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
+ 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
+ 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
+ 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
+};
+static const u8 pkex_resp_y_p256[32] = {
+ 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
+ 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
+ 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
+ 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
+};
+
+/* NIST P-384 */
+static const u8 pkex_init_x_p384[48] = {
+ 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
+ 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
+ 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
+ 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
+ 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
+ 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
+};
+static const u8 pkex_init_y_p384[48] = {
+ 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
+ 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
+ 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
+ 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
+ 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
+ 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
+};
+static const u8 pkex_resp_x_p384[48] = {
+ 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
+ 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
+ 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
+ 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
+ 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
+ 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
+};
+static const u8 pkex_resp_y_p384[48] = {
+ 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
+ 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
+ 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
+ 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
+ 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
+ 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
+};
+
+/* NIST P-521 */
+static const u8 pkex_init_x_p521[66] = {
+ 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
+ 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
+ 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
+ 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
+ 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
+ 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
+ 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
+ 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
+ 0x97, 0x76
+};
+static const u8 pkex_init_y_p521[66] = {
+ 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
+ 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
+ 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
+ 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
+ 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
+ 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
+ 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
+ 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
+ 0x03, 0xa8
+};
+static const u8 pkex_resp_x_p521[66] = {
+ 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
+ 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
+ 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
+ 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
+ 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
+ 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
+ 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
+ 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
+ 0x84, 0xb4
+};
+static const u8 pkex_resp_y_p521[66] = {
+ 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
+ 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
+ 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
+ 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
+ 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
+ 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
+ 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
+ 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
+ 0xce, 0xe1
+};
+
+/* Brainpool P-256r1 */
+static const u8 pkex_init_x_bp_p256r1[32] = {
+ 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
+ 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
+ 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
+ 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
+};
+static const u8 pkex_init_y_bp_p256r1[32] = {
+ 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
+ 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
+ 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
+ 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
+};
+static const u8 pkex_resp_x_bp_p256r1[32] = {
+ 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
+ 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
+ 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
+ 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
+};
+static const u8 pkex_resp_y_bp_p256r1[32] = {
+ 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
+ 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
+ 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
+ 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
+};
+
+/* Brainpool P-384r1 */
+static const u8 pkex_init_x_bp_p384r1[48] = {
+ 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
+ 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
+ 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
+ 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
+ 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
+ 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
+};
+static const u8 pkex_init_y_bp_p384r1[48] = {
+ 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
+ 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
+ 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
+ 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
+ 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
+ 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
+};
+static const u8 pkex_resp_x_bp_p384r1[48] = {
+ 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
+ 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
+ 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
+ 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
+ 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
+ 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
+};
+static const u8 pkex_resp_y_bp_p384r1[48] = {
+ 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
+ 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
+ 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
+ 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
+ 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
+ 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
+};
+
+/* Brainpool P-512r1 */
+static const u8 pkex_init_x_bp_p512r1[64] = {
+ 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
+ 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
+ 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
+ 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
+ 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
+ 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
+ 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
+ 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
+};
+static const u8 pkex_init_y_bp_p512r1[64] = {
+ 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
+ 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
+ 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
+ 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
+ 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
+ 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
+ 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
+ 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
+};
+static const u8 pkex_resp_x_bp_p512r1[64] = {
+ 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
+ 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
+ 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
+ 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
+ 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
+ 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
+ 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
+ 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
+};
+static const u8 pkex_resp_y_bp_p512r1[64] = {
+ 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
+ 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
+ 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
+ 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
+ 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
+ 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
+ 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
+ 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
+};
+
+
+static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
+ int init)
+{
+ EC_GROUP *group;
+ size_t len = curve->prime_len;
+ const u8 *x, *y;
+ EVP_PKEY *res;
+
+ switch (curve->ike_group) {
+ case 19:
+ x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
+ y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
+ break;
+ case 20:
+ x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
+ y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
+ break;
+ case 21:
+ x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
+ y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
+ break;
+ case 28:
+ x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
+ y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
+ break;
+ case 29:
+ x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
+ y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
+ break;
+ case 30:
+ x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
+ y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
+ break;
+ default:
+ return NULL;
+ }
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group)
+ return NULL;
+ res = dpp_set_pubkey_point_group(group, x, y, len);
+ EC_GROUP_free(group);
+ return res;
+}
+
+
+EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+ const u8 *mac_init, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group)
+{
+ u8 hash[DPP_MAX_HASH_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ unsigned int num_elem = 0;
+ EC_POINT *Qi = NULL;
+ EVP_PKEY *Pi = NULL;
+ const EC_KEY *Pi_ec;
+ const EC_POINT *Pi_point;
+ BIGNUM *hash_bn = NULL;
+ const EC_GROUP *group = NULL;
+ EC_GROUP *group2 = NULL;
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+
+ wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
+ addr[num_elem] = mac_init;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+ if (identifier) {
+ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+ identifier);
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ addr[num_elem] = (const u8 *) code;
+ len[num_elem] = os_strlen(code);
+ num_elem++;
+ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: H(MAC-Initiator | [identifier |] code)",
+ hash, curve->hash_len);
+ Pi = dpp_pkex_get_role_elem(curve, 1);
+ if (!Pi)
+ goto fail;
+ dpp_debug_print_key("DPP: Pi", Pi);
+ Pi_ec = EVP_PKEY_get0_EC_KEY(Pi);
+ if (!Pi_ec)
+ goto fail;
+ Pi_point = EC_KEY_get0_public_key(Pi_ec);
+
+ group = EC_KEY_get0_group(Pi_ec);
+ if (!group)
+ goto fail;
+ group2 = EC_GROUP_dup(group);
+ if (!group2)
+ goto fail;
+ Qi = EC_POINT_new(group2);
+ if (!Qi) {
+ EC_GROUP_free(group2);
+ goto fail;
+ }
+ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+ if (!hash_bn ||
+ EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
+ goto fail;
+ if (EC_POINT_is_at_infinity(group, Qi)) {
+ wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: Qi", group, Qi);
+out:
+ EVP_PKEY_free(Pi);
+ BN_clear_free(hash_bn);
+ if (ret_group && Qi)
+ *ret_group = group2;
+ else
+ EC_GROUP_free(group2);
+ return Qi;
+fail:
+ EC_POINT_free(Qi);
+ Qi = NULL;
+ goto out;
+}
+
+
+EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+ const u8 *mac_resp, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group)
+{
+ u8 hash[DPP_MAX_HASH_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ unsigned int num_elem = 0;
+ EC_POINT *Qr = NULL;
+ EVP_PKEY *Pr = NULL;
+ const EC_KEY *Pr_ec;
+ const EC_POINT *Pr_point;
+ BIGNUM *hash_bn = NULL;
+ const EC_GROUP *group = NULL;
+ EC_GROUP *group2 = NULL;
+
+ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+
+ wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
+ addr[num_elem] = mac_resp;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+ if (identifier) {
+ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+ identifier);
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ addr[num_elem] = (const u8 *) code;
+ len[num_elem] = os_strlen(code);
+ num_elem++;
+ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: H(MAC-Responder | [identifier |] code)",
+ hash, curve->hash_len);
+ Pr = dpp_pkex_get_role_elem(curve, 0);
+ if (!Pr)
+ goto fail;
+ dpp_debug_print_key("DPP: Pr", Pr);
+ Pr_ec = EVP_PKEY_get0_EC_KEY(Pr);
+ if (!Pr_ec)
+ goto fail;
+ Pr_point = EC_KEY_get0_public_key(Pr_ec);
+
+ group = EC_KEY_get0_group(Pr_ec);
+ if (!group)
+ goto fail;
+ group2 = EC_GROUP_dup(group);
+ if (!group2)
+ goto fail;
+ Qr = EC_POINT_new(group2);
+ if (!Qr) {
+ EC_GROUP_free(group2);
+ goto fail;
+ }
+ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+ if (!hash_bn ||
+ EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
+ goto fail;
+ if (EC_POINT_is_at_infinity(group, Qr)) {
+ wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: Qr", group, Qr);
+out:
+ EVP_PKEY_free(Pr);
+ BN_clear_free(hash_bn);
+ if (ret_group && Qr)
+ *ret_group = group2;
+ else
+ EC_GROUP_free(group2);
+ return Qr;
+fail:
+ EC_POINT_free(Qr);
+ Qr = NULL;
+ goto out;
+}
+
+
+int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+ const u8 *Mx, size_t Mx_len,
+ const u8 *Nx, size_t Nx_len,
+ const char *code,
+ const u8 *Kx, size_t Kx_len,
+ u8 *z, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ int res;
+ u8 *info, *pos;
+ size_t info_len;
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+
+ /* HKDF-Extract(<>, IKM=K.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+ prk, hash_len);
+ info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
+ info = os_malloc(info_len);
+ if (!info)
+ return -1;
+ pos = info;
+ os_memcpy(pos, mac_init, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, mac_resp, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, Mx, Mx_len);
+ pos += Mx_len;
+ os_memcpy(pos, Nx, Nx_len);
+ pos += Nx_len;
+ os_memcpy(pos, code, os_strlen(code));
+
+ /* HKDF-Expand(PRK, info, L) */
+ if (hash_len == 32)
+ res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else if (hash_len == 48)
+ res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else if (hash_len == 64)
+ res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else
+ res = -1;
+ os_free(info);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
+ z, hash_len);
+ return 0;
+}
+
+
+int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
+ const u8 *net_access_key,
+ size_t net_access_key_len,
+ struct json_token *peer_net_access_key)
+{
+ BN_CTX *bnctx = NULL;
+ EVP_PKEY *own_key = NULL, *peer_key = NULL;
+ BIGNUM *sum = NULL, *q = NULL, *mx = NULL;
+ EC_POINT *m = NULL;
+ const EC_KEY *cR, *pR;
+ const EC_GROUP *group;
+ const BIGNUM *cR_bn, *pR_bn;
+ const EC_POINT *CI_point;
+ const EC_KEY *CI;
+ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 prk[DPP_MAX_HASH_LEN];
+ const struct dpp_curve_params *curve;
+ int res = -1;
+
+ own_key = dpp_set_keypair(&auth->curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ dpp_auth_fail(auth, "Failed to parse own netAccessKey");
+ goto fail;
+ }
+
+ peer_key = dpp_parse_jwk(peer_net_access_key, &curve);
+ if (!peer_key)
+ goto fail;
+ dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+
+ if (auth->curve != curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mismatching netAccessKey curves (%s != %s)",
+ auth->curve->name, curve->name);
+ goto fail;
+ }
+
+ auth->own_protocol_key = dpp_gen_keypair(curve);
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ /* M = { cR + pR } * CI */
+ cR = EVP_PKEY_get0_EC_KEY(own_key);
+ pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
+ group = EC_KEY_get0_group(pR);
+ bnctx = BN_CTX_new();
+ sum = BN_new();
+ mx = BN_new();
+ q = BN_new();
+ m = EC_POINT_new(group);
+ if (!cR || !pR || !bnctx || !sum || !mx || !q || !m)
+ goto fail;
+ cR_bn = EC_KEY_get0_private_key(cR);
+ pR_bn = EC_KEY_get0_private_key(pR);
+ if (!cR_bn || !pR_bn)
+ goto fail;
+ CI = EVP_PKEY_get0_EC_KEY(peer_key);
+ CI_point = EC_KEY_get0_public_key(CI);
+ if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+ BN_mod_add(sum, cR_bn, pR_bn, q, bnctx) != 1 ||
+ EC_POINT_mul(group, m, NULL, CI_point, sum, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
+
+ /* ke = HKDF(I-nonce, "dpp reconfig key", M.x) */
+
+ /* HKDF-Extract(I-nonce, M.x) */
+ if (dpp_hmac(curve->hash_len, auth->i_nonce, curve->nonce_len,
+ Mx, curve->prime_len, prk) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
+
+ /* HKDF-Expand(PRK, "dpp reconfig key", L) */
+ if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
+ "dpp reconfig key", auth->ke, curve->hash_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: ke = HKDF(I-nonce, \"dpp reconfig key\", M.x)",
+ auth->ke, curve->hash_len);
+
+ res = 0;
+ EVP_PKEY_free(auth->reconfig_old_protocol_key);
+ auth->reconfig_old_protocol_key = own_key;
+ own_key = NULL;
+fail:
+ forced_memzero(prk, sizeof(prk));
+ forced_memzero(Mx, sizeof(Mx));
+ EC_POINT_clear_free(m);
+ BN_free(q);
+ BN_clear_free(mx);
+ BN_clear_free(sum);
+ EVP_PKEY_free(own_key);
+ EVP_PKEY_free(peer_key);
+ BN_CTX_free(bnctx);
+ return res;
+}
+
+
+int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
+ const u8 *r_proto, u16 r_proto_len,
+ struct json_token *net_access_key)
+{
+ BN_CTX *bnctx = NULL;
+ EVP_PKEY *pr = NULL, *peer_key = NULL;
+ EC_POINT *sum = NULL, *m = NULL;
+ BIGNUM *mx = NULL;
+ const EC_KEY *cI, *CR, *PR;
+ const EC_GROUP *group;
+ const EC_POINT *CR_point, *PR_point;
+ const BIGNUM *cI_bn;
+ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 prk[DPP_MAX_HASH_LEN];
+ int res = -1;
+ const struct dpp_curve_params *curve;
+
+ pr = dpp_set_pubkey_point(auth->conf->connector_key,
+ r_proto, r_proto_len);
+ if (!pr) {
+ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+ goto fail;
+ }
+ dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+ EVP_PKEY_free(auth->peer_protocol_key);
+ auth->peer_protocol_key = pr;
+ pr = NULL;
+
+ peer_key = dpp_parse_jwk(net_access_key, &curve);
+ if (!peer_key)
+ goto fail;
+ dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+ if (auth->curve != curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mismatching netAccessKey curves (%s != %s)",
+ auth->curve->name, curve->name);
+ goto fail;
+ }
+
+ /* M = cI * { CR + PR } */
+ cI = EVP_PKEY_get0_EC_KEY(auth->conf->connector_key);
+ cI_bn = EC_KEY_get0_private_key(cI);
+ group = EC_KEY_get0_group(cI);
+ bnctx = BN_CTX_new();
+ sum = EC_POINT_new(group);
+ m = EC_POINT_new(group);
+ mx = BN_new();
+ CR = EVP_PKEY_get0_EC_KEY(peer_key);
+ PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
+ CR_point = EC_KEY_get0_public_key(CR);
+ PR_point = EC_KEY_get0_public_key(PR);
+ if (!bnctx || !sum || !m || !mx ||
+ EC_POINT_add(group, sum, CR_point, PR_point, bnctx) != 1 ||
+ EC_POINT_mul(group, m, NULL, sum, cI_bn, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
+ bnctx) != 1 ||
+ dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
+
+ /* ke = HKDF(I-nonce, "dpp reconfig key", M.x) */
+
+ /* HKDF-Extract(I-nonce, M.x) */
+ if (dpp_hmac(curve->hash_len, auth->i_nonce, curve->nonce_len,
+ Mx, curve->prime_len, prk) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
+
+ /* HKDF-Expand(PRK, "dpp reconfig key", L) */
+ if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
+ "dpp reconfig key", auth->ke, curve->hash_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: ke = HKDF(I-nonce, \"dpp reconfig key\", M.x)",
+ auth->ke, curve->hash_len);
+
+ res = 0;
+fail:
+ forced_memzero(prk, sizeof(prk));
+ forced_memzero(Mx, sizeof(Mx));
+ EVP_PKEY_free(pr);
+ EVP_PKEY_free(peer_key);
+ EC_POINT_clear_free(sum);
+ EC_POINT_clear_free(m);
+ BN_clear_free(mx);
+ BN_CTX_free(bnctx);
+ return res;
+}
+
+
+static char *
+dpp_build_jws_prot_hdr(struct dpp_configurator *conf, size_t *signed1_len)
+{
+ struct wpabuf *jws_prot_hdr;
+ char *signed1;
+
+ jws_prot_hdr = wpabuf_alloc(100);
+ if (!jws_prot_hdr)
+ return NULL;
+ json_start_object(jws_prot_hdr, NULL);
+ json_add_string(jws_prot_hdr, "typ", "dppCon");
+ json_value_sep(jws_prot_hdr);
+ json_add_string(jws_prot_hdr, "kid", conf->kid);
+ json_value_sep(jws_prot_hdr);
+ json_add_string(jws_prot_hdr, "alg", conf->curve->jws_alg);
+ json_end_object(jws_prot_hdr);
+ signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
+ wpabuf_len(jws_prot_hdr),
+ signed1_len);
+ wpabuf_free(jws_prot_hdr);
+ return signed1;
+}
+
+
+static char *
+dpp_build_conn_signature(struct dpp_configurator *conf,
+ const char *signed1, size_t signed1_len,
+ const char *signed2, size_t signed2_len,
+ size_t *signed3_len)
+{
+ const struct dpp_curve_params *curve;
+ char *signed3 = NULL;
+ unsigned char *signature = NULL;
+ const unsigned char *p;
+ size_t signature_len;
+ EVP_MD_CTX *md_ctx = NULL;
+ ECDSA_SIG *sig = NULL;
+ char *dot = ".";
+ const EVP_MD *sign_md;
+ const BIGNUM *r, *s;
+
+ curve = conf->curve;
+ if (curve->hash_len == SHA256_MAC_LEN) {
+ sign_md = EVP_sha256();
+ } else if (curve->hash_len == SHA384_MAC_LEN) {
+ sign_md = EVP_sha384();
+ } else if (curve->hash_len == SHA512_MAC_LEN) {
+ sign_md = EVP_sha512();
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+ goto fail;
+ }
+
+ md_ctx = EVP_MD_CTX_create();
+ if (!md_ctx)
+ goto fail;
+
+ ERR_clear_error();
+ if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, conf->csign) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ signature = os_malloc(signature_len);
+ if (!signature)
+ goto fail;
+ if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
+ signature, signature_len);
+ /* Convert to raw coordinates r,s */
+ p = signature;
+ sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
+ if (!sig)
+ goto fail;
+ ECDSA_SIG_get0(sig, &r, &s);
+ if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(s, signature + curve->prime_len,
+ curve->prime_len) < 0)
+ goto fail;
+ signature_len = 2 * curve->prime_len;
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
+ signature, signature_len);
+ signed3 = base64_url_encode(signature, signature_len, signed3_len);
+fail:
+ EVP_MD_CTX_destroy(md_ctx);
+ ECDSA_SIG_free(sig);
+ os_free(signature);
+ return signed3;
+}
+
+char * dpp_sign_connector(struct dpp_configurator *conf,
+ const struct wpabuf *dppcon)
+{
+ char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+ char *signed_conn = NULL, *pos;
+ size_t signed1_len, signed2_len, signed3_len;
+
+ signed1 = dpp_build_jws_prot_hdr(conf, &signed1_len);
+ signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
+ &signed2_len);
+ if (!signed1 || !signed2)
+ goto fail;
+
+ signed3 = dpp_build_conn_signature(conf, signed1, signed1_len,
+ signed2, signed2_len, &signed3_len);
+ if (!signed3)
+ goto fail;
+
+ signed_conn = os_malloc(signed1_len + signed2_len + signed3_len + 3);
+ if (!signed_conn)
+ goto fail;
+ pos = signed_conn;
+ os_memcpy(pos, signed1, signed1_len);
+ pos += signed1_len;
+ *pos++ = '.';
+ os_memcpy(pos, signed2, signed2_len);
+ pos += signed2_len;
+ *pos++ = '.';
+ os_memcpy(pos, signed3, signed3_len);
+ pos += signed3_len;
+ *pos = '\0';
+
+fail:
+ os_free(signed1);
+ os_free(signed2);
+ os_free(signed3);
+ return signed_conn;
+}
+
+
+#ifdef CONFIG_DPP2
+
+struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
+ size_t net_access_key_len)
+{
+ struct wpabuf *pub = NULL;
+ EVP_PKEY *own_key;
+ struct dpp_pfs *pfs;
+
+ pfs = os_zalloc(sizeof(*pfs));
+ if (!pfs)
+ return NULL;
+
+ own_key = dpp_set_keypair(&pfs->curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+ goto fail;
+ }
+ EVP_PKEY_free(own_key);
+
+ pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
+ if (!pfs->ecdh)
+ goto fail;
+
+ pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
+ pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
+ if (!pub)
+ goto fail;
+
+ pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
+ if (!pfs->ie)
+ goto fail;
+ wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
+ wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
+ wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
+ wpabuf_put_buf(pfs->ie, pub);
+ wpabuf_free(pub);
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
+ pfs->ie);
+
+ return pfs;
+fail:
+ wpabuf_free(pub);
+ dpp_pfs_free(pfs);
+ return NULL;
+}
+
+
+int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
+{
+ if (peer_ie_len < 2)
+ return -1;
+ if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
+ wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
+ return -1;
+ }
+
+ pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
+ peer_ie_len - 2);
+ pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
+ if (!pfs->secret) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
+ return -1;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
+ return 0;
+}
+
+
+void dpp_pfs_free(struct dpp_pfs *pfs)
+{
+ if (!pfs)
+ return;
+ crypto_ecdh_deinit(pfs->ecdh);
+ wpabuf_free(pfs->ie);
+ wpabuf_clear_free(pfs->secret);
+ os_free(pfs);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve)
+{
+ BN_CTX *ctx;
+ BIGNUM *x, *y;
+ int ret = -1;
+ EC_GROUP *group;
+ EC_POINT *point;
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group)
+ return -1;
+
+ ctx = BN_CTX_new();
+ point = EC_POINT_new(group);
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !point || !x || !y)
+ goto fail;
+
+ if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ /* Generate a random y coordinate that results in a point that is not
+ * on the curve. */
+ for (;;) {
+ if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
+ ctx) != 1) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
+ /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
+ * return an error from EC_POINT_set_affine_coordinates_GFp()
+ * when the point is not on the curve. */
+ break;
+#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
+ goto fail;
+#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx))
+ break;
+ }
+
+ if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ if (ret < 0)
+ wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
+ BN_free(x);
+ BN_free(y);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+ EC_GROUP_free(group);
+
+ return ret;
+}
+
+
+char * dpp_corrupt_connector_signature(const char *connector)
+{
+ char *tmp, *pos, *signed3 = NULL;
+ unsigned char *signature = NULL;
+ size_t signature_len = 0, signed3_len;
+
+ tmp = os_zalloc(os_strlen(connector) + 5);
+ if (!tmp)
+ goto fail;
+ os_memcpy(tmp, connector, os_strlen(connector));
+
+ pos = os_strchr(tmp, '.');
+ if (!pos)
+ goto fail;
+
+ pos = os_strchr(pos + 1, '.');
+ if (!pos)
+ goto fail;
+ pos++;
+
+ wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
+ pos);
+ signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
+ if (!signature || signature_len == 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
+ signature, signature_len);
+ signature[signature_len - 1] ^= 0x01;
+ wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
+ signature, signature_len);
+ signed3 = base64_url_encode(signature, signature_len, &signed3_len);
+ if (!signed3)
+ goto fail;
+ os_memcpy(pos, signed3, signed3_len);
+ pos[signed3_len] = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
+ pos);
+
+out:
+ os_free(signature);
+ os_free(signed3);
+ return tmp;
+fail:
+ os_free(tmp);
+ tmp = NULL;
+ goto out;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
new file mode 100644
index 0000000..e66eb6c
--- /dev/null
+++ b/src/common/dpp_i.h
@@ -0,0 +1,148 @@
+/*
+ * DPP module internal definitions
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_I_H
+#define DPP_I_H
+
+#ifdef CONFIG_DPP
+
+struct dpp_global {
+ void *msg_ctx;
+ struct dl_list bootstrap; /* struct dpp_bootstrap_info */
+ struct dl_list configurator; /* struct dpp_configurator */
+#ifdef CONFIG_DPP2
+ struct dl_list controllers; /* struct dpp_relay_controller */
+ struct dpp_controller *controller;
+ struct dl_list tcp_init; /* struct dpp_connection */
+ void *cb_ctx;
+ int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+ void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
+#endif /* CONFIG_DPP2 */
+};
+
+/* dpp.c */
+
+void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status);
+void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash);
+unsigned int dpp_next_id(struct dpp_global *dpp);
+struct wpabuf * dpp_build_conn_status(enum dpp_status_error result,
+ const u8 *ssid, size_t ssid_len,
+ const char *channel_list);
+struct json_token * dpp_parse_own_connector(const char *own_connector);
+int dpp_connector_match_groups(struct json_token *own_root,
+ struct json_token *peer_root, bool reconfig);
+int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+ const char *kid, const struct dpp_curve_params *curve);
+EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+ const struct dpp_curve_params **key_curve);
+int dpp_prepare_channel_list(struct dpp_authentication *auth,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes, u16 num_modes);
+void dpp_auth_fail(struct dpp_authentication *auth, const char *txt);
+int dpp_gen_uri(struct dpp_bootstrap_info *bi);
+void dpp_write_adv_proto(struct wpabuf *buf);
+void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query);
+
+/* dpp_backup.c */
+
+void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key);
+struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth);
+int dpp_conf_resp_env_data(struct dpp_authentication *auth,
+ const u8 *env_data, size_t env_data_len);
+
+/* dpp_crypto.c */
+
+struct dpp_signed_connector_info {
+ unsigned char *payload;
+ size_t payload_len;
+};
+
+enum dpp_status_error
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+ EVP_PKEY *csign_pub, const char *connector);
+enum dpp_status_error
+dpp_check_signed_connector(struct dpp_signed_connector_info *info,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len);
+const struct dpp_curve_params * dpp_get_curve_name(const char *name);
+const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name);
+const struct dpp_curve_params * dpp_get_curve_nid(int nid);
+int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len);
+struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix);
+EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+ const u8 *buf_x, const u8 *buf_y,
+ size_t len);
+EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len);
+int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len);
+int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+ const char *label, u8 *out, size_t outlen);
+int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len);
+void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+ const EC_POINT *point);
+void dpp_debug_print_key(const char *title, EVP_PKEY *key);
+int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
+ const u8 *salt, size_t salt_len, unsigned int iterations,
+ u8 *buf, size_t buflen);
+int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len);
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
+int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len);
+EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len);
+EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve);
+int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len);
+int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len);
+int dpp_derive_bk_ke(struct dpp_authentication *auth);
+int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth);
+int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth);
+int dpp_auth_derive_l_responder(struct dpp_authentication *auth);
+int dpp_auth_derive_l_initiator(struct dpp_authentication *auth);
+int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len);
+int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+ EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid);
+EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+ const u8 *mac_init, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group);
+EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+ const u8 *mac_resp, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group);
+int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+ const u8 *Mx, size_t Mx_len,
+ const u8 *Nx, size_t Nx_len,
+ const char *code,
+ const u8 *Kx, size_t Kx_len,
+ u8 *z, unsigned int hash_len);
+int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
+ const u8 *net_access_key,
+ size_t net_access_key_len,
+ struct json_token *peer_net_access_key);
+int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
+ const u8 *r_proto, u16 r_proto_len,
+ struct json_token *net_access_key);
+char * dpp_sign_connector(struct dpp_configurator *conf,
+ const struct wpabuf *dppcon);
+int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve);
+
+/* dpp_tcp.c */
+
+void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+void dpp_tcp_init_flush(struct dpp_global *dpp);
+void dpp_relay_flush_controllers(struct dpp_global *dpp);
+
+#endif /* CONFIG_DPP */
+#endif /* DPP_I_H */
diff --git a/src/common/dpp_pkex.c b/src/common/dpp_pkex.c
new file mode 100644
index 0000000..807ab7d
--- /dev/null
+++ b/src/common/dpp_pkex.c
@@ -0,0 +1,1324 @@
+/*
+ * DPP PKEX functionality
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/crypto.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_ephemeral_key_override[600];
+size_t dpp_pkex_ephemeral_key_override_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
+#endif
+
+
+static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
+{
+ const EC_KEY *X_ec;
+ const EC_POINT *X_point;
+ BN_CTX *bnctx = NULL;
+ EC_GROUP *group = NULL;
+ EC_POINT *Qi = NULL, *M = NULL;
+ struct wpabuf *M_buf = NULL;
+ BIGNUM *Mx = NULL, *My = NULL;
+ struct wpabuf *msg = NULL;
+ size_t attr_len;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
+ pkex->identifier, bnctx, &group);
+ if (!Qi)
+ goto fail;
+
+ /* Generate a random ephemeral keypair x/X */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key x/X");
+ pkex->x = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->x = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ pkex->x = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!pkex->x)
+ goto fail;
+
+ /* M = X + Qi */
+ X_ec = EVP_PKEY_get0_EC_KEY(pkex->x);
+ if (!X_ec)
+ goto fail;
+ X_point = EC_KEY_get0_public_key(X_ec);
+ if (!X_point)
+ goto fail;
+ dpp_debug_print_point("DPP: X", group, X_point);
+ M = EC_POINT_new(group);
+ Mx = BN_new();
+ My = BN_new();
+ if (!M || !Mx || !My ||
+ EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
+ goto fail;
+ dpp_debug_print_point("DPP: M", group, M);
+
+ /* Initiator -> Responder: group, [identifier,] M */
+ attr_len = 4 + 2;
+ if (pkex->identifier)
+ attr_len += 4 + os_strlen(pkex->identifier);
+ attr_len += 4 + 2 * curve->prime_len;
+ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
+ if (!msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
+ goto skip_finite_cyclic_group;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Finite Cyclic Group attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, curve->ike_group);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_finite_cyclic_group:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Code Identifier attribute */
+ if (pkex->identifier) {
+ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+ wpabuf_put_str(msg, pkex->identifier);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* M in Encrypted Key attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+ wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+out:
+ wpabuf_free(M_buf);
+ EC_POINT_free(M);
+ EC_POINT_free(Qi);
+ BN_clear_free(Mx);
+ BN_clear_free(My);
+ BN_CTX_free(bnctx);
+ EC_GROUP_free(group);
+ return msg;
+fail:
+ wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
+{
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
+struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const char *identifier,
+ const char *code)
+{
+ struct dpp_pkex *pkex;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ return NULL;
+ pkex->msg_ctx = msg_ctx;
+ pkex->initiator = 1;
+ pkex->own_bi = bi;
+ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+ if (identifier) {
+ pkex->identifier = os_strdup(identifier);
+ if (!pkex->identifier)
+ goto fail;
+ }
+ pkex->code = os_strdup(code);
+ if (!pkex->code)
+ goto fail;
+ pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
+ if (!pkex->exchange_req)
+ goto fail;
+ return pkex;
+fail:
+ dpp_pkex_free(pkex);
+ return NULL;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
+ enum dpp_status_error status,
+ const BIGNUM *Nx, const BIGNUM *Ny)
+{
+ struct wpabuf *msg = NULL;
+ size_t attr_len;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+ /* Initiator -> Responder: DPP Status, [identifier,] N */
+ attr_len = 4 + 1;
+ if (pkex->identifier)
+ attr_len += 4 + os_strlen(pkex->identifier);
+ attr_len += 4 + 2 * curve->prime_len;
+ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
+ if (!msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ }
+
+ if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 255;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Code Identifier attribute */
+ if (pkex->identifier) {
+ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+ wpabuf_put_str(msg, pkex->identifier);
+ }
+
+ if (status != DPP_STATUS_OK)
+ goto skip_encrypted_key;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* N in Encrypted Key attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+ wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+skip_encrypted_key:
+ if (status == DPP_STATUS_BAD_GROUP) {
+ /* Finite Cyclic Group attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, curve->ike_group);
+ }
+
+ return msg;
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
+ const char *identifier)
+{
+ if (!attr_id && identifier) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PKEX code identifier received, but expected one");
+ return 0;
+ }
+
+ if (attr_id && !identifier) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX code identifier received, but not expecting one");
+ return 0;
+ }
+
+ if (attr_id && identifier &&
+ (os_strlen(identifier) != attr_id_len ||
+ os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
+ wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
+ struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const u8 *peer_mac,
+ const char *identifier,
+ const char *code,
+ const u8 *buf, size_t len)
+{
+ const u8 *attr_group, *attr_id, *attr_key;
+ u16 attr_group_len, attr_id_len, attr_key_len;
+ const struct dpp_curve_params *curve = bi->curve;
+ u16 ike_group;
+ struct dpp_pkex *pkex = NULL;
+ EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
+ BN_CTX *bnctx = NULL;
+ EC_GROUP *group = NULL;
+ BIGNUM *Mx = NULL, *My = NULL;
+ const EC_KEY *Y_ec;
+ EC_KEY *X_ec = NULL;
+ const EC_POINT *Y_point;
+ BIGNUM *Nx = NULL, *Ny = NULL;
+ u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Kx_len;
+ int res;
+
+ if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "PKEX counter t limit reached - ignore message");
+ return NULL;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_id_len = 0;
+ attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
+ &attr_id_len);
+ if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
+ return NULL;
+
+ attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (!attr_group || attr_group_len != 2) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid Finite Cyclic Group attribute");
+ return NULL;
+ }
+ ike_group = WPA_GET_LE16(attr_group);
+ if (ike_group != curve->ike_group) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Mismatching PKEX curve: peer=%u own=%u",
+ ike_group, curve->ike_group);
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->own_bi = bi;
+ pkex->failed = 1;
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(
+ pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
+ if (!pkex->exchange_resp)
+ goto fail;
+ return pkex;
+ }
+
+ /* M in Encrypted Key attribute */
+ attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
+ attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing Encrypted Key attribute");
+ return NULL;
+ }
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
+ &group);
+ if (!Qi)
+ goto fail;
+
+ /* X' = M - Qi */
+ X = EC_POINT_new(group);
+ M = EC_POINT_new(group);
+ Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+ My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+ if (!X || !M || !Mx || !My ||
+ EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, M) ||
+ !EC_POINT_is_on_curve(group, M, bnctx) ||
+ EC_POINT_invert(group, Qi, bnctx) != 1 ||
+ EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, X) ||
+ !EC_POINT_is_on_curve(group, X, bnctx)) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Invalid Encrypted Key value");
+ bi->pkex_t++;
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: M", group, M);
+ dpp_debug_print_point("DPP: X'", group, X);
+
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->t = bi->pkex_t;
+ pkex->msg_ctx = msg_ctx;
+ pkex->own_bi = bi;
+ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+ if (identifier) {
+ pkex->identifier = os_strdup(identifier);
+ if (!pkex->identifier)
+ goto fail;
+ }
+ pkex->code = os_strdup(code);
+ if (!pkex->code)
+ goto fail;
+
+ os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
+
+ X_ec = EC_KEY_new();
+ if (!X_ec ||
+ EC_KEY_set_group(X_ec, group) != 1 ||
+ EC_KEY_set_public_key(X_ec, X) != 1)
+ goto fail;
+ pkex->x = EVP_PKEY_new();
+ if (!pkex->x ||
+ EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
+ goto fail;
+
+ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+ Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
+ if (!Qr)
+ goto fail;
+
+ /* Generate a random ephemeral keypair y/Y */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key y/Y");
+ pkex->y = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->y = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ pkex->y = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!pkex->y)
+ goto fail;
+
+ /* N = Y + Qr */
+ Y_ec = EVP_PKEY_get0_EC_KEY(pkex->y);
+ if (!Y_ec)
+ goto fail;
+ Y_point = EC_KEY_get0_public_key(Y_ec);
+ if (!Y_point)
+ goto fail;
+ dpp_debug_print_point("DPP: Y", group, Y_point);
+ N = EC_POINT_new(group);
+ Nx = BN_new();
+ Ny = BN_new();
+ if (!N || !Nx || !Ny ||
+ EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
+ goto fail;
+ dpp_debug_print_point("DPP: N", group, N);
+
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
+ Nx, Ny);
+ if (!pkex->exchange_resp)
+ goto fail;
+
+ /* K = y * X' */
+ if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+ Kx, Kx_len);
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+ res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
+ pkex->Mx, curve->prime_len,
+ pkex->Nx, curve->prime_len, pkex->code,
+ Kx, Kx_len, pkex->z, curve->hash_len);
+ os_memset(Kx, 0, Kx_len);
+ if (res < 0)
+ goto fail;
+
+ pkex->exchange_done = 1;
+
+out:
+ BN_CTX_free(bnctx);
+ EC_POINT_free(Qi);
+ EC_POINT_free(Qr);
+ BN_free(Mx);
+ BN_free(My);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_POINT_free(M);
+ EC_POINT_free(N);
+ EC_POINT_free(X);
+ EC_KEY_free(X_ec);
+ EC_GROUP_free(group);
+ return pkex;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
+ dpp_pkex_free(pkex);
+ pkex = NULL;
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
+ const struct wpabuf *A_pub, const u8 *u)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ size_t clear_len, attr_len;
+ struct wpabuf *clear = NULL;
+ u8 *wrapped;
+ u8 octet;
+ const u8 *addr[2];
+ size_t len[2];
+
+ /* {A, u, [bootstrapping info]}z */
+ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* A in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(A_pub));
+ wpabuf_put_buf(clear, A_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
+ goto skip_i_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len - 1);
+ wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
+ goto skip_i_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* u in I-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = DPP_HDR_LEN;
+ octet = 0;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(pkex->z, curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+out:
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+ const u8 *peer_mac,
+ const u8 *buf, size_t buflen)
+{
+ const u8 *attr_status, *attr_id, *attr_key, *attr_group;
+ u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
+ EC_GROUP *group = NULL;
+ BN_CTX *bnctx = NULL;
+ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
+ BIGNUM *Nx = NULL, *Ny = NULL;
+ EC_KEY *Y_ec = NULL;
+ size_t Jx_len, Kx_len;
+ u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 u[DPP_MAX_HASH_LEN];
+ int res;
+
+ if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX Exchange Response");
+ pkex->failed = 1;
+ return NULL;
+ }
+
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+
+ attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
+ &attr_status_len);
+ if (!attr_status || attr_status_len != 1) {
+ dpp_pkex_fail(pkex, "No DPP Status attribute");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
+
+ if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
+ attr_group = dpp_get_attr(buf, buflen,
+ DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (attr_group && attr_group_len == 2) {
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Peer indicated mismatching PKEX group - proposed %u",
+ WPA_GET_LE16(attr_group));
+ return NULL;
+ }
+ }
+
+ if (attr_status[0] != DPP_STATUS_OK) {
+ dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
+ return NULL;
+ }
+
+ attr_id_len = 0;
+ attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
+ &attr_id_len);
+ if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
+ pkex->identifier)) {
+ dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
+ return NULL;
+ }
+
+ /* N in Encrypted Key attribute */
+ attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
+ dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
+ return NULL;
+ }
+
+ /* Qr = H(MAC-Responder | [identifier |] code) * Pr */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
+ pkex->identifier, bnctx, &group);
+ if (!Qr)
+ goto fail;
+
+ /* Y' = N - Qr */
+ Y = EC_POINT_new(group);
+ N = EC_POINT_new(group);
+ Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+ Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+ if (!Y || !N || !Nx || !Ny ||
+ EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, N) ||
+ !EC_POINT_is_on_curve(group, N, bnctx) ||
+ EC_POINT_invert(group, Qr, bnctx) != 1 ||
+ EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, Y) ||
+ !EC_POINT_is_on_curve(group, Y, bnctx)) {
+ dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
+ pkex->t++;
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: N", group, N);
+ dpp_debug_print_point("DPP: Y'", group, Y);
+
+ pkex->exchange_done = 1;
+
+ /* ECDH: J = a * Y' */
+ Y_ec = EC_KEY_new();
+ if (!Y_ec ||
+ EC_KEY_set_group(Y_ec, group) != 1 ||
+ EC_KEY_set_public_key(Y_ec, Y) != 1)
+ goto fail;
+ pkex->y = EVP_PKEY_new();
+ if (!pkex->y ||
+ EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
+ goto fail;
+ if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+ Jx, Jx_len);
+
+ /* u = HMAC(J.x, MAC-Initiator | A.x | Y'.x | X.x) */
+ A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ if (!A_pub || !Y_pub || !X_pub)
+ goto fail;
+ addr[0] = pkex->own_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(A_pub);
+ len[1] = wpabuf_len(A_pub) / 2;
+ addr[2] = wpabuf_head(Y_pub);
+ len[2] = wpabuf_len(Y_pub) / 2;
+ addr[3] = wpabuf_head(X_pub);
+ len[3] = wpabuf_len(X_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
+
+ /* K = x * Y' */
+ if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+ Kx, Kx_len);
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+ res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
+ pkex->Mx, curve->prime_len,
+ attr_key /* N.x */, attr_key_len / 2,
+ pkex->code, Kx, Kx_len,
+ pkex->z, curve->hash_len);
+ os_memset(Kx, 0, Kx_len);
+ if (res < 0)
+ goto fail;
+
+ msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
+ if (!msg)
+ goto fail;
+
+out:
+ wpabuf_free(A_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ EC_POINT_free(Qr);
+ EC_POINT_free(Y);
+ EC_POINT_free(N);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_KEY_free(Y_ec);
+ BN_CTX_free(bnctx);
+ EC_GROUP_free(group);
+ return msg;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
+ const struct wpabuf *B_pub, const u8 *v)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 octet;
+ u8 *wrapped;
+ struct wpabuf *clear = NULL;
+ size_t clear_len, attr_len;
+
+ /* {B, v [bootstrapping info]}z */
+ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* B in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(B_pub));
+ wpabuf_put_buf(clear, B_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
+ goto skip_r_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len - 1);
+ wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
+ goto skip_r_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* v in R-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_r_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = DPP_HDR_LEN;
+ octet = 1;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(pkex->z, curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+out:
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+ const u8 *hdr,
+ const u8 *buf, size_t buflen)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ size_t Jx_len, Lx_len;
+ u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ const u8 *wrapped_data, *b_key, *peer_u;
+ u16 wrapped_data_len, b_key_len, peer_u_len = 0;
+ const u8 *addr[4];
+ size_t len[4];
+ u8 octet;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+ struct wpabuf *B_pub = NULL;
+ u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Request");
+ pkex->failed = 1;
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ octet = 0;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (aes_siv_decrypt(pkex->z, curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->failed = 1;
+ pkex->t++;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+ &b_key_len);
+ if (!b_key || b_key_len != 2 * curve->prime_len) {
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+ goto fail;
+ }
+ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+ b_key_len);
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Peer bootstrap public key",
+ pkex->peer_bootstrap_key);
+
+ /* ECDH: J' = y * A' */
+ if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+ Jx, Jx_len);
+
+ /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
+ A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ if (!A_pub || !Y_pub || !X_pub)
+ goto fail;
+ addr[0] = pkex->peer_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(A_pub);
+ len[1] = wpabuf_len(A_pub) / 2;
+ addr[2] = wpabuf_head(Y_pub);
+ len[2] = wpabuf_len(Y_pub) / 2;
+ addr[3] = wpabuf_head(X_pub);
+ len[3] = wpabuf_len(X_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+ goto fail;
+
+ peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+ &peer_u_len);
+ if (!peer_u || peer_u_len != curve->hash_len ||
+ os_memcmp(peer_u, u, curve->hash_len) != 0) {
+ dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
+ u, curve->hash_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
+ pkex->t++;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
+
+ /* ECDH: L = b * X' */
+ if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+ Lx, Lx_len);
+
+ /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
+ B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ if (!B_pub)
+ goto fail;
+ addr[0] = pkex->own_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(B_pub);
+ len[1] = wpabuf_len(B_pub) / 2;
+ addr[2] = wpabuf_head(X_pub);
+ len[2] = wpabuf_len(X_pub) / 2;
+ addr[3] = wpabuf_head(Y_pub);
+ len[3] = wpabuf_len(Y_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
+
+ msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
+ if (!msg)
+ goto fail;
+
+out:
+ os_free(unwrapped);
+ wpabuf_free(A_pub);
+ wpabuf_free(B_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ return msg;
+fail:
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX Commit-Reveal Request processing failed");
+ goto out;
+}
+
+
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+ const u8 *buf, size_t buflen)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ const u8 *wrapped_data, *b_key, *peer_v;
+ u16 wrapped_data_len, b_key_len, peer_v_len = 0;
+ const u8 *addr[4];
+ size_t len[4];
+ u8 octet;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ int ret = -1;
+ u8 v[DPP_MAX_HASH_LEN];
+ size_t Lx_len;
+ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Response");
+ pkex->failed = 1;
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ octet = 1;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (aes_siv_decrypt(pkex->z, curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->t++;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+ &b_key_len);
+ if (!b_key || b_key_len != 2 * curve->prime_len) {
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+ goto fail;
+ }
+ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+ b_key_len);
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Peer bootstrap public key",
+ pkex->peer_bootstrap_key);
+
+ /* ECDH: L' = x * B' */
+ if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+ Lx, Lx_len);
+
+ /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
+ B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ if (!B_pub || !X_pub || !Y_pub)
+ goto fail;
+ addr[0] = pkex->peer_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(B_pub);
+ len[1] = wpabuf_len(B_pub) / 2;
+ addr[2] = wpabuf_head(X_pub);
+ len[2] = wpabuf_len(X_pub) / 2;
+ addr[3] = wpabuf_head(Y_pub);
+ len[3] = wpabuf_len(Y_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+ goto fail;
+
+ peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
+ &peer_v_len);
+ if (!peer_v || peer_v_len != curve->hash_len ||
+ os_memcmp(peer_v, v, curve->hash_len) != 0) {
+ dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
+ v, curve->hash_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
+ pkex->t++;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
+
+ ret = 0;
+out:
+ wpabuf_free(B_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ os_free(unwrapped);
+ return ret;
+fail:
+ goto out;
+}
+
+
+struct dpp_bootstrap_info *
+dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
+ unsigned int freq)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return NULL;
+ bi->id = dpp_next_id(dpp);
+ bi->type = DPP_BOOTSTRAP_PKEX;
+ os_memcpy(bi->mac_addr, peer, ETH_ALEN);
+ bi->num_freq = 1;
+ bi->freq[0] = freq;
+ bi->curve = pkex->own_bi->curve;
+ bi->pubkey = pkex->peer_bootstrap_key;
+ pkex->peer_bootstrap_key = NULL;
+ if (dpp_bootstrap_key_hash(bi) < 0) {
+ dpp_bootstrap_info_free(bi);
+ return NULL;
+ }
+ dpp_pkex_free(pkex);
+ dl_list_add(&dpp->bootstrap, &bi->list);
+ return bi;
+}
+
+
+void dpp_pkex_free(struct dpp_pkex *pkex)
+{
+ if (!pkex)
+ return;
+
+ os_free(pkex->identifier);
+ os_free(pkex->code);
+ EVP_PKEY_free(pkex->x);
+ EVP_PKEY_free(pkex->y);
+ EVP_PKEY_free(pkex->peer_bootstrap_key);
+ wpabuf_free(pkex->exchange_req);
+ wpabuf_free(pkex->exchange_resp);
+ os_free(pkex);
+}
diff --git a/src/common/dpp_reconfig.c b/src/common/dpp_reconfig.c
new file mode 100644
index 0000000..0bb00cc
--- /dev/null
+++ b/src/common/dpp_reconfig.c
@@ -0,0 +1,881 @@
+/*
+ * DPP reconfiguration
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "utils/json.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_DPP2
+
+static void dpp_build_attr_csign_key_hash(struct wpabuf *msg, const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: Configurator C-sign key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_C_SIGN_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
+ size_t csign_key_len)
+{
+ struct wpabuf *msg;
+ EVP_PKEY *csign = NULL;
+ const unsigned char *p;
+ struct wpabuf *uncomp;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[1];
+ size_t len[1];
+ int res;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Reconfig Announcement frame");
+
+ p = csign_key;
+ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ if (!csign) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to parse local C-sign-key information");
+ return NULL;
+ }
+
+ uncomp = dpp_get_pubkey_point(csign, 1);
+ EVP_PKEY_free(csign);
+ if (!uncomp)
+ return NULL;
+ addr[0] = wpabuf_head(uncomp);
+ len[0] = wpabuf_len(uncomp);
+ wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed C-sign key", addr[0], len[0]);
+ res = sha256_vector(1, addr, len, hash);
+ wpabuf_free(uncomp);
+ if (res < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "DPP: kid = SHA256(uncompressed C-sign key)",
+ hash, SHA256_MAC_LEN);
+
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, 4 + SHA256_MAC_LEN);
+ if (!msg)
+ return NULL;
+
+ /* Configurator C-sign key Hash */
+ dpp_build_attr_csign_key_hash(msg, hash);
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Announcement frame attributes", msg);
+ return msg;
+}
+
+
+static struct wpabuf * dpp_reconfig_build_req(struct dpp_authentication *auth)
+{
+ struct wpabuf *msg;
+ size_t attr_len;
+
+ /* Build DPP Reconfig Authentication Request frame attributes */
+ attr_len = 4 + 1 + 4 + 1 + 4 + os_strlen(auth->conf->connector) +
+ 4 + auth->curve->nonce_len;
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_REQ, attr_len);
+ if (!msg)
+ return NULL;
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, auth->transaction_id);
+
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+
+ /* DPP Connector */
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(auth->conf->connector));
+ wpabuf_put_str(msg, auth->conf->connector);
+
+ /* I-nonce */
+ wpabuf_put_le16(msg, DPP_ATTR_I_NONCE);
+ wpabuf_put_le16(msg, auth->curve->nonce_len);
+ wpabuf_put_data(msg, auth->i_nonce, auth->curve->nonce_len);
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Authentication Request frame attributes",
+ msg);
+
+ return msg;
+}
+
+
+static int dpp_configurator_build_own_connector(struct dpp_configurator *conf)
+{
+ struct wpabuf *dppcon = NULL;
+ int ret = -1;
+
+ if (conf->connector)
+ return 0; /* already generated */
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Sign own Configurator Connector for reconfiguration with curve %s",
+ conf->curve->name);
+ conf->connector_key = dpp_gen_keypair(conf->curve);
+ if (!conf->connector_key)
+ goto fail;
+
+ /* Connector (JSON dppCon object) */
+ dppcon = wpabuf_alloc(1000 + 2 * conf->curve->prime_len * 4 / 3);
+ if (!dppcon)
+ goto fail;
+ json_start_object(dppcon, NULL);
+ json_start_array(dppcon, "groups");
+ json_start_object(dppcon, NULL);
+ json_add_string(dppcon, "groupId", "*");
+ json_value_sep(dppcon);
+ json_add_string(dppcon, "netRole", "configurator");
+ json_end_object(dppcon);
+ json_end_array(dppcon);
+ json_value_sep(dppcon);
+ if (dpp_build_jwk(dppcon, "netAccessKey", conf->connector_key, NULL,
+ conf->curve) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
+ goto fail;
+ }
+ json_end_object(dppcon);
+ wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
+ (const char *) wpabuf_head(dppcon));
+
+ conf->connector = dpp_sign_connector(conf, dppcon);
+ if (!conf->connector)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: signedConnector: %s", conf->connector);
+
+ ret = 0;
+fail:
+ wpabuf_free(dppcon);
+ return ret;
+}
+
+
+struct dpp_authentication *
+dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_configurator *conf, unsigned int freq)
+{
+ struct dpp_authentication *auth;
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ return NULL;
+
+ auth->conf = conf;
+ auth->reconfig = 1;
+ auth->initiator = 1;
+ auth->waiting_auth_resp = 1;
+ auth->allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ auth->configurator = 1;
+ auth->curve = conf->curve;
+ auth->transaction_id = 1;
+ if (freq && dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
+ goto fail;
+
+ if (dpp_configurator_build_own_connector(conf) < 0)
+ goto fail;
+
+ if (random_get_bytes(auth->i_nonce, auth->curve->nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+
+ auth->reconfig_req_msg = dpp_reconfig_build_req(auth);
+ if (!auth->reconfig_req_msg)
+ goto fail;
+
+out:
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+
+
+static int dpp_reconfig_build_resp(struct dpp_authentication *auth,
+ const char *own_connector,
+ struct wpabuf *conn_status)
+{
+ struct wpabuf *msg = NULL, *clear, *pr = NULL;
+ u8 *attr_start, *attr_end;
+ size_t clear_len, attr_len, len[2];
+ const u8 *addr[2];
+ u8 *wrapped;
+ int res = -1;
+
+ /* Build DPP Reconfig Authentication Response frame attributes */
+ clear_len = 2 * (4 + auth->curve->nonce_len) +
+ 4 + wpabuf_len(conn_status);
+ clear = wpabuf_alloc(clear_len);
+ if (!clear)
+ goto fail;
+
+ /* I-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_I_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->i_nonce, auth->curve->nonce_len);
+
+ /* R-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_R_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->r_nonce, auth->curve->nonce_len);
+
+ /* Connection Status (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS);
+ wpabuf_put_le16(clear, wpabuf_len(conn_status));
+ wpabuf_put_buf(clear, conn_status);
+
+ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pr)
+ goto fail;
+
+ attr_len = 4 + 1 + 4 + 1 +
+ 4 + os_strlen(own_connector) +
+ 4 + wpabuf_len(pr) +
+ 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_RESP, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, auth->transaction_id);
+
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+
+ /* R-Connector */
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(own_connector));
+ wpabuf_put_str(msg, own_connector);
+
+ /* Responder Protocol Key (Pr) */
+ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pr));
+ wpabuf_put_buf(msg, pr);
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ /* Wrapped Data: {I-nonce, R-nonce, Connection Status}ke */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Authentication Response frame attributes",
+ msg);
+
+ wpabuf_free(auth->reconfig_resp_msg);
+ auth->reconfig_resp_msg = msg;
+
+ res = 0;
+out:
+ wpabuf_free(clear);
+ wpabuf_free(pr);
+ return res;
+fail:
+ wpabuf_free(msg);
+ goto out;
+}
+
+
+struct dpp_authentication *
+dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
+ const char *own_connector,
+ const u8 *net_access_key, size_t net_access_key_len,
+ const u8 *csign_key, size_t csign_key_len,
+ unsigned int freq, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ struct dpp_authentication *auth = NULL;
+ const u8 *trans_id, *version, *i_connector, *i_nonce;
+ u16 trans_id_len, version_len, i_connector_len, i_nonce_len;
+ struct dpp_signed_connector_info info;
+ enum dpp_status_error res;
+ struct json_token *root = NULL, *own_root = NULL, *token;
+ unsigned char *own_conn = NULL;
+ struct wpabuf *conn_status = NULL;
+
+ os_memset(&info, 0, sizeof(info));
+
+ trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Transaction ID");
+ goto fail;
+ }
+
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (!version || version_len < 1 || version[0] < 2) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing or invalid Protocol Version attribute");
+ goto fail;
+ }
+
+ i_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
+ &i_connector_len);
+ if (!i_connector) {
+ wpa_printf(MSG_DEBUG, "DPP: Missing I-Connector attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: I-Connector",
+ i_connector, i_connector_len);
+
+ i_nonce = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len > DPP_MAX_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing or invalid I-Nonce attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-Nonce", i_nonce, i_nonce_len);
+
+ res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
+ i_connector, i_connector_len);
+ if (res != DPP_STATUS_OK) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid I-Connector");
+ goto fail;
+ }
+
+ root = json_parse((const char *) info.payload, info.payload_len);
+ own_root = dpp_parse_own_connector(own_connector);
+ if (!root || !own_root ||
+ !dpp_connector_match_groups(own_root, root, true)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: I-Connector does not include compatible group netrole with own connector");
+ goto fail;
+ }
+
+ token = json_get_member(root, "expiry");
+ if (token && token->type == JSON_STRING &&
+ dpp_key_expired(token->string, NULL)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: I-Connector (netAccessKey) has expired");
+ goto fail;
+ }
+
+ token = json_get_member(root, "netAccessKey");
+ if (!token || token->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+ goto fail;
+ }
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ return NULL;
+
+ auth->reconfig = 1;
+ auth->allowed_roles = DPP_CAPAB_ENROLLEE;
+ if (dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
+ goto fail;
+
+ auth->transaction_id = trans_id[0];
+
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+
+ os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+ if (dpp_reconfig_derive_ke_responder(auth, net_access_key,
+ net_access_key_len, token) < 0)
+ goto fail;
+
+ if (i_nonce_len != auth->curve->nonce_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected I-nonce length %u (curve nonce len %zu)",
+ i_nonce_len, auth->curve->nonce_len);
+ goto fail;
+ }
+
+ if (random_get_bytes(auth->r_nonce, auth->curve->nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: R-nonce",
+ auth->r_nonce, auth->curve->nonce_len);
+
+ /* Build Connection Status object */
+ /* TODO: Get appropriate result value */
+ /* TODO: ssid64 and channelList */
+ conn_status = dpp_build_conn_status(DPP_STATUS_NO_AP, NULL, 0, NULL);
+ if (!conn_status)
+ goto fail;
+
+ if (dpp_reconfig_build_resp(auth, own_connector, conn_status) < 0)
+ goto fail;
+
+out:
+ os_free(info.payload);
+ os_free(own_conn);
+ json_free(root);
+ json_free(own_root);
+ wpabuf_free(conn_status);
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_build_reconfig_flags(enum dpp_connector_key connector_key)
+{
+ struct wpabuf *json;
+
+ json = wpabuf_alloc(100);
+ if (!json)
+ return NULL;
+ json_start_object(json, NULL);
+ json_add_int(json, "connectorKey", connector_key);
+ json_end_object(json);
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Reconfig-Flags JSON",
+ wpabuf_head(json), wpabuf_len(json));
+
+ return json;
+}
+
+
+struct wpabuf *
+dpp_reconfig_build_conf(struct dpp_authentication *auth)
+{
+ struct wpabuf *msg = NULL, *clear = NULL, *reconfig_flags;
+ u8 *attr_start, *attr_end;
+ size_t clear_len, attr_len, len[2];
+ const u8 *addr[2];
+ u8 *wrapped;
+
+ reconfig_flags = dpp_build_reconfig_flags(DPP_CONFIG_REPLACEKEY);
+ if (!reconfig_flags)
+ goto fail;
+
+ /* Build DPP Reconfig Authentication Confirm frame attributes */
+ clear_len = 4 + 1 + 4 + 1 + 2 * (4 + auth->curve->nonce_len) +
+ 4 + wpabuf_len(reconfig_flags);
+ clear = wpabuf_alloc(clear_len);
+ if (!clear)
+ goto fail;
+
+ /* Transaction ID */
+ wpabuf_put_le16(clear, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(clear, 1);
+ wpabuf_put_u8(clear, auth->transaction_id);
+
+ /* Protocol Version */
+ wpabuf_put_le16(clear, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(clear, 1);
+ wpabuf_put_u8(clear, auth->peer_version);
+
+ /* I-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_I_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->i_nonce, auth->curve->nonce_len);
+
+ /* R-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_R_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->r_nonce, auth->curve->nonce_len);
+
+ /* Reconfig-Flags (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_RECONFIG_FLAGS);
+ wpabuf_put_le16(clear, wpabuf_len(reconfig_flags));
+ wpabuf_put_buf(clear, reconfig_flags);
+
+ attr_len = 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_CONF, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ /* Wrapped Data */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Authentication Confirm frame attributes",
+ msg);
+
+out:
+ wpabuf_free(reconfig_flags);
+ wpabuf_free(clear);
+ return msg;
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf *
+dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *trans_id, *version, *r_connector, *r_proto, *wrapped_data,
+ *i_nonce, *r_nonce, *conn_status;
+ u16 trans_id_len, version_len, r_connector_len, r_proto_len,
+ wrapped_data_len, i_nonce_len, r_nonce_len, conn_status_len;
+ struct wpabuf *conf = NULL;
+ char *signed_connector = NULL;
+ struct dpp_signed_connector_info info;
+ enum dpp_status_error res;
+ struct json_token *root = NULL, *token, *conn_status_json = NULL;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+
+ os_memset(&info, 0, sizeof(info));
+
+ if (!auth->reconfig || !auth->configurator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ dpp_auth_fail(auth, "Peer did not include Transaction ID");
+ goto fail;
+ }
+ if (trans_id[0] != auth->transaction_id) {
+ dpp_auth_fail(auth, "Transaction ID mismatch");
+ goto fail;
+ }
+
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (!version || version_len < 1 || version[0] < 2) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Protocol Version attribute");
+ goto fail;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+
+ r_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
+ &r_connector_len);
+ if (!r_connector) {
+ dpp_auth_fail(auth, " Missing R-Connector attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: R-Connector",
+ r_connector, r_connector_len);
+
+ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+ &r_proto_len);
+ if (!r_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Responder Protocol Key attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+ r_proto, r_proto_len);
+
+ signed_connector = os_malloc(r_connector_len + 1);
+ if (!signed_connector)
+ goto fail;
+ os_memcpy(signed_connector, r_connector, r_connector_len);
+ signed_connector[r_connector_len] = '\0';
+
+ res = dpp_process_signed_connector(&info, auth->conf->csign,
+ signed_connector);
+ if (res != DPP_STATUS_OK) {
+ dpp_auth_fail(auth, "Invalid R-Connector");
+ goto fail;
+ }
+
+ root = json_parse((const char *) info.payload, info.payload_len);
+ if (!root) {
+ dpp_auth_fail(auth, "Invalid Connector payload");
+ goto fail;
+ }
+
+ /* Do not check netAccessKey expiration for reconfiguration to allow
+ * expired Connector to be updated. */
+
+ token = json_get_member(root, "netAccessKey");
+ if (!token || token->type != JSON_OBJECT) {
+ dpp_auth_fail(auth, "No netAccessKey object found");
+ goto fail;
+ }
+
+ if (dpp_reconfig_derive_ke_initiator(auth, r_proto, r_proto_len,
+ token) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len ||
+ os_memcmp(i_nonce, auth->i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid R-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+ os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+ conn_status = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONN_STATUS, &conn_status_len);
+ if (!conn_status) {
+ dpp_auth_fail(auth, "Missing Connection Status attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus",
+ conn_status, conn_status_len);
+
+ conn_status_json = json_parse((const char *) conn_status,
+ conn_status_len);
+ if (!conn_status_json) {
+ dpp_auth_fail(auth, "Could not parse connStatus");
+ goto fail;
+ }
+ /* TODO: use connStatus information */
+
+ conf = dpp_reconfig_build_conf(auth);
+ if (conf)
+ auth->reconfig_success = true;
+
+out:
+ json_free(root);
+ json_free(conn_status_json);
+ bin_clear_free(unwrapped, unwrapped_len);
+ os_free(info.payload);
+ os_free(signed_connector);
+ return conf;
+fail:
+ wpabuf_free(conf);
+ conf = NULL;
+ goto out;
+}
+
+
+int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *trans_id, *version, *wrapped_data, *i_nonce, *r_nonce,
+ *reconfig_flags;
+ u16 trans_id_len, version_len, wrapped_data_len, i_nonce_len,
+ r_nonce_len, reconfig_flags_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ struct json_token *root = NULL, *token;
+ int res = -1;
+
+ if (!auth->reconfig || auth->configurator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = 0;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ trans_id = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_TRANSACTION_ID, &trans_id_len);
+ if (!trans_id || trans_id_len != 1 ||
+ trans_id[0] != auth->transaction_id) {
+ dpp_auth_fail(auth,
+ "Peer did not include valid Transaction ID");
+ goto fail;
+ }
+
+ version = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_PROTOCOL_VERSION, &version_len);
+ if (!version || version_len < 1 || version[0] != DPP_VERSION) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Protocol Version attribute");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len ||
+ os_memcmp(i_nonce, auth->i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len ||
+ os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Missing or invalid R-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+
+ reconfig_flags = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_RECONFIG_FLAGS,
+ &reconfig_flags_len);
+ if (!reconfig_flags) {
+ dpp_auth_fail(auth, "Missing or invalid Reconfig-Flags");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Reconfig-Flags",
+ reconfig_flags, reconfig_flags_len);
+ root = json_parse((const char *) reconfig_flags, reconfig_flags_len);
+ if (!root) {
+ dpp_auth_fail(auth, "Could not parse Reconfig-Flags");
+ goto fail;
+ }
+ token = json_get_member(root, "connectorKey");
+ if (!token || token->type != JSON_NUMBER) {
+ dpp_auth_fail(auth, "No connectorKey in Reconfig-Flags");
+ goto fail;
+ }
+ if (token->number != DPP_CONFIG_REUSEKEY &&
+ token->number != DPP_CONFIG_REPLACEKEY) {
+ dpp_auth_fail(auth,
+ "Unsupported connectorKey value in Reconfig-Flags");
+ goto fail;
+ }
+ auth->reconfig_connector_key = token->number;
+
+ auth->reconfig_success = true;
+ res = 0;
+fail:
+ json_free(root);
+ bin_clear_free(unwrapped, unwrapped_len);
+ return res;
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
new file mode 100644
index 0000000..fc53b8a
--- /dev/null
+++ b/src/common/dpp_tcp.c
@@ -0,0 +1,1512 @@
+/*
+ * DPP over TCP
+ * Copyright (c) 2019-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "utils/ip_addr.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+#ifdef CONFIG_DPP2
+
+struct dpp_connection {
+ struct dl_list list;
+ struct dpp_controller *ctrl;
+ struct dpp_relay_controller *relay;
+ struct dpp_global *global;
+ struct dpp_authentication *auth;
+ int sock;
+ u8 mac_addr[ETH_ALEN];
+ unsigned int freq;
+ u8 msg_len[4];
+ size_t msg_len_octets;
+ struct wpabuf *msg;
+ struct wpabuf *msg_out;
+ size_t msg_out_pos;
+ unsigned int read_eloop:1;
+ unsigned int write_eloop:1;
+ unsigned int on_tcp_tx_complete_gas_done:1;
+ unsigned int on_tcp_tx_complete_remove:1;
+ unsigned int on_tcp_tx_complete_auth_ok:1;
+};
+
+/* Remote Controller */
+struct dpp_relay_controller {
+ struct dl_list list;
+ struct dpp_global *global;
+ u8 pkhash[SHA256_MAC_LEN];
+ struct hostapd_ip_addr ipaddr;
+ void *cb_ctx;
+ void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
+ size_t len);
+ void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
+ int prot, struct wpabuf *buf);
+ struct dl_list conn; /* struct dpp_connection */
+};
+
+/* Local Controller */
+struct dpp_controller {
+ struct dpp_global *global;
+ u8 allowed_roles;
+ int qr_mutual;
+ int sock;
+ struct dl_list conn; /* struct dpp_connection */
+ char *configurator_params;
+};
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+ int initiator);
+
+
+static void dpp_connection_free(struct dpp_connection *conn)
+{
+ if (conn->sock >= 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d",
+ conn->sock);
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_READ);
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+ close(conn->sock);
+ }
+ eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
+ conn, NULL);
+ wpabuf_free(conn->msg);
+ wpabuf_free(conn->msg_out);
+ dpp_auth_deinit(conn->auth);
+ os_free(conn);
+}
+
+
+static void dpp_connection_remove(struct dpp_connection *conn)
+{
+ dl_list_del(&conn->list);
+ dpp_connection_free(conn);
+}
+
+
+int dpp_relay_add_controller(struct dpp_global *dpp,
+ struct dpp_relay_config *config)
+{
+ struct dpp_relay_controller *ctrl;
+
+ if (!dpp)
+ return -1;
+
+ ctrl = os_zalloc(sizeof(*ctrl));
+ if (!ctrl)
+ return -1;
+ dl_list_init(&ctrl->conn);
+ ctrl->global = dpp;
+ os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
+ os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
+ ctrl->cb_ctx = config->cb_ctx;
+ ctrl->tx = config->tx;
+ ctrl->gas_resp_tx = config->gas_resp_tx;
+ dl_list_add(&dpp->controllers, &ctrl->list);
+ return 0;
+}
+
+
+static struct dpp_relay_controller *
+dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash)
+{
+ struct dpp_relay_controller *ctrl;
+
+ if (!dpp)
+ return NULL;
+
+ dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
+ list) {
+ if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0)
+ return ctrl;
+ }
+
+ return NULL;
+}
+
+
+static void dpp_controller_gas_done(struct dpp_connection *conn)
+{
+ struct dpp_authentication *auth = conn->auth;
+ void *msg_ctx;
+
+ if (auth->peer_version >= 2 &&
+ auth->conf_resp_status == DPP_STATUS_OK) {
+ wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+ auth->waiting_conf_result = 1;
+ return;
+ }
+
+ if (conn->ctrl)
+ msg_ctx = conn->ctrl->global->msg_ctx;
+ else
+ msg_ctx = auth->msg_ctx;
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ dpp_connection_remove(conn);
+}
+
+
+static int dpp_tcp_send(struct dpp_connection *conn)
+{
+ int res;
+
+ if (!conn->msg_out) {
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+ conn->write_eloop = 0;
+ return -1;
+ }
+ res = send(conn->sock,
+ wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos,
+ wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s",
+ strerror(errno));
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ conn->msg_out_pos += res;
+ if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: %u/%u bytes of message sent to Controller",
+ (unsigned int) conn->msg_out_pos,
+ (unsigned int) wpabuf_len(conn->msg_out));
+ if (!conn->write_eloop &&
+ eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready, conn, NULL) == 0)
+ conn->write_eloop = 1;
+ return 1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP");
+ wpabuf_free(conn->msg_out);
+ conn->msg_out = NULL;
+ conn->msg_out_pos = 0;
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+ conn->write_eloop = 0;
+ if (!conn->read_eloop &&
+ eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+ dpp_controller_rx, conn, NULL) == 0)
+ conn->read_eloop = 1;
+ if (conn->on_tcp_tx_complete_remove) {
+ dpp_connection_remove(conn);
+ } else if (conn->auth && (conn->ctrl || conn->auth->configurator) &&
+ conn->on_tcp_tx_complete_gas_done) {
+ dpp_controller_gas_done(conn);
+ } else if (conn->on_tcp_tx_complete_auth_ok) {
+ conn->on_tcp_tx_complete_auth_ok = 0;
+ dpp_controller_auth_success(conn, 1);
+ }
+
+ return 0;
+}
+
+
+static int dpp_tcp_send_msg(struct dpp_connection *conn,
+ const struct wpabuf *msg)
+{
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
+ if (!conn->msg_out)
+ return -1;
+ wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1);
+ wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1,
+ wpabuf_len(msg) - 1);
+
+ if (dpp_tcp_send(conn) == 1) {
+ if (!conn->write_eloop) {
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready,
+ conn, NULL) < 0)
+ return -1;
+ conn->write_eloop = 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void dpp_controller_start_gas_client(struct dpp_connection *conn)
+{
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *buf;
+ int netrole_ap = 0; /* TODO: make this configurable */
+
+ buf = dpp_build_conf_req_helper(auth, "Test", netrole_ap, NULL, NULL);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No configuration request data available");
+ return;
+ }
+
+ dpp_tcp_send_msg(conn, buf);
+ wpabuf_free(buf);
+}
+
+
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+ int initiator)
+{
+ struct dpp_authentication *auth = conn->auth;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+ wpa_msg(conn->global->msg_ctx, MSG_INFO,
+ DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ if (auth->configurator) {
+ /* Prevent GAS response */
+ auth->auth_success = 0;
+ }
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->configurator)
+ dpp_controller_start_gas_client(conn);
+}
+
+
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock);
+ dpp_tcp_send(conn);
+}
+
+
+static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen,
+ const struct hostapd_ip_addr *ipaddr,
+ int port)
+{
+ struct sockaddr_in *dst;
+#ifdef CONFIG_IPV6
+ struct sockaddr_in6 *dst6;
+#endif /* CONFIG_IPV6 */
+
+ switch (ipaddr->af) {
+ case AF_INET:
+ dst = (struct sockaddr_in *) addr;
+ os_memset(dst, 0, sizeof(*dst));
+ dst->sin_family = AF_INET;
+ dst->sin_addr.s_addr = ipaddr->u.v4.s_addr;
+ dst->sin_port = htons(port);
+ *addrlen = sizeof(*dst);
+ break;
+#ifdef CONFIG_IPV6
+ case AF_INET6:
+ dst6 = (struct sockaddr_in6 *) addr;
+ os_memset(dst6, 0, sizeof(*dst6));
+ dst6->sin6_family = AF_INET6;
+ os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6,
+ sizeof(struct in6_addr));
+ dst6->sin6_port = htons(port);
+ *addrlen = sizeof(*dst6);
+ break;
+#endif /* CONFIG_IPV6 */
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct dpp_connection *
+dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src,
+ unsigned int freq)
+{
+ struct dpp_connection *conn;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ char txt[100];
+
+ if (dl_list_len(&ctrl->conn) >= 15) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
+ return NULL;
+ }
+
+ if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen,
+ &ctrl->ipaddr, DPP_TCP_PORT) < 0)
+ return NULL;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn)
+ return NULL;
+
+ conn->global = ctrl->global;
+ conn->relay = ctrl;
+ os_memcpy(conn->mac_addr, src, ETH_ALEN);
+ conn->freq = freq;
+
+ conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (conn->sock < 0)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s",
+ conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
+
+ if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) {
+ if (errno != EINPROGRESS) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * Continue connecting in the background; eloop will call us
+ * once the connection is ready (or failed).
+ */
+ }
+
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready, conn, NULL) < 0)
+ goto fail;
+ conn->write_eloop = 1;
+
+ /* TODO: eloop timeout to clear a connection if it does not complete
+ * properly */
+
+ dl_list_add(&ctrl->conn, &conn->list);
+ return conn;
+fail:
+ dpp_connection_free(conn);
+ return NULL;
+}
+
+
+static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct wpabuf *msg;
+
+ msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len);
+ if (!msg)
+ return NULL;
+ wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len);
+ wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+ wpabuf_put_data(msg, hdr, DPP_HDR_LEN);
+ wpabuf_put_data(msg, buf, len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+ return msg;
+}
+
+
+static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr,
+ const u8 *buf, size_t len)
+{
+ u8 type = hdr[DPP_HDR_LEN - 1];
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue already established Relay/Controller connection for this session");
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+ if (!conn->msg_out) {
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ /* TODO: for proto ver 1, need to do remove connection based on GAS Resp
+ * TX status */
+ if (type == DPP_PA_CONFIGURATION_RESULT)
+ conn->on_tcp_tx_complete_remove = 1;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
+ const u8 *buf, size_t len, unsigned int freq,
+ const u8 *i_bootstrap, const u8 *r_bootstrap)
+{
+ struct dpp_relay_controller *ctrl;
+ struct dpp_connection *conn;
+ u8 type = hdr[DPP_HDR_LEN - 1];
+
+ /* Check if there is an already started session for this peer and if so,
+ * continue that session (send this over TCP) and return 0.
+ */
+ if (type != DPP_PA_PEER_DISCOVERY_REQ &&
+ type != DPP_PA_PEER_DISCOVERY_RESP &&
+ type != DPP_PA_PRESENCE_ANNOUNCEMENT &&
+ type != DPP_PA_RECONFIG_ANNOUNCEMENT) {
+ dl_list_for_each(ctrl, &dpp->controllers,
+ struct dpp_relay_controller, list) {
+ dl_list_for_each(conn, &ctrl->conn,
+ struct dpp_connection, list) {
+ if (os_memcmp(src, conn->mac_addr,
+ ETH_ALEN) == 0)
+ return dpp_relay_tx(conn, hdr, buf, len);
+ }
+ }
+ }
+
+ if (type == DPP_PA_PRESENCE_ANNOUNCEMENT ||
+ type == DPP_PA_RECONFIG_ANNOUNCEMENT) {
+ /* TODO: Could send this to all configured Controllers. For now,
+ * only the first Controller is supported. */
+ ctrl = dl_list_first(&dpp->controllers,
+ struct dpp_relay_controller, list);
+ } else {
+ if (!r_bootstrap)
+ return -1;
+ ctrl = dpp_relay_controller_get(dpp, r_bootstrap);
+ }
+ if (!ctrl)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication Request for a configured Controller");
+ conn = dpp_relay_new_conn(ctrl, src, freq);
+ if (!conn)
+ return -1;
+
+ conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+ if (!conn->msg_out) {
+ dpp_connection_remove(conn);
+ return -1;
+ }
+ /* Message will be sent in dpp_conn_tx_ready() */
+
+ return 0;
+}
+
+
+int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
+ size_t data_len)
+{
+ struct dpp_relay_controller *ctrl;
+ struct dpp_connection *conn, *found = NULL;
+ struct wpabuf *msg;
+
+ /* Check if there is a successfully completed authentication for this
+ * and if so, continue that session (send this over TCP) and return 0.
+ */
+ dl_list_for_each(ctrl, &dpp->controllers,
+ struct dpp_relay_controller, list) {
+ if (found)
+ break;
+ dl_list_for_each(conn, &ctrl->conn,
+ struct dpp_connection, list) {
+ if (os_memcmp(src, conn->mac_addr,
+ ETH_ALEN) == 0) {
+ found = conn;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return -1;
+
+ msg = wpabuf_alloc(4 + 1 + data_len);
+ if (!msg)
+ return -1;
+ wpabuf_put_be32(msg, 1 + data_len);
+ wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ);
+ wpabuf_put_data(msg, data, data_len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = msg;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+static void dpp_controller_free(struct dpp_controller *ctrl)
+{
+ struct dpp_connection *conn, *tmp;
+
+ if (!ctrl)
+ return;
+
+ dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+ list)
+ dpp_connection_remove(conn);
+
+ if (ctrl->sock >= 0) {
+ close(ctrl->sock);
+ eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ);
+ }
+ os_free(ctrl->configurator_params);
+ os_free(ctrl);
+}
+
+
+static int dpp_controller_rx_auth_req(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ const u8 *r_bootstrap, *i_bootstrap;
+ u16 r_bootstrap_len, i_bootstrap_len;
+ struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
+
+ if (!conn->ctrl)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Request");
+
+ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_printf(MSG_INFO,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+
+ i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_printf(MSG_INFO,
+ "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+
+ /* Try to find own and peer bootstrapping key matches based on the
+ * received hash values */
+ dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap,
+ &own_bi, &peer_bi);
+ if (!own_bi) {
+ wpa_printf(MSG_INFO,
+ "No matching own bootstrapping key found - ignore message");
+ return -1;
+ }
+
+ if (conn->auth) {
+ wpa_printf(MSG_INFO,
+ "Already in DPP authentication exchange - ignore new one");
+ return 0;
+ }
+
+ conn->auth = dpp_auth_req_rx(conn->ctrl->global,
+ conn->ctrl->global->msg_ctx,
+ conn->ctrl->allowed_roles,
+ conn->ctrl->qr_mutual,
+ peer_bi, own_bi, -1, hdr, buf, len);
+ if (!conn->auth) {
+ wpa_printf(MSG_DEBUG, "DPP: No response generated");
+ return -1;
+ }
+
+ if (dpp_set_configurator(conn->auth,
+ conn->ctrl->configurator_params) < 0) {
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ return dpp_tcp_send_msg(conn, conn->auth->resp_msg);
+}
+
+
+static int dpp_controller_rx_auth_resp(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *msg;
+ int res;
+
+ if (!auth)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Response");
+
+ msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+ if (!msg) {
+ if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Start wait for full response");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ conn->on_tcp_tx_complete_auth_ok = 1;
+ res = dpp_tcp_send_msg(conn, msg);
+ wpabuf_free(msg);
+ return res;
+}
+
+
+static int dpp_controller_rx_auth_conf(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation");
+
+ if (!auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Authentication in progress - drop");
+ return -1;
+ }
+
+ if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+ return -1;
+ }
+
+ dpp_controller_auth_success(conn, 0);
+ return 0;
+}
+
+
+void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+
+ if (!conn->auth->waiting_conf_result)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Timeout while waiting for Connection Status Result");
+ wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+ DPP_EVENT_CONN_STATUS_RESULT "timeout");
+ dpp_connection_remove(conn);
+}
+
+
+static int dpp_controller_rx_conf_result(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ enum dpp_status_error status;
+ void *msg_ctx;
+
+ if (!conn->ctrl && (!auth || !auth->configurator))
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Configuration Result");
+
+ if (!auth || !auth->waiting_conf_result) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Configuration waiting for result - drop");
+ return -1;
+ }
+ if (conn->ctrl)
+ msg_ctx = conn->ctrl->global->msg_ctx;
+ else
+ msg_ctx = auth->msg_ctx;
+
+ status = dpp_conf_result_rx(auth, hdr, buf, len);
+ if (status == DPP_STATUS_OK && auth->send_conn_status) {
+ wpa_msg(msg_ctx, MSG_INFO,
+ DPP_EVENT_CONF_SENT "wait_conn_status=1");
+ wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
+ eloop_cancel_timeout(
+ dpp_controller_conn_status_result_wait_timeout,
+ conn, NULL);
+ eloop_register_timeout(
+ 16, 0, dpp_controller_conn_status_result_wait_timeout,
+ conn, NULL);
+ return 0;
+ }
+ if (status == DPP_STATUS_OK)
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ else
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ enum dpp_status_error status;
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len = 0;
+ char *channel_list = NULL;
+
+ if (!conn->ctrl)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
+
+ if (!auth || !auth->waiting_conn_status_result) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Configuration waiting for connection status result - drop");
+ return -1;
+ }
+
+ status = dpp_conn_status_result_rx(auth, hdr, buf, len,
+ ssid, &ssid_len, &channel_list);
+ wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+ DPP_EVENT_CONN_STATUS_RESULT
+ "result=%d ssid=%s channel_list=%s",
+ status, wpa_ssid_txt(ssid, ssid_len),
+ channel_list ? channel_list : "N/A");
+ os_free(channel_list);
+ return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_presence_announcement(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ const u8 *r_bootstrap;
+ u16 r_bootstrap_len;
+ struct dpp_bootstrap_info *peer_bi;
+ struct dpp_authentication *auth;
+ struct dpp_global *dpp = conn->ctrl->global;
+
+ if (conn->auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Presence Announcement during ongoing Authentication");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
+
+ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ peer_bi = dpp_bootstrap_find_chirp(dpp, r_bootstrap);
+ if (!peer_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching bootstrapping information found");
+ return -1;
+ }
+
+ auth = dpp_auth_init(dpp, dpp->msg_ctx, peer_bi, NULL,
+ DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
+ if (!auth)
+ return -1;
+ if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ conn->auth = auth;
+ return dpp_tcp_send_msg(conn, conn->auth->req_msg);
+}
+
+
+static int dpp_controller_rx_reconfig_announcement(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ const u8 *csign_hash;
+ u16 csign_hash_len;
+ struct dpp_configurator *conf;
+ struct dpp_global *dpp = conn->ctrl->global;
+ struct dpp_authentication *auth;
+
+ if (conn->auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement");
+
+ csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+ &csign_hash_len);
+ if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+ wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Configurator C-sign key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+ csign_hash, csign_hash_len);
+ conf = dpp_configurator_find_kid(dpp, csign_hash);
+ if (!conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching Configurator information found");
+ return -1;
+ }
+
+ auth = dpp_reconfig_init(dpp, dpp->msg_ctx, conf, 0);
+ if (!auth)
+ return -1;
+ if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ conn->auth = auth;
+ return dpp_tcp_send_msg(conn, auth->reconfig_req_msg);
+}
+
+
+static int dpp_controller_rx_reconfig_auth_resp(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *conf;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response");
+
+ if (!auth || !auth->reconfig || !auth->configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Reconfig Authentication in progress - drop");
+ return -1;
+ }
+
+ conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+ if (!conf)
+ return -1;
+
+ res = dpp_tcp_send_msg(conn, conf);
+ wpabuf_free(conf);
+ return res;
+}
+
+
+static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg,
+ size_t len)
+{
+ const u8 *pos, *end;
+ u8 type;
+
+ wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP");
+ pos = msg;
+ end = msg + len;
+
+ if (end - pos < DPP_HDR_LEN ||
+ WPA_GET_BE24(pos) != OUI_WFA ||
+ pos[3] != DPP_OUI_TYPE) {
+ wpa_printf(MSG_DEBUG, "DPP: Unrecognized header");
+ return -1;
+ }
+
+ if (pos[4] != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u",
+ pos[4]);
+ return -1;
+ }
+ type = pos[5];
+ wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type);
+ pos += DPP_HDR_LEN;
+
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes",
+ pos, end - pos);
+ if (dpp_check_attrs(pos, end - pos) < 0)
+ return -1;
+
+ if (conn->relay) {
+ wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+ conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr,
+ conn->freq, msg, len);
+ return 0;
+ }
+
+ switch (type) {
+ case DPP_PA_AUTHENTICATION_REQ:
+ return dpp_controller_rx_auth_req(conn, msg, pos, end - pos);
+ case DPP_PA_AUTHENTICATION_RESP:
+ return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos);
+ case DPP_PA_AUTHENTICATION_CONF:
+ return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos);
+ case DPP_PA_CONFIGURATION_RESULT:
+ return dpp_controller_rx_conf_result(conn, msg, pos, end - pos);
+ case DPP_PA_CONNECTION_STATUS_RESULT:
+ return dpp_controller_rx_conn_status_result(conn, msg, pos,
+ end - pos);
+ case DPP_PA_PRESENCE_ANNOUNCEMENT:
+ return dpp_controller_rx_presence_announcement(conn, msg, pos,
+ end - pos);
+ case DPP_PA_RECONFIG_ANNOUNCEMENT:
+ return dpp_controller_rx_reconfig_announcement(conn, msg, pos,
+ end - pos);
+ case DPP_PA_RECONFIG_AUTH_RESP:
+ return dpp_controller_rx_reconfig_auth_resp(conn, msg, pos,
+ end - pos);
+ default:
+ /* TODO: missing messages types */
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported frame subtype %d", type);
+ return -1;
+ }
+}
+
+
+static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
+ size_t len)
+{
+ const u8 *pos, *end, *next;
+ u8 dialog_token;
+ const u8 *adv_proto;
+ u16 slen;
+ struct wpabuf *resp, *buf;
+ struct dpp_authentication *auth = conn->auth;
+
+ if (len < 1 + 2)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Configuration Request over TCP");
+
+ if (!auth || (!conn->ctrl && !auth->configurator) ||
+ (!auth->auth_success && !auth->reconfig_success)) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return -1;
+ }
+
+ pos = msg;
+ end = msg + len;
+
+ dialog_token = *pos++;
+ adv_proto = pos++;
+ slen = *pos++;
+ if (*adv_proto != WLAN_EID_ADV_PROTO ||
+ slen > end - pos || slen < 2)
+ return -1;
+
+ next = pos + slen;
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+ pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+ pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+ return -1;
+
+ pos = next;
+ /* Query Request */
+ if (end - pos < 2)
+ return -1;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (slen > end - pos)
+ return -1;
+
+ resp = dpp_conf_req_rx(auth, pos, slen);
+ if (!resp)
+ return -1;
+
+ buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp));
+ if (!buf) {
+ wpabuf_free(resp);
+ return -1;
+ }
+
+ wpabuf_put_be32(buf, 18 + wpabuf_len(resp));
+
+ wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
+
+ dpp_write_adv_proto(buf);
+ dpp_write_gas_query(buf, resp);
+ wpabuf_free(resp);
+
+ /* Send Config Response over TCP; GAS fragmentation is taken care of by
+ * the Relay */
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = buf;
+ conn->on_tcp_tx_complete_gas_done = 1;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
+{
+ struct dpp_authentication *auth = conn->auth;
+ int res;
+ struct wpabuf *msg;
+ enum dpp_status_error status;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Configuration Response for local stack from TCP");
+
+ if (auth)
+ res = dpp_conf_resp_rx(auth, resp);
+ else
+ res = -1;
+ wpabuf_free(resp);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+ return -1;
+ }
+
+ if (conn->global->process_conf_obj)
+ res = conn->global->process_conf_obj(conn->global->cb_ctx,
+ auth);
+ else
+ res = 0;
+
+ if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+ status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK;
+ msg = dpp_build_conf_result(auth, status);
+ if (!msg)
+ return -1;
+
+ conn->on_tcp_tx_complete_remove = 1;
+ res = dpp_tcp_send_msg(conn, msg);
+ wpabuf_free(msg);
+
+ /* This exchange will be terminated in the TX status handler */
+
+ return res;
+}
+
+
+static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
+ size_t len)
+{
+ struct wpabuf *buf;
+ u8 dialog_token;
+ const u8 *pos, *end, *next, *adv_proto;
+ u16 status, slen;
+
+ if (len < 5 + 2)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Configuration Response over TCP");
+
+ pos = msg;
+ end = msg + len;
+
+ dialog_token = *pos++;
+ status = WPA_GET_LE16(pos);
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status);
+ return -1;
+ }
+ pos += 2;
+ pos += 2; /* ignore GAS Comeback Delay */
+
+ adv_proto = pos++;
+ slen = *pos++;
+ if (*adv_proto != WLAN_EID_ADV_PROTO ||
+ slen > end - pos || slen < 2)
+ return -1;
+
+ next = pos + slen;
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+ pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+ pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+ return -1;
+
+ pos = next;
+ /* Query Response */
+ if (end - pos < 2)
+ return -1;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (slen > end - pos)
+ return -1;
+
+ buf = wpabuf_alloc(slen);
+ if (!buf)
+ return -1;
+ wpabuf_put_data(buf, pos, slen);
+
+ if (!conn->relay &&
+ (!conn->ctrl || (conn->ctrl->allowed_roles & DPP_CAPAB_ENROLLEE)))
+ return dpp_tcp_rx_gas_resp(conn, buf);
+
+ if (!conn->relay) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ wpabuf_free(buf);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+ conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr,
+ dialog_token, 0, buf);
+
+ return 0;
+}
+
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+ int res;
+ const u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)",
+ sd);
+
+ if (conn->msg_len_octets < 4) {
+ u32 msglen;
+
+ res = recv(sd, &conn->msg_len[conn->msg_len_octets],
+ 4 - conn->msg_len_octets, 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: recv failed: %s",
+ strerror(errno));
+ dpp_connection_remove(conn);
+ return;
+ }
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No more data available over TCP");
+ dpp_connection_remove(conn);
+ return;
+ }
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received %d/%d octet(s) of message length field",
+ res, (int) (4 - conn->msg_len_octets));
+ conn->msg_len_octets += res;
+
+ if (conn->msg_len_octets < 4) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Need %d more octets of message length field",
+ (int) (4 - conn->msg_len_octets));
+ return;
+ }
+
+ msglen = WPA_GET_BE32(conn->msg_len);
+ wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen);
+ if (msglen > 65535) {
+ wpa_printf(MSG_INFO, "DPP: Unexpectedly long message");
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ wpabuf_free(conn->msg);
+ conn->msg = wpabuf_alloc(msglen);
+ }
+
+ if (!conn->msg) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No buffer available for receiving the message");
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload",
+ (unsigned int) wpabuf_tailroom(conn->msg));
+
+ res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno));
+ dpp_connection_remove(conn);
+ return;
+ }
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP");
+ dpp_connection_remove(conn);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res);
+ wpabuf_put(conn->msg, res);
+
+ if (wpabuf_tailroom(conn->msg) > 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Need %u more octets of message payload",
+ (unsigned int) wpabuf_tailroom(conn->msg));
+ return;
+ }
+
+ conn->msg_len_octets = 0;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg);
+ if (wpabuf_len(conn->msg) < 1) {
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ pos = wpabuf_head(conn->msg);
+ switch (*pos) {
+ case WLAN_PA_VENDOR_SPECIFIC:
+ if (dpp_controller_rx_action(conn, pos + 1,
+ wpabuf_len(conn->msg) - 1) < 0)
+ dpp_connection_remove(conn);
+ break;
+ case WLAN_PA_GAS_INITIAL_REQ:
+ if (dpp_controller_rx_gas_req(conn, pos + 1,
+ wpabuf_len(conn->msg) - 1) < 0)
+ dpp_connection_remove(conn);
+ break;
+ case WLAN_PA_GAS_INITIAL_RESP:
+ if (dpp_rx_gas_resp(conn, pos + 1,
+ wpabuf_len(conn->msg) - 1) < 0)
+ dpp_connection_remove(conn);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u",
+ *pos);
+ break;
+ }
+}
+
+
+static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_controller *ctrl = eloop_ctx;
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ int fd;
+ struct dpp_connection *conn;
+
+ wpa_printf(MSG_DEBUG, "DPP: New TCP connection");
+
+ fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len);
+ if (fd < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to accept new connection: %s",
+ strerror(errno));
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn)
+ goto fail;
+
+ conn->global = ctrl->global;
+ conn->ctrl = ctrl;
+ conn->sock = fd;
+
+ if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+ dpp_controller_rx, conn, NULL) < 0)
+ goto fail;
+ conn->read_eloop = 1;
+
+ /* TODO: eloop timeout to expire connections that do not complete in
+ * reasonable time */
+ dl_list_add(&ctrl->conn, &conn->list);
+ return;
+
+fail:
+ close(fd);
+ os_free(conn);
+}
+
+
+int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
+ const struct hostapd_ip_addr *addr, int port)
+{
+ struct dpp_connection *conn;
+ struct sockaddr_storage saddr;
+ socklen_t addrlen;
+ const u8 *hdr, *pos, *end;
+ char txt[100];
+
+ wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d",
+ hostapd_ip_txt(addr, txt, sizeof(txt)), port);
+ if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen,
+ addr, port) < 0) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ conn->global = dpp;
+ conn->auth = auth;
+ conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (conn->sock < 0)
+ goto fail;
+
+ if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) {
+ if (errno != EINPROGRESS) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * Continue connecting in the background; eloop will call us
+ * once the connection is ready (or failed).
+ */
+ }
+
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready, conn, NULL) < 0)
+ goto fail;
+ conn->write_eloop = 1;
+
+ hdr = wpabuf_head(auth->req_msg);
+ end = hdr + wpabuf_len(auth->req_msg);
+ hdr += 2; /* skip Category and Actiom */
+ pos = hdr + DPP_HDR_LEN;
+ conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos);
+ if (!conn->msg_out)
+ goto fail;
+ /* Message will be sent in dpp_conn_tx_ready() */
+
+ /* TODO: eloop timeout to clear a connection if it does not complete
+ * properly */
+ dl_list_add(&dpp->tcp_init, &conn->list);
+ return 0;
+fail:
+ dpp_connection_free(conn);
+ return -1;
+}
+
+
+int dpp_controller_start(struct dpp_global *dpp,
+ struct dpp_controller_config *config)
+{
+ struct dpp_controller *ctrl;
+ int on = 1;
+ struct sockaddr_in sin;
+ int port;
+
+ if (!dpp || dpp->controller)
+ return -1;
+
+ ctrl = os_zalloc(sizeof(*ctrl));
+ if (!ctrl)
+ return -1;
+ ctrl->global = dpp;
+ if (config->configurator_params)
+ ctrl->configurator_params =
+ os_strdup(config->configurator_params);
+ dl_list_init(&ctrl->conn);
+ ctrl->allowed_roles = config->allowed_roles;
+ ctrl->qr_mutual = 0;
+
+ ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (ctrl->sock < 0)
+ goto fail;
+
+ if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: setsockopt(SO_REUSEADDR) failed: %s",
+ strerror(errno));
+ /* try to continue anyway */
+ }
+
+ if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) {
+ wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /* TODO: IPv6 */
+ os_memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT;
+ sin.sin_port = htons(port);
+ if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ wpa_printf(MSG_INFO,
+ "DPP: Failed to bind Controller TCP port: %s",
+ strerror(errno));
+ goto fail;
+ }
+ if (listen(ctrl->sock, 10 /* max backlog */) < 0 ||
+ fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 ||
+ eloop_register_sock(ctrl->sock, EVENT_TYPE_READ,
+ dpp_controller_tcp_cb, ctrl, NULL))
+ goto fail;
+
+ dpp->controller = ctrl;
+ wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port);
+ return 0;
+fail:
+ dpp_controller_free(ctrl);
+ return -1;
+}
+
+
+void dpp_controller_stop(struct dpp_global *dpp)
+{
+ if (dpp) {
+ dpp_controller_free(dpp->controller);
+ dpp->controller = NULL;
+ }
+}
+
+
+void dpp_tcp_init_flush(struct dpp_global *dpp)
+{
+ struct dpp_connection *conn, *tmp;
+
+ dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection,
+ list)
+ dpp_connection_remove(conn);
+}
+
+
+static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
+{
+ struct dpp_connection *conn, *tmp;
+
+ dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+ list)
+ dpp_connection_remove(conn);
+ os_free(ctrl);
+}
+
+
+void dpp_relay_flush_controllers(struct dpp_global *dpp)
+{
+ struct dpp_relay_controller *ctrl, *tmp;
+
+ if (!dpp)
+ return;
+
+ dl_list_for_each_safe(ctrl, tmp, &dpp->controllers,
+ struct dpp_relay_controller, list) {
+ dl_list_del(&ctrl->list);
+ dpp_relay_controller_free(ctrl);
+ }
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 42c916b..3c3e875 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -298,6 +298,11 @@
elems->short_ssid_list = pos;
elems->short_ssid_list_len = elen;
break;
+ case WLAN_EID_EXT_HE_6GHZ_BAND_CAP:
+ if (elen < sizeof(struct ieee80211_he_6ghz_band_cap))
+ break;
+ elems->he_6ghz_band_cap = pos;
+ break;
default:
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index e395769..55f7aa6 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -113,6 +113,7 @@
const u8 *he_capabilities;
const u8 *he_operation;
const u8 *short_ssid_list;
+ const u8 *he_6ghz_band_cap;
u8 ssid_len;
u8 supp_rates_len;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 4a5eb16..65cc4df 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -473,6 +473,7 @@
#define WLAN_EID_EXT_SPATIAL_REUSE 39
#define WLAN_EID_EXT_OCV_OCI 54
#define WLAN_EID_EXT_SHORT_SSID_LIST 58
+#define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
#define WLAN_EID_EXT_EDMG_CAPABILITIES 61
#define WLAN_EID_EXT_EDMG_OPERATION 62
#define WLAN_EID_EXT_REJECTED_GROUPS 92
@@ -2155,12 +2156,46 @@
le32 he_oper_params; /* HE Operation Parameters[3] and
* BSS Color Information[1] */
le16 he_mcs_nss_set;
- u8 vht_op_info_chwidth;
- u8 vht_op_info_chan_center_freq_seg0_idx;
- u8 vht_op_info_chan_center_freq_seg1_idx;
- /* Followed by conditional MaxBSSID Indicator subfield (u8) */
+ /* Followed by conditional VHT Operation Information (3 octets),
+ * Max Co-Hosted BSSID Indicator subfield (1 octet), and/or 6 GHz
+ * Operation Information subfield (5 octets). */
} STRUCT_PACKED;
+/* IEEE P802.11ax/D6.0, Figure 9-787k - 6 GHz Operation Information field */
+struct ieee80211_he_6ghz_oper_info {
+ u8 primary_chan;
+ u8 control;
+ u8 chan_center_freq_seg0;
+ u8 chan_center_freq_seg1;
+ u8 min_rate;
+} STRUCT_PACKED;
+
+#define HE_6GHZ_OPER_INFO_CTRL_CHAN_WIDTH_MASK (BIT(0) | BIT(1))
+#define HE_6GHZ_OPER_INFO_CTRL_DUP_BEACON BIT(2)
+
+/* IEEE P802.11ax/D6.0, 9.4.2.261 HE 6 GHz Band Capabilities element */
+struct ieee80211_he_6ghz_band_cap {
+ /* Minimum MPDU Start Spacing B0..B2
+ * Maximum A-MPDU Length Exponent B3..B5
+ * Maximum MPDU Length B6..B7 */
+ u8 a_mpdu_params; /* B0..B7 */
+ u8 info; /* B8..B15 */
+} STRUCT_PACKED;
+
+#define HE_6GHZ_BAND_CAP_MIN_MPDU_START_SPACE_MASK 0x7
+#define HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK 0x7
+#define HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT 3
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_MASK 0x3
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_SHIFT 6
+
+#define HE_6GHZ_BAND_CAP_SMPS_MASK (BIT(1) | BIT(2))
+#define HE_6GHZ_BAND_CAP_SMPS_STATIC 0
+#define HE_6GHZ_BAND_CAP_SMPS_DYNAMIC BIT(1)
+#define HE_6GHZ_BAND_CAP_SMPS_DISABLED (BIT(1) | BIT(2))
+#define HE_6GHZ_BAND_CAP_RD_RESPONDER BIT(3)
+#define HE_6GHZ_BAND_CAP_RX_ANTENNA_PATTERN BIT(4)
+#define HE_6GHZ_BAND_CAP_TX_ANTENNA_PATTERN BIT(5)
+
/*
* IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element
*/
diff --git a/src/common/sae.c b/src/common/sae.c
index 543640d..1b4ec6d 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -169,7 +169,7 @@
* being smaller than prime. */
in_range = const_time_fill_msb((unsigned int) cmp_prime);
/* The algorithm description would skip the next steps if
- * cmp_prime >= 0 (reutnr 0 here), but go through them regardless to
+ * cmp_prime >= 0 (return 0 here), but go through them regardless to
* minimize externally observable differences in behavior. */
x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index eb1861a..1e7498a 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -2305,7 +2305,7 @@
case WPA_CIPHER_TKIP:
return WPA_ALG_TKIP;
case WPA_CIPHER_AES_128_CMAC:
- return WPA_ALG_IGTK;
+ return WPA_ALG_BIP_CMAC_128;
case WPA_CIPHER_BIP_GMAC_128:
return WPA_ALG_BIP_GMAC_128;
case WPA_CIPHER_BIP_GMAC_256:
@@ -2801,6 +2801,15 @@
return 0;
}
+ if (pos[1] >= RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_DPP) {
+ ie->dpp_kde = pos + 2 + RSN_SELECTOR_LEN;
+ ie->dpp_kde_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: DPP KDE in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
return 2;
}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index c0ef689..065dc71 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -124,6 +124,7 @@
#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 WFA_KEY_DATA_TRANSITION_DISABLE RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x20)
+#define WFA_KEY_DATA_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x21)
#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
@@ -362,6 +363,10 @@
#define TRANSITION_DISABLE_WPA3_ENTERPRISE BIT(2)
#define TRANSITION_DISABLE_ENHANCED_OPEN BIT(3)
+/* DPP KDE Flags */
+#define DPP_KDE_PFS_ALLOWED BIT(0)
+#define DPP_KDE_PFS_REQUIRED BIT(1)
+
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
@@ -528,6 +533,8 @@
const u8 *ip_addr_alloc;
const u8 *transition_disable;
size_t transition_disable_len;
+ const u8 *dpp_kde;
+ size_t dpp_kde_len;
const u8 *oci;
size_t oci_len;
const u8 *osen;
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index ca1c35f..354de28 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -190,6 +190,8 @@
#define DPP_EVENT_INTRO "DPP-INTRO "
#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX "
#define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED "
+#define DPP_EVENT_MUD_URL "DPP-MUD-URL "
+#define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT "
/* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 7c7515f..de2b077 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -485,7 +485,7 @@
u8 *data, size_t data_len);
/**
- * crypto_get_random - Generate cryptographically strong pseudy-random bytes
+ * crypto_get_random - Generate cryptographically strong pseudo-random bytes
* @buf: Buffer for data
* @len: Number of bytes to generate
* Returns: 0 on success, -1 on failure
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
index dc68bd6..2e4bf89 100644
--- a/src/crypto/crypto_wolfssl.c
+++ b/src/crypto/crypto_wolfssl.c
@@ -1104,19 +1104,21 @@
{
int ret = 0;
WC_RNG rng;
+ size_t len;
+ u8 *buf;
if (TEST_FAIL())
return -1;
if (wc_InitRng(&rng) != 0)
return -1;
- if (mp_rand_prime((mp_int *) r,
- (mp_count_bits((mp_int *) m) + 7) / 8 * 2,
- &rng, NULL) != 0)
- ret = -1;
- if (ret == 0 &&
+ len = (mp_count_bits((mp_int *) m) + 7) / 8;
+ buf = os_malloc(len);
+ if (!buf || wc_RNG_GenerateBlock(&rng, buf, len) != 0 ||
+ mp_read_unsigned_bin((mp_int *) r, buf, len) != MP_OKAY ||
mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0)
ret = -1;
wc_FreeRng(&rng);
+ bin_clear_free(buf, len);
return ret;
}
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index d222d14..11e6582 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -1741,7 +1741,7 @@
if (!conn)
return NULL;
- wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data));
+ wpa_printf(MSG_DEBUG, "SSL: encrypt: %zu bytes", wpabuf_len(in_data));
wolfssl_reset_out_data(&conn->output);
@@ -1792,7 +1792,7 @@
}
wpabuf_put(buf, res);
- wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf));
+ wpa_printf(MSG_DEBUG, "SSL: decrypt: %zu bytes", wpabuf_len(buf));
return buf;
}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index e3b13bc..1e2e332 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1553,8 +1553,9 @@
* alg - Encryption algorithm
*
* (%WPA_ALG_NONE, %WPA_ALG_WEP, %WPA_ALG_TKIP, %WPA_ALG_CCMP,
- * %WPA_ALG_IGTK, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256,
- * %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256, %WPA_ALG_BIP_CMAC_256);
+ * %WPA_ALG_BIP_AES_CMAC_128, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256,
+ * %WPA_ALG_CCMP_256, %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256,
+ * %WPA_ALG_BIP_CMAC_256);
* %WPA_ALG_NONE clears the key. */
enum wpa_alg alg;
@@ -1658,6 +1659,73 @@
enum key_flag key_flag;
};
+enum wpa_driver_if_type {
+ /**
+ * WPA_IF_STATION - Station mode interface
+ */
+ WPA_IF_STATION,
+
+ /**
+ * WPA_IF_AP_VLAN - AP mode VLAN interface
+ *
+ * This interface shares its address and Beacon frame with the main
+ * BSS.
+ */
+ WPA_IF_AP_VLAN,
+
+ /**
+ * WPA_IF_AP_BSS - AP mode BSS interface
+ *
+ * This interface has its own address and Beacon frame.
+ */
+ WPA_IF_AP_BSS,
+
+ /**
+ * WPA_IF_P2P_GO - P2P Group Owner
+ */
+ WPA_IF_P2P_GO,
+
+ /**
+ * WPA_IF_P2P_CLIENT - P2P Client
+ */
+ WPA_IF_P2P_CLIENT,
+
+ /**
+ * WPA_IF_P2P_GROUP - P2P Group interface (will become either
+ * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
+ */
+ WPA_IF_P2P_GROUP,
+
+ /**
+ * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
+ * abstracted P2P Device function in the driver
+ */
+ WPA_IF_P2P_DEVICE,
+
+ /*
+ * WPA_IF_MESH - Mesh interface
+ */
+ WPA_IF_MESH,
+
+ /*
+ * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+ */
+ WPA_IF_TDLS,
+
+ /*
+ * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+ */
+ WPA_IF_IBSS,
+
+ /*
+ * WPA_IF_NAN - NAN Device
+ */
+ WPA_IF_NAN,
+
+ /* keep last */
+ WPA_IF_MAX
+};
+
/**
* struct wpa_driver_capa - Driver capability information
*/
@@ -1681,6 +1749,7 @@
#define WPA_DRIVER_CAPA_KEY_MGMT_SAE 0x00010000
/** Bitfield of supported key management suites */
unsigned int key_mgmt;
+ unsigned int key_mgmt_iftype[WPA_IF_MAX];
#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001
#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002
@@ -2018,6 +2087,7 @@
u8 vht_opmode;
const struct ieee80211_he_capabilities *he_capab;
size_t he_capab_len;
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
u32 flags; /* bitmask of WPA_STA_* flags */
u32 flags_mask; /* unset bits in flags */
#ifdef CONFIG_MESH
@@ -2045,65 +2115,6 @@
struct mac_address mac_acl[0];
};
-enum wpa_driver_if_type {
- /**
- * WPA_IF_STATION - Station mode interface
- */
- WPA_IF_STATION,
-
- /**
- * WPA_IF_AP_VLAN - AP mode VLAN interface
- *
- * This interface shares its address and Beacon frame with the main
- * BSS.
- */
- WPA_IF_AP_VLAN,
-
- /**
- * WPA_IF_AP_BSS - AP mode BSS interface
- *
- * This interface has its own address and Beacon frame.
- */
- WPA_IF_AP_BSS,
-
- /**
- * WPA_IF_P2P_GO - P2P Group Owner
- */
- WPA_IF_P2P_GO,
-
- /**
- * WPA_IF_P2P_CLIENT - P2P Client
- */
- WPA_IF_P2P_CLIENT,
-
- /**
- * WPA_IF_P2P_GROUP - P2P Group interface (will become either
- * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
- */
- WPA_IF_P2P_GROUP,
-
- /**
- * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
- * abstracted P2P Device function in the driver
- */
- WPA_IF_P2P_DEVICE,
-
- /*
- * WPA_IF_MESH - Mesh interface
- */
- WPA_IF_MESH,
-
- /*
- * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
- */
- WPA_IF_TDLS,
-
- /*
- * WPA_IF_IBSS - IBSS interface (used for pref freq only)
- */
- WPA_IF_IBSS,
-};
-
struct wpa_init_params {
void *global_priv;
const u8 *bssid;
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index d630c3d..9b4166d 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -532,7 +532,7 @@
cipher = IEEE80211_CIPHER_AES_GCM_256;
break;
#endif /* ATH_GCM_SUPPORT */
- case WPA_ALG_IGTK:
+ case WPA_ALG_BIP_CMAC_128:
cipher = IEEE80211_CIPHER_AES_CMAC;
break;
#ifdef ATH_GCM_SUPPORT
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index b4400d7..5adee13 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -1451,6 +1451,7 @@
#define GETPARAM(drv, param, v) \
(((v) = get80211param(drv, param)) != -1)
struct bsd_driver_data *drv;
+ int i;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
@@ -1486,6 +1487,10 @@
if (wpa_driver_bsd_capa(drv))
goto fail;
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
/* Down interface during setup. */
if (bsd_get_iface_flags(drv) < 0)
goto fail;
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 529fc3b..b5fff48 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -2809,6 +2809,7 @@
{
struct wpa_driver_ndis_data *drv;
u32 mode;
+ int i;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
@@ -2855,6 +2856,11 @@
}
wpa_driver_ndis_get_capability(drv);
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
+
/* Make sure that the driver does not have any obsolete PMKID entries.
*/
wpa_driver_ndis_flush_pmkid(drv);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index d48f8cb..8be593a 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -2328,10 +2328,19 @@
/* FT Action frames */
if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
ret = -1;
- else
+ else if (!drv->has_driver_key_mgmt) {
+ int i;
+
+ /* Update supported AKMs only if the driver doesn't advertize
+ * any AKM capabilities. */
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+ }
+
/* WNM - BSS Transition Management Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
ret = -1;
@@ -2918,7 +2927,7 @@
return RSN_CIPHER_SUITE_CCMP_256;
case WPA_ALG_GCMP_256:
return RSN_CIPHER_SUITE_GCMP_256;
- case WPA_ALG_IGTK:
+ case WPA_ALG_BIP_CMAC_128:
return RSN_CIPHER_SUITE_AES_128_CMAC;
case WPA_ALG_BIP_GMAC_128:
return RSN_CIPHER_SUITE_BIP_GMAC_128;
@@ -3275,10 +3284,7 @@
goto fail2;
if (!key_msg ||
nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
- nla_put_flag(key_msg, (alg == WPA_ALG_IGTK ||
- alg == WPA_ALG_BIP_GMAC_128 ||
- alg == WPA_ALG_BIP_GMAC_256 ||
- alg == WPA_ALG_BIP_CMAC_256) ?
+ nla_put_flag(key_msg, wpa_alg_bip(alg) ?
(key_idx == 6 || key_idx == 7 ?
NL80211_KEY_DEFAULT_BEACON :
NL80211_KEY_DEFAULT_MGMT) :
@@ -3347,7 +3353,7 @@
if (!suite)
return -1;
- if (defkey && alg == WPA_ALG_IGTK) {
+ if (defkey && wpa_alg_bip(alg)) {
if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
return -1;
} else if (defkey) {
@@ -4323,7 +4329,7 @@
num_suites = wpa_key_mgmt_to_suites(params->key_mgmt_suites,
suites, ARRAY_SIZE(suites));
if (num_suites > NL80211_MAX_NR_AKM_SUITES)
- wpa_printf(MSG_WARNING,
+ wpa_printf(MSG_DEBUG,
"nl80211: Not enough room for all AKM suites (num_suites=%d > NL80211_MAX_NR_AKM_SUITES)",
num_suites);
else if (num_suites &&
@@ -5056,6 +5062,10 @@
return "P2P_GO";
case NL80211_IFTYPE_P2P_DEVICE:
return "P2P_DEVICE";
+ case NL80211_IFTYPE_OCB:
+ return "OCB";
+ case NL80211_IFTYPE_NAN:
+ return "NAN";
default:
return "unknown";
}
@@ -6261,6 +6271,20 @@
}
}
+ if (i == 0 && was_ap && !is_ap_interface(nlmode) &&
+ bss->brname[0] &&
+ (bss->added_if_into_bridge || bss->already_in_bridge)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Remove AP interface %s temporarily from the bridge %s to allow its mode to be set to STATION",
+ bss->ifname, bss->brname);
+ if (linux_br_del_if(drv->global->ioctl_sock,
+ bss->brname, bss->ifname) < 0)
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to remove interface %s from bridge %s: %s",
+ bss->ifname, bss->brname,
+ strerror(errno));
+ }
+
/* Try to set the mode again while the interface is down */
mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
if (mode_switch_res == -EBUSY) {
@@ -6333,6 +6357,29 @@
}
+void nl80211_restore_ap_mode(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int was_ap = is_ap_interface(drv->nlmode);
+
+ wpa_driver_nl80211_set_mode(bss, drv->ap_scan_as_station);
+ if (!was_ap && is_ap_interface(drv->ap_scan_as_station) &&
+ bss->brname[0] &&
+ (bss->added_if_into_bridge || bss->already_in_bridge)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add AP interface %s back into the bridge %s",
+ bss->ifname, bss->brname);
+ if (linux_br_add_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0) {
+ wpa_printf(MSG_WARNING,
+ "nl80211: Failed to add interface %s into bridge %s: %s",
+ bss->ifname, bss->brname, strerror(errno));
+ }
+ }
+ drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+}
+
+
int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
enum nl80211_iftype nlmode)
{
@@ -7742,7 +7789,8 @@
struct ieee80211_hdr *hdr;
int offchanok = 1;
- if (is_ap_interface(drv->nlmode) && (int) freq == bss->freq)
+ if (is_ap_interface(drv->nlmode) && (int) freq == bss->freq &&
+ bss->beacon_set)
offchanok = 0;
wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index dc80a17..895f9d7 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -111,6 +111,7 @@
unsigned int num_iface_ext_capa;
int has_capability;
+ int has_driver_key_mgmt;
int operstate;
@@ -169,7 +170,6 @@
unsigned int set_wifi_conf_vendor_cmd_avail:1;
unsigned int fetch_bss_trans_status:1;
unsigned int roam_vendor_cmd_avail:1;
- unsigned int get_supported_akm_suites_avail:1;
unsigned int add_sta_node_vendor_cmd_avail:1;
unsigned int control_port_ap:1;
unsigned int multicast_registrations:1;
@@ -275,6 +275,8 @@
const char * nl80211_iftype_str(enum nl80211_iftype mode);
+void nl80211_restore_ap_mode(struct i802_bss *bss);
+
#ifdef ANDROID
int android_nl_socket_set_nonblocking(struct nl_sock *handle);
int android_pno_start(struct i802_bss *bss,
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index f997577..a3341a0 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -79,6 +79,8 @@
unsigned int mac_addr_rand_scan_supported:1;
unsigned int mac_addr_rand_sched_scan_supported:1;
unsigned int update_ft_ies_supported:1;
+ unsigned int has_key_mgmt:1;
+ unsigned int has_key_mgmt_iftype:1;
};
@@ -252,6 +254,158 @@
}
+static unsigned int get_akm_suites_info(struct nlattr *tb)
+{
+ int i, num;
+ unsigned int key_mgmt = 0;
+ u32 *akms;
+
+ if (!tb)
+ return 0;
+
+ num = nla_len(tb) / sizeof(u32);
+ akms = nla_data(tb);
+ for (i = 0; i < num; i++) {
+ switch (akms[i]) {
+ case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+ break;
+ case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_PSK:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+ break;
+ case RSN_AUTH_KEY_MGMT_OWE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
+ break;
+ case RSN_AUTH_KEY_MGMT_DPP:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_SAE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+ break;
+ }
+ }
+
+ return key_mgmt;
+}
+
+
+static void get_iface_akm_suites_info(struct wiphy_info_data *info,
+ struct nlattr *nl_akms)
+{
+ struct nlattr *tb[NL80211_IFTYPE_AKM_ATTR_MAX + 1];
+ struct nlattr *nl_iftype;
+ unsigned int key_mgmt;
+ int i;
+
+ if (!nl_akms)
+ return;
+
+ nla_parse(tb, NL80211_IFTYPE_AKM_ATTR_MAX,
+ nla_data(nl_akms), nla_len(nl_akms), NULL);
+
+ if (!tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES] ||
+ !tb[NL80211_IFTYPE_AKM_ATTR_SUITES])
+ return;
+
+ info->has_key_mgmt_iftype = 1;
+ key_mgmt = get_akm_suites_info(tb[NL80211_IFTYPE_AKM_ATTR_SUITES]);
+
+ nla_for_each_nested(nl_iftype, tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES], i) {
+ switch (nla_type(nl_iftype)) {
+ case NL80211_IFTYPE_ADHOC:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_IBSS] = key_mgmt;
+ break;
+ case NL80211_IFTYPE_STATION:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_STATION] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_AP:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_AP_BSS] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_AP_VLAN] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_MESH] = key_mgmt;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_CLIENT] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_GO] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_DEVICE] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_NAN:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_NAN] = key_mgmt;
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: %s supported key_mgmt 0x%x",
+ nl80211_iftype_str(nla_type(nl_iftype)),
+ key_mgmt);
+ }
+}
+
+
+static void wiphy_info_iftype_akm_suites(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ struct nlattr *nl_if;
+ int rem_if;
+
+ if (!tb)
+ return;
+
+ nla_for_each_nested(nl_if, tb, rem_if)
+ get_iface_akm_suites_info(info, nl_if);
+}
+
+
+static void wiphy_info_akm_suites(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ if (!tb)
+ return;
+
+ info->has_key_mgmt = 1;
+ info->capa->key_mgmt = get_akm_suites_info(tb);
+ wpa_printf(MSG_DEBUG, "nl80211: wiphy supported key_mgmt 0x%x",
+ info->capa->key_mgmt);
+}
+
+
static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
struct nlattr *tb)
{
@@ -696,6 +850,8 @@
wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
+ wiphy_info_akm_suites(info, tb[NL80211_ATTR_AKM_SUITES]);
+ wiphy_info_iftype_akm_suites(info, tb[NL80211_ATTR_IFTYPE_AKM_SUITES]);
if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
@@ -809,9 +965,6 @@
case QCA_NL80211_VENDOR_SUBCMD_ROAM:
drv->roam_vendor_cmd_avail = 1;
break;
- case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS:
- drv->get_supported_akm_suites_avail = 1;
- break;
case QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE:
drv->add_sta_node_vendor_cmd_avail = 1;
break;
@@ -990,126 +1143,6 @@
}
-static unsigned int get_akm_suites_info(struct nlattr *tb)
-{
- int i, num;
- unsigned int key_mgmt = 0;
- u32 *akms;
-
- if (!tb)
- return 0;
-
- num = nla_len(tb) / sizeof(u32);
- akms = nla_data(tb);
- for (i = 0; i < num; i++) {
- u32 a = akms[i];
-
- wpa_printf(MSG_DEBUG,
- "nl80211: Supported AKM %02x-%02x-%02x:%u",
- a >> 24, (a >> 16) & 0xff,
- (a >> 8) & 0xff, a & 0xff);
- switch (a) {
- case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
- break;
- case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
- break;
- case RSN_AUTH_KEY_MGMT_FT_802_1X:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
- break;
- case RSN_AUTH_KEY_MGMT_FT_PSK:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
- break;
- case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
- break;
- case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
- break;
- case RSN_AUTH_KEY_MGMT_OWE:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
- break;
- case RSN_AUTH_KEY_MGMT_DPP:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
- break;
- case RSN_AUTH_KEY_MGMT_FILS_SHA256:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
- break;
- case RSN_AUTH_KEY_MGMT_FILS_SHA384:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
- break;
- case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
- break;
- case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
- break;
- case RSN_AUTH_KEY_MGMT_SAE:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
- break;
- }
- }
-
- return key_mgmt;
-}
-
-
-static int get_akm_suites_handler(struct nl_msg *msg, void *arg)
-{
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- unsigned int *key_mgmt = arg;
-
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
-
- if (tb[NL80211_ATTR_VENDOR_DATA]) {
- struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
- struct nlattr *tb_data[NL80211_ATTR_MAX + 1];
-
- nla_parse(tb_data, NL80211_ATTR_MAX,
- nla_data(nl_vend), nla_len(nl_vend), NULL);
-
- *key_mgmt =
- get_akm_suites_info(tb_data[NL80211_ATTR_AKM_SUITES]);
- }
-
- return NL_SKIP;
-}
-
-
-static int qca_nl80211_get_akm_suites(struct wpa_driver_nl80211_data *drv)
-{
- struct nl_msg *msg;
- unsigned int key_mgmt = 0;
- int ret;
-
- if (!drv->get_supported_akm_suites_avail)
- return -1;
-
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
- QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS)) {
- nlmsg_free(msg);
- return -1;
- }
-
- ret = send_and_recv_msgs(drv, msg, get_akm_suites_handler, &key_mgmt);
- if (!ret) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Replace capa.key_mgmt based on driver advertised capabilities: 0x%x",
- key_mgmt);
- drv->capa.key_mgmt = key_mgmt;
- }
-
- return ret;
-}
-
-
struct features_info {
u8 *flags;
size_t flags_len;
@@ -1221,6 +1254,8 @@
int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
{
struct wiphy_info_data info;
+ int i;
+
if (wpa_driver_nl80211_get_info(drv, &info))
return -1;
@@ -1228,33 +1263,62 @@
return -1;
drv->has_capability = 1;
- drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
- WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
- WPA_DRIVER_CAPA_KEY_MGMT_OWE |
- WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+ drv->has_driver_key_mgmt = info.has_key_mgmt | info.has_key_mgmt_iftype;
- if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
- WPA_DRIVER_CAPA_ENC_GCMP_256))
- drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+ /* Fallback to hardcoded defaults if the driver does nott advertize any
+ * AKM capabilities. */
+ if (!drv->has_driver_key_mgmt) {
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
+ WPA_DRIVER_CAPA_KEY_MGMT_OWE |
+ WPA_DRIVER_CAPA_KEY_MGMT_DPP;
- if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
- drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
- WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
- WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
- WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
- WPA_DRIVER_CAPA_KEY_MGMT_SAE;
- else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
- drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
- WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
+ WPA_DRIVER_CAPA_ENC_GCMP_256))
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
-#ifdef CONFIG_DRIVER_NL80211_QCA
- /* Override drv->capa.key_mgmt based on driver advertised capability
- * constraints, if available. */
- qca_nl80211_get_akm_suites(drv);
-#endif /* CONFIG_DRIVER_NL80211_QCA */
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+ else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ }
+
+ if (!info.has_key_mgmt_iftype) {
+ /* If the driver does not advertize per interface AKM
+ * capabilities, consider all interfaces to support default AKMs
+ * in key_mgmt. */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+ } else if (info.has_key_mgmt_iftype && !info.has_key_mgmt) {
+ /* If the driver advertizes only per interface supported AKMs
+ * but does not advertize per wiphy AKM capabilities, consider
+ * the default key_mgmt as a mask of per interface supported
+ * AKMs. */
+ drv->capa.key_mgmt = 0;
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt |= drv->capa.key_mgmt_iftype[i];
+ } else if (info.has_key_mgmt_iftype && info.has_key_mgmt) {
+ /* If the driver advertizes AKM capabilities both per wiphy and
+ * per interface, consider the interfaces for which per
+ * interface AKM capabilities were not received to support the
+ * default key_mgmt capabilities.
+ */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ if (!drv->capa.key_mgmt_iftype[i])
+ drv->capa.key_mgmt_iftype[i] =
+ drv->capa.key_mgmt;
+ }
drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 1152312..6a2de1f 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -2581,11 +2581,8 @@
if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
(cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
- cmd == NL80211_CMD_SCAN_ABORTED)) {
- wpa_driver_nl80211_set_mode(drv->first_bss,
- drv->ap_scan_as_station);
- drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
- }
+ cmd == NL80211_CMD_SCAN_ABORTED))
+ nl80211_restore_ap_mode(bss);
switch (cmd) {
case NL80211_CMD_TRIGGER_SCAN:
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 17e8b2c..dc91a29 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -166,11 +166,8 @@
wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan");
- if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
- wpa_driver_nl80211_set_mode(drv->first_bss,
- drv->ap_scan_as_station);
- drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
- }
+ if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED)
+ nl80211_restore_ap_mode(drv->first_bss);
wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results");
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 978e1cf..0f0ad1f 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -968,6 +968,7 @@
static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
{
int send_rfkill_event = 0;
+ int i;
if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
if (rfkill_is_blocked(drv->rfkill)) {
@@ -996,6 +997,10 @@
wpa_driver_wext_get_range(drv);
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
/*
* Unlock the driver's BSSID and force to a random SSID to clear any
* previous association the driver might have when the supplicant
@@ -1768,7 +1773,7 @@
case WPA_ALG_CCMP:
ext->alg = IW_ENCODE_ALG_CCMP;
break;
- case WPA_ALG_IGTK:
+ case WPA_ALG_BIP_CMAC_128:
ext->alg = IW_ENCODE_ALG_AES_CMAC;
break;
default:
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index e3a57e7..1c11cb6 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -56,6 +56,7 @@
}
+PRINTF_FORMAT(4, 5)
static void eapol_auth_vlogger(struct eapol_authenticator *eapol,
const u8 *addr, eapol_logger_level level,
const char *fmt, ...)
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index 6c49fa9..83faa05 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -28,6 +28,7 @@
*/
u8 fils_cache_id[2];
unsigned int fils_cache_id_set:1;
+ unsigned int dpp_pfs:1;
os_time_t reauth_time;
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 33e7f41..b9b1f0f 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -21,6 +21,7 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/ocv.h"
+#include "common/dpp.h"
#include "eap_common/eap_defs.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "drivers/driver.h"
@@ -737,7 +738,8 @@
kde_buf = os_malloc(kde_len +
2 + RSN_SELECTOR_LEN + 3 +
sm->assoc_rsnxe_len +
- 2 + RSN_SELECTOR_LEN + 1);
+ 2 + RSN_SELECTOR_LEN + 1 +
+ 2 + RSN_SELECTOR_LEN + 2);
if (!kde_buf)
goto failed;
os_memcpy(kde_buf, kde, kde_len);
@@ -782,6 +784,27 @@
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && sm->key_mgmt == WPA_KEY_MGMT_DPP) {
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "DPP: Add DPP KDE into EAPOL-Key 2/4");
+ pos = kde + kde_len;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 2;
+ RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_DPP);
+ pos += RSN_SELECTOR_LEN;
+ *pos++ = DPP_VERSION; /* Protocol Version */
+ *pos = 0; /* Flags */
+ if (sm->dpp_pfs == 0)
+ *pos |= DPP_KDE_PFS_ALLOWED;
+ else if (sm->dpp_pfs == 1)
+ *pos |= DPP_KDE_PFS_ALLOWED | DPP_KDE_PFS_REQUIRED;
+ pos++;
+ kde_len = pos - kde;
+ }
+#endif /* CONFIG_DPP2 */
+
if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
kde, kde_len, ptk) < 0)
goto failed;
@@ -1693,6 +1716,20 @@
}
#endif /* CONFIG_OCV */
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && ie.dpp_kde) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: peer Protocol Version %u Flags 0x%x",
+ ie.dpp_kde[0], ie.dpp_kde[1]);
+ if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_pfs != 2 &&
+ (ie.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) && !sm->dpp_z) {
+ wpa_printf(MSG_INFO,
+ "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
+ goto failed;
+ }
+ }
+#endif /* CONFIG_DPP2 */
+
if (sm->use_ext_key_id &&
wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX))
goto failed;
@@ -3255,6 +3292,11 @@
sm->ft_rsnxe_used = value;
break;
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_DPP2
+ case WPA_PARAM_DPP_PFS:
+ sm->dpp_pfs = value;
+ break;
+#endif /* CONFIG_DPP2 */
default:
break;
}
@@ -3292,6 +3334,15 @@
return pos - buf;
pos += ret;
+#ifdef CONFIG_DPP2
+ if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+ ret = os_snprintf(pos, end - pos, "dpp_pfs=1\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_DPP2 */
+
if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) {
struct wpa_ie_data rsn;
if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn)
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 0986c6c..dfc156b 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -107,6 +107,7 @@
WPA_PARAM_EXT_KEY_ID,
WPA_PARAM_USE_EXT_KEY_ID,
WPA_PARAM_FT_RSNXE_USED,
+ WPA_PARAM_DPP_PFS,
};
struct rsn_supp_config {
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 497d128..f7d9f62 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -183,6 +183,7 @@
#ifdef CONFIG_DPP2
struct wpabuf *dpp_z;
+ int dpp_pfs;
#endif /* CONFIG_DPP2 */
};
diff --git a/src/utils/common.h b/src/utils/common.h
index 8e5cfe1..45f72bb 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -482,7 +482,8 @@
void inc_byte_array(u8 *counter, size_t len);
void buf_shift_right(u8 *buf, size_t len, size_t bits);
void wpa_get_ntp_timestamp(u8 *buf);
-int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
+ PRINTF_FORMAT(3, 4);
int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
char sep);
int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);